/* SWIFT4DATA diagram system.
   Source of truth: tools/site_css/s4d-diagrams.css
   Mirrored into the site's Custom CSS field and assets/css/site.css.
   Mobile first: vertical stack by default, horizontal from UIkit's `s` breakpoint. */

:root {
  /* Brand colors, sampled directly from content/s4d_logo_norm_large.png. */
  --s4d-navy: #0A0A48;
  --s4d-orange: #FFA83E;
  --s4d-yellow: #FDBD35;
  --s4d-teal: #0096DB; /* blue-teal from the hexagon logo background, for reference/accent use */

  /* 2026-09-28 (client: "the dark/light colors of the S4D logo"): the
     "By hand, today" / "With your own tool" step cards and the outcome
     figures below the diagram (.s4d-figure__value/__caption, which also
     read --s4d-auto/--s4d-manual -- see the comment on those rules further
     down) were warm brown/beige vs. teal, an arbitrary color-coding
     unrelated to the brand. Recolored to navy (the old, less-desirable
     "manual" way) vs. brand teal (the better, automated way), matching the
     logo's own two-tone palette. --s4d-structure (the "auto" step's actual
     text/arrow color, #004d65 -- a separate darker teal used for
     legibility/hierarchy, not one of the four vars this pass touches) is
     left as-is; it already reads as part of the same teal family. */
  --s4d-manual: var(--s4d-navy);
  --s4d-manual-bg: color-mix(in srgb, var(--s4d-navy) 8%, white);
  --s4d-auto: var(--s4d-teal);
  --s4d-auto-bg: color-mix(in srgb, var(--s4d-teal) 10%, white);
  --s4d-structure: #004d65;
}

/* Dark-mode variants of the tokens above -- same three-block pattern
   (system-preference fallback, explicit dark, explicit light) as the
   surface/text tokens in tools/site_css/s4d-site.css. Needed because a flat
   navy (--s4d-manual, #0A0A48) as TEXT is close to unreadable on the dark
   theme's near-black surfaces (#14141f/#1c1c2a) -- the light-mode value only
   works against a light card background. --s4d-manual lightens toward white
   for contrast; the two *-bg tokens mix toward the dark card surface
   (#1c1c2a, s4d-site.css's --s4d-card-bg dark value) instead of toward white,
   so the cards read as tinted-dark rather than blown-out light boxes on a
   dark page. --s4d-auto (teal) is bright enough to stay legible on both
   themes unchanged.

   --s4d-structure (the "auto" step's own text/icon/arrow color, light-mode
   #004d65 -- a dark teal, unrelated to the 4 vars named in the brief) DOES
   need a dark-mode override too, found by actually looking at the rendered
   result rather than stopping at the brief's literal 4-variable list: once
   --s4d-auto-bg became a saturated dark-teal tint (above) instead of the
   old, nearly-white #e6f4f9, the existing dark #004d65 text sat on a
   background close to its own luminance -- verified close to unreadable in
   a screenshot at 1440px, dark theme. Lightened for dark mode only; the
   light-mode value is untouched and still reads fine there. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --s4d-manual: color-mix(in srgb, var(--s4d-fill) 35%, white);
    --s4d-manual-bg: color-mix(in srgb, var(--s4d-fill) 30%, #1c1c2a);
    --s4d-auto-bg: color-mix(in srgb, var(--s4d-teal) 22%, #1c1c2a);
    --s4d-structure: color-mix(in srgb, var(--s4d-teal) 60%, white);
  }
}

:root[data-theme="dark"] {
  --s4d-manual: color-mix(in srgb, var(--s4d-fill) 35%, white);
  --s4d-manual-bg: color-mix(in srgb, var(--s4d-fill) 30%, #1c1c2a);
  --s4d-auto-bg: color-mix(in srgb, var(--s4d-teal) 22%, #1c1c2a);
  --s4d-structure: color-mix(in srgb, var(--s4d-teal) 60%, white);
}

:root[data-theme="light"] {
  --s4d-manual: var(--s4d-navy);
  --s4d-manual-bg: color-mix(in srgb, var(--s4d-navy) 8%, white);
  --s4d-auto-bg: color-mix(in srgb, var(--s4d-teal) 10%, white);
  --s4d-structure: #004d65;
}

.s4d-flow {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.s4d-flow__step {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: 1 1 0;
  /* Bumped from 0.875rem 1rem (2026-09-28, "looks boring" pass) to match
     the larger label text/icon below -- otherwise the enlarged content
     starts to crowd the card's own edges. */
  padding: 1.125rem 1.25rem;
  border-radius: 0.5rem;
  border: 1.5px solid transparent;
  /* Bumped from 0.9375rem (client: "too small") -- 1.125rem verified in the
     browser at desktop and 375px mobile: still balanced against the
     diagram's arrows/figures, no wrapping onto a third line at mobile
     width. */
  font-size: 1.125rem;
  line-height: 1.35;
}

.s4d-step--manual {
  background: var(--s4d-manual-bg);
  /* Bumped from 0.35 to 0.6 alpha (and 1px to 1.5px above) so this reads as
     a bordered card instead of blending into its own light background --
     part of the sitewide "cards need a visible border" pass. Kept on the
     existing manual/auto hues rather than switching to navy, since that hue
     is this diagram's own established color-coding (manual vs. automated
     step), not a brand-palette element. */
  border-color: rgba(138, 129, 120, 0.6);
  color: var(--s4d-manual);
}

.s4d-step--auto {
  background: var(--s4d-auto-bg);
  border-color: rgba(0, 140, 180, 0.6);
  color: var(--s4d-structure);
}

.s4d-step__icon {
  flex: 0 0 auto;
  /* Bumped from 1.5rem (2026-09-28, proportionate to the 0.9375rem ->
     1.125rem label bump above) -- checked at 375px mobile width that the
     larger icon doesn't crowd the label text. */
  width: 1.875rem;
  height: 1.875rem;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.s4d-step__label { flex: 1 1 auto; }

.s4d-flow__arrow {
  align-self: center;
  width: 1rem;
  height: 1rem;
  stroke: var(--s4d-structure);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform: rotate(90deg);
  opacity: 0.55;
}

/* Time box beside each route (owner, 2026-10-21): how long the route takes. Thick border for
   emphasis; manual is grey (the slower, heavier way), auto uses the brand orange that already
   emphasises text. Stacked under the steps below 960px,
   to the right of them from 960px. */
.s4d-flowrow { display: flex; flex-direction: column; gap: 0.75rem; }
.s4d-flowrow .s4d-flow { flex: 1 1 auto; }

/* The bold arrow from the last step to the time box (owner, 2026-10-21). Points down while the
   route is stacked (<960px), right from 960px. */
.s4d-flow__arrow--strong {
  align-self: center;
  width: 1.75rem;
  height: 1.75rem;
  stroke-width: 3.5;
  opacity: 1;
  transform: rotate(90deg);
}

/* Phones (<640px): narrower, centred boxes with the icon above the text. */
@media (max-width: 639.98px) {
  .s4d-flowrow .s4d-flow__step,
  .s4d-flowrow .s4d-time {
    width: min(100%, 17rem);
    margin-left: auto;
    margin-right: auto;
  }
  .s4d-flowrow .s4d-flow__step {
    flex-direction: column;
    justify-content: center;
    gap: 0.5rem;
    text-align: center;
  }
  .s4d-flowrow .s4d-step__label { flex: 0 1 auto; }
}

.s4d-time {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 1rem 1.25rem;
  border-radius: 0.5rem;
  border: 4px solid;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.35;
  text-align: center;
}

/* Colours (owner, 2026-10-21, third pass): the boxes follow the theme like the step cards --
   light fill and dark text in light theme, dark fill and light text in dark theme. Manual is
   grey, auto is orange; the thick edge is the stronger shade of each. Same three-block pattern
   as the tokens at the top of this file. */
.s4d-time--manual {
  --s4d-time-bg: #e3e3e8;
  --s4d-time-fg: #3a3a44;
  --s4d-time-edge: #7a7a86;
}

.s4d-time--auto {
  --s4d-time-bg: color-mix(in srgb, var(--s4d-orange) 24%, white);
  --s4d-time-fg: #7a3f00;
  --s4d-time-edge: var(--s4d-orange);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-time--manual { --s4d-time-bg: #2e2e3a; --s4d-time-fg: #d8d8e0; --s4d-time-edge: #8d8d9a; }
  :root:not([data-theme]) .s4d-time--auto { --s4d-time-bg: color-mix(in srgb, var(--s4d-orange) 22%, #1c1c2a); --s4d-time-fg: #ffc47a; --s4d-time-edge: var(--s4d-orange); }
}

:root[data-theme="dark"] .s4d-time--manual { --s4d-time-bg: #2e2e3a; --s4d-time-fg: #d8d8e0; --s4d-time-edge: #8d8d9a; }
:root[data-theme="dark"] .s4d-time--auto { --s4d-time-bg: color-mix(in srgb, var(--s4d-orange) 22%, #1c1c2a); --s4d-time-fg: #ffc47a; --s4d-time-edge: var(--s4d-orange); }

.s4d-time {
  background: var(--s4d-time-bg);
  border-color: var(--s4d-time-edge);
  color: var(--s4d-time-fg);
}

/* From 960px a route is one grid row: four steps with an arrow slot between each, and the time
   box in the fifth column, so all five boxes get the same width (owner, 2026-10-21). The <ul>
   is display:contents so its steps and arrows become the grid items. */
@media (min-width: 960px) {
  .s4d-flowrow {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr) 1.5rem) minmax(0, 1fr) 1.5rem minmax(0, 1fr);
    align-items: stretch;
  }
  .s4d-flowrow .s4d-flow { display: contents; }
  .s4d-flowrow .s4d-flow__arrow { justify-self: center; align-self: center; }
  .s4d-flowrow .s4d-flow__arrow--strong { transform: none; }
  .s4d-flowrow .s4d-time { grid-column: 9; }
  .s4d-flowrow .s4d-flow__step { padding-left: 0.75rem; padding-right: 0.75rem; }
}

/* On wide screens the diagram breaks out of the text column so every step label fits on one
   line; narrower screens let the labels wrap. */
@media (min-width: 1200px) {
  .s4d-diagram--before-after {
    position: relative;
    left: 50%;
    width: min(1440px, calc(100vw - 3rem));
    transform: translateX(-50%);
  }
}

@media (min-width: 1480px) {
  .s4d-flowrow .s4d-step__label { white-space: nowrap; }
}

.s4d-figure { text-align: center; }
.s4d-figure__value {
  display: block;
  font-size: 2.25rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--s4d-auto);
}
.s4d-figure__caption {
  display: block;
  margin-top: 0.25rem;
  /* Bumped from 0.875rem (client: "too small"). */
  font-size: 1.0625rem;
  color: var(--s4d-manual);
}

@media (min-width: 640px) {
  .s4d-flow { flex-direction: row; align-items: stretch; }
  .s4d-flow__step { flex-direction: column; text-align: center; gap: 0.5rem; }
  .s4d-flow__arrow { transform: none; }
}

@media (min-width: 640px) and (max-width: 959.98px) {
  .s4d-flow__arrow--strong { transform: rotate(90deg); }
}

@media (prefers-reduced-motion: no-preference) {
  .s4d-flow__step { transition: border-color 200ms ease; }
}

/* Title + description inside a flow step (workflow and how-it-runs diagrams). */
.s4d-step__title { display: block; font-weight: 600; }
.s4d-step__body { display: block; margin-top: 0.125rem; font-size: 0.875rem; }

/* Five-step flow (DESKWORKS workflow): a row of five plus four arrows is too tight
   below 960px, so the steps wrap 3+2 there and the arrows are dropped. */
@media (min-width: 640px) and (max-width: 959px) {
  .s4d-flow--five { flex-wrap: wrap; }
  .s4d-flow--five .s4d-flow__step { flex: 1 1 28%; }
  .s4d-flow--five .s4d-flow__arrow { display: none; }
}

/* Typical project timeline (section_5_7b_timeline in tools/build_home.py).
   Two inline SVGs: horizontal (>= 960px) and vertical; uk-visible@m /
   uk-hidden@m show one. Colours are theme tokens only, so light, prefers-dark
   and [data-theme] all work without extra rules. */
.s4d-timeline { width: min(62.5rem, calc(100vw - 3rem)); margin: 1.5rem auto 0; }
.s4d-timeline__view--v { width: min(26rem, 100%); margin-left: auto; margin-right: auto; }
.s4d-tl__svg { display: block; width: 100%; height: auto; overflow: visible; }
.s4d-tl__line { stroke-width: 2.5; fill: none; }
.s4d-tl__span { stroke-width: 1.5; }
.s4d-tl__span--a { fill: color-mix(in srgb, var(--s4d-orange) 30%, transparent); stroke: var(--s4d-orange); }
.s4d-tl__span--b { fill: color-mix(in srgb, var(--s4d-teal) 28%, transparent); stroke: var(--s4d-teal); }
.s4d-tl__stem { fill: none; stroke: var(--s4d-text); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.s4d-tl__dot { fill: var(--s4d-text); }
.s4d-tl__num { font-size: 16px; font-weight: 700; fill: var(--s4d-text); }
.s4d-tl__cap { font-size: 15px; fill: var(--s4d-text); opacity: 0.75; }
.s4d-tl__title { font-size: 18px; font-weight: 700; fill: var(--s4d-text); }
.s4d-tl__sub { font-size: 16px; fill: var(--s4d-text); }
.s4d-timeline__view--v .s4d-tl__title { font-size: 18px; }
.s4d-timeline__view--v .s4d-tl__sub { font-size: 16px; }
.s4d-timeline__note { max-width: 44rem; margin: 1rem auto 0; font-size: 1.0625rem; }

/* Only armed by JS (so no-JS shows everything); skipped for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .s4d-timeline--armed .s4d-tl__line { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .s4d-timeline--armed .s4d-tl__span,
  .s4d-timeline--armed .s4d-tl__num,
  .s4d-timeline--armed .s4d-tl__cap,
  .s4d-timeline--armed .s4d-tl__mark { opacity: 0; }
  .s4d-timeline--play .s4d-tl__line { stroke-dashoffset: 0; transition: stroke-dashoffset 1.2s ease-out; }
  .s4d-timeline--play .s4d-tl__span,
  .s4d-timeline--play .s4d-tl__num,
  .s4d-timeline--play .s4d-tl__cap { opacity: 1; transition: opacity 0.8s ease 0.3s; }
  .s4d-timeline--play .s4d-tl__mark {
    opacity: 1;
    transition: opacity 0.6s ease calc(1.3s + var(--i) * 0.45s);
  }
}

/* SWIFT4DATA site-wide extras (not diagram-specific).
   Source of truth: tools/site_css/s4d-site.css
   Currently: the homepage hero's scroll-down hint, the switched-off
   testimonials scaffold (spec 5.6 -- proof strip went live 2026-09-29),
   brand-color-driven button and card styling, and the light/dark theme
   variable layer + toggle button (see the block below starting at the
   --s4d-bg custom properties). */

/* sectiontoggle "false" only strips uk-section's styling classes -- the
   <section> still renders (snippets/layout/layout.php:11-34) -- so this rule
   is what actually keeps a pending section off the page until real content
   (testimonial quotes I4) replaces the placeholder and the class is dropped. */
.s4d-pending {
  display: none;
}

/* Every full-viewport section now carries its own scroll hint (Task 3,
   2026-09-28: hero, shift, diagram, clients, software, how-we-work; proof
   joined 2026-09-30 once it became a real s4d-viewport section -- see each
   row's html_block(SCROLL_HINT) call in tools/build_home.py), not just
   #hero, so the position:relative anchor generalizes from that one id to the
   shared .s4d-viewport class every one of those sections already carries. */
.s4d-viewport {
  position: relative;
}

.s4d-scroll-hint {
  position: absolute;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  display: flex;
  color: #8a8178;
  opacity: 1;
  transition: opacity 300ms ease;
  pointer-events: none;
}

.s4d-scroll-hint svg {
  width: 1.75rem;
  height: 1.75rem;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.s4d-scroll-hint--hidden {
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .s4d-scroll-hint svg {
    animation: s4d-scroll-bounce 1.8s ease-in-out infinite;
  }
}

@keyframes s4d-scroll-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

/* Light/dark theme surface + text tokens. This is the contract a follow-up
   task can build section-level dark-mode markup against: read a variable
   here, don't hardcode a hex. Light values below match the compiled UIkit
   defaults exactly (html/body #fff on #423653, .uk-card-default /
   .uk-section-muted / .uk-background-muted #f7f7f7), so applying them is a
   visual no-op until dark mode is active.
   Toggle button: site/plugins/zero-one/snippets/header/navbar.php (#s4d-theme-toggle).
   FOUC-prevention script: site/plugins/zero-one/snippets/header/head.php.
   Click handler + persistence: assets/js/theme-toggle.js. */
:root {
  /* Highlight text (.s4d-hl): the same orange in light and dark mode (owner decision 2026-10-27). */
  --s4d-hl: var(--s4d-orange);
  --s4d-hl-link: var(--s4d-orange);
  --s4d-bg: #ffffff;
  --s4d-text: #423653;
  --s4d-bg-muted: #f7f7f7;
  --s4d-card-bg: #ffffff;
  --s4d-card-border: #e6e7ee;
  --s4d-card-shadow: 0 1px 2px rgba(10, 10, 72, 0.05), 0 8px 24px -6px rgba(10, 10, 72, 0.10);
  --s4d-panel-bg: #ffffff;
  --s4d-panel-shadow: 0 0 90px 10px rgba(10, 10, 72, 0.12), 0 30px 80px -10px rgba(10, 10, 72, 0.14);
  --s4d-fill: var(--s4d-navy);
  --s4d-fill-card: var(--s4d-fill);
  --s4d-navy-shadow: 0 2px 6px rgba(10, 10, 72, 0.14), 0 16px 36px -8px rgba(10, 10, 72, 0.38);
  --s4d-quote-by: var(--s4d-navy);
  --s4d-dd-bg: #ffffff;
  --s4d-dd-shadow: 0 14px 40px -6px rgba(10, 10, 72, 0.28), 0 3px 10px rgba(10, 10, 72, 0.14), 0 0 0 1px rgba(10, 10, 72, 0.10);
  --s4d-dd-link: var(--s4d-text);
  --s4d-dd-link-hover: var(--s4d-navy);
  --s4d-navbar-bg: #ffffff;
  --s4d-footer-bg: #0A0A48;
  --s4d-footer-text: #ffffff;
  --s4d-input-bg: #ffffff;
  --s4d-input-text: #423653;
  --s4d-input-border: rgba(66, 54, 83, 0.3);
  --s4d-border: rgba(66, 54, 83, 0.15);
  /* Idle button outline/text (see the button restyle below). Same navy the
     approved mockup specifies in light mode; swapped to the light text
     token in dark mode, where navy-on-near-black would be unreadable. The
     hover gradient sweep and its white text are unaffected either way. */
  --s4d-button-idle: var(--s4d-navy);
}

/* Dark mode: system-preference fallback (only while no explicit choice has
   been made) and the explicit data-theme overrides. Keep all three blocks
   in sync -- standard pattern: the media query applies only when no
   data-theme attribute is present yet; once the toggle sets one explicitly,
   these two selectors take over regardless of system preference. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --s4d-hl: var(--s4d-orange);
    --s4d-hl-link: var(--s4d-orange);
    --s4d-bg: #14141f;
    --s4d-text: #e7e5ee;
    --s4d-bg-muted: #1b1b2c;
    --s4d-card-bg: #272740;
    --s4d-card-border: rgba(255, 255, 255, 0.10);
    --s4d-card-shadow: 0 2px 4px rgba(0, 0, 0, 0.45), 0 10px 24px -4px rgba(0, 0, 0, 0.60);
    --s4d-panel-bg: #1d1d30;
    --s4d-panel-shadow: 0 0 70px 6px rgba(0, 0, 0, 0.55), 0 24px 60px -10px rgba(0, 0, 0, 0.60);
    --s4d-fill: #3a6d99;
    --s4d-fill-card: #2c5478;
    --s4d-navy-shadow: var(--s4d-card-shadow);
    --s4d-quote-by: #9cc3e0;
    --s4d-dd-bg: #3a3a5e;
    --s4d-dd-shadow: 0 18px 44px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.24);
    --s4d-dd-link: #e5e5e5;
    --s4d-dd-link-hover: #ffffff;
    --s4d-navbar-bg: #14141f;
    --s4d-footer-bg: #05050f;
    --s4d-footer-text: #ffffff;
    --s4d-input-bg: #1c1c2a;
    --s4d-input-text: #e7e5ee;
    --s4d-input-border: rgba(231, 229, 238, 0.25);
    --s4d-border: rgba(231, 229, 238, 0.15);
    --s4d-button-idle: #e7e5ee;
  }
}

:root[data-theme="dark"] {
  --s4d-hl: var(--s4d-orange);
  --s4d-hl-link: var(--s4d-orange);
  --s4d-bg: #14141f;
  --s4d-text: #e7e5ee;
  --s4d-bg-muted: #1b1b2c;
  --s4d-card-bg: #272740;
  --s4d-card-border: rgba(255, 255, 255, 0.10);
  --s4d-card-shadow: 0 2px 4px rgba(0, 0, 0, 0.45), 0 10px 24px -4px rgba(0, 0, 0, 0.60);
  --s4d-panel-bg: #1d1d30;
  --s4d-panel-shadow: 0 0 70px 6px rgba(0, 0, 0, 0.55), 0 24px 60px -10px rgba(0, 0, 0, 0.60);
  --s4d-fill: #3a6d99;
  --s4d-fill-card: #2c5478;
  --s4d-navy-shadow: var(--s4d-card-shadow);
  --s4d-quote-by: #9cc3e0;
  --s4d-dd-bg: #3a3a5e;
  --s4d-dd-shadow: 0 18px 44px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(255, 255, 255, 0.24);
  --s4d-dd-link: #e5e5e5;
  --s4d-dd-link-hover: #ffffff;
  --s4d-navbar-bg: #14141f;
  --s4d-footer-bg: #05050f;
  --s4d-footer-text: #ffffff;
  --s4d-input-bg: #1c1c2a;
  --s4d-input-text: #e7e5ee;
  --s4d-input-border: rgba(231, 229, 238, 0.25);
  --s4d-border: rgba(231, 229, 238, 0.15);
  --s4d-button-idle: #e7e5ee;
}

:root[data-theme="light"] {
  --s4d-hl: var(--s4d-orange);
  --s4d-hl-link: var(--s4d-orange);
  --s4d-bg: #ffffff;
  --s4d-text: #423653;
  --s4d-bg-muted: #f7f7f7;
  --s4d-card-bg: #ffffff;
  --s4d-card-border: #e6e7ee;
  --s4d-card-shadow: 0 1px 2px rgba(10, 10, 72, 0.05), 0 8px 24px -6px rgba(10, 10, 72, 0.10);
  --s4d-panel-bg: #ffffff;
  --s4d-panel-shadow: 0 0 90px 10px rgba(10, 10, 72, 0.12), 0 30px 80px -10px rgba(10, 10, 72, 0.14);
  --s4d-fill: var(--s4d-navy);
  --s4d-quote-by: var(--s4d-navy);
  --s4d-dd-bg: #ffffff;
  --s4d-dd-shadow: 0 14px 40px -6px rgba(10, 10, 72, 0.28), 0 3px 10px rgba(10, 10, 72, 0.14), 0 0 0 1px rgba(10, 10, 72, 0.10);
  --s4d-dd-link: var(--s4d-text);
  --s4d-dd-link-hover: var(--s4d-navy);
  --s4d-navbar-bg: #ffffff;
  --s4d-footer-bg: #0A0A48;
  --s4d-footer-text: #ffffff;
  --s4d-input-bg: #ffffff;
  --s4d-input-text: #423653;
  --s4d-input-border: rgba(66, 54, 83, 0.3);
  --s4d-border: rgba(66, 54, 83, 0.15);
  --s4d-button-idle: var(--s4d-navy);
}

/* Map the tokens above onto the actual UIkit / zero-one classes this site
   uses (confirmed by grepping content/*.txt and the zero-one snippets --
   uk-section-default, uk-section-muted, uk-background-muted, uk-card /
   uk-card-default, uk-navbar-container are the real cases; uk-background-primary
   /-secondary and an image-background footer are not used on this site today
   but are harmless no-ops if adopted later). Transitions are kept short so
   the toggle doesn't feel laggy. */
/* 2026-09-30 background layers: only <html> paints --s4d-bg. body and
   .uk-section-default are transparent so the fixed body::before/::after wave layers
   (z-index:-1, painted above the root background but below in-flow block
   backgrounds) shows through. Navbar, cards, muted panels, footer stay opaque. */
html {
  background: var(--s4d-bg);
  transition: background-color 0.2s ease;
}

body {
  background: transparent;
  color: var(--s4d-text);
  transition: color 0.2s ease;
}

.uk-section-default {
  background: transparent;
}

/* 2026-09-30 silk-wave background: two fixed layers, cool (::before) and warm
   (::after). assets/js/scroll-bg.js writes --s4d-scroll (0..1); only opacity
   and transform change with it. The layers overhang the viewport (negative
   inset) so the drift never exposes an edge. Blur is a CSS filter, not an SVG
   feGaussianBlur: Chrome rasterizes a filtered SVG background in visible tiles. */
:root { --s4d-scroll: 0; --s4d-wave-k: 1; }
/* narrower screens: text covers more of the viewport, so soften the waves */
@media (max-width: 639px) { :root { --s4d-wave-k: 0.65; } }

body::before,
body::after {
  content: "";
  position: fixed;
  inset: -25vh -15vw;
  z-index: -1;
  pointer-events: none;
  background: url(/assets/icons/wave-cool.svg) center / cover no-repeat;
  filter: blur(30px);
  will-change: transform, opacity;
}

body::before {
  opacity: calc((1 - var(--s4d-scroll) * 0.85) * var(--s4d-wave-k));
  transform: translate3d(calc(var(--s4d-scroll) * -6vw), calc(var(--s4d-scroll) * -18vh), 0)
    rotate(calc(var(--s4d-scroll) * -4deg));
}

body::after {
  background-image: url(/assets/icons/wave-warm.svg);
  opacity: calc(var(--s4d-scroll) * 0.9 * var(--s4d-wave-k));
  transform: translate3d(calc((1 - var(--s4d-scroll)) * 6vw), calc((1 - var(--s4d-scroll)) * 18vh), 0)
    rotate(calc((1 - var(--s4d-scroll)) * 4deg));
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) body::before { background-image: url(/assets/icons/wave-cool-dark.svg); }
  :root:not([data-theme]) body::after { background-image: url(/assets/icons/wave-warm-dark.svg); }
}

:root[data-theme="dark"] body::before { background-image: url(/assets/icons/wave-cool-dark.svg); }
:root[data-theme="dark"] body::after { background-image: url(/assets/icons/wave-warm-dark.svg); }
:root[data-theme="light"] body::before { background-image: url(/assets/icons/wave-cool.svg); }
:root[data-theme="light"] body::after { background-image: url(/assets/icons/wave-warm.svg); }

/* Script-driven layers (assets/js/scroll-bg.js): same look as the ::before/::after pair above, but
   real elements so the scroll code can set their opacity and transform directly instead of
   changing a variable that every element on the page inherits. The pseudo-elements stay as the
   no-script / reduced-motion fallback. */
html.s4d-wave-js body::before,
html.s4d-wave-js body::after { display: none; }

.s4d-wave {
  position: fixed;
  inset: -25vh -15vw;
  z-index: -1;
  pointer-events: none;
  background: center / cover no-repeat;
  filter: blur(30px);
  will-change: transform, opacity;
}
.s4d-wave--cool { background-image: url(/assets/icons/wave-cool.svg); opacity: var(--s4d-wave-k); }
.s4d-wave--warm { background-image: url(/assets/icons/wave-warm.svg); opacity: 0; }

/* 2026-10-30: phones. A 30px blur over two fixed layers of 130vw x 150vh is re-rasterised on the
   GPU memory-bound mobile chips and made scrolling jumpy. The wave SVGs are soft gradients
   already, so drop the filter below 960px. */
@media (max-width: 959.98px) {
  body::before,
  body::after,
  .s4d-wave { filter: none; }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-wave--cool { background-image: url(/assets/icons/wave-cool-dark.svg); }
  :root:not([data-theme]) .s4d-wave--warm { background-image: url(/assets/icons/wave-warm-dark.svg); }
}
:root[data-theme="dark"] .s4d-wave--cool { background-image: url(/assets/icons/wave-cool-dark.svg); }
:root[data-theme="dark"] .s4d-wave--warm { background-image: url(/assets/icons/wave-warm-dark.svg); }
:root[data-theme="light"] .s4d-wave--cool { background-image: url(/assets/icons/wave-cool.svg); }
:root[data-theme="light"] .s4d-wave--warm { background-image: url(/assets/icons/wave-warm.svg); }

/* Reduced motion: static cool wave, no drift, warm layer hidden. */
@media (prefers-reduced-motion: reduce) {
  body::before { opacity: var(--s4d-wave-k); transform: none; will-change: auto; }
  body::after { display: none; }
}

.uk-section-muted,
.uk-background-muted {
  background: var(--s4d-bg-muted);
}

.uk-card,
.uk-card-default {
  background-color: var(--s4d-card-bg);
  color: var(--s4d-text);
}

.uk-navbar-container:not(.uk-navbar-transparent) {
  background: var(--s4d-navbar-bg);
  transition: background-color 0.2s ease;
}

footer#footer.uk-background-muted {
  background: var(--s4d-footer-bg);
  color: var(--s4d-footer-text);
}

/* Form fields (contact form etc., site/theme/snippets/blocks/formfields/*.php)
   -- keep legible in dark mode instead of staying white-on-dark. */
.uk-input,
.uk-textarea,
.uk-select,
.formfield__input {
  background-color: var(--s4d-input-bg);
  color: var(--s4d-input-text);
  border-color: var(--s4d-input-border);
}

.uk-input::placeholder,
.uk-textarea::placeholder {
  color: var(--s4d-input-text);
  opacity: 0.5;
}

/* Headings and lead text are hardcoded to near-black (#130a28) in
   uikit.app.min.css (h1,h2,h3,h4,h5,h6,.uk-h1-6{color:#130a28} and
   .uk-text-lead{color:#130a28}), which the html/body color mapping above
   does NOT reach -- an element's own explicit color always wins over an
   inherited one, regardless of that rule's specificity. Left as-is this is
   dark-navy-on-dark-background, i.e. exactly the "invisible text" dark mode
   must avoid, so it needs its own dark-mode-only override here (confirmed
   by inspecting matched CSSOM rules on the live page, not guessed).
   Scoped with :where() around the [data-theme] / :not([data-theme]) part so
   the override's specificity is just the plain element/class selector
   itself -- equal to UIkit's own rule (so ours wins on source order) but
   still LOWER than .uk-light h1 / .uk-section-primary h1 etc. (a class +
   element, i.e. more specific), so sections already styled for a dark
   background via uk-light (the CTA / footer bands) keep their own white
   text untouched. uk-button-text (the Totop button) has the same hardcoded
   color and gets the same treatment; uk-text-meta/-muted use a mid-gray
   (#aeaeae) that already has reasonable contrast on the dark background, so
   they're left alone for this first pass. */
:where([data-theme="dark"]) h1,
:where([data-theme="dark"]) h2,
:where([data-theme="dark"]) h3,
:where([data-theme="dark"]) h4,
:where([data-theme="dark"]) h5,
:where([data-theme="dark"]) h6,
:where([data-theme="dark"]) .uk-h1,
:where([data-theme="dark"]) .uk-h2,
:where([data-theme="dark"]) .uk-h3,
:where([data-theme="dark"]) .uk-h4,
:where([data-theme="dark"]) .uk-h5,
:where([data-theme="dark"]) .uk-h6,
:where([data-theme="dark"]) .uk-text-lead,
:where([data-theme="dark"]) .uk-button-text {
  color: var(--s4d-text);
}

/* .uk-accordion-title needs its own rule at matching specificity: UIkit's
   own rule is `.uk-accordion-default .uk-accordion-title` (two classes),
   which beats a bare `:where([data-theme="dark"]) .uk-accordion-title`
   (the :where() wrapper carries zero specificity, so that reduces to a
   single class) -- confirmed live via getComputedStyle, the first attempt
   at this rule (grouped with the heading selectors above) lost silently. */
:where([data-theme="dark"]) .uk-accordion-default .uk-accordion-title {
  color: var(--s4d-text);
}

@media (prefers-color-scheme: dark) {
  :where(:root:not([data-theme])) h1,
  :where(:root:not([data-theme])) h2,
  :where(:root:not([data-theme])) h3,
  :where(:root:not([data-theme])) h4,
  :where(:root:not([data-theme])) h5,
  :where(:root:not([data-theme])) h6,
  :where(:root:not([data-theme])) .uk-h1,
  :where(:root:not([data-theme])) .uk-h2,
  :where(:root:not([data-theme])) .uk-h3,
  :where(:root:not([data-theme])) .uk-h4,
  :where(:root:not([data-theme])) .uk-h5,
  :where(:root:not([data-theme])) .uk-h6,
  :where(:root:not([data-theme])) .uk-text-lead,
  :where(:root:not([data-theme])) .uk-button-text {
    color: var(--s4d-text);
  }

  :where(:root:not([data-theme])) .uk-accordion-default .uk-accordion-title {
    color: var(--s4d-text);
  }
}

/* Dark-mode navbar link legibility bug: uikit.app.min.css hardcodes
   `.uk-navbar-nav>li>a{color:#130a28}` (near-black) for the RESTING state --
   confirmed live via getComputedStyle() on the rendered page (rgb(19,10,40)),
   not guessed -- which reads as almost invisible against the dark navbar
   background (--s4d-navbar-bg ~#14141f). The hover/current-page states
   (`.uk-navbar-nav>li:hover>a`, `.uk-navbar-nav>li.uk-active>a`, etc.) are
   separate, MORE specific selectors with their own already-legible purple
   accent color (rgb(110,102,204), unaffected by html/body's color mapping
   for the same "explicit color always wins" reason as h1-h6 above) -- left
   untouched. :where() around the [data-theme] part keeps this rule's
   specificity tied with UIkit's own plain `.uk-navbar-nav>li>a` (so it wins
   on source order, s4d-site.css loading after uikit.app.min.css) but still
   below the hover/active selectors' higher specificity, so those keep their
   own color exactly as before. */
:where([data-theme="dark"]) .uk-navbar-nav > li > a {
  color: var(--s4d-text);
}

@media (prefers-color-scheme: dark) {
  :where(:root:not([data-theme])) .uk-navbar-nav > li > a {
    color: var(--s4d-text);
  }
}

/* Button restyle (approved mockup): navy outline at rest, an orange/yellow
   gradient sweep on hover. Applies to every button rendered by
   snippets/blocks/button.php (uk-button-primary and the bare
   uk-button-default/uk-button case -- the only two buttonStyle values found
   in content/*.txt) plus the menu-button in header/navbar.php, since both
   render "uk-button<buttonStyle>" the same way. uk-button-link is a
   text-style link (tm-button-link-line underline), not a boxed button, so it
   keeps its own look below rather than getting the sweep treatment. */
.uk-button-primary,
.uk-button-default,
.uk-button:not(.uk-button-link):not(.uk-button-text) {
  position: relative;
  overflow: hidden;
  z-index: 0;
  background: transparent;
  /* Neutralize the theme's own decorative corner blob
     (.uk-button-primary:not(.uk-button-text):not(.uk-button-link){background-image:
     radial-gradient(...)} in uikit.app.min.css) -- its selector specificity
     ties ours, so this needs to be set here too or it would still win by
     being more specific than a plain .uk-button-primary rule. */
  background-image: none;
  /* --s4d-button-idle is navy in light mode (the approved mockup) and
     switches to the light text token in dark mode, where navy-on-near-black
     would fail the "nothing becomes unreadable" bar; the hover state below
     is unaffected either way (gradient sweep + white text). */
  color: var(--s4d-button-idle);
  font-weight: 400;
  border: none;
  /* An inset box-shadow instead of a real border: it paints inside the same
     padding-box that overflow:hidden clips to (unlike an actual border,
     which sits outside that clip box and would leave a see-through ring
     once its color turned transparent on hover), and it doesn't add any
     layout thickness, so uk-button's existing padding is untouched. */
  box-shadow: inset 0 0 0 1.5px var(--s4d-button-idle);
  transition: color 0.3s ease, box-shadow 0.3s ease;
}

.uk-button-primary::before,
.uk-button-default::before,
.uk-button:not(.uk-button-link):not(.uk-button-text)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(90deg, var(--s4d-orange), var(--s4d-yellow));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}

.uk-button-primary:hover,
.uk-button-default:hover,
.uk-button:not(.uk-button-link):not(.uk-button-text):hover,
.uk-button-primary:focus-visible,
.uk-button-default:focus-visible,
.uk-button:not(.uk-button-link):not(.uk-button-text):focus-visible {
  color: #fff;
  box-shadow: inset 0 0 0 1.5px transparent;
}

.uk-button-primary:hover::before,
.uk-button-default:hover::before,
.uk-button:not(.uk-button-link):not(.uk-button-text):hover::before,
.uk-button-primary:focus-visible::before,
.uk-button-default:focus-visible::before,
.uk-button:not(.uk-button-link):not(.uk-button-text):focus-visible::before {
  transform: scaleX(1);
}

/* Icon spans inside the button (see button.php: uk-margin-small-right /
   uk-margin-small-left) sit above the sweep and inherit the same color
   transition, so they turn white with the label. */
.uk-button-primary > [uk-icon],
.uk-button-default > [uk-icon],
.uk-button:not(.uk-button-link):not(.uk-button-text) > [uk-icon] {
  position: relative;
  z-index: 1;
  color: inherit;
  transition: color 0.3s ease;
}

/* Touch-only devices (phones, tablets) have no hover, so the sweep never plays. Show its
   final state permanently instead: the orange-to-yellow fill. The label turns dark navy
   rather than the hover state's white, since white on yellow is only ~1.7:1 contrast and
   here it is the resting state, not a passing hover. `hover: none` leaves laptops with a
   touchscreen alone (their primary input still hovers). */
@media (hover: none) {
  .uk-button-primary,
  .uk-button-default,
  .uk-button:not(.uk-button-link):not(.uk-button-text),
  .uk-button-primary:hover,
  .uk-button-default:hover,
  .uk-button:not(.uk-button-link):not(.uk-button-text):hover,
  .uk-button-primary:focus-visible,
  .uk-button-default:focus-visible,
  .uk-button:not(.uk-button-link):not(.uk-button-text):focus-visible {
    color: var(--s4d-navy);
    box-shadow: inset 0 0 0 1.5px transparent;
  }

  .uk-button-primary::before,
  .uk-button-default::before,
  .uk-button:not(.uk-button-link):not(.uk-button-text)::before {
    transform: scaleX(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .uk-button-primary,
  .uk-button-default,
  .uk-button:not(.uk-button-link):not(.uk-button-text),
  .uk-button-primary::before,
  .uk-button-default::before,
  .uk-button:not(.uk-button-link):not(.uk-button-text)::before {
    transition: none;
  }
}

/* 2026-10-23: the hero's secondary button ("See client work", uk-button-default) is a plain outline
   on every device. Without this the touch rule above fills it with the same orange-to-yellow
   gradient as the primary quiz button. A thinner border keeps it visibly secondary on desktop too;
   on hover/focus the normal sweep still plays. */
#hero .uk-button-default {
  box-shadow: inset 0 0 0 1px var(--s4d-button-idle);
}

#hero .uk-button-default:hover,
#hero .uk-button-default:focus-visible {
  box-shadow: inset 0 0 0 1px transparent;
}

@media (hover: none) {
  #hero .uk-button-default,
  #hero .uk-button-default:hover,
  #hero .uk-button-default:focus-visible {
    color: var(--s4d-button-idle);
    box-shadow: inset 0 0 0 1px var(--s4d-button-idle);
  }

  #hero .uk-button-default::before {
    transform: scaleX(0);
  }
}

/* Product/process cards (.uk-card.uk-card-default, e.g. the PROCONTROL /
   DESKWORKS cards on the homepage -- confirmed via
   grep '"columnstyle":"card"' content/home/home.en.txt, which resolves to
   "uk-card uk-card-body" + columncardcolor "uk-card-default" in
   snippets/layout/layout.php:46-49). UIkit's own .uk-card only has a
   near-invisible box-shadow (6% opacity), which is the "blends into the
   background" default this replaces with a real border, plus a guaranteed
   bottom margin so stacked/wrapped cards never sit flush against whatever
   follows. Note: the visually similar "By hand, today" / "With your own
   tool" 4-step diagram on the homepage is NOT built from uk-card -- it's the
   .s4d-flow / .s4d-step--manual / .s4d-step--auto diagram in
   s4d-diagrams.css, which got its own border-visibility bump there instead,
   keeping its established teal/brown color-coding rather than switching to
   navy. */
.uk-card.uk-card-default {
  border: 1.5px solid var(--s4d-card-border);
  margin-bottom: 1.5rem;
}

/* Horizontal gap between side-by-side cards (2026-09-29 follow-up #2: the
   2026-09-28 follow-up above -- a plain `margin-right` on every non-last
   card -- was ALSO wrong, and is replaced here). UIkit's grid gutter is
   padding-left on each cell + a matching negative margin on the row
   (assets/app/dist/css/uikit.app.css ~4795) -- normally invisible as a real
   gap, but for a `columnstyle="card"` cell the card's own border/background
   is drawn on that SAME padded element (snippets/layout/layout.php's
   $columnCard), so the padding ends up *inside* the card instead of
   *between* cards -- adjacent card borders touch directly (confirmed via
   live getBoundingClientRect: 0px gap).

   Why plain margin-right doesn't work: .uk-grid is `display:flex;
   flex-wrap:wrap`, and each card's width comes from UIkit's percentage grid
   classes (e.g. uk-width-1-2@s = 50%, uk-width-1-3@m = 33.33%). The
   flex-wrap line-breaking algorithm decides whether an item fits on the
   current line using its flex-basis (== the `width` property here, since
   flex-basis is auto) BEFORE flex-shrink is ever applied within a line.
   Two 50%-width cards plus any added margin-right always sum to over 100%
   of the line, so the browser breaks to a new line at the second card --
   reproduced live regardless of the container's absolute width. So instead
   of adding margin on top of 100%-summing widths, each card's own width is
   reduced by more than half the desired gap, with margin-right making up
   the (smaller) rest -- keeping every line's total safely UNDER 100%.

   How much under matters more than the textbook math suggests. Reducing
   each card by exactly half the gap (width: calc(50% - 0.75rem) for a
   1.5rem gap, summing to EXACTLY 100%) renders correctly on a plain page
   -- verified on /software's own copy of this row -- but reproducibly
   still wraps on the homepage's copy of the same row, same markup, same
   measured 670px available width (getBoundingClientRect), because these
   rows sit inside `s4d-viewport` scroll-snap sections. Live binary search
   (temporary inline <style> overrides + getBoundingClientRect, both pages,
   both the 2-up and 3-up breakpoints) found the homepage needs roughly
   20-25px more slack per line than the exact-100% math implies before it
   reliably stays side-by-side -- e.g. at a 670px row, cards had to shrink
   to ~303px (not the textbook 319px) before both pages agreed; at a 994px
   row (3-up), to ~290px (not the textbook 311px). That's a roughly
   constant pixel amount, not a percentage, consistent with the scroll-snap
   container contributing some fixed rendering overhead rather than a
   sub-pixel rounding fluke -- so the rem values below subtract noticeably
   more than half the gap, not just a hair more. Re-verify on both pages if
   these rows ever move to a different section type. Scoped per row (rather
   than a single global .uk-grid > .uk-card rule) because the width
   fraction to subtract from differs: #software's two cards are 50% each
   (uk-width-1-2@s, software_cards_row() in tools/build_home.py, shared by
   the homepage's #software row and /software's own copy); #clients' four
   cards (2026-09-30: were three) are 100% on mobile, 50% from 640px, and
   still 50% from 960px (2x2)
   (section_5_5_clients()'s row-level tabletwidth="uk-width-1-2@s " plus
   the no-columnwidth-set fallback to uk-width-1-3@m in layout.php:50-52).

   justify-content:center on the row: the client's actual complaint was
   "the block of cards should be centered horizontally", and .uk-container's
   own auto margins only center the CONTAINER, not the cards within it --
   that's normally invisible because .uk-grid's default justify-content
   (flex-start) plus cards sized to nearly fill 100% leaves no visible slack
   to expose it. The safety margin above (each card noticeably narrower than
   a bare 100%-fill split) changes that: without justify-content:center the
   card group visibly hugs the row's left edge, leaving empty space on the
   right (confirmed live via screenshot on both #software and #clients,
   both pages) -- exactly the "not centered" symptom the client described,
   just introduced by this fix's own safety margin rather than the original
   bug. Centering the row's flex content, not just its container, fixes it.

   margin-left:0 alongside it: .uk-grid's own default is margin-left:-30px
   (the negative-margin half of UIkit's gutter trick -- cancels the FIRST
   child's padding-left so it lines up with the container edge, see the
   large comment above). That makes the ROW's own box asymmetric inside
   .uk-container (flush with the container's outer left edge, but inset
   from its right edge by the container's own right padding) -- confirmed
   live via getBoundingClientRect. justify-content:center on an
   asymmetric box centers the cards within THAT box, not within the
   container, producing a visible few-percent leftward bias (measured ~27px
   off center at a 700px viewport). Zeroing the row's margin-left removes
   the asymmetry (row box becomes flush with the container's own content
   box on both sides, confirmed live), which is safe here because these
   rows no longer depend on that negative margin for anything -- the width
   rule above already accounts for each card's own padding-left as part of
   its border-box width, it was never relying on the row-level cancellation. */
@media (min-width: 640px) {
  #software .uk-grid,
  #clients .uk-grid,
  #work-cases .uk-grid,
  #what .uk-grid {
    justify-content: center;
    margin-left: 0;
  }

  #software .uk-card,
  #clients .uk-card,
  #work-cases .uk-card,
  #what .uk-card {
    width: calc(50% - 2rem);
  }

  #software .uk-card:not(:last-child),
  #clients .uk-card:not(:last-child),
  #work-cases .uk-card:not(:last-child),
  #what .uk-card:not(:last-child) {
    margin-right: 1.5rem;
  }
}

@media (min-width: 960px) {
  #what .uk-card {
    width: calc(33.333% - 2.75rem);
  }
}

/* Phones (2026-09-30): the same -30px grid margin, with only 15px of
   .uk-container padding below 640px, pushed every stacked card 15px past
   the screen's left edge (measured -15..375 at 390px, all card rows on
   home, /software and both product pages). Cards are full width here, so
   zeroing the margin is all that's needed. */
@media (max-width: 639px) {
  #software .uk-grid,
  #clients .uk-grid,
  #work-cases .uk-grid,
  #what .uk-grid,
  #who .uk-grid {
    margin-left: 0;
  }
}

/* PROCONTROL/DESKWORKS "Who it's for" cards (#who, tools/build_software.py's
   pc_who()/dw_who()): unlike #what, each column sets its OWN
   tabletwidth="uk-width-1-3@s" (cols(3) helper), so this row is already
   3-up starting at 640px -- no intermediate 2-up stage -- so it needs its
   own bucket rather than sharing #clients/#what's 640px-2-up/960px-3-up one. */
@media (min-width: 640px) {
  #who .uk-grid {
    justify-content: center;
    margin-left: 0;
  }

  #who .uk-card {
    width: calc(33.333% - 2.75rem);
  }

  #who .uk-card:not(:last-child) {
    margin-right: 1.5rem;
  }
}

/* PROCONTROL/DESKWORKS product-status pill ("Available" / "Early access",
   tools/build_software.py, plain UIkit .uk-label -- currently the only two
   uses of that class sitewide, confirmed via grep). Client asked for the
   same yellow used at the end of the button hover-sweep gradient, not
   UIkit's default indigo. */
.uk-label {
  background: var(--s4d-yellow);
  color: var(--s4d-navy);
}

/* Light/dark theme toggle button (site/plugins/zero-one/snippets/header/navbar.php).
   No bundled uk-icon for sun/moon (checked assets/app/dist/js/uikit-icons.min.js --
   only a 44-icon custom subset, no sun/moon/brightness/contrast entries), so
   the button holds two inline SVGs and CSS swaps which one is visible. */
.s4d-theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  cursor: pointer;
  border-radius: 50%;
  transition: background-color 0.2s ease;
}

.s4d-theme-toggle:hover,
.s4d-theme-toggle:focus-visible {
  /* --s4d-button-idle tracks the theme (navy tint on the light navbar,
     light tint on the dark navbar) so this stays visible either way. */
  background-color: color-mix(in srgb, var(--s4d-button-idle) 12%, transparent);
}

.s4d-theme-toggle__icon {
  display: inline-flex;
  width: 20px;
  height: 20px;
  pointer-events: none;
}

.s4d-theme-toggle__icon svg {
  width: 100%;
  height: 100%;
}

.s4d-theme-toggle__icon--moon {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-theme-toggle__icon--sun {
    display: none;
  }
  :root:not([data-theme]) .s4d-theme-toggle__icon--moon {
    display: inline-flex;
  }
}

:root[data-theme="dark"] .s4d-theme-toggle__icon--sun {
  display: none;
}

:root[data-theme="dark"] .s4d-theme-toggle__icon--moon {
  display: inline-flex;
}

/* Header logo, icon-only theme swap (site/plugins/zero-one/snippets/header/
   navbar/logo.php). Same show/hide-by-[data-theme] mechanism as the sun/moon
   toggle icons directly above -- two <img> elements always render, CSS picks
   which one is visible. */
.s4d-logo-icon--dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-logo-icon--light {
    display: none;
  }
  :root:not([data-theme]) .s4d-logo-icon--dark {
    display: inline-flex;
  }
}

:root[data-theme="dark"] .s4d-logo-icon--light {
  display: none;
}

:root[data-theme="dark"] .s4d-logo-icon--dark {
  display: inline-flex;
}

/* Homepage hero logo lockup (section_5_1_hero() in tools/build_home.py):
   icon-over-wordmark, same light/dark swap mechanism as the header logo
   above. blockClass lands on each image block's wrapping <figure>
   (site/plugins/zero-one/snippets/blocks/image.php), which is a block-level
   element by default, so display:none on the whole figure is enough -- no
   need to reach into the <img>. The two icon figures and two wordmark
   figures share their own size/gap rules (.s4d-hero-logo__icon /
   .s4d-hero-logo__text) regardless of theme; only the --light/--dark
   variants toggle visibility. */
.s4d-hero-logo__icon,
.s4d-hero-logo__text {
  margin-top: 0;
  margin-bottom: 0;
}

.s4d-hero-logo__text {
  margin-top: 0.65rem;
  margin-bottom: 1rem;
}

.s4d-hero-logo__icon--dark,
.s4d-hero-logo__text--dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-hero-logo__icon--light,
  :root:not([data-theme]) .s4d-hero-logo__text--light {
    display: none;
  }
  :root:not([data-theme]) .s4d-hero-logo__icon--dark,
  :root:not([data-theme]) .s4d-hero-logo__text--dark {
    display: block;
  }
}

:root[data-theme="dark"] .s4d-hero-logo__icon--light,
:root[data-theme="dark"] .s4d-hero-logo__text--light {
  display: none;
}

:root[data-theme="dark"] .s4d-hero-logo__icon--dark,
:root[data-theme="dark"] .s4d-hero-logo__text--dark {
  display: block;
}

/* Hero logo DISPLAY size (2026-09-28, "bigger and responsive" pass).
   build_home.py's resizeWidth (220 icon / 400 wordmark) only controls the
   generated thumbnail's max source resolution -- the zero-one image snippet
   (site/plugins/zero-one/snippets/blocks/image.php) still writes its own
   `style="max-width:{resizeWidth}px"` inline on the <img>, so the display
   size has to be won back here with !important, clamped so it scales
   smoothly across viewport widths instead of jumping between fixed
   breakpoints. Verified in-browser at 375/768/1440/1920px, both themes:
   the icon reads clearly without dominating the fold on mobile, and neither
   image looks tiny on a 1920px display or clips/overflows its container at
   any width -- clamp() preferred sizes below are a vw-scaled compromise
   found by that check, not the task's own suggested numbers taken as-is
   (the icon's preferred term especially needed to come down slightly from
   16vw so it doesn't outgrow the wordmark beneath it at in-between widths). */
/* 2026-09-30 hero-logo-bigger: max ~1.4x (176->250, 340->480); minimums kept
   so the 360px mobile fold is unchanged. */
.s4d-hero-logo__icon img {
  width: clamp(96px, 19.5vw, 250px) !important;
  max-width: 100% !important;
  height: auto !important;
}

.s4d-hero-logo__text img {
  width: clamp(190px, 42vw, 480px) !important;
  max-width: 100% !important;
  height: auto !important;
}

/* Phones (2026-09-30, hero-mobile-fold-2): the logo lockup plus the 8-line
   24px intro pushed both CTAs off the first screen (second button bottom at
   y=942 on 360x800). A smaller logo alone only reached 896, so the intro and
   headline shrink too. Measured result: second button bottom at y=698,
   leaving room for a real phone's browser toolbar. */
@media (max-width: 639px) {
  .s4d-hero-logo__icon img {
    width: 64px !important;
  }
  .s4d-hero-logo__text img {
    width: 150px !important;
  }
  .s4d-hero-logo__text {
    margin-top: 0.4rem;
    margin-bottom: 0.5rem;
  }
  #hero h1 {
    font-size: 30px;
  }
  #hero .uk-text-lead {
    font-size: 19px;
    line-height: 1.45;
  }
  /* The scroll hint is absolutely placed 1.5rem above the section's bottom
     edge; with the shorter hero it landed on the second button. */
  #hero {
    padding-bottom: 4.5rem;
  }
}

/* "How it works" step icons (2026-09-28, "looks boring" pass). Same
   hand-authored inline-SVG technique as the before/after diagram
   (s4d-diagrams.css's .s4d-step__icon), sized independently since this is a
   different visual context (one large centered icon per step, not a small
   icon inline with a card label). Orange (--s4d-orange) rather than the
   diagram's currentColor/teal-brown coding: this is a plain brand accent
   here, not a manual-vs-automated status color, and orange reads clearly
   against both the light navy-on-white and dark near-black card
   backgrounds (checked both themes) -- distinct enough from the
   navy heading text above it and the orange/yellow button-hover sweep
   elsewhere on the page to read as intentional brand reuse, not a clash. */
.s4d-hww__icon {
  display: block;
  margin: 0 auto 0.75rem;
  width: 2.5rem;
  height: 2.5rem;
  stroke: var(--s4d-orange);
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* "How it works" step description text (client: "the small text"). Scoped
   to its own blockclass (set via text(..., blockclass="s4d-hww__desc") in
   build_home.py) rather than a bare ".s4d-how-we-work p" selector, so this
   does NOT also catch the "Runs on your infrastructure..." text block in
   the same row/section, which already carries its own deliberate
   uk-text-large sizing from a prior pass. */
.s4d-hww__desc {
  font-size: 1.0625rem;
}

/* Clock icon above "How much of your week..." (section_5_3_shift,
   2026-09-28 client annotation). Same size/color/stroke treatment as
   .s4d-hww__icon above -- a separate class (not a reuse of that one) since
   this lives in its own section, but kept 1:1 in its properties so it reads
   as the same icon language as the rest of the page. */
.s4d-shift__icon {
  display: block;
  margin: 0 auto 0.75rem;
  width: 2.5rem;
  height: 2.5rem;
  stroke: var(--s4d-orange);
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Section icons (2026-09-30): the one icon above each homepage section
   heading is twice the size of the step icons inside a section
   (.s4d-hww__icon alone), so it reads as the section's own mark. Stroke is
   thinned so the doubled line weight doesn't look heavy. Set via the
   s4d-section-icon class in tools/build_home.py. */
.s4d-hww__icon.s4d-section-icon,
.s4d-shift__icon.s4d-section-icon {
  width: 5rem;
  height: 5rem;
  stroke-width: 1.25;
  margin-bottom: 1rem;
}

/* Case-study / software card body text (card()'s one_liner paragraph, tools/
   build_home.py -- item 7's font-size audit). Bumped from the text block's
   plain default (16px) to 1.0625rem/17px, the audit's floor for anything
   meant to be read as a sentence -- distinct from the uk-text-meta
   badge/audience line directly above it in the same card, which stays at
   its smaller secondary size per the client's own "meta text can stay
   smaller" carve-out. */
.s4d-card__desc {
  font-size: 1.0625rem;
}

/* Diagnostic quiz (assets/diagnostic/diagnostic.css) dark-mode legibility:
   its cards/gates use low-alpha black borders and backgrounds (rgba(0,0,0,.03-.12))
   that read as barely-there once the page background goes dark. Text already
   inherits body color via --s4d-text above, so only the borders/fills need a
   dark-aware swap. */
:root[data-theme="dark"] .s4d-diag__card,
:root[data-theme="dark"] .s4d-diag__gate {
  border-color: var(--s4d-border);
}

:root[data-theme="dark"] .s4d-diag__figure,
:root[data-theme="dark"] .s4d-diag__thanks {
  background: var(--s4d-bg-muted);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-diag__card,
  :root:not([data-theme]) .s4d-diag__gate {
    border-color: var(--s4d-border);
  }
  :root:not([data-theme]) .s4d-diag__figure,
  :root:not([data-theme]) .s4d-diag__thanks {
    background: var(--s4d-bg-muted);
  }
}

/* Homepage full-viewport scroll-snap groups (spec 8, tools/build_home.py).
   Scoped under main#main.s4d-home-scroll (site/theme/templates/home.php)
   rather than bare .s4d-viewport, because .s4d-viewport also appears on rows
   shared with other pages (software_cards_row(), reused by /software via
   tools/build_software.py) -- without this scope the same class would force
   min-height:100dvh there too. 960px is this theme's actual @m breakpoint,
   confirmed against assets/app/dist/css/uikit.app.min.css's own media
   queries (640/960/1200/1600px = UIkit's default s/m/l/xl), not assumed.
   `proximity` (not `mandatory`): mandatory snap can trap keyboard/wheel
   scrolling on a group taller than the viewport (the diagram+figures group
   is the one most likely to overflow on a short laptop screen), so this
   nudges toward each group without hard-locking the scroll position -- the
   reasoning in the original brief, confirmed still correct on review.
   Below 960px this whole block doesn't apply at all: natural document flow,
   since several groups (diagram especially) won't comfortably fit one small
   viewport. */
/* Desktop slideshow (2026-10-23, owner): from 960px up every .s4d-viewport section fills the screen
   below the sticky header (100px, in the page flow) with its content centred, and
   assets/js/home-scroll.js shows exactly one section per scroll gesture. scroll-margin-top keeps a
   section's top (icons included) clear of the header when the script or an anchor jumps to it.
   A section taller than the screen simply grows and scrolls natively. svh, not dvh, so a
   collapsing browser bar never resizes the slides. No scroll-snap here: the script does the
   jumping (it is not attached for prefers-reduced-motion, which then scrolls natively). */
:root { --s4d-header-h: 100px; }
@media (min-width: 960px) {
  main#main.s4d-home-scroll .s4d-viewport {
    min-height: calc(100svh - var(--s4d-header-h));
    display: flex;
    flex-direction: column;
    justify-content: center;
    box-sizing: border-box;
    scroll-margin-top: var(--s4d-header-h);
  }
  main#main.s4d-home-scroll .s4d-viewport > .uk-container {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
  }
}

/* Phones and tablets (<960px; owner, 2026-10-21: "same slideshow feel on a small screen"): the same
   section-by-section feel as desktop, but as a soft CSS snap only -- no script. Each section is at
   least one screen high (the sticky header is 100px, so the section fills what is left; svh, not
   dvh, so the address bar sliding in and out does not resize it), its content is centred in it,
   but the page does NOT snap (2026-10-30: the earlier `scroll-snap-type: y proximity` on html made
   phone scrolling jumpy -- the browser kept pulling the page to a section start mid-gesture, and
   the diagram and "How it works" sections are taller than a screen). */
@media (max-width: 959.98px) and (prefers-reduced-motion: no-preference) {
  main#main.s4d-home-scroll .s4d-viewport {
    min-height: calc(100svh - 100px);
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  /* As a flex item the container would grow to its widest content (the logo marquee track) and
     push the page sideways; pin it to the section width. */
  main#main.s4d-home-scroll .s4d-viewport > .uk-container {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
  }
}

/* Breathing room above the "How much of your week..." heading (section
   s4d-shift), per the client's annotation asking for a gap between the
   previous section and this one. A dedicated small rule rather than the
   row's own sectionsize field: sectionsize's only options (xsmall/small/
   large/xlarge, blueprints/fields/sectionSize.yml) pad top AND bottom
   uniformly, and section_5_1_hero's own docstring in build_home.py already
   found "large" clips content at a mobile fold. On desktop the row is also
   a centered s4d-viewport flex column, so 0 extra top padding there is
   enough (centering already keeps it off the previous section's edge); on
   mobile (no scroll-snap/flex centering applies below 960px) it gets real
   padding instead. */
.s4d-shift-gap {
  padding-top: 2.5rem;
}

/* Client logo marquee (section_5_2_proof). Hand-written CSS animation
   rather than a block-based structure: UIkit has no marquee component, and
   this is more reliable than forcing a block/column layout to scroll.
   The loop relies on the track holding exactly two back-to-back copies of
   the same item list (logo_marquee() in build_home.py). 2026-10-02: the
   keyframe end state is NOT a plain translateX(-50%) -- the track has one
   gap fewer than two full rounds (2n items, 2n-1 gaps), so -50% is half a
   gap short of one round and every loop visibly jumped by that much. The end state adds the half gap back, tied to the gap through
   --s4d-marquee-gap so a gap change cannot reintroduce the jump.

   max-width below caps the visible window (980px, under 4 * 260px pitch = 1040px), centered via margin:auto. With exactly 4 unique
   logos and the track holding two back-to-back copies (see above), any
   window narrower than 4 pitches can never show the same logo twice at
   once -- 980px keeps that guarantee. */
.s4d-logo-marquee {
  overflow: hidden;
  width: 100%;
  max-width: 980px;
  margin: 0 auto;
  /* 2026-09-30 soft edges: ~215px fade each side; 980px stays under 4 pitches
     (1040px) so no duplicate logo is ever visible. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 78%, transparent 100%);
}

.s4d-logo-marquee__track {
  display: flex;
  align-items: center;
  --s4d-marquee-gap: 2.5rem;
  gap: var(--s4d-marquee-gap);
  width: max-content;
  /* 2026-10-02: 28s -> 22s, slightly faster */
  animation: s4d-logo-marquee-scroll 22s linear infinite;
}

/* Pause under the pointer only where a pointer can hover: on a phone :hover sticks after a tap,
   which left the strip frozen and made the next tap on a logo count as the "hover" tap. */
@media (hover: hover) {
  .s4d-logo-marquee:hover .s4d-logo-marquee__track,
  .s4d-logo-marquee:focus-within .s4d-logo-marquee__track {
    animation-play-state: paused;
  }
}

@keyframes s4d-logo-marquee-scroll {
  from {
    transform: translateX(0);
  }
  to {
    /* One round = half the track plus half a gap: the track holds two copies
       but only one gap fewer than two full rounds, so -50% alone restarts
       half a gap short (a visible jump). */
    transform: translateX(calc(-50% - var(--s4d-marquee-gap) / 2));
  }
}

.s4d-logo-marquee__item {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 220px;
  height: 110px;
  padding: 16px 20px;
  border: 1px solid var(--s4d-card-border);
  border-radius: 4px;
  background: var(--s4d-card-bg);
  color: var(--s4d-text);
  font: inherit;
  cursor: pointer;
  opacity: 0.7;
  transition: border-color 0.2s ease, background-color 0.2s ease, opacity 0.2s ease;
}

@media (max-width: 640px) {
  .s4d-logo-marquee {
    max-width: 100%;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
  }

  .s4d-logo-marquee__item {
    width: 160px;
    height: 80px;
    padding: 12px 16px;
  }
}

.s4d-logo-marquee__item:hover,
.s4d-logo-marquee__item:focus-visible {
  border-color: var(--s4d-navy);
  opacity: 1;
}

/* Hover pushes the flat logo past full opacity: darker in light mode, brighter in dark mode. */
.s4d-logo-marquee__item .s4d-logo-marquee__logo {
  transition: filter 0.2s ease;
}
.s4d-logo-marquee__item:hover .s4d-logo-marquee__logo,
.s4d-logo-marquee__item:focus-visible .s4d-logo-marquee__logo {
  filter: brightness(0.35);
}
:root[data-theme="dark"] .s4d-logo-marquee__item:hover .s4d-logo-marquee__logo,
:root[data-theme="dark"] .s4d-logo-marquee__item:focus-visible .s4d-logo-marquee__logo {
  filter: brightness(2);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-logo-marquee__item:hover .s4d-logo-marquee__logo,
  :root:not([data-theme]) .s4d-logo-marquee__item:focus-visible .s4d-logo-marquee__logo {
    filter: brightness(2);
  }
}

/* Each logo is pre-recolored to a single flat color per theme (see
   tools/recolor_client_logos.py) -- two <img> always render, same
   show/hide-by-[data-theme] swap as .s4d-logo-icon--light/--dark above. */
.s4d-logo-marquee__logo {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

.s4d-logo-marquee__logo--dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-logo-marquee__logo--light {
    display: none;
  }
  :root:not([data-theme]) .s4d-logo-marquee__logo--dark {
    display: inline;
  }
}

:root[data-theme="dark"] .s4d-logo-marquee__logo--light {
  display: none;
}

:root[data-theme="dark"] .s4d-logo-marquee__logo--dark {
  display: inline;
}

@media (prefers-reduced-motion: reduce) {
  .s4d-logo-marquee__track {
    animation: none;
  }
}

/* Dark-mode fix found while spot-checking the new columnstyle="card" case-
   study cards (section_5_5_clients): uikit.app.min.css carries its own
   `.uk-card-default h1..h6{color:#423653 !important}` rule (checked directly
   in the compiled CSS), which -- being !important -- beats the sitewide
   h1-h6 dark-mode override above even though that override exists for
   exactly this "hardcoded color, unreachable by the html/body mapping"
   reason. Confirmed via getComputedStyle() in the browser: card headings
   stayed light-mode navy (#423653) on the dark card background while every
   other heading on the page correctly switched. This is a pre-existing site-
   wide issue (also affects the already-live PROCONTROL/DESKWORKS cards, not
   just the newly-carded client-study ones), so the fix is written generally
   rather than scoped to just .s4d-clients. Needs its own !important to win
   against UIkit's. */
:root[data-theme="dark"] .uk-card-default h1,
:root[data-theme="dark"] .uk-card-default h2,
:root[data-theme="dark"] .uk-card-default h3,
:root[data-theme="dark"] .uk-card-default h4,
:root[data-theme="dark"] .uk-card-default h5,
:root[data-theme="dark"] .uk-card-default h6 {
  color: var(--s4d-text) !important;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .uk-card-default h1,
  :root:not([data-theme]) .uk-card-default h2,
  :root:not([data-theme]) .uk-card-default h3,
  :root:not([data-theme]) .uk-card-default h4,
  :root:not([data-theme]) .uk-card-default h5,
  :root:not([data-theme]) .uk-card-default h6 {
    color: var(--s4d-text) !important;
  }
}

/* Per-logo modal (logo_modal() in build_home.py). UIkit's own
   .uk-modal-dialog is hardcoded to a light background in uikit.app.min.css,
   which the dark-mode surface mapping above doesn't reach (same "an
   element's own explicit color/background always wins" issue documented
   above for h1-h6/.uk-text-lead) -- this is a site-wide fix, not just for
   the logo modals, so it also benefits the theme's existing language/search
   modals (navbar/language-modal.php, navbar/search-modal.php) for free. */
.uk-modal-dialog {
  background: var(--s4d-card-bg);
  color: var(--s4d-text);
}

/* Footer recolor (Task 5, 2026-09-28). content/site.en.txt's own footer row
   (site-wide Layout field, rendered by footer.php -- the one hand-edited
   content file on this site, via tools/set_footer_css_vars.py) used to carry
   three literal colors: shapedividercolor "#000000" (the wave,
   snippets/layout/shape-divider.php, inline SVG fill), and the gradient band
   itself -- backgroundoverlaycolor "rgb(0 54 74)" (dark teal) /
   backgroundoverlaycolor2 "rgb(68 68 68)" (grey), rendered as an inline
   `style="background-image:linear-gradient(to top, color1, color2)"` in
   snippets/layout/layout.php. Per the CSS `to top` direction, color1 sits at
   the BOTTOM of the band and color2 at the TOP (nearest the page content
   above it) -- confirmed against the reference screenshots, where the grey
   band sits directly under the wave and the page fades into deeper teal
   toward the very bottom.
   set_footer_css_vars.py swapped those three literals for var() references;
   the values below are the light-mode default (byte-identical to the old
   literals, so light mode is pixel-identical to before -- verified via
   screenshot) plus a dark-mode override.
   Design choice for dark mode: the grey top (rgb(68,68,68)) was the client's
   flagged "dead"/off-brand spot, so it's replaced rather than just darkened
   -- warmed toward the brand's orange accent (the same orange used in the
   button hover sweep elsewhere on this site) blended into navy, echoing that
   established brand language instead of introducing a generic grey-fix. The
   teal-navy bottom was already fairly on-brand per the task brief, so it
   only gets a "subtle lift" -- more teal blended into the same navy base,
   not a hue change. Both stay anchored in --s4d-navy so the two ends of the
   gradient read as one cohesive family rather than two clashing accents.
   The wave itself moves from flat black to a deep navy-black blend, so it
   no longer reads as a harsh, unrelated cut between the page above and the
   gradient below. color-mix() is already an established pattern in this
   file (see --s4d-card-border above), not a new technique. */
:root {
  --s4d-footer-divider: #000000;
  --s4d-footer-grad-1: rgb(0 54 74);
  --s4d-footer-grad-2: rgb(68 68 68);
  /* Second wave stacked above the footer's own divider, in every page
     (2026-09-28 follow-up). content/3_about-us has its own extra
     bottom-divider on its last content row (#008cb4, "curve-opacity") that
     happens to sit right against the footer below it -- the client read
     that pairing as "the footer" and asked why other pages only show one
     wave. This gives every page that same second wave from inside the
     shared footer component itself (a new content-free row prepended to
     the footer's own Layout row in content/site.en.txt, see
     tools/set_footer_top_wave.py) rather than duplicating a per-page
     divider setting across every template. Light default reuses
     About Us's own #008cb4 tone for visual continuity. */
  --s4d-footer-divider-top: #008cb4;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --s4d-footer-divider: color-mix(in srgb, var(--s4d-fill) 55%, #000000);
    --s4d-footer-grad-1: color-mix(in srgb, var(--s4d-teal) 28%, var(--s4d-fill));
    --s4d-footer-grad-2: color-mix(in srgb, var(--s4d-orange) 22%, var(--s4d-fill));
    /* Client's own suggestion: yellow/orange in dark mode. */
    --s4d-footer-divider-top: color-mix(in srgb, var(--s4d-orange) 55%, var(--s4d-yellow));
  }
}

:root[data-theme="dark"] {
  --s4d-footer-divider: color-mix(in srgb, var(--s4d-fill) 55%, #000000);
  --s4d-footer-grad-1: color-mix(in srgb, var(--s4d-teal) 28%, var(--s4d-fill));
  --s4d-footer-grad-2: color-mix(in srgb, var(--s4d-orange) 22%, var(--s4d-fill));
  --s4d-footer-divider-top: color-mix(in srgb, var(--s4d-orange) 55%, var(--s4d-yellow));
}

:root[data-theme="light"] {
  --s4d-footer-divider: #000000;
  --s4d-footer-grad-1: rgb(0 54 74);
  --s4d-footer-grad-2: rgb(68 68 68);
  --s4d-footer-divider-top: #008cb4;
}

/* Footer Panel-color fix (2026-09-29). Kirby's colorpicker field only
   accepts real hex/rgb/hsl values -- it can't store or preview a var()
   reference, so content/site.en.txt's footer rows (which set_footer_css_vars.py
   and set_footer_top_wave.py had pointed at the --s4d-footer-* custom
   properties above) showed as broken/unset in the Panel. tools/fix_footer_panel_colors.py
   put literal light-mode colors back into content -- the two rows are
   identified here by their own sectionid, "s4d-footer-top" and
   "s4d-footer-band" -- and dark mode is restored below by overriding those
   specific rendered elements directly, instead of the content field itself.
   Same fix also corrected a real rendering bug on the top row: its
   shapedividercolor had been set to the exact same value as its own
   backgroundoverlaycolor (both var(--s4d-footer-divider-top)), so the
   curve-opacity wave -- which works by laying partially-transparent copies
   of shapedividercolor over the section's OWN background (see
   .tm-shape-divider-wrap rules in uikit.app.css) -- blended a color into
   itself and was invisible, leaving a flat rectangle where a wave should
   have been (confirmed via Playwright screenshot before touching anything).

   Follow-up (2026-09-29, tools/fix_footer_top_wave_gradient.py): that first
   pass made the top row's own background a flat #008cb4 with a black wave
   accent -- visible, but the client read the black-against-white edge as
   "upside down". curve-opacity is built as a BOTTOM divider (solid near its
   own boundary, fading away from it, via the opacity-layered paths in
   .tm-shape-divider-wrap[data-style="curve-opacity"] above); rotated for
   "top" position that solid edge lands flush against whatever's above the
   row, so a color unrelated to that context reads as a hard, wrong-way cut.

   Second follow-up (2026-09-29, live Panel edit): the client found a
   simpler, better fix than the gradient above -- put the row's own
   shapedividerposition back to its default (empty = bottom, unrotated) and
   make its background flat white (both overlaycolor and overlaycolor2 =
   rgb(255 255 255)), so the row is visually invisible against the white
   page and the curve-opacity divider's own three opacity-layered paths (see
   the mountains/fan/curve-opacity rules above) do 100% of the work --
   their 15%/30%/100% layering over white naturally reads as "different
   shades of blue" fading to white, exactly as the divider was designed to
   look, with no gradient hack needed. This block now mirrors that: the top
   row's background override is flat --s4d-bg (dark-to-dark, matching the
   client's own light-mode white-to-white) instead of the earlier
   gradient-to-dark attempt, so the same "wave alone creates the shading"
   effect works in dark mode too. There's no separate dark-mode field in the
   Panel for this -- content/site.en.txt only ever holds the light-mode
   literal (Kirby's colorpicker can't store a var()), so dark mode has to
   live here, overriding the specific rendered elements by their sectionid. */
#s4d-footer-top .tm-shape-divider-wrap svg {
  fill: var(--s4d-footer-divider-top);
}
#s4d-footer-band .tm-shape-divider-wrap svg {
  fill: var(--s4d-footer-divider);
}
/* 2026-09-30: the strip behind the footer's top wave divider painted a solid
   page color (white from the Panel, --s4d-bg in dark), cutting the fixed silk
   wave off in a hard horizontal line. Transparent lets the wave run on
   behind the divider. The band rules below use ">" so they only repaint the
   section's own cover, not the text column's (that drew a mismatched second
   gradient box behind the footer text in dark mode). */
#s4d-footer-top > .uk-position-cover {
  background: none !important;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    #s4d-footer-band > .uk-position-cover {
      background-image: linear-gradient(to top, var(--s4d-footer-grad-1), var(--s4d-footer-grad-2)) !important;
    }
  }
}
:root[data-theme="dark"] {
  #s4d-footer-band > .uk-position-cover {
    background-image: linear-gradient(to top, var(--s4d-footer-grad-1), var(--s4d-footer-grad-2)) !important;
  }
}

/* Sticky footer (Task 5, 2026-09-28): short pages like /software left a
   band of empty background below the footer before the actual end of the
   viewport (see the /software reference screenshot). #page-content
   (site/plugins/zero-one/snippets/header.php) is the real wrapper around
   both <main> and <footer id="footer"> -- opened right after the navbar and
   closed at the very end of footer.php -- so this is the standard flex
   sticky-footer pattern applied one level in from html/body/main/footer,
   matching this theme's actual DOM rather than assuming a flatter structure.
   Verified this doesn't break the homepage's own scroll-snap sections: main
   only gains flex:1 0 auto as an ITEM of #page-content's flex column, which
   doesn't touch how main lays out its OWN children (the .s4d-viewport
   min-height/flex rules are a separate, unrelated flex context scoped to
   main#main.s4d-home-scroll's descendants). */
html,
body {
  min-height: 100dvh;
}

body {
  display: flex;
  flex-direction: column;
}

#page-content {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
}

#page-content > main {
  flex: 1 0 auto;
}

footer#footer {
  flex-shrink: 0;
}

/* Contact page vertical centering (Task 6, 2026-09-28), same treatment as
   the diagnostic quiz page (assets/diagnostic/diagnostic.css's
   main.s4d-diag-page rule) -- site/theme/templates/contact.php overrides the
   plugin's own contact.php template (same roots.templates precedence
   home.php/quiz.php already rely on) purely to add the s4d-contact-page
   class to <main>, since the plugin template has no page-specific hook of
   its own to select on otherwise. 80px is the same navbar-height allowance
   diagnostic.css already uses. */
main.s4d-contact-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: calc(100dvh - 80px);
}

main.s4d-contact-page > .uk-section {
  width: 100%;
}

/* /software card row (Task 7, 2026-09-28; superseded 2026-09-28 follow-up).
   This used to narrow + center the row on the theory that "a real gap falls
   out naturally once the pair isn't stretched full width" -- disproved by
   live measurement (0px gap even with this rule applied), and combined
   with the real gap fix added above (.uk-grid > .uk-card:not(:last-child)),
   the 900px cap left too little room for two 50%-width cards plus a 24px
   margin, forcing them to wrap onto separate lines. The real gap now comes
   from that global rule, and centering falls out for free from
   .uk-container's own default auto margins at full width, so this
   page-specific override is no longer needed. */

/* Yellow emphasis inside lead paragraphs (2026-09-30, section-icons).
   .uk-text-lead is weight 300 and UIkit's `strong { font-weight: bolder }`
   only lifts that to 400, so the emphasis read as colored but not bold. */
.uk-text-lead strong {
  font-weight: 600;
}

/* Downloads table (/devs, 2026-09-30). UIkit's dark-mode table header and
   <code> chip colours are too dim/loud on the dark theme, so inherit the
   text colour and soften it with opacity instead. */
.s4d-downloads .uk-table th {
  color: inherit;
  opacity: 0.75;
}
.s4d-downloads code {
  background: transparent;
  color: inherit;
  padding: 0;
  border: 0;
}
.s4d-downloads h2 {
  text-transform: none;
  margin-top: 2rem;
}

/* About page "Company details" card (2026-09-30). The card is a grid cell, so
   its box starts at the cell edge, the grid gutter (30px, 40px from 1200px) left of the text column above it
   (which sits inside the cell's gutter padding). Pull it in so the card is
   exactly as wide as the text column. 1080px, not 960px: this site's stock
   UIkit build puts @m at 1080px (uk-width-1-2@m, uk-visible@m). */
#company-details .uk-card {
  margin-left: 30px;
  width: calc(100% - 30px);
}
@media (min-width: 1080px) {
  #company-details .uk-card {
    width: calc(50% - 30px);
  }
}
@media (min-width: 1200px) {
  #company-details .uk-card {
    margin-left: 40px;
    width: calc(50% - 40px);
  }
}

/* 2026-10-01: the card sits close under the "More about Johannes" line, carries the icon-over-wordmark
   logo on its right (build_about.logo_blocks) and, from 1080px, reaches
   --s4d-card-widen past the text column on each side to make room for it.
   The logo is absolutely placed, centred vertically, so the address text
   keeps its normal flow; on phones it drops below the text instead. */
#s4d-about.uk-section {
  padding-bottom: 20px;
}
#company-details.uk-section {
  padding-top: 0;
}
#company-details .uk-card {
  --s4d-card-widen: 60px;
  --s4d-logo-icon: 96px;
  --s4d-logo-text: 200px;
  --s4d-logo-gap: 14px;
  /* wordmark height from its 1163x170 source */
  --s4d-logo-text-h: calc(var(--s4d-logo-text) * 170 / 1163);
  --s4d-logo-h: calc(var(--s4d-logo-icon) + var(--s4d-logo-gap) + var(--s4d-logo-text-h));
  position: relative;
}
#company-details .uk-card figure {
  margin: 0;
  max-width: none !important;
}
#company-details .uk-card figure img {
  display: block;
  height: auto;
}
.s4d-company-logo__icon img {
  width: var(--s4d-logo-icon);
}
.s4d-company-logo__text img {
  width: var(--s4d-logo-text);
}
/* The logo lockup is centred in the card's right-hand column: that column is the card's right padding,
   --s4d-logo-text + 100px wide, so the wordmark sits 50px from the card edge and the icon is centred
   over it. Larger from 1080px, where the card is wide enough. */
@media (min-width: 640px) {
  #company-details .uk-card {
    --s4d-logo-icon: 120px;
    --s4d-logo-text: 240px;
    padding-right: calc(var(--s4d-logo-text) + 100px);
  }
  .s4d-company-logo__icon,
  .s4d-company-logo__text {
    position: absolute;
  }
  .s4d-company-logo__icon {
    top: calc(50% - var(--s4d-logo-h) / 2);
    right: calc(50px + (var(--s4d-logo-text) - var(--s4d-logo-icon)) / 2);
  }
  .s4d-company-logo__text {
    top: calc(50% - var(--s4d-logo-h) / 2 + var(--s4d-logo-icon) + var(--s4d-logo-gap));
    right: 50px;
  }
}
@media (min-width: 1080px) {
  #company-details .uk-card {
    --s4d-logo-icon: 160px;
    --s4d-logo-text: 300px;
  }
}
@media (max-width: 639px) {
  .s4d-company-logo__icon {
    margin: 1.5rem auto 0 !important;
    width: var(--s4d-logo-icon);
  }
  .s4d-company-logo__text {
    margin: var(--s4d-logo-gap) auto 0 !important;
    width: var(--s4d-logo-text);
  }
}
@media (min-width: 1080px) {
  #company-details .uk-card {
    margin-left: calc(30px - var(--s4d-card-widen));
    width: calc(50% - 30px + 2 * var(--s4d-card-widen));
  }
}
@media (min-width: 1200px) {
  #company-details .uk-card {
    margin-left: calc(40px - var(--s4d-card-widen));
    width: calc(50% - 40px + 2 * var(--s4d-card-widen));
  }
}

.s4d-company-logo__icon--dark,
.s4d-company-logo__text--dark {
  display: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-company-logo__icon--light,
  :root:not([data-theme]) .s4d-company-logo__text--light {
    display: none;
  }
  :root:not([data-theme]) .s4d-company-logo__icon--dark,
  :root:not([data-theme]) .s4d-company-logo__text--dark {
    display: block;
  }
}
:root[data-theme="dark"] .s4d-company-logo__icon--light,
:root[data-theme="dark"] .s4d-company-logo__text--light {
  display: none;
}
:root[data-theme="dark"] .s4d-company-logo__icon--dark,
:root[data-theme="dark"] .s4d-company-logo__text--dark {
  display: block;
}

/* Side panel behind the header's "more" icon (#offcanvas-flip, text in the
   site's Moreicon field), 2026-09-30: the inverse of the page theme. Light
   pages keep UIkit's dark panel with the orange logo; dark pages get a light
   panel with the blue logo. */
.s4d-offcanvas-logo--on-light {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) #offcanvas-flip .uk-offcanvas-bar {
    background: #ffffff;
    color: #423653;
  }
  :root:not([data-theme]) #offcanvas-flip .uk-offcanvas-bar :is(h1, h2, h3, a, .uk-icon, .uk-offcanvas-close) {
    color: #130a28;
  }
  :root:not([data-theme]) .s4d-offcanvas-logo--on-dark {
    display: none;
  }
  :root:not([data-theme]) .s4d-offcanvas-logo--on-light {
    display: inline;
  }
}

:root[data-theme="dark"] #offcanvas-flip .uk-offcanvas-bar {
  background: #ffffff;
  color: #423653;
}
:root[data-theme="dark"] #offcanvas-flip .uk-offcanvas-bar :is(h1, h2, h3, a, .uk-icon, .uk-offcanvas-close) {
  color: #130a28;
}
:root[data-theme="dark"] .s4d-offcanvas-logo--on-dark {
  display: none;
}
:root[data-theme="dark"] .s4d-offcanvas-logo--on-light {
  display: inline;
}

/* "How it works" security note under the infrastructure line
   (section_5_7_how_we_work, 2026-09-30): a readable paragraph, narrower
   than the section so it doesn't run edge to edge on wide screens. */
.s4d-hww__note {
  max-width: 44rem;
  margin-left: auto;
  margin-right: auto;
  font-size: 1.0625rem;
}

/* Client modals and key-facts card (2026-10-02). */
.s4d-hl,
.s4d-modal__highlight strong {
  color: var(--s4d-hl);
  font-weight: 700;
}

/* Blur whatever is behind an open modal. Site-wide: covers the language and search
   modals as well as the client modals. */
.uk-modal {
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.s4d-modal__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 1.25rem;
  padding-right: 2rem; /* clear the close button */
}

.s4d-modal__head .uk-modal-title {
  margin-bottom: 0.25rem;
}

.s4d-modal__site {
  font-size: 0.95rem;
}

/* Two equal columns from 640px: description + figure left, big logo right, both centred
   vertically against each other. Phones: see the <640px block below. */
.s4d-modal__top {
  display: flex;
  align-items: center;
  gap: 2rem;
}

.s4d-modal__text {
  flex: 1 1 0;
  min-width: 0;
}

/* Flat light/dark logo, no box, beside the text block and vertically centred against it
   (owner, 2026-10-27; was stuck to the top edge). */
.s4d-modal__logo {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
}
.s4d-modal__logo-link { display: flex; align-items: center; justify-content: center; max-width: 100%; }
.s4d-modal__logo-link:hover { opacity: 0.8; }

.s4d-modal__site { white-space: nowrap; }
.s4d-modal__site-icon { vertical-align: -0.15em; margin-left: 0.15em; }

/* Contact-form success message: the "Book a call" link must read on the green alert. */
.uk-alert-success a { color: inherit; font-weight: 600; text-decoration: underline; }

/* Inline SVG arrow replacing the old arrow glyphs (no emoji-like glyphs on the site). */
.s4d-ico { display: inline-block; vertical-align: -0.12em; margin-left: 0.25em; }

.s4d-modal__logo .s4d-logo-marquee__logo {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 9rem;
}

/* Client logo banner at the top of a /work/<slug> case page: a full-width card-like band,
   logo centred inside, never upscaled past its natural size. The light/dark swap comes from
   the .s4d-logo-marquee__logo--light/--dark rules; display is deliberately not set here. */
.s4d-case-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.5rem;
  margin-bottom: 2rem;
  border: 1px solid var(--s4d-card-border);
  border-radius: 4px;
  background: var(--s4d-card-bg);
}

.s4d-case-logo .s4d-logo-marquee__logo {
  width: auto;
  height: auto;
  max-width: min(100%, 420px);
  max-height: 96px;
}

.s4d-modal__highlight {
  font-size: 1.0625rem;
}

.s4d-quote {
  margin: 1rem 0 0;
  padding: 0 0 0 1rem;
  border-left: 3px solid var(--s4d-orange);
  font-style: italic;
}

.s4d-quote p {
  margin: 0 0 0.35rem;
}

.s4d-quote footer {
  font-style: normal;
  font-size: 0.875rem;
  opacity: 0.8;
}

.s4d-quote--pending {
  opacity: 0.55;
}

.s4d-keyfacts {
  margin-top: 2.5rem;
  margin-bottom: 2.5rem;
  padding: 1.5rem;
  border-radius: 8px;
  background: var(--s4d-card-bg);
  border: 1px solid rgba(128, 128, 128, 0.3);
}

.s4d-keyfacts dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1.25rem;
  margin: 0;
}

.s4d-keyfacts dt {
  font-weight: 700;
}

.s4d-keyfacts dd {
  margin: 0;
}

.s4d-logo-modal .uk-modal-dialog {
  width: 680px;
  max-width: calc(100vw - 2rem);
  min-height: 36rem; /* all four cards the same size (tallest natural height is ~561px) */
}

.s4d-quote p {
  font-size: 1rem;
  line-height: 1.55;
}

/* Phones and tablets (<960px, matching home-scroll.js): fade each block in as it scrolls into
   view (assets/js/mobile-reveal.js adds the classes), and switch off the theme's whole-section
   fade, which fires once per 600-2100px section and so is never seen. */
@media (max-width: 959.98px) and (prefers-reduced-motion: no-preference) {
  section[uk-scrollspy] {
    opacity: 1 !important;
    animation: none !important;
  }

  .s4d-reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.6s ease, transform 0.6s ease;
  }

  .s4d-reveal.s4d-reveal--in {
    opacity: 1;
    transform: none;
  }
}

/* Phones only (<640px, UIkit's @s): centre the client logo at the bottom of the modal, the
   modal's "Read the case" link and the case-card buttons. */
@media (max-width: 639.98px) {
  .s4d-logo-modal .uk-modal-dialog {
    min-height: 0;
  }

  .s4d-modal__top {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: 0.75rem;
  }

  /* Logo, company name and website link: stacked and centred. */
  .s4d-modal__logo {
    flex: 0 0 auto;
    align-self: center;
    margin: 0;
  }

  .s4d-modal__logo .s4d-logo-marquee__logo {
    max-width: 9rem;
    max-height: 3.5rem;
  }

  .s4d-modal__head {
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding-right: 0;
    row-gap: 0.25rem;
  }

  .s4d-case-logo {
    padding: 1.25rem 1rem;
    margin-bottom: 1.5rem;
  }

  .s4d-case-logo .s4d-logo-marquee__logo {
    max-height: 64px;
  }

  .s4d-logo-modal .uk-modal-body > p:last-child {
    text-align: center;
  }

  .uk-card .uk-button[href*="/work/"] {
    display: table;
    margin-left: auto;
    margin-right: auto;
  }
}

/* Screenshot viewer on a case page (tools/build_work.py case_gallery): a fixed 4:3 viewport that
   steps through cropped details; the image is contained (never cropped further), a click opens
   the full screenshot in the UIkit lightbox. */
.s4d-gallery {
  margin: 1.5rem 0 2rem;
}

.s4d-gallery .uk-slideshow-items {
  border: 1px solid var(--s4d-card-border);
  border-radius: 4px;
  background: var(--s4d-card-bg);
}

.s4d-gallery__slide {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

.s4d-gallery__slide a {
  display: block;
  flex: 1 1 auto;
  min-height: 0;
  cursor: zoom-in;
}

.s4d-gallery__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.s4d-gallery .uk-slidenav {
  color: #ffffff;
  background: rgba(20, 20, 31, 0.6);
  border-radius: 50%;
}

.s4d-gallery .uk-slidenav:hover {
  color: #ffffff;
  background: rgba(20, 20, 31, 0.85);
}

/* The caption sits in a strip at the bottom of the viewport, below the image, so it never
   hides detail. */
.s4d-gallery__caption {
  flex: 0 0 auto;
  margin: 0;
  padding: 0.5rem 3.5rem;
  font-size: 0.9375rem;
  text-align: center;
  border-top: 1px solid var(--s4d-card-border);
}

.s4d-gallery__hint {
  margin: 0.5rem 0 0;
  text-align: center;
}

/* Case page banner with a full-colour client logo: white plate in both themes, so a logo that
   was not designed for the dark theme stays readable. */
.s4d-case-logo--color {
  background: #ffffff;
}

.s4d-case-logo--color img {
  width: auto;
  height: auto;
  max-width: min(100%, 260px);
  max-height: 150px;
}

@media (max-width: 639.98px) {
  .s4d-case-logo--color img {
    max-height: 110px;
  }
}



/* Touch devices: links and buttons only react to the first tap if the browser sees nothing change
   under the "hover" it emulates -- iOS Safari treats a :hover rule that starts a CSS transition
   (colour, border, the button sweep) as the tap's whole job and withholds the click, so every link
   needed two taps (owner, 2026-10-21). No transitions on links and buttons where hover is not
   available; the resting states are already set for them above. */
@media (hover: none) {
  a, a *, a::before, a::after,
  button, button *, button::before, button::after,
  .uk-button, .uk-button::before, .uk-button::after {
    transition: none !important;
  }
}

/* Phones: the time-route labels above the two diagram rows sit centred over their boxes. */
@media (max-width: 639.98px) {
  .s4d-diagram--before-after > .uk-text-meta {
    text-align: center;
  }
}

/* "<- Swipe ->" under the logo strip, on phones and tablets only (the strip can be dragged there). */
.s4d-marquee-hint {
  display: none;
  margin: 0.75rem 0 0;
  text-align: center;
  font-size: 1rem;
  letter-spacing: 0.04em;
  color: var(--s4d-text);
  opacity: 0.75;
}

@media (max-width: 959.98px), (hover: none) {
  .s4d-marquee-hint { display: block; }
}

/* Logo strip hint (2026-10-23): touch/narrow screens get "Swipe · tap a logo", pointer screens get "Click a logo". */
.s4d-marquee-hint--pointer { display: block; }
@media (max-width: 959.98px), (hover: none) {
  .s4d-marquee-hint--pointer { display: none; }
}
@media (min-width: 960px) and (hover: hover) {
  .s4d-marquee-hint--touch { display: none; }
  .s4d-marquee-hint--pointer { display: block; }
}

/* Homepage sections added 2026-10-23: they take their content's height (no viewport fill). */
#who .s4d-card__desc { max-width: 40rem; }
@media (max-width: 959.98px) { #who .s4d-card__desc { margin-left: auto; margin-right: auto; } }

/* ==========================================================================
   Round 2 layout (2026-10-23, approved mockup): multi-column sections.
   ========================================================================== */

/* Eyebrow label: contrast on white 5.7:1, on #f7f7f7 5.3:1 (light); 9:1 on the dark surfaces. */
:root { --s4d-eyebrow: #006d9f; }
:root[data-theme="light"] { --s4d-eyebrow: #006d9f; }
:root[data-theme="dark"] { --s4d-eyebrow: #5cc4f2; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) { --s4d-eyebrow: #5cc4f2; }
}
.s4d-eyebrow {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--s4d-eyebrow);
}
.s4d-eyebrow-wrap { margin-bottom: 0.5rem; }
.s4d-eyebrow-wrap + h2 { margin-top: 0; }

/* Gap-based grid: the theme's -30px/padding-left gutter trick would paint a column's own
   background into the gutter, so rows with boxed columns drop it and use a flex gap. */
.s4d-grid .uk-grid { margin-left: 0; gap: var(--s4d-gap, 1.5rem); align-items: stretch; }
.s4d-grid .uk-grid > .s4d-gc {
  padding-left: 0;
  margin-top: 0;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
.s4d-grid .uk-grid > .s4d-gc--full { width: 100%; }
.s4d-pain { --s4d-gap: 3rem; }
@media (min-width: 640px) {
  .s4d-cards4 .uk-grid > .s4d-gc:not(.s4d-gc--full),
  .s4d-cols-2-s .uk-grid > .s4d-gc:not(.s4d-gc--full) { width: calc(50% - var(--s4d-gap, 1.5rem) / 2); }
}
@media (min-width: 960px) {
  .s4d-cols-2-m .uk-grid > .s4d-gc:not(.s4d-gc--full) { width: calc(50% - var(--s4d-gap, 1.5rem) / 2); }
}
@media (min-width: 1200px) {
  .s4d-cards4 .uk-grid > .s4d-gc:not(.s4d-gc--full) { width: calc(25% - var(--s4d-gap, 1.5rem) * 3 / 4); }
}

/* Boxed columns */
.s4d-card-col,
.s4d-navy-card {
  border-radius: 1.25rem;
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
}
.s4d-card-col {
  background: var(--s4d-bg);
  border: 1px solid var(--s4d-card-border);
}
.s4d-navy-card {
  background: var(--s4d-fill-card);
  border: 1px solid transparent;
  padding: 1.75rem;
  justify-content: center;
}
.s4d-card-col.s4d-navy-card { justify-content: flex-start; }
:root[data-theme="dark"] .s4d-navy-card { border-color: var(--s4d-card-border); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-navy-card { border-color: var(--s4d-card-border); }
}
.s4d-navy-card,
.s4d-navy-card h2,
.s4d-navy-card h3,
.s4d-navy-card p,
.s4d-navy-card .uk-text-meta { color: var(--s4d-footer-text); }
.s4d-navy-card h3 { margin-top: 0; }
.s4d-card-col h3 { margin-top: 0.75rem; }
.s4d-card-col > .s4d-card__proof { margin-top: auto; padding-top: 0.75rem; }
.s4d-card-col > .s4d-card__proof p { margin: 0; }
.s4d-card-col > .uk-button { margin-top: auto; align-self: flex-start; }
.s4d-card__link a { font-weight: 600; text-decoration: none; white-space: nowrap; }
.s4d-card__link p, p.s4d-card__link { margin: 0; }
.s4d-card-icon { stroke: var(--s4d-teal); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.s4d-navy-card .s4d-card-icon { stroke: var(--s4d-orange); }

/* Closing section: the three buttons stack and centre on phones. */
@media (max-width: 639.98px) {
  #close .uk-button { display: block; width: fit-content; margin-left: auto; margin-right: auto; }
}

/* Orange pill button */
.uk-button.s4d-btn-orange:not(.uk-button-link):not(.uk-button-text) {
  background: var(--s4d-orange);
  color: var(--s4d-navy);
  border-radius: 500px;
  font-weight: 700;
  box-shadow: none;
  text-transform: none;
}
.uk-button.s4d-btn-orange:not(.uk-button-link):not(.uk-button-text)::before { display: none; }
.uk-button.s4d-btn-orange:not(.uk-button-link):not(.uk-button-text):hover,
.uk-button.s4d-btn-orange:not(.uk-button-link):not(.uk-button-text):focus-visible {
  background: var(--s4d-yellow);
  color: var(--s4d-navy);
  box-shadow: none;
}

/* Who is behind it: photo left, text right from 960px */
.s4d-who__photo {
  width: 200px;
  height: 200px;
  max-width: 100%;
  object-fit: cover;
  border-radius: 50%;
}
@media (max-width: 959.98px) {
  .s4d-who-row .uk-button { margin-left: auto; margin-right: auto; }
  /* Quiz buttons in the navy cards, the closing button after the FAQ and the topic-page
     closing band: centred on phones and tablets (owner, 2026-10-27). */
  .s4d-navy-card > .uk-button,
  .s4d-card-col > .uk-button,
  #faq .uk-button,
  .s4d-band-navy .uk-button {
    display: block;
    width: fit-content;
    align-self: center;
    margin-left: auto;
    margin-right: auto;
  }
}

/* Topic pages */
.s4d-ba { border-radius: 1.25rem; padding: 1.5rem; }
.s4d-ba h3 { margin-top: 0; }
.s4d-ba--before { background: color-mix(in srgb, var(--s4d-text) 7%, var(--s4d-bg)); }
.s4d-ba--after { background: color-mix(in srgb, var(--s4d-teal) 12%, var(--s4d-bg)); }
.s4d-case-card .s4d-case-hl p { margin: 0.5rem 0 0; }
.s4d-case-card .s4d-quote { margin-top: 0.75rem; }
.s4d-case-card > .uk-button { margin-top: auto; }
.s4d-case-card > .s4d-case-quote { margin-bottom: 1rem; }
.uk-section.s4d-band-navy { background: var(--s4d-navy); }
.s4d-band-navy h2,
.s4d-band-navy p,
.s4d-band-navy .uk-text-lead { color: var(--s4d-footer-text); }
.s4d-pills a {
  display: inline-block;
  margin: 0 0.75rem 0.75rem 0;
  padding: 0.6rem 1.1rem;
  border: 1.5px solid var(--s4d-button-idle);
  border-radius: 500px;
  color: var(--s4d-button-idle);
  font-weight: 600;
  text-decoration: none;
}
.s4d-pills a:hover,
.s4d-pills a:focus-visible { background: var(--s4d-orange); border-color: var(--s4d-orange); color: var(--s4d-navy); }
.s4d-work-hl { margin: 0.5rem 0 0; }

/* Round 2 fixes: boxed columns keep their padding (the gutter reset above is more specific than
   the plain box rules), tighter spacing inside, readable meta and link colours. */
.s4d-grid .uk-grid > .s4d-card-col,
.s4d-grid .uk-grid > .s4d-ba { padding: 1.5rem; }
.s4d-grid .uk-grid > .s4d-navy-card { padding: 1.75rem; }
.s4d-card-col > *,
.s4d-navy-card > *,
.s4d-ba > * { margin-top: 0; margin-bottom: 0.75rem; }
.s4d-card-col > :last-child,
.s4d-navy-card > :last-child,
.s4d-ba > :last-child { margin-bottom: 0; }
.s4d-card-col > .s4d-card__proof { margin-top: auto; }
.s4d-card-col > .uk-button { margin-top: auto; }
.s4d-card-col > .s4d-card__proof + * { margin-top: 0; }
.s4d-navy-card > .uk-button { align-self: flex-start; margin-top: auto; }
.s4d-navy-card > h3 + div { margin-top: 0; }
.s4d-card-col .uk-text-meta { color: var(--s4d-text); opacity: 0.8; }
.s4d-card__link a { color: var(--s4d-button-idle); }
.s4d-card__link a:hover { color: var(--s4d-teal); }
.s4d-pain .s4d-navy-card > .uk-button { margin-top: 0.5rem; }
#who.s4d-who-row .uk-grid { justify-content: flex-start; }
#who.s4d-who-row .uk-grid { margin-left: 0; gap: 1.5rem 3rem; }
#who.s4d-who-row .uk-grid > * { padding-left: 0; margin-top: 0; }

/* ==========================================================================
   Round 3 (2026-10-23)
   ========================================================================== */

/* C. Topic pages: wider content container, and client example cards that line up row by row. */
.uk-container.s4d-container-topic { max-width: 1120px; }

.s4d-case-card .s4d-case-logo,
.s4d-case-card .s4d-case-logo--color {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 100%;
  height: 6.5rem;
  margin: 0;
  padding: 0.75rem 1rem;
  border-radius: 0.75rem;
  text-align: center;
}
.s4d-case-card .s4d-case-logo img {
  width: auto;
  height: 4.25rem;
  max-width: 100%;
  max-height: none;
  object-fit: contain;
  margin: 0 auto;
}

/* From 960px the two cards of a row share their row tracks (subgrid): logo box, title, sector line,
   time-saved line, quote and button each sit at the same height in both cards. Below 960px the
   cards simply stack (the plain flex column of .s4d-card-col). */
@media (min-width: 960px) {
  .s4d-topic-cases .uk-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 var(--s4d-gap, 1.5rem);
  }
  .s4d-topic-cases .uk-grid > .s4d-gc--full { grid-column: 1 / -1; margin-bottom: 1.5rem; }
  .s4d-topic-cases .uk-grid > .s4d-case-card:not(.s4d-gc--full) {
    width: auto;
    display: grid;
    grid-row: span 6;
    grid-template-rows: subgrid;
    row-gap: 0;
    margin-bottom: var(--s4d-gap, 1.5rem);
  }
  .s4d-case-card > * { margin: 0; }
  .s4d-case-card > div:first-child { grid-row: 1; padding-bottom: 0.75rem; }
  .s4d-case-card > h3 { grid-row: 2; margin: 0; padding-bottom: 0.5rem; }
  .s4d-case-card > .tm-content-margin:not(.s4d-case-hl):not(.s4d-case-quote) { grid-row: 3; padding-bottom: 0.5rem; }
  .s4d-case-card > .s4d-case-hl { grid-row: 4; padding-bottom: 0.5rem; }
  .s4d-case-card > .s4d-case-quote { grid-row: 5; padding-bottom: 1rem; }
  .s4d-case-card > .uk-button { grid-row: 6; align-self: end; justify-self: start; }
}
.s4d-case-card .s4d-case-hl p,
.s4d-case-card .s4d-case-quote .s4d-quote { margin-top: 0; }

/* D. /work index: equal-height cards in aligned rows, button pinned to the bottom. */
.s4d-work-index .uk-grid > .s4d-card-col { height: auto; }
.s4d-work-index .s4d-card-col > .uk-button { margin-top: auto; }
.s4d-work-index .s4d-card-col > .tm-content-margin { flex: 1 0 auto; }

/* Short desktop windows (laptops, ~800px high): the slideshow wants each section on ONE screen, so
   the section padding, the hero logo, the section icons and the gaps between blocks shrink with the
   window height. Taller windows keep the generous defaults. */
@media (min-width: 960px) and (max-height: 900px) {
  main#main.s4d-home-scroll .s4d-viewport {
    padding-top: 1rem;
    padding-bottom: 1rem;
  }
  main#main.s4d-home-scroll .s4d-viewport .tm-content-margin { margin-top: 12px; }
  main#main.s4d-home-scroll .s4d-viewport .s4d-hww__icon.s4d-section-icon,
  main#main.s4d-home-scroll .s4d-viewport .s4d-shift__icon.s4d-section-icon {
    width: min(5rem, 7svh);
    height: min(5rem, 7svh);
    margin-bottom: 0.5rem;
  }
  main#main.s4d-home-scroll #how-we-work h3 { margin-bottom: 14px; }
  main#main.s4d-home-scroll .s4d-hero-logo__icon img { width: min(19.5vw, 27svh) !important; }
  main#main.s4d-home-scroll .s4d-hero-logo__text img { width: min(42vw, 58svh) !important; }
}

/* Round 3b: "What clients say" quote cards and the mockup-style "How it works" columns. */
.s4d-qcard > * { margin-top: 0; }
.s4d-qcard .s4d-qcard__text { margin: 0; padding: 0; border: 0; font-size: 1.0625rem; line-height: 1.55; }
.s4d-qcard .s4d-qcard__text p { margin: 0; }
.s4d-qcard > .s4d-qcard__by-wrap { margin-top: auto; padding-top: 0.5rem; font-size: 0.9375rem; }
.s4d-qcard .s4d-qcard__by strong { color: var(--s4d-text); }
.s4d-qcard .s4d-qcard__by { margin: 0; }
.s4d-qcard .s4d-qcard__by a { color: var(--s4d-button-idle); font-weight: 600; }
.s4d-quotes .s4d-gc--full a { font-weight: 600; color: var(--s4d-button-idle); }

@media (min-width: 960px) {
  .s4d-cols-3-m .uk-grid > .s4d-gc:not(.s4d-gc--full) { width: calc(33.333% - var(--s4d-gap, 1.5rem) * 2 / 3); }
}
.s4d-step-col h3 { margin: 0 0 0.5rem; }
.s4d-step-col.s4d-card-col h3 { margin-top: 0; font-size: 1.3rem; line-height: 1.3; }
.s4d-step-col p { margin: 0; }
.s4d-hww__below { text-align: center; }
.s4d-hww__below .s4d-hww__note { margin-left: auto; margin-right: auto; max-width: 50rem; }
#how-we-work .s4d-hww__below { margin-top: 1rem; }
/* "Book a call" under step 1: centred in the column, close to the text. */
.s4d-step-col > a.uk-button { display: block; width: fit-content; margin: 0.75rem auto 0; }

@media (min-width: 960px) and (max-height: 900px) {
  .s4d-quotes { --s4d-gap: 1rem; }
  .s4d-grid .uk-grid > .s4d-qcard { padding: 1.1rem 1.25rem; }
  .s4d-qcard .s4d-qcard__text { font-size: 1rem; line-height: 1.5; }
  .s4d-qcard > * { margin-bottom: 0.5rem; }
}

/* ==========================================================================
   Round 4 (2026-10-23, owner feedback)
   ========================================================================== */

/* Text selection and the active/hover menu marker use the brand orange. */
::selection { background: #FFA83E; color: #14141f; text-shadow: none; }
::-moz-selection { background: #FFA83E; color: #14141f; text-shadow: none; }
.uk-navbar-nav > li > a::before,
.uk-navbar-nav > li.uk-active > a::before { background-color: var(--s4d-orange); height: 2px; }
.uk-navbar-nav > li:hover > a,
.uk-navbar-nav > li > a[aria-expanded="true"],
.uk-navbar-nav > li.uk-active > a { color: var(--s4d-button-idle); }

/* Automation dropdown: a surface clearly lighter than the page in dark mode, with a real shadow. */
.uk-navbar-dropdown { background: var(--s4d-dd-bg); box-shadow: var(--s4d-dd-shadow); border-radius: 0.75rem; }
.uk-navbar-dropdown-nav > li > a,
.uk-navbar-dropdown-nav > li > a:focus { color: var(--s4d-dd-link); }
.uk-navbar-dropdown-nav > li > a:hover,
.uk-navbar-dropdown-nav > li.uk-active > a { color: var(--s4d-dd-link-hover); }
.uk-navbar-dropdown-nav > li.uk-active > a { text-decoration-color: var(--s4d-orange); }

/* Header icons (theme toggle, menu button) follow the theme text colour. */
.s4d-theme-toggle,
.uk-navbar-container .uk-navbar-toggle,
.uk-navbar-container .uk-navbar-item > a.uk-icon { color: var(--s4d-text); }

/* One card style everywhere: card surface, 1px very light border, soft shadow (dark: darker real
   shadow on a lighter surface, no glow). */
.s4d-card-col,
.s4d-ba,
.s4d-panel-col,
.s4d-acc,
.uk-card-default {
  background: var(--s4d-card-bg);
  border: 1px solid var(--s4d-card-border);
  border-radius: 1.25rem;
  box-shadow: var(--s4d-card-shadow);
}
.s4d-ba--before,
.s4d-ba--after { background: var(--s4d-card-bg); }
.s4d-navy-card {
  background: var(--s4d-fill-card);
  border: 1px solid transparent;
  box-shadow: var(--s4d-navy-shadow, var(--s4d-card-shadow));
}
.s4d-grid .uk-grid > .s4d-panel-col { padding: 1.75rem; }
.s4d-panel-col > * { margin: 0 0 0.75rem; }
.s4d-panel-col > :last-child { margin-bottom: 0; }
.s4d-panel-col ul { margin: 0; }

/* Panels: a container that is itself a contained, inset box with edges and the soft shadow. */
.uk-container.s4d-panel {
  box-sizing: border-box;
  width: calc(100% - 2rem);
  padding: 1.75rem 1.25rem;
  background: var(--s4d-panel-bg);
  border: 1px solid var(--s4d-card-border);
  border-radius: 1.5rem;
  box-shadow: var(--s4d-panel-shadow);
}
@media (min-width: 960px) {
  .uk-container.s4d-panel { padding: 2.25rem 2.5rem; }
  main#main.s4d-home-scroll .s4d-viewport > .uk-container.s4d-panel { width: calc(100% - 3rem); }
}
.uk-container.s4d-panel--fill { background: var(--s4d-fill); border-color: transparent; }
.uk-section.s4d-band-navy { background: transparent; }

/* Topic page intro: H1/intro/button left, "Sounds familiar?" panel right (stacked below 960px). */
.s4d-intro .uk-grid { align-items: center; }
.s4d-symptoms h2 { margin-top: 0; }

/* Expandable questions: UIkit accordion cards (one list per column), two columns from 960px.
   The whole card (question row + answer) is one surface, open or closed. */
.s4d-acc-grid { display: grid; grid-template-columns: 1fr; gap: 0.75rem 1.25rem; align-items: start; margin: 1.5rem 0 0; }
@media (min-width: 960px) { .s4d-acc-grid { grid-template-columns: 1fr 1fr; } }
.s4d-acc-col { display: flex; flex-direction: column; gap: 0.75rem; margin: 0; padding: 0; list-style: none; }
.s4d-acc-col > li.s4d-acc,
.s4d-acc-col > li.s4d-acc:nth-child(n+2) {
  margin: 0;
  padding: 0;
  /* UIkit's `.uk-accordion li:not(:last-child)` draws a #dedbe0 divider that would otherwise
     show as a light line under every card but the last (visible in dark mode). */
  border-bottom: 1px solid var(--s4d-card-border);
  overflow: hidden;
}
.s4d-acc > .uk-accordion-title,
.s4d-acc > .uk-accordion-title:hover,
.s4d-acc > .uk-accordion-title:focus {
  position: relative;
  display: block;
  padding: 1rem 3rem 1rem 1.25rem;
  font-size: inherit;
  font-weight: 600;
  line-height: 1.4;
  color: var(--s4d-text);
  text-decoration: none;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.s4d-acc > .uk-accordion-title:hover { color: var(--s4d-teal); }
.s4d-acc > .uk-accordion-title:focus-visible { outline: 2px solid var(--s4d-orange); outline-offset: -4px; border-radius: 1.25rem; }
.s4d-acc > .uk-accordion-title::before,
.s4d-acc.uk-open > .uk-accordion-title::before {
  content: "";
  position: absolute;
  right: 1.4rem;
  top: 50%;
  float: none;
  margin: 0;
  width: 0.5rem;
  height: 0.5rem;
  background: none;
  border-right: 2px solid var(--s4d-teal);
  border-bottom: 2px solid var(--s4d-teal);
  transform: translateY(-70%) rotate(45deg);
  transition: transform 0.3s ease;
}
.s4d-acc.uk-open > .uk-accordion-title::before { transform: translateY(-30%) rotate(-135deg); }
/* UIkit wraps the content in a .uk-transition div while it animates, so these are descendant selectors. */
.s4d-acc .uk-accordion-content { margin: 0; padding: 0 1.25rem 1.1rem; }
.s4d-acc .uk-accordion-content p { margin: 0; }
@media (prefers-reduced-motion: reduce) {
  .s4d-acc > .uk-accordion-title::before { transition: none !important; }
  /* UIkit pins an inline height while it animates; with no animation, show the answer at once. */
  .s4d-acc > .uk-transition {
    transition: none !important;
    height: auto !important;
    overflow: visible !important;
  }
}

/* Quote author names: navy in light mode, light steel blue in dark mode (never orange). */
.s4d-quote footer a,
.s4d-qcard .s4d-qcard__name { color: var(--s4d-quote-by); font-weight: 600; }
.s4d-quote footer { opacity: 1; color: color-mix(in srgb, var(--s4d-text) 85%, transparent); }
.s4d-qcard .s4d-qcard__by { line-height: 1.45; }
.s4d-qcard .s4d-qcard__role { opacity: 0.85; }
.s4d-who__name a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--s4d-orange);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
}

/* No-JS: the theme fades #page-content in via .transition--active and removes the
   full-screen white .loader overlay, both only in JS, so without JavaScript the page
   stayed blank (content at opacity 0 under a z-index 9999 overlay). */
@media (scripting: none) {
  [class*="transition--"] { opacity: 1; transform: none; }
  .loader { display: none; }
}

/* ==========================================================================
   Homepage review round (2026-10-27)
   ========================================================================== */

/* Company name in a quote attribution: a link, but typeset like the text around it. */
a.s4d-company-link,
.s4d-qcard .s4d-qcard__by a.s4d-company-link,
.s4d-quote footer a.s4d-company-link {
  color: inherit;
  font-weight: inherit;
  text-decoration: none;
  background-image: none; /* the theme draws link underlines as a background gradient */
}
a.s4d-company-link:hover,
a.s4d-company-link:focus-visible { color: var(--s4d-teal); }

/* Inline link in the emphasis colour (orange; darker orange on light surfaces for contrast). */
a.s4d-link-hl,
a.s4d-link-hl:hover,
a.s4d-link-hl:focus-visible {
  color: var(--s4d-hl-link);
  background-image: none !important; /* the theme draws link underlines as a background gradient */
  border-bottom: 0;
  text-decoration: underline;
  text-decoration-color: var(--s4d-hl-link);
  text-underline-offset: 0.2em;
}

/* Hero buttons: the two outer ones share one width (the middle one is wider by its text). */
@media (min-width: 640px) {
  #hero .uk-button { min-width: 14rem; }
}

/* One shared measure for lead / intro paragraphs: narrower, more lines, balanced. Centred
   blocks (UIkit text-center classes on the block or its column) centre the narrow measure. */
.uk-text-lead,
.s4d-intro-col > p,
main.s4d-contact-page .uk-section .tm-content-margin > p {
  max-width: 36rem;
  text-wrap: balance;
}
[class*="uk-text-center"] .uk-text-lead,
[class*="uk-text-center"].uk-text-lead {
  margin-left: auto;
  margin-right: auto;
}

/* "More information" links sit on one shared bottom row whatever the card text length: the
   home-scroll rule `.tm-content-margin { margin-top: 12px }` outranks the auto margin that pushes
   the proof line (and the link under it) to the card bottom, so restate it at that specificity. */
main#main.s4d-home-scroll .s4d-viewport .s4d-card-col > .s4d-card__proof { margin-top: auto; }
main#main.s4d-home-scroll .s4d-viewport .s4d-card-col > .s4d-card__proof + .tm-content-margin { margin-top: 0; }

/* Header "Book a call" (a menu-builder link to /book) drawn as a button, same look as the
   quiz button next to it: outline at rest, orange/yellow sweep on hover. */
@media (min-width: 960px) {
  .uk-navbar-nav > li:has(> a[href="/book"]) { display: flex; align-items: center; }
  .uk-navbar-nav a[href="/book"] {
    position: relative;
    overflow: hidden;
    z-index: 0;
    min-height: 0;
    height: 50px;
    align-self: center;
    padding: 0 30px;
    line-height: 50px;
    border-radius: 500px;
    background-image: none;
    color: var(--s4d-button-idle);
    box-shadow: inset 0 0 0 1.5px var(--s4d-button-idle);
    transition: color 0.3s ease, box-shadow 0.3s ease;
  }
  /* The theme draws a 1px underline on every navbar link with this same ::before (left/right/bottom,
     moved on li:hover); pin the geometry so the sweep fills the whole button instead. */
  .uk-navbar-nav a[href="/book"]::before,
  .uk-navbar-nav > li:hover > a[href="/book"]::before,
  .uk-navbar-nav > li > a[href="/book"]:active::before {
    content: "";
    position: absolute;
    top: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    height: auto !important;
    width: auto !important;
    z-index: -1;
    background: linear-gradient(90deg, var(--s4d-orange), var(--s4d-yellow));
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s ease;
    will-change: auto;
  }
  .uk-navbar-nav a[href="/book"]:hover,
  .uk-navbar-nav a[href="/book"]:focus-visible {
    color: #fff;
    box-shadow: inset 0 0 0 1.5px transparent;
  }
  .uk-navbar-nav a[href="/book"]:hover::before,
  .uk-navbar-nav > li:hover > a[href="/book"]::before,
  .uk-navbar-nav a[href="/book"]:focus-visible::before { transform: scaleX(1); }
}
@media (min-width: 960px) and (hover: none) {
  .uk-navbar-nav a[href="/book"],
  .uk-navbar-nav a[href="/book"]:hover { color: var(--s4d-navy); box-shadow: inset 0 0 0 1.5px transparent; }
  .uk-navbar-nav a[href="/book"]::before { transform: scaleX(1); }
}

/* Plain text links in the page content (no class of their own) take the emphasis orange instead of the
   theme's purple: normal weight, orange underline. Links that have their own look keep it: card links,
   the Who name, quote names, the topic pills, the gallery, the footer (.uk-light) and alerts. */
#page-content a:not([class]):not(.uk-alert a):not(.uk-light a):not(.s4d-card__link a):not(.s4d-who__name a):not(.s4d-quote a):not(.s4d-pills a):not(.s4d-gallery a):not(.s4d-quotes a),
#page-content a:not([class]):not(.uk-alert a):not(.uk-light a):not(.s4d-card__link a):not(.s4d-who__name a):not(.s4d-quote a):not(.s4d-pills a):not(.s4d-gallery a):not(.s4d-quotes a):hover,
#page-content a:not([class]):not(.uk-alert a):not(.uk-light a):not(.s4d-card__link a):not(.s4d-who__name a):not(.s4d-quote a):not(.s4d-pills a):not(.s4d-gallery a):not(.s4d-quotes a):focus-visible {
  color: var(--s4d-hl-link);
  background-image: none !important;
  border-bottom: 0;
  text-decoration: underline;
  text-decoration-color: var(--s4d-hl-link);
  text-underline-offset: 0.2em;
  font-weight: inherit;
}

/* "What we automate" and case cards: a subtle lift on hover (devices that can hover only; no motion if the
   visitor prefers reduced motion). */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .s4d-cards4 .uk-grid > .s4d-card-col,
  .s4d-work-index .s4d-card-col,
  .s4d-topic-cases .s4d-card-col {
    transition: transform 0.25s ease, box-shadow 0.25s ease;
  }
  .s4d-cards4 .uk-grid > .s4d-card-col:hover,
  .s4d-work-index .s4d-card-col:hover,
  .s4d-topic-cases .s4d-card-col:hover {
    transform: translateY(-4px);
    box-shadow: 0 2px 6px rgba(10, 10, 72, 0.08), 0 14px 28px -10px rgba(10, 10, 72, 0.28);
  }
  .s4d-cards4 .uk-grid > .s4d-card-col.s4d-navy-card:hover {
    box-shadow: 0 2px 6px rgba(10, 10, 72, 0.16), 0 20px 40px -8px rgba(10, 10, 72, 0.45);
  }
  :root[data-theme="dark"] .s4d-cards4 .uk-grid > .s4d-card-col:hover,
  :root[data-theme="dark"] .s4d-work-index .s4d-card-col:hover,
  :root[data-theme="dark"] .s4d-topic-cases .s4d-card-col:hover {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), 0 14px 28px -10px rgba(0, 0, 0, 0.6);
  }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) and (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-cards4 .uk-grid > .s4d-card-col:hover,
  :root:not([data-theme]) .s4d-work-index .s4d-card-col:hover,
  :root:not([data-theme]) .s4d-topic-cases .s4d-card-col:hover {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), 0 14px 28px -10px rgba(0, 0, 0, 0.6);
  }
}

/* Client example cards (topic pages) and /work index cards. Must sort after s4d-site.css (hence the z):
   tools/install_css.py concatenates this folder in sorted order. */

/* Flat mono logo in a card: no plate, no border, no background. Light/dark swap comes from the
   .s4d-logo-marquee__logo--light/--dark rules (also for the manual theme toggle). */
.s4d-case-card .s4d-card-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 100%;
  height: 8rem;
  margin: 0;
  padding: 0.5rem 0;
  border: 0;
  background: none;
  text-align: center;
}
.s4d-case-card .s4d-card-logo .s4d-logo-marquee__logo {
  width: auto;
  height: auto;
  max-width: min(100%, 17rem);
  max-height: 6.25rem;
  object-fit: contain;
  margin: 0 auto;
}

/* Company name inside the "Sector - Client" meta line: a link that looks like the meta text. */
.s4d-card-col a.s4d-client-link,
.s4d-card-col a.s4d-client-link:visited {
  color: inherit;
  font-weight: inherit;
  text-decoration: none;
  background-image: none; /* the theme draws link underlines as a background gradient */
  border-bottom: 0;
}
.s4d-card-col a.s4d-client-link:hover,
.s4d-card-col a.s4d-client-link:focus-visible {
  color: inherit;
  text-decoration: none;
  opacity: 0.7;
}

/* A topic page with a single client example: that card sits centred, as wide as one of two cards. */
@media (min-width: 960px) {
  .s4d-topic-cases--single .uk-grid > .s4d-case-card:not(.s4d-gc--full) {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc((100% - var(--s4d-gap, 1.5rem)) / 2);
    min-width: 0;
  }
}


/* Previous / next success case.
   1) Chevrons beside the client logo plate (.s4d-case-logo). Thin line, muted until hover/focus.
      Inside the plate on narrow screens; outside it from 1080px up, where the empty side columns
      of the case row appear (see build_privacy.narrow_row), so there is room beside the plate.
   2) .s4d-case-pager: the same two links at the end of the case. */
.s4d-case-logo { position: relative; }

.s4d-case-nav-arrow {
  position: absolute;
  top: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 3.5rem;
  margin-top: -1.75rem;
  border-radius: 0.5rem;
  color: inherit;
  opacity: 0.55;
  text-decoration: none;
  transition: opacity 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.s4d-case-nav-arrow--prev { left: 0.25rem; }
.s4d-case-nav-arrow--next { right: 0.25rem; }
.s4d-case-nav-arrow:hover,
.s4d-case-nav-arrow:focus-visible {
  opacity: 1;
  color: var(--s4d-orange);
  text-decoration: none;
}
.s4d-case-nav-arrow:focus-visible { outline: 2px solid var(--s4d-orange); outline-offset: 2px; }
.s4d-case-nav-arrow--prev:hover { transform: translateX(-2px); }
.s4d-case-nav-arrow--next:hover { transform: translateX(2px); }
/* The full-colour plate is always white, so keep the chevrons dark on it in both themes
   (only while they sit inside the plate; outside it they follow the page colour again). */
.s4d-case-logo--color .s4d-case-nav-arrow { color: #423653; }
.s4d-case-logo--color .s4d-case-nav-arrow:hover,
.s4d-case-logo--color .s4d-case-nav-arrow:focus-visible { color: var(--s4d-orange); }

@media (min-width: 1080px) {
  .s4d-case-nav-arrow--prev { left: -3.75rem; }
  .s4d-case-nav-arrow--next { right: -3.75rem; }
  .s4d-case-logo--color .s4d-case-nav-arrow { color: inherit; }
  .s4d-case-logo--color .s4d-case-nav-arrow:hover,
  .s4d-case-logo--color .s4d-case-nav-arrow:focus-visible { color: var(--s4d-orange); }
}

.s4d-case-pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  margin: 2.5rem 0 0;
  padding: 1.5rem 0 0;
  border-top: 1px solid var(--s4d-card-border);
}
.s4d-case-pager__link {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: inherit;
  text-decoration: none;
}
.s4d-case-pager__link--next { grid-column: 2; text-align: right; align-items: flex-end; }
.s4d-case-pager__label {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.875rem;
  opacity: 0.65;
}
.s4d-case-pager__label svg { flex: none; }
.s4d-case-pager__read {
  margin-top: 0.15rem;
  color: var(--s4d-orange);
}
.s4d-case-pager__title {
  color: var(--s4d-orange);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  line-height: 1.4;
}
.s4d-case-pager__link:hover .s4d-case-pager__label { opacity: 1; }
.s4d-case-pager__link:hover .s4d-case-pager__title,
.s4d-case-pager__link:hover .s4d-case-pager__read { opacity: 0.8; }

@media (max-width: 639.98px) {
  .s4d-case-pager { grid-template-columns: 1fr; gap: 1.25rem; }
  .s4d-case-pager__link--next { grid-column: 1; text-align: left; align-items: flex-start; }
}


/* Tooltips (UIkit uk-tooltip, e.g. the previous/next case chevrons): the same surface as the
   Automation dropdown (.uk-navbar-dropdown): its background, shadow and radius tokens, light and
   dark, and its text colour. */
.uk-tooltip {
  padding: 0.55rem 0.9rem;
  max-width: 17rem;
  background: var(--s4d-dd-bg);
  color: var(--s4d-dd-link);
  box-shadow: var(--s4d-dd-shadow);
  border-radius: 0.75rem;
  font-size: 0.875rem;
  line-height: 1.4;
}

/* Case page logo plate: the same height for every case, so the pages line up; each logo is sized
   to its own shape (a wide logo can be larger than a squarish one in the same box).
   Light: white plate, soft shadow under the logo. Dark: dark plate matching the page and a light
   glow along the logo's edge (the logos are transparent PNGs, so the glow follows the artwork). */
.s4d-case-logo {
  box-sizing: border-box;
  height: 12.5rem;
  padding: 1.5rem;
}
.s4d-case-logo--color img {
  max-width: min(100%, 280px);
  max-height: 130px;
  filter: drop-shadow(0 2px 5px rgba(10, 10, 72, 0.22));
}
.s4d-case-logo--telwesa img { max-width: min(100%, 420px); }
.s4d-case-logo--beyond-silicon img { max-width: min(100%, 300px); }
.s4d-case-logo--ecomemb img { max-height: 124px; }
.s4d-case-logo--sunflex img { max-height: 118px; }

@media (max-width: 639.98px) {
  .s4d-case-logo { height: 9.5rem; padding: 1rem; }
  .s4d-case-logo--color img { max-height: 76px; }
  .s4d-case-logo--ecomemb img,
  .s4d-case-logo--sunflex img { max-height: 72px; }
  .s4d-case-logo--telwesa img { max-width: min(100%, 300px); }
}

:root[data-theme="dark"] .s4d-case-logo--color { background: var(--s4d-card-bg); }
:root[data-theme="dark"] .s4d-case-logo--color img {
  filter: drop-shadow(0 0 1.5px rgba(255, 255, 255, 0.7)) drop-shadow(0 0 6px rgba(255, 255, 255, 0.22));
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-case-logo--color { background: var(--s4d-card-bg); }
  :root:not([data-theme]) .s4d-case-logo--color img {
    filter: drop-shadow(0 0 1.5px rgba(255, 255, 255, 0.7)) drop-shadow(0 0 6px rgba(255, 255, 255, 0.22));
  }
}
/* On the dark plate the chevrons follow the page colour again, not the dark ink used on white. */
:root[data-theme="dark"] .s4d-case-logo--color .s4d-case-nav-arrow { color: inherit; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .s4d-case-logo--color .s4d-case-nav-arrow { color: inherit; }
}
