/* ==========================================================================
   Sugibana, MB — landing page v2
   Design contract: docs/UI-SPEC.md   Content + decisions: docs/BUILD-PROMPT.md
   Vanilla CSS. No build step, no framework, no CDN.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts — self-hosted. See assets/fonts/README.md.
   latin-ext is NOT optional: it carries ą č ę ė į š ų ū ž.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Instrument Serif';
  src: url('assets/fonts/instrument-serif-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  src: url('assets/fonts/instrument-serif-400-latin-ext.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Manrope';
  src: url('assets/fonts/manrope-var-latin.woff2') format('woff2-variations');
  font-weight: 400 600;          /* variable axis — one file covers both weights */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Manrope';
  src: url('assets/fonts/manrope-var-cyrillic.woff2') format('woff2-variations');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Manrope';
  src: url('assets/fonts/manrope-var-latin-ext.woff2') format('woff2-variations');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   2. Tokens — every value used anywhere in this file is declared here.
   Palette sampled from the logo; ratios measured, not estimated.
   -------------------------------------------------------------------------- */

:root {
  /* ---- brand scale (theme-independent) --------------------------------
     Lime is a DARK-GROUND colour: #A3DC29 measures 1.53 on a light page and
     1.64 as a button with white text. On light it can only appear as the deep
     olive-lime below. On dark it becomes the accent proper — and the polarity
     flips: buttons take DARK text on lime, never white. */
  --lime-800: #2F5309;
  --lime-700: #3D6B0B;
  --lime-600: #4E880F;
  --lime-400: #A3DC29;
  --lime-300: #C8EE7A;
  --lime-200: #B8E84F;
  --lime-tint: #E4F2C9;

  --ink:      #101828;   /* literal near-black, theme-independent */
  --blue-900: #1D4067;
  --blue-700: #275483;
  --blue-400: #4682AE;

  /* ---- semantic layer — everything below is what components actually use,
     and it is the ONLY thing the dark theme overrides. --------------------- */
  --surface:     #FBFCF7;   /* page */
  --surface-2:   #EFF3E6;   /* section rhythm band */
  --surface-3:   #FFFFFF;   /* cards, inputs */
  --surface-dk:  #1D4067;   /* always-dark sections (hero, contact, presenter) */
  --surface-dkr: #101828;   /* footer, one step darker still */

  --text:        #1D4067;   /* 10.28 on --surface */
  --text-2:      #275483;   /* secondary body copy — 7.34 on --surface */
  --text-muted:  #5A6448;   /* 6.08 on --surface, 5.56 on --surface-2 */
  --text-on-dk:  #FFFFFF;

  /* The fill and the text colour are SEPARATE, because no single hue does
     both: vivid yellow as a link on white is 1.39, and the olive-lime as a
     button fill is muddy. Fill is loud, text is legible. */
  --accent-fill: #FFD400;   /* primary button — dark text on it 12.29 */
  --accent-fill-hi: #F0C400;/* hover — 10.55 */
  --accent-on:   #1A1A05;   /* text ON the fill, never white (white = 1.43) */
  --accent:      #3D6B0B;   /* links, eyebrows, underlines — 6.16 on --surface */
  --accent-hi:   #2F5309;
  --accent-dk:   #C8EE7A;   /* accent on the always-dark sections — 8.06 */
  --accent-tint: #E4F2C9;   /* badge / icon-hover ground */

  --line:        #D6DCC8;   /* decorative hairlines */
  --border:      #8C9478;   /* input + card borders — 3.08 on --surface */

  --error:   #B3261E;    /* 6.34 on --surface, 5.80 on --surface-2 */
  --success: #2F6B3A;    /* 6.20 on --surface */

  /* semantic — form states only, on light surfaces only */
  --error:   #B3261E;    /* 6.34 on --page, 5.80 on --band */
  --success: #2F6B3A;    /* 6.20 on --page */

  /* spacing — 4/8 based. 12/96/128 are declared extensions, see UI-SPEC. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;
  --space-10: 128px;

  /* type — clamp values hit their px endpoints exactly across 390→1280 */
  --text-display: clamp(2.5rem, 1.7331rem + 3.1461vw, 4.25rem);  /* 40 → 68 */
  --text-h2:      clamp(1.75rem, 1.4213rem + 1.3483vw, 2.5rem);  /* 28 → 40 */
  --text-h3:      clamp(1.25rem, 1.1404rem + 0.4494vw, 1.5rem);  /* 20 → 24 */
  --text-body:    clamp(1rem, 0.9452rem + 0.2247vw, 1.125rem);   /* 16 → 18 */
  --text-label:   14px;

  --font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --font-body:    'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 999px;

  --nav-h: 72px;
  --section-pad: var(--space-8);
  --container: 1280px;

  --shadow-sm: 0 1px 2px rgb(16 24 40 / 0.06), 0 1px 3px rgb(16 24 40 / 0.10);
  --shadow-md: 0 4px 8px rgb(16 24 40 / 0.06), 0 8px 24px rgb(16 24 40 / 0.10);
  --shadow-lg: 0 12px 24px rgb(16 24 40 / 0.08), 0 24px 48px rgb(16 24 40 / 0.14);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* The logo mark is raster artwork in dark steel, drawn for a light ground. On
     a dark surface it sinks into it, so it gets lifted. A filter rather than a
     second asset: one file, no swap, no extra request. Light theme = no lift by
     default; the two always-dark grounds opt in below. */
  --mark-lift: none;
}

@media (min-width: 768px)  { :root { --section-pad: var(--space-9); } }
@media (min-width: 1024px) { :root { --nav-h: 88px; } }
@media (min-width: 1280px) { :root { --section-pad: var(--space-10); } }


/* --------------------------------------------------------------------------
   2b. Dark theme
   Only the semantic layer flips — no component rule below knows a theme exists.

   Two states, three behaviours: the OS preference applies by default, and an
   explicit [data-theme] on <html> wins in either direction so the toggle can
   override the system both ways.

   In dark, lime stops being a text colour and becomes a FILL, so the polarity
   inverts: buttons take dark text on lime. White on lime measures 1.64 and is
   never correct in either theme.
   -------------------------------------------------------------------------- */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --form-card-bg: #1A2637;
    --surface:     #0E1620;
    --surface-2:   #141F2C;
    --surface-3:   #1A2637;
    --surface-dk:  #141F2C;
    --surface-dkr: #0A1017;

    --text:        #E7EDE3;       /* 15.27 on --surface */
    --text-2:  #B3C3AE;   /* 9.82 on --surface, 8.23 on cards */
  --text-muted:  #9FAE96; /* 7.77 on --surface, 6.52 on cards */
    --text-on-dk:  #E7EDE3;

    --accent:      #A3DC29;     /* 11.11 on --surface */
    --accent-hi:   #B8E84F;
    --accent-on:   #0E1A05;  /* DARK text on the lime fill — 10.98 */
    --accent-fill: #A3DC29;    /* lime IS the fill in dark — dark text on it 10.98 */
    --accent-fill-hi: #B8E84F;
    --accent-dk:   #A3DC29;
    --accent-tint: #21301A;

    --line:        #243040;
    --border:      #5E7288;           /* 3.67 on --surface, 3.08 on cards */

    --error:   #FF9A90;            /* 7.47 on a card */
    --success: #8FD69B;             /* 8.91 on a card */

    --mark-lift: brightness(1.35) contrast(1.06);

    color-scheme: dark;
  }
}

:root[data-theme='dark'] {
    --form-card-bg: #1A2637;
  --surface:     #0E1620;
  --surface-2:   #141F2C;
  --surface-3:   #1A2637;
  --surface-dk:  #141F2C;
  --surface-dkr: #0A1017;

  --text:        #E7EDE3;
  --text-2:      #B3C3AE;
  --text-muted:  #9FAE96;
  --text-on-dk:  #E7EDE3;

  --accent:      #A3DC29;
  --accent-hi:   #B8E84F;
  --accent-on:   #0E1A05;
  --accent-fill: #A3DC29;    /* lime IS the fill in dark — dark text on it 10.98 */
  --accent-fill-hi: #B8E84F;
  --accent-dk:   #A3DC29;
  --accent-tint: #21301A;

  --line:        #243040;
  --border:      #5E7288;

  --error:   #FF9A90;
  --success: #8FD69B;

  --mark-lift: brightness(1.35) contrast(1.06);

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   3. Reset and base
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: auto;
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: 400;
  line-height: 1.6;
  color: var(--text);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, picture, svg, video { display: block; max-width: 100%; }
img, video { height: auto; }

input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }

h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }

/* Focus — visible on everything interactive, never removed without replacement */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.on-dark :focus-visible,
.nav--transparent :focus-visible { outline-color: #fff; }

::selection { background: var(--accent-dk); color: var(--ink); }

/* Anchor targets clear the sticky nav */
/* Anchor scroll offset. The naive `nav-h + gap` double-counts: it parks the
   section's TOP EDGE below the nav, and then the section's own top padding
   (--section-pad, up to 128px) pushes the heading down again, leaving a 144px
   void under the nav at desktop widths.
   So subtract the padding the section already contributes. max(0px, …) because
   at wide viewports the padding alone exceeds the nav and the offset wants to
   go negative. Lands the heading ~24-40px under the nav at every breakpoint. */
:where(section, [id]) {
  scroll-margin-top: max(0px, calc(var(--nav-h) + var(--space-5) - var(--section-pad)));
}

/* The UA stylesheet's [hidden] { display: none } loses to any component rule
   that sets display — .theme-toggle is inline-flex, so hiding it from script
   silently did nothing. This is the standard fix and the reason it needs the
   !important: it has to beat every component rule, not just the UA one. */
[hidden] { display: none !important; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

  /* skip link */
.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 200;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-3);
  color: var(--text);
  font-size: var(--text-label);
  font-weight: 600;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-4);
}
@media (min-width: 768px)  { .container { padding-inline: var(--space-6); } }
@media (min-width: 1280px) { .container { padding-inline: var(--space-8); } }

/* --section-pad is a token, not three literals, because the anchor scroll
   offset has to subtract it — see the scroll-margin-top rule above. Changing
   the rhythm here without that would silently reopen the gap. */
.section { padding-block: var(--section-pad); }

/* Section register — the rhythm that stops the page reading as white bands */
.section--light { background: var(--surface); }
.section--tint  { background: var(--surface-2); }
.section--dark  { background: var(--surface-dk); color: var(--text-on-dk); }

.section__head { max-width: 62ch; margin-bottom: var(--space-7); }
.section__head h2 { margin-bottom: var(--space-3); }
.section__eyebrow {
  display: inline-block;
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-3);
}
.section--dark .section__eyebrow { color: var(--accent-dk); }

:lang(uk) h1, :lang(uk) h2, :lang(uk) .h1, :lang(uk) .h2,
:lang(uk) .step::before {
  /* Instrument Serif ships latin + latin-ext only — no Cyrillic. Left alone,
     every Ukrainian heading would fall back to whatever serif the OS supplies
     and that language would look like a different site. Manrope carries
     Cyrillic, so Ukrainian sets its display type in the body face instead. */
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: -0.02em;
}
:lang(uk) h1, :lang(uk) .h1 {
  line-height: 1.12;
  /* Manrope 600 at the serif's display size is far heavier than Instrument
     Serif, and Ukrainian runs longer than English — capped so the hero lede
     stays above the fold. */
  font-size: clamp(2.125rem, 1.5rem + 2.56vw, 3.5rem);   /* 34 → 56 */
}
:lang(uk) h2, :lang(uk) .h2 { line-height: 1.2; }

h1, .h1 {
  font-family: var(--font-display);
  font-size: var(--text-display);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.01em;
}
h2, .h2 {
  font-family: var(--font-display);
  font-size: var(--text-h2);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.005em;
}
h3, .h3 {
  font-family: var(--font-body);
  font-size: var(--text-h3);
  font-weight: 600;
  line-height: 1.3;
}
.lede { font-size: var(--text-h3); line-height: 1.5; color: var(--text-2); }
.section--dark .lede { color: var(--accent-dk); }

.small {
  font-size: var(--text-label);
  font-weight: 400;
  line-height: 1.5;
}

a { color: var(--accent-hi); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--accent); }
.section--dark a { color: var(--accent-dk); }

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-3) var(--space-6);
  border: 2px solid transparent;
  border-radius: var(--radius-md);
  font-size: var(--text-body);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 150ms ease-out, border-color 150ms ease-out,
              color 150ms ease-out, transform 150ms ease-out, box-shadow 150ms ease-out;
}
@media (prefers-reduced-motion: reduce) { .btn { transition: none; } }

.btn--primary { background: var(--accent-fill); color: var(--accent-on); }
.btn--primary:hover { background: var(--accent-fill-hi); color: var(--accent-on); transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn--primary:active { background: var(--accent-fill-hi); transform: translateY(0); box-shadow: none; }

.btn--secondary { background: transparent; border-color: currentColor; color: var(--text); }
.btn--secondary:hover { background: color-mix(in srgb, currentColor 10%, transparent); color: var(--text); transform: translateY(-1px); }
.btn--secondary:active { transform: translateY(0); }

.on-dark .btn--secondary { border-color: #fff; color: #fff; }
.on-dark .btn--secondary:hover { background: rgb(255 255 255 / 0.12); color: #fff; }

.btn[disabled], .btn[aria-disabled='true'] {
  background: var(--line);
  border-color: var(--line);
  color: var(--border);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* --------------------------------------------------------------------------
   6. Nav
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background: transparent;
  transition: background-color 200ms ease, box-shadow 200ms ease;
}
@media (prefers-reduced-motion: reduce) { .nav { transition: none; } }

.nav--solid {
  background: var(--surface);
  box-shadow: 0 1px 0 var(--line), var(--shadow-sm);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  width: 100%;
}

/* Logo = inline SVG mark + live text wordmark.
   The mark is drawn in `currentColor`, so the light/dark nav states are a single
   colour change on one element — no second asset, no image swap, no reflow. */
.logo {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  text-decoration: none;
  color: #fff;
  border-radius: var(--radius-sm);
  transition: color 200ms ease;
}
.nav--solid .logo { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .logo { transition: none; } }

/* The wordmark is charred wood photographed on a WHITE ground. Keyed to
   transparency it is still nearly black, which is invisible on the navy nav —
   so on dark grounds it is inverted. invert() alone would also flip the copper
   inlay to blue; the paired hue-rotate(180deg) restores the hue so only
   lightness flips.

   height + width:auto against the intrinsic 720x178 is what keeps it in the
   nav. Without a height it renders at 720px and crushes everything else in the
   bar to zero width — which is exactly what happened. */
.logo__img {
  display: block;
  height: 30px;
  width: auto;
}
@media (min-width: 1024px) { .logo__img { height: 40px; } }

.nav--transparent .logo__img,
:root[data-theme='dark'] .logo__img { filter: invert(1) hue-rotate(180deg); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .logo__img { filter: invert(1) hue-rotate(180deg); }
}
:root[data-skin='rustic'] .logo__img { filter: invert(1) hue-rotate(180deg) sepia(0.3); }

.logo__word {
  font-family: var(--font-body);
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1;
}
@media (min-width: 1024px) { .logo__word { font-size: 27px; } }

.logo__dot { color: var(--accent); }
.nav--transparent .logo__dot,
.on-dark .logo__dot,
.footer .logo__dot { color: var(--accent-dk); }

.nav__links {
  display: none;
  align-items: center;
  gap: var(--space-6);
  margin-inline-start: auto;
  list-style: none;
  padding: 0;
}
@media (min-width: 1024px) { .nav__links { display: flex; } }

.nav__link {
  position: relative;
  display: inline-block;
  padding-block: var(--space-2);
  font-size: var(--text-label);
  font-weight: 600;
  color: #fff;
  text-decoration: none;
  transition: color 150ms ease;
}
.nav--solid .nav__link { color: var(--text); }
.nav__link:hover { color: var(--accent-dk); }
.nav--solid .nav__link:hover { color: var(--accent); }

.nav__link::after {
  content: '';
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transition: transform 150ms var(--ease-out);
}
.nav__link[aria-current='true']::after { transform: scaleX(1); }
.nav__link[aria-current='true'] { color: var(--accent-dk); }
.nav--solid .nav__link[aria-current='true'] { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .nav__link::after { transition: none; } }

/* Language switcher — plain links, not a <select>, so each language is a real
   crawlable URL rather than a JS-only state. */
.langs {
  display: none;
  align-items: center;
  gap: var(--space-1);
  margin-inline-start: auto;
}
@media (min-width: 1024px) { .langs { display: flex; } }

.langs a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  min-height: 36px;
  padding-inline: var(--space-2);
  font-size: var(--text-label);
  font-weight: 600;
  color: inherit;
  text-decoration: none;
  border-radius: var(--radius-md);
  opacity: 0.65;
  transition: opacity 150ms ease, background-color 150ms ease;
}
.langs a:hover { opacity: 1; background: rgb(255 255 255 / 0.12); }
.nav--solid .langs a:hover { background: rgb(29 64 103 / 0.08); }
.langs a[aria-current='true'] { opacity: 1; }
.nav--transparent .langs a { color: #fff; }
.nav--solid .langs a { color: var(--text); }
.nav--solid .langs a[aria-current='true'] { color: var(--accent); }
.nav--transparent .langs a[aria-current='true'] { color: var(--accent-dk); }
@media (prefers-reduced-motion: reduce) { .langs a { transition: none; } }

/* In the mobile overlay it sits under the CTA, full width, always visible. */
.langs--menu {
  display: flex;
  margin-block-start: var(--space-6);
  margin-inline-start: 0;
  gap: var(--space-2);
}
.langs--menu a {
  min-width: 48px;
  min-height: 44px;
  color: #fff;
  border: 1px solid rgb(255 255 255 / 0.22);
}
.langs--menu a[aria-current='true'] { color: var(--accent-dk); border-color: var(--accent-dk); }

/* The nav toggle is desktop-only, so the overlay carries its own — otherwise
   phone and tablet users have no way to switch theme at all. Labelled rather
   than icon-only, since there is room for it here. */
.menu__theme {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  margin-block-start: var(--space-4);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-3);
  font-size: var(--text-label);
  font-weight: 600;
  color: #fff;
  background: transparent;
  border: 1px solid rgb(255 255 255 / 0.22);
  border-radius: var(--radius-full);
}
.menu__theme:hover { background: rgb(255 255 255 / 0.10); }
.menu__theme-icons { display: inline-flex; }

/* With the switcher taking the auto margin, the links no longer need it. */
.nav__links { margin-inline-start: 0; }
@media (max-width: 1023px) { .nav__burger { margin-inline-start: auto; } }

.theme-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  padding: 0;
  color: inherit;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  opacity: 0.75;
  transition: opacity 150ms ease, background-color 150ms ease;
}
@media (min-width: 1024px) { .theme-toggle { display: inline-flex; } }
.theme-toggle:hover { opacity: 1; background: color-mix(in srgb, currentColor 12%, transparent); }
.nav--transparent .theme-toggle { color: #fff; }
.nav--solid .theme-toggle { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .theme-toggle { transition: none; } }

/* Show the icon for the theme you would switch TO — a sun while dark means
   "go light". Written as: moon is the default (light is active), and only the
   three dark-active cases flip to the sun. */
.theme-toggle__sun { display: none; }
.theme-toggle__moon { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .theme-toggle__sun { display: block; }
  :root:not([data-theme='light']) .theme-toggle__moon { display: none; }
}
:root[data-theme='dark'] .theme-toggle__sun { display: block; }
:root[data-theme='dark'] .theme-toggle__moon { display: none; }
:root[data-theme='light'] .theme-toggle__sun { display: none; }
:root[data-theme='light'] .theme-toggle__moon { display: block; }

.nav__cta { display: none; }
@media (min-width: 1024px) { .nav__cta { display: inline-flex; padding: var(--space-3) var(--space-5); } }

.nav__burger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  margin-inline-start: auto;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: #fff;
}
.nav--solid .nav__burger { color: var(--text); }
@media (min-width: 1024px) { .nav__burger { display: none; } }

/* Mobile overlay menu */
.menu {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: flex;
  flex-direction: column;
  padding: var(--space-4);
  background: var(--surface-dk);
  color: var(--text-on-dk);
  opacity: 0;
  visibility: hidden;
  transform: scale(0.98);
  /* visibility is switched, never interpolated: delayed to the end when closing
     so the fade-out stays visible, instant when opening. Transitioning it over
     the same 220ms leaves everything inside the overlay computing as hidden for
     the whole duration, which makes .focus() on those children a silent no-op —
     the dialog opens with focus still outside it and the trap never engages. */
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out), visibility 0s linear 220ms;
}
.menu[data-open='true'] {
  opacity: 1; visibility: visible; transform: scale(1);
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out), visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce) {
  .menu { transition: opacity 100ms linear, visibility 0s linear 100ms; transform: none; }
  .menu[data-open='true'] { transform: none; transition: opacity 100ms linear, visibility 0s linear 0s; }
}

.menu__top { display: flex; justify-content: flex-end; height: var(--nav-h); align-items: center; }
.menu__close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  background: transparent; border: 0; border-radius: var(--radius-md); color: #fff;
}
.menu__list {
  display: flex; flex-direction: column; gap: var(--space-2);
  list-style: none; margin: 0; padding: 0;
  margin-block-start: var(--space-6);
}
.menu__link {
  display: block;
  padding: var(--space-3) var(--space-2);
  font-size: var(--text-h3);
  font-weight: 600;
  color: #fff;
  text-decoration: none;
}
.menu__link:hover { color: var(--accent-dk); }
.menu__cta { margin-block-start: var(--space-7); }

body[data-menu-open='true'] { overflow: hidden; }

/* --------------------------------------------------------------------------
   7. Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  min-height: 100svh;
  padding-block: calc(var(--nav-h) + var(--space-9)) var(--space-9);
  background: #1D4067;          /* literal: the hero is dark in BOTH themes */
  color: #fff;
  overflow: hidden;
}
@media (min-width: 1280px) { .hero { min-height: 90vh; max-height: 960px; } }

/* Aurora — the "3D feel" without a WebGL layer competing for the GPU.
 *
 * There is NO filter: blur() here, and that is load-bearing. It used to carry
 * blur(28px), and because the element is also transform-animated, Chrome
 * re-rasterised the blur every single frame: measured at 1512x780 on a 2x
 * display, 44 of 115 frames blew past 20ms and the p95 was 41.7ms. The canvas
 * dust and light lines sitting on top of that stuttered visibly — which is
 * what "the hero is flickering" actually was. It was never the canvas: hiding
 * the canvas entirely left 35 of 115 frames still over budget, while removing
 * this one blur dropped it to 0 of 115 at a flat 8.3ms.
 *
 * will-change: transform does NOT fix it — measured, 46 of 115. The blur has
 * to go. The softness is now in the gradients themselves (wider, fully faded
 * stops) and .hero__grain still handles the banding the blur used to hide. */
.hero__aurora {
  position: absolute;
  inset: -20%;
  z-index: -3;
  background:
    radial-gradient(46% 54% at 22% 32%, rgb(70 130 174 / 0.50), rgb(70 130 174 / 0) 78%),
    radial-gradient(40% 48% at 76% 22%, rgb(39 84 131 / 0.55), rgb(39 84 131 / 0) 78%),
    radial-gradient(48% 52% at 66% 76%, rgb(168 60 36 / 0.26), rgb(168 60 36 / 0) 78%),
    radial-gradient(54% 60% at 30% 82%, rgb(29 64 103 / 0.70), rgb(29 64 103 / 0) 78%);
}
@media (prefers-reduced-motion: no-preference) {
  .hero__aurora { animation: aurora-drift 28s ease-in-out infinite alternate; }
}
@keyframes aurora-drift {
  from { transform: translate3d(-1.2%, -0.8%, 0) scale(1); }
  to   { transform: translate3d(1.2%, 0.8%, 0) scale(1.06); }
}

.hero__canvas { position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; }

/* Grain — kills gradient banding on the aurora */
.hero__grain {
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.14;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* Scrim — mandatory, measured against the brightest frame */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg,
    rgb(29 64 103 / 0.78) 0%,
    rgb(29 64 103 / 0.62) 45%,
    rgb(16 24 40 / 0.52) 100%);
}

.hero__grid { display: grid; align-items: center; gap: var(--space-7); }
.hero__content { max-width: 46rem; }

/* The workshop photograph. Full-bleed behind every other hero layer, anchored
   right: its subject sits right and its negative space left, which is exactly
   where the headline goes.

   Attached inside the media query on purpose — below 1024 it is never painted
   and therefore never requested. A background is the only way that holds; an
   <img> downloads even when display:none. */
.hero__photo { position: absolute; inset: 0; z-index: -3; }

@media (min-width: 1024px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); }
  .hero__photo {
    background: url('assets/hero/workshop.webp') right center / cover no-repeat;
  }
}
.hero h1 { margin-bottom: var(--space-5); }
.hero__lede { font-size: var(--text-h3); line-height: 1.5; color: rgb(255 255 255 / 0.92); margin-bottom: var(--space-7); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-4); }

.hero__pause,
.footer__pause {
  position: absolute;
  inset-block-end: var(--space-4);
  inset-inline-end: var(--space-4);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  padding: 0;
  color: #fff;
  background: rgb(29 64 103 / 0.55);
  border: 1px solid rgb(255 255 255 / 0.22);
  border-radius: var(--radius-full);
  backdrop-filter: blur(8px);
  transition: background-color 150ms ease;
}
.hero__pause:hover,
.footer__pause:hover { background: rgb(29 64 103 / 0.8); }

.hero__cue {
  position: absolute;
  inset-block-end: var(--space-5);
  inset-inline-start: 50%;
  translate: -50% 0;
  z-index: 2;
  color: rgb(255 255 255 / 0.7);
  display: inline-flex;
  width: 44px; height: 44px;
  align-items: center;
  justify-content: center;
}
@media (prefers-reduced-motion: no-preference) {
  .hero__cue { animation: cue-bob 2.4s ease-in-out infinite; }
}
@keyframes cue-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }
@media (max-width: 767px) { .hero__cue { display: none; } }

/* --------------------------------------------------------------------------
   8. Cards and grids
   -------------------------------------------------------------------------- */

.grid { display: grid; gap: var(--space-5); }
@media (min-width: 768px)  { .grid--3 { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); } }
@media (min-width: 1024px) { .grid--3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
  padding: var(--space-6);
  background: var(--surface-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: transform 150ms ease-out, box-shadow 150ms ease-out, border-color 150ms ease-out;
}
@media (prefers-reduced-motion: reduce) { .card { transition: none; } }
.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--blue-400); }

.card__icon {
  display: inline-flex;
  width: 48px; height: 48px;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-2);
  color: var(--text-2);
  background: var(--surface-2);
  border-radius: var(--radius-md);
  transition: color 150ms ease-out, background-color 150ms ease-out;
}
.card:hover .card__icon { color: var(--accent); background: var(--accent-tint); }
.card__body { color: var(--text-2); }

/* Numbered process steps */
.steps { counter-reset: step; display: grid; gap: var(--space-6); }
@media (min-width: 768px) { .steps { grid-template-columns: repeat(3, 1fr); gap: var(--space-6); } }
.step { counter-increment: step; padding-block-start: var(--space-5); border-block-start: 2px solid var(--line); }
.step::before {
  content: counter(step, decimal-leading-zero);
  display: block;
  margin-bottom: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--text-h2);
  line-height: 1;
  color: var(--accent);
}
.step h3 { margin-bottom: var(--space-2); }
.step p { color: var(--text-2); }

/* Why-us list */
.reasons { display: grid; gap: var(--space-5); list-style: none; padding: 0; }
@media (min-width: 768px) { .reasons { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); } }
.reason { display: flex; gap: var(--space-4); }
.reason__mark { flex-shrink: 0; color: var(--accent); margin-block-start: 2px; }
.reason h3 { margin-bottom: var(--space-1); }
.reason p { color: var(--text-2); }

/* --------------------------------------------------------------------------
   9. Founder intro (HeyGen presenter)
   -------------------------------------------------------------------------- */

.presenter { display: grid; gap: var(--space-7); align-items: center; }
@media (min-width: 1024px) { .presenter { grid-template-columns: 1fr 1.1fr; gap: var(--space-8); } }

.player {
  position: relative;
  aspect-ratio: 16 / 9;          /* reserved — no CLS when the video arrives */
  overflow: hidden;
  background: var(--ink);
  border: 1px solid rgb(255 255 255 / 0.14);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
}
.player__poster { width: 100%; height: 100%; object-fit: cover; }
.player__video { width: 100%; height: 100%; object-fit: cover; background: var(--ink); }
.player__video[hidden] { display: none; }

.player__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-6);
  text-align: center;
  color: rgb(255 255 255 / 0.62);
  background:
    radial-gradient(60% 60% at 50% 40%, rgb(70 130 174 / 0.28), transparent 70%),
    var(--ink);
}
.player__play {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px; height: 64px;
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-md);
  transition: transform 150ms ease-out, background-color 150ms ease-out;
}
.player__play:hover { background: var(--accent-hi); transform: scale(1.05); }
.player__play[disabled] { background: var(--border); cursor: not-allowed; transform: none; }
@media (prefers-reduced-motion: reduce) { .player__play { transition: none; } }

.disclosure {
  display: flex;
  gap: var(--space-2);
  margin-block-start: var(--space-4);
  font-size: var(--text-label);
  line-height: 1.5;
  color: var(--accent-dk);
}
.disclosure svg { flex-shrink: 0; margin-block-start: 2px; }

.transcript { margin-block-start: var(--space-4); }
.transcript > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  font-size: var(--text-label);
  font-weight: 600;
  color: #fff;
  cursor: pointer;
  list-style: none;
}
.transcript > summary::-webkit-details-marker { display: none; }
.transcript > summary svg { transition: transform 150ms ease; }
.transcript[open] > summary svg { transform: rotate(180deg); }
.transcript__body {
  margin-block-start: var(--space-3);
  padding: var(--space-4);
  font-size: var(--text-label);
  line-height: 1.6;
  color: rgb(255 255 255 / 0.86);
  background: rgb(255 255 255 / 0.06);
  border-radius: var(--radius-md);
}
.transcript__body p + p { margin-block-start: var(--space-3); }

/* --------------------------------------------------------------------------
   10. Flagship product block
   -------------------------------------------------------------------------- */

.flagship {
  display: grid;
  gap: var(--space-7);
  padding: var(--space-6);
  background: var(--surface-2);
  border-block-start: 3px solid var(--accent);
  border-radius: var(--radius-xl);
}
@media (min-width: 1024px) {
  /* The screenshot is a 2:1 band and the copy column is roughly twice as tall,
     so an even split leaves the image looking marooned. Give the image the
     larger share to close the gap. */
  .flagship { grid-template-columns: 1fr 1.25fr; gap: var(--space-8); padding: var(--space-8); align-items: center; }
}
.flagship__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-label);
  font-weight: 600;
  color: var(--accent-hi);
  background: var(--accent-tint);
  border-radius: var(--radius-full);
  margin-bottom: var(--space-4);
}
.flagship h2 { margin-bottom: var(--space-3); }
.flagship__body { color: var(--text-2); margin-bottom: var(--space-5); }
.flagship__list { display: grid; gap: var(--space-2); margin: 0 0 var(--space-6); padding: 0; list-style: none; }
.flagship__list li { display: flex; gap: var(--space-3); color: var(--text-2); }
.flagship__list svg { flex-shrink: 0; color: var(--accent); margin-block-start: 5px; }
/* Product screenshot. Both exports are 1200x593, so one aspect-ratio fits both
   and the box is reserved before the image arrives — no layout shift.

   The file is attached per theme. Only the background whose rule actually wins
   is fetched, so a light-theme visitor never downloads the dark capture. The
   selectors mirror the theme rules in section 2b exactly: OS preference unless
   an explicit [data-theme] overrides it, in either direction. */
.flagship__shot {
  aspect-ratio: 1200 / 593;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-3) url('assets/logo/flow-orders-light.webp') center / cover no-repeat;
  box-shadow: var(--shadow-md);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .flagship__shot {
    background-image: url('assets/logo/flow-orders-dark.webp');
  }
}
:root[data-theme='dark'] .flagship__shot {
  background-image: url('assets/logo/flow-orders-dark.webp');
}
:root[data-theme='light'] .flagship__shot {
  background-image: url('assets/logo/flow-orders-light.webp');
}

/* --------------------------------------------------------------------------
   10b. Founder
   -------------------------------------------------------------------------- */

.founder { display: grid; gap: var(--space-6); }
@media (min-width: 1024px) {
  .founder { grid-template-columns: 1fr auto; gap: var(--space-8); align-items: start; }
}
/* Measure, not full width: two long paragraphs across a 1280px container are
   unreadable. Matches the .prose cap in privacy.html. */
.founder__body { max-width: 68ch; }
.founder__body p { color: var(--text-2); }
.founder__body p + p { margin-block-start: var(--space-4); }

/* The tech list is supporting detail, not a claim — it reads at label weight so
   it does not compete with the two paragraphs above it. */
.founder__tools {
  margin-block-start: var(--space-5);
  font-size: var(--text-label);
  line-height: 1.7;
  color: var(--text-muted);
}
.founder__tools-label {
  display: block;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-block-end: var(--space-1);
}

/* justify-items:center, not start: the LinkedIn button stretches to the column
   width, so a left-aligned portrait above it sits visibly off-axis. */
.founder__aside { display: grid; gap: var(--space-4); align-content: start; justify-items: center; }
@media (min-width: 1024px) { .founder__aside { width: 15rem; } }
.founder__aside .btn { width: 100%; justify-content: center; }

/* The file is already a circle with transparent corners, so no border-radius is
   needed to shape it — the ring is decoration, and it sits on the token so it
   holds in both themes. */
.founder__photo {
  width: 11rem;
  height: auto;
  border-radius: var(--radius-full);
  box-shadow: 0 0 0 1px var(--line), var(--shadow-md);
}

.founder__link { display: inline-flex; align-items: center; gap: var(--space-2); }
.founder__aside .small { color: var(--text-muted); max-width: 28ch; text-align: center; }

/* --------------------------------------------------------------------------
   10c. Connection budget and skins

   THE MECHANISM, because it is easy to break: a background-image on an element
   that is `display: none` is NEVER REQUESTED. That is not true of an <img>,
   which downloads even when hidden — measured, 101 KB on a phone for something
   never rendered. So every heavy decorative image on this site is a background
   behind a display rule, and that is the only reason [data-conn="slow"] costs
   nothing rather than merely hiding what it already paid for.

   [data-conn="slow"] is set by the inline head script BEFORE first layout, from
   the previous visit's measured throughput plus saveData / effectiveType where
   the browser has them. Getting it after layout would be too late.
   -------------------------------------------------------------------------- */

/* A slow connection is also the best proxy the page has for a weak device.
   The scenes cost no bytes, but they cost frames, so they go too. */
:root[data-conn='slow'] .footer__canvas,
:root[data-conn='slow'] .footer__deep,
:root[data-conn='slow'] .footer__scrim,
:root[data-conn='slow'] .footer__pause { display: none; }
:root[data-conn='slow'] .footer { min-height: 0; display: block; }

:root[data-conn='slow'] .hero__photo,
:root[data-conn='slow'] .flagship__shot,
:root[data-skin='lite'] .hero__photo,
:root[data-skin='lite'] .flagship__shot { display: none; }

/* The flagship is a two-column grid; with the screenshot gone the second track
   would sit there empty. */
:root[data-conn='slow'] .flagship,
:root[data-skin='lite'] .flagship { grid-template-columns: 1fr; }

/* prefers-reduced-data is Chrome-only today, but it is the standards-track
   signal and costs nothing to honour where it exists. */
@media (prefers-reduced-data: reduce) {
  .hero__photo, .flagship__shot { display: none; }
  .flagship { grid-template-columns: 1fr; }
}

/* ---- Skin: lite -----------------------------------------------------------
   Same type scale and spacing, so it still reads as Sugibana. What goes is
   everything that costs a request or a composite: images, the canvas, the
   aurora, the grain, every shadow, every radius. The theme is pinned to light
   by the head script, so no token overrides are needed here. */

:root[data-skin='lite'] {
  --radius-sm: 0; --radius-md: 0; --radius-lg: 0; --radius-xl: 0;
  --shadow-sm: none; --shadow-md: none; --shadow-lg: none;
}
:root[data-skin='lite'] .hero__canvas,
:root[data-skin='lite'] .hero__aurora,
:root[data-skin='lite'] .hero__grain,
:root[data-skin='lite'] .hero__pause,
:root[data-skin='lite'] .footer__canvas,
:root[data-skin='lite'] .footer__deep,
:root[data-skin='lite'] .footer__scrim,
:root[data-skin='lite'] .footer__pause,
:root[data-skin='lite'] .founder__photo { display: none; }

:root[data-skin='lite'] .hero { min-height: 0; }
:root[data-skin='lite'] .footer { min-height: 0; display: block; }
:root[data-skin='lite'] .card,
:root[data-skin='lite'] .flagship,
:root[data-skin='lite'] .form-card { box-shadow: none; border: 1px solid var(--line); }
/* Underline the links, the way everything did before rounded corners. */
:root[data-skin='lite'] .founder__body a,
:root[data-skin='lite'] main p a { text-decoration: underline; }

/* ---- Skin: rustic ---------------------------------------------------------
   Dark only — the head script pins data-theme="dark" — and it gives the
   discarded rust palette somewhere to live.

   ⚠️ Light text ON the accent fill here, which looks like it contradicts the
   house rule. It does not: that rule is about LIME, which measures 1.64 under
   white. Rust is dark enough to carry it — BUILD-PROMPT §7 measured white on
   --rust-600 at 6.29. Different hue, different answer. */

:root[data-skin='rustic'] {
  --surface:     #17110E;
  --surface-2:   #1E1611;
  --surface-3:   #241A14;
  --surface-dk:  #1E1611;
  --surface-dkr: #120D0A;

  --text:        #F5EAE3;
  --text-2:      #DCC7B9;
  --text-muted:  #BFA595;

  --accent:      #E8A08C;
  --accent-hi:   #F2B9A8;
  --accent-dk:   #E8A08C;
  --accent-fill: #A83C24;
  --accent-fill-hi: #8F3220;
  --accent-on:   #FFF4EF;
  --accent-tint: #3A2018;

  --line:        #3A2A20;
  --border:      #7A6152;
}
:root[data-skin='rustic'] .hero__canvas,
:root[data-skin='rustic'] .hero__pause,
:root[data-skin='rustic'] .footer__canvas,
:root[data-skin='rustic'] .footer__deep,
:root[data-skin='rustic'] .footer__scrim,
:root[data-skin='rustic'] .footer__pause { display: none; }

:root[data-skin='rustic'] .footer { min-height: 0; display: block; }
/* The hero's navy is a LITERAL, deliberately outside the theme tokens so it
   stays dark in both. That also means a skin cannot reach it through the token
   layer, so it is restated here — otherwise a rusted cat sits on a blue field. */
:root[data-skin='rustic'] .hero { background: #2A1A12; }
:root[data-skin='rustic'] .hero__scrim {
  background: linear-gradient(180deg,
    rgb(42 26 18 / 0.80) 0%,
    rgb(42 26 18 / 0.60) 45%,
    rgb(18 13 10 / 0.55) 100%);
}
:root[data-skin='rustic'] .hero__aurora { filter: hue-rotate(-140deg) saturate(0.75); }
/* The photograph is already charcoal and copper — the rustic palette exactly —
   so the skin regrades it rather than shipping a second file. */
:root[data-skin='rustic'] .hero__photo { filter: sepia(0.3) saturate(1.15); }
/* Keep the grain — on a rust skin the texture is the point. */
:root[data-skin='rustic'] .hero__grain { opacity: 0.22; }

/* ---- The picker ------------------------------------------------------------
   A hover-opened dropdown in the nav. Three things keep it from being the bad
   kind of hover menu:
     - it also opens on click and on :focus-within, so a keyboard reaches it;
     - there is NO gap between the button and the panel, so the pointer cannot
       fall through the crack on its way down (WCAG 2.1 SC 1.4.13 "hoverable");
     - the whole thing is display:none below 1024px, where the nav is a
       hamburger and there is no hover to speak of — the overlay carries the
       same three buttons as a plain row instead. */

.skinmenu { position: relative; display: none; }
@media (min-width: 1024px) { .skinmenu:not([hidden]) { display: block; } }

.skinmenu__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  color: inherit; background: transparent; border: 0; border-radius: var(--radius-full);
  cursor: pointer;
}
/* The nav's own text colour is dark in the light theme, so `color: inherit`
   alone left this icon dark-on-navy over the hero — present, hoverable, and
   invisible. It needs the same two states .theme-toggle has. */
.nav--transparent .skinmenu__btn { color: #fff; }
.nav--solid .skinmenu__btn { color: var(--text); }
.skinmenu__btn:hover { background: color-mix(in srgb, currentColor 14%, transparent); }

.skinmenu__pop {
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: 100%;      /* flush with the button — no hover gap */
  z-index: 120;
  display: none;
  min-width: 13rem;
  padding: var(--space-3);
  background: var(--surface-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.skinmenu:hover .skinmenu__pop,
.skinmenu:focus-within .skinmenu__pop,
.skinmenu[data-open='true'] .skinmenu__pop { display: grid; gap: var(--space-2); }

/* Inside the panel the buttons sit on a light card, not the dark nav. */
.skinmenu__pop .skins__btn {
  width: 100%;
  color: var(--text);
  border-color: var(--border);
}
.skinmenu__pop .skins__btn:hover { border-color: var(--text-2); }
.skinmenu__hint {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
}

.skins__row { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-2) var(--space-3); }
.skins__btn {
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: var(--text-label);
  color: var(--text-on-dk);
  background: transparent;
  border: 1px solid rgb(255 255 255 / 0.28);
  border-radius: var(--radius-full);
  cursor: pointer;
}
.skins__btn:hover { border-color: rgb(255 255 255 / 0.55); }
.skins__btn[aria-pressed='true'] {
  color: var(--accent-on);
  background: var(--accent-fill);
  border-color: var(--accent-fill);
}
:root[data-skin='lite'] .skins__btn { border-radius: 0; }

/* --------------------------------------------------------------------------
   11. Contact — form and map
   -------------------------------------------------------------------------- */

/* Source order is lead → form → aside, which is the right reading order on a
   phone. On desktop the aside is lifted into the second column and spans both
   rows, so the phone number, the city and the map land level with the heading
   instead of a screen below it. Landing on #contact used to show a headline
   against empty navy with everything useful under the fold. */
.contact { display: grid; gap: var(--space-6); }
/* A grid item defaults to min-width: auto, so its longest unbreakable word can
   push the track wider than the viewport. That was harmless while the heading
   sat OUTSIDE this grid; moving it in made the Ukrainian page 362px wide at a
   320px viewport, because Ukrainian has the longest words on the site. This is
   exactly what the i18n gate is watching for. */
.contact > * { min-width: 0; }
@media (min-width: 1024px) {
  .contact {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    column-gap: var(--space-8);
    row-gap: var(--space-6);
    align-items: start;
  }
  .contact__lead   { grid-column: 1; grid-row: 1; }
  .contact .form-card { grid-column: 1; grid-row: 2; }
  .contact__aside  { grid-column: 2; grid-row: 1 / span 2; }
}
.contact__lead .section__head { margin-bottom: 0; }

/* Glass card keeps error/success colours on a light surface */
.form-card {
  padding: var(--space-6);
  color: var(--text);
  background: var(--form-card-bg, rgb(251 252 247 / 0.96));
  backdrop-filter: blur(12px);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
}
@media (min-width: 768px) { .form-card { padding: var(--space-6); } }

/* Back to 44px targets where the pointer is a finger. Screen height is not the
   scarce resource on a phone — the form is the only thing in its column. */
.field input, .field select, .field textarea {
  width: 100%;
  /* 40, not the 44 this used to be. Still comfortably past the 24px floor
     WCAG 2.5.8 sets for a coarse pointer, and the coarse-pointer block below
     puts 44 back where the pointer is actually a finger. Font size is NOT
     reduced: below 16px iOS zooms the viewport on focus, and there is a gate. */
  min-height: 40px;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.field textarea { min-height: 84px; resize: vertical; }
.field select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%231D4067' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right var(--space-3) center; padding-inline-end: var(--space-7); }

.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgb(168 60 36 / 0.25);
}
.field input[aria-invalid='true'], .field select[aria-invalid='true'], .field textarea[aria-invalid='true'] {
  border-color: var(--error);
}
.field input[disabled], .field select[disabled], .field textarea[disabled] {
  background: var(--surface-2);
  color: var(--border);
  cursor: not-allowed;
}

@media (pointer: coarse) {
  .field input, .field select, .field textarea { min-height: 44px; padding: var(--space-3) var(--space-4); }
  .field textarea { min-height: 120px; }
}

.field { display: flex; flex-direction: column; gap: var(--space-1); margin-bottom: var(--space-4); }
.field > label { font-size: var(--text-label); font-weight: 600; }
.field__optional { font-weight: 400; color: var(--text-muted); }

/* --------------------------------------------------------------------------
   Compact static form.

   The form has to fit the column under the heading and beside the map, all of
   it, without the section growing a scroll of its own. That constraint is why
   there is no longer a one-question-at-a-time reveal here: a form that changes
   height as you fill it cannot be laid out to fit a fixed space.

   What buys the room, in order of how much: pairing the four short fields into
   two rows, a shorter textarea, and tighter vertical rhythm. NOT smaller type
   — the inputs stay at 16px, because anything less makes iOS zoom the viewport
   on focus, and there is a gate on it.
   -------------------------------------------------------------------------- */

/* minmax(0, …) on both tracks, and at both widths. A bare `1fr` — or the
   implicit `auto` single column below 560 — sizes to max-content, so the
   longest Ukrainian label pushes the row past the viewport. */
.field-row { display: grid; gap: var(--space-4); grid-template-columns: minmax(0, 1fr); }
.field-row > * { min-width: 0; }
@media (min-width: 560px) {
  .field-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.field__error {
  display: none;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-label);
  line-height: 1.5;
  color: var(--error);
}
.field__error[data-show='true'] { display: flex; }
.field__error svg { flex-shrink: 0; }

/* Honeypot — off-screen, never display:none (bots skip hidden fields) */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --------------------------------------------------------------------------
   Questionnaire panel — floats over the page, dragged by its grip.
   -------------------------------------------------------------------------- */

.qpanel {
  position: fixed;
  z-index: 60;
  inline-size: min(23rem, calc(100vw - 2 * var(--space-4)));
  max-block-size: min(30rem, calc(100vh - 2 * var(--space-5)));
  display: flex;
  flex-direction: column;
  /* Explicit colour AND background, both from the semantic layer. It floats
     over .section--dark, so anything it leaves to inheritance it gets from a
     dark ground: the first cut asked for a --surface-1 that does not exist in
     this stylesheet, resolved to transparent, and painted --text navy straight
     onto the navy section. Nothing was readable and nothing warned. */
  color: var(--text);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.qpanel[data-dragging='true'] { user-select: none; cursor: grabbing; }

.qpanel__bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-2);
  border-block-end: 1px solid var(--border);
}
.qpanel__title { flex: 1; font-family: var(--font-body); font-size: var(--text-label); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; margin: 0; }

.qpanel__grip,
.qpanel__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 28px;
  block-size: 28px;
  padding: 0;
  color: var(--text-muted);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.qpanel__grip { cursor: grab; touch-action: none; }
.qpanel[data-dragging='true'] .qpanel__grip { cursor: grabbing; }
.qpanel__grip:hover, .qpanel__close:hover { color: var(--text); border-color: var(--border); }

.qpanel__body { padding: var(--space-4); overflow-y: auto; }
.qpanel__lede { font-size: var(--text-label); line-height: 1.5; color: var(--text-2); margin-bottom: var(--space-3); }
.qpanel__list { margin: 0 0 var(--space-4); padding: 0; list-style: none; display: grid; gap: var(--space-3); }
.qpanel__list li {
  position: relative;
  padding-inline-start: var(--space-5);
  font-size: var(--text-label);
  line-height: 1.5;
}
.qpanel__list li::before {
  /* Own counter. <li> already increments the implicit `list-item`, so
     incrementing that as well numbered the questions 2, 4, 6, 8, 10. */
  content: counter(qn);
  counter-increment: qn;
  position: absolute;
  inset-inline-start: 0;
  font-weight: 700;
  color: var(--accent);
}
.qpanel__list { counter-reset: qn; }
/* The panel is a LIGHT card that happens to live inside .section--dark, so it
   has to opt out of the on-dark button rules by hand. `.on-dark .btn--secondary`
   sets white text and a white border, which on this white card was an invisible
   button — the third time this exact trap has been paid for on this site. */
.qpanel .btn--secondary,
.on-dark .qpanel .btn--secondary {
  color: var(--text);
  border-color: var(--border);
  background: none;
}
.qpanel .btn--secondary:hover,
.on-dark .qpanel .btn--secondary:hover {
  color: var(--text);
  background: var(--surface-2);
  border-color: var(--text-muted);
}
.qpanel__insert { inline-size: 100%; min-block-size: 40px; padding-inline: var(--space-4); font-size: var(--text-label); }
.qpanel__status { margin-block-start: var(--space-2); font-size: var(--text-label); color: var(--success); }

/* On a phone it stops floating and sits at the bottom of the screen: a
   draggable window on a 390px viewport is a way to lose the form behind it. */
@media (max-width: 767px) {
  .qpanel {
    inset-inline: var(--space-3);
    inset-block-end: var(--space-3);
    inset-block-start: auto;
    inline-size: auto;
    max-block-size: 60vh;
  }
  .qpanel__grip { display: none; }
}

@media print { .qpanel { display: none !important; } }

.form__foot { display: flex; flex-direction: column; gap: var(--space-4); }
/* The note and the status are both empty until something has to be said, and
   an empty flex child still earns its gap — 32px of dead card under the Send
   button, in a form whose whole point is fitting a fixed space. */
.form__note:empty, .form__status:empty { display: none; }
.form__note { font-size: var(--text-label); line-height: 1.5; color: var(--text-2); }

/* The form card is a LIGHT surface inside a dark section, so `.section--dark a`
   (lime-300, built for dark grounds) must not reach it. */
.form-card a { color: var(--accent-hi); }
.form-card a:hover { color: var(--accent); }

.form__status { font-size: var(--text-label); line-height: 1.5; }
.form__status[data-state='error'] { color: var(--error); font-weight: 600; }
.form__status[data-state='success'] { color: var(--success); font-weight: 600; }

.form-success { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
.form-success svg { color: var(--success); }

.spinner {
  width: 16px; height: 16px;
  border: 2px solid rgb(255 255 255 / 0.4);
  border-top-color: #fff;
  border-radius: 50%;
}
@media (prefers-reduced-motion: no-preference) { .spinner { animation: spin 700ms linear infinite; } }
@keyframes spin { to { transform: rotate(360deg); } }

/* Contact side panel */
.contact__aside { display: flex; flex-direction: column; gap: var(--space-5); }
.contact__row { display: flex; gap: var(--space-3); align-items: flex-start; }
.contact__row svg { flex-shrink: 0; color: var(--accent-dk); margin-block-start: 3px; }
.contact__row a { color: #fff; font-weight: 600; text-decoration-color: var(--accent-dk); }
.contact__row a:hover { color: var(--accent-dk); }
.contact__hint { font-size: var(--text-label); color: rgb(255 255 255 / 0.72); }

/* Map — click to load, no third-party request before consent */
.map {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: linear-gradient(140deg, #24507E, #16304D);
  border: 1px solid rgb(255 255 255 / 0.16);
  border-radius: var(--radius-lg);
}
.map__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-5);
  text-align: center;
  color: #fff;
}
.map__placeholder svg { color: var(--accent-dk); }
.map__placeholder p { font-size: var(--text-label); color: rgb(255 255 255 / 0.78); max-width: 32ch; }
.map iframe { width: 100%; height: 100%; border: 0; }

/* --------------------------------------------------------------------------
   12. Footer
   -------------------------------------------------------------------------- */

.footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: var(--space-6);
  color: rgb(255 255 255 / 0.8);
  background: var(--ink);
  /* blue-400 not blue-700: 4.28 vs 2.27 on ink — the darker one is invisible */
  border-block-start: 1px solid var(--blue-400);
}
@media (min-width: 1024px) { .footer { padding-block: var(--space-8); } }

/* The space scene. Deep enough that a starfield reads as travel rather than
   as noise in a 120px strip — below that the perspective has nowhere to run. */
@media (min-width: 768px) { .footer { min-height: 34rem; display: flex; align-items: center; } }

.footer__canvas { position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%; }

/* Deep space, painted once. Static gradients under the canvas cost nothing per
   frame — this is the lesson from .hero__aurora, which is why there is no blur
   and no animation anywhere in this stack. It gives the starfield something to
   fly through instead of flat black. */
.footer__deep {
  position: absolute;
  inset: 0;
  z-index: -3;
  background:
    radial-gradient(58% 76% at 18% 26%, rgb(39 84 131 / 0.42), rgb(39 84 131 / 0) 76%),
    radial-gradient(46% 62% at 82% 68%, rgb(92 84 136 / 0.30), rgb(92 84 136 / 0) 76%),
    radial-gradient(70% 90% at 52% 108%, rgb(168 60 36 / 0.20), rgb(168 60 36 / 0) 72%);
}

/* The scene dims ITSELF behind the footer columns (see scene()'s guard box),
   which is what actually holds the contrast floor. This scrim only settles the
   ground colour so nothing reads as a bright field directly under running text. */
.footer__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg,
    rgb(16 24 40 / 0.34) 0%,
    rgb(16 24 40 / 0.08) 40%,
    rgb(16 24 40 / 0.44) 100%);
}

.footer > .container { position: relative; z-index: 1; width: 100%; }

.footer__grid { display: grid; gap: var(--space-6); }
@media (min-width: 768px) { .footer__grid { grid-template-columns: 1.4fr 1fr 1fr; gap: var(--space-7); } }

.footer__logo { margin-bottom: var(--space-4); }
.footer__logo .logo { color: #fff; }
.footer h2 {
  font-family: var(--font-body);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 0.6);
  margin-bottom: var(--space-3);
}
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.footer a { color: #fff; text-decoration: none; }
.footer a:hover { color: var(--accent-dk); text-decoration: underline; }
.footer__legal {
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid rgb(255 255 255 / 0.12);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  font-size: var(--text-label);
  color: rgb(255 255 255 / 0.72);
}

/* --------------------------------------------------------------------------
   13. Scroll reveal — opt-in, and inert under reduced motion
   -------------------------------------------------------------------------- */

/* Scoped to .js — added by script.js. If the script never runs, nothing is
   hidden and the page renders in full rather than as a blank white column. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal { opacity: 0; transform: translateY(16px); }
  .js .reveal[data-visible='true'] {
    opacity: 1;
    transform: none;
    transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
  }
}

/* --------------------------------------------------------------------------
   14. Mobile — touch targets and battery
   Measured on an iPhone 13 profile: 13 links came in at 19–23px tall, which
   fails WCAG 2.2 SC 2.5.8 (24×24 minimum, Level AA) and the 44px floor
   UI-SPEC asks for. Pointer-coarse rather than a width query, so it keys off
   the input device rather than guessing from viewport size.
   -------------------------------------------------------------------------- */

@media (pointer: coarse) {
  /* Lists of links are navigation, not prose, so the inline-link exception in
     2.5.8 does not apply to them. */
  .footer ul a,
  .footer__legal a,
  .contact__row a,
  .transcript > summary,
  .contact__hint a,
  .disclosure a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .footer ul { gap: 0; }
  .footer__legal { gap: var(--space-1) var(--space-5); }

  .logo { min-height: 44px; }

  /* Anything still icon-sized gets a hit area larger than its paint area. */
  .hero__cue, .hero__pause, .footer__pause, .nav__burger, .menu__close { min-width: 44px; min-height: 44px; }

  /* The default blue flash reads as a browser artefact on a branded page. */
  a, button, summary, [role='button'] {
    -webkit-tap-highlight-color: color-mix(in srgb, currentColor 14%, transparent);
  }
}

/* Print — the contact details are the point */
@media print {
  .nav, .menu, .hero__pause, .footer__pause, .hero__cue, .map, .player { display: none !important; }
  .footer__canvas, .footer__scrim, .footer__deep { display: none !important; }
  .footer { min-height: 0; display: block; }
  body { color: #000; background: #fff; }
  .section--dark { background: #fff; color: #000; }
}
