/* =============================================================================
   ODUEX core.css
   THE design system. Every page loads this file first and consumes it verbatim.

   CONTRACT FOR PAGE BUILDERS (full detail in /PAGE-CONTRACT.md):
   1. Page CSS lives in /assets/css/pages/<name>.css and may only add
      page-scoped rules. It must NEVER redefine a token declared here.
   2. Ad-hoc values are banned in page CSS. If a value is not a token, it is
      not allowed. Ask for a token to be added here instead.
   3. --gold-surface is a FILL. It may never sit on a `color:` property against
      a light background (2.29:1, which fails AA and AA-large). Use var(--gold-text),
      which resolves to --gold-ink on light and --gold-surface on the dark stage.
   4. Never write `background-color:`, `border-color:` or `outline-color:` with
      a gold value. Use the shorthands `background:`, `border: 1px solid …`,
      `outline: 2px solid …`. The guard script greps for `color:` followed by a
      gold value and cannot tell `background-color:` from `color:`.
   5. Exactly three font stacks exist: --font-display, --font-body, --font-mono.
      There are exactly three font-family property declarations outside the
      @font-face block, and they are all in the "Typography" section below.
      Do not add a fourth anywhere in the codebase.
   6. Gold budget: at most three gold moments per viewport, and the primary CTA
      is always one of them. Buy drama back with the dark stage, not more gold.
   ============================================================================= */

/* =============================================================================
   1. TOKENS
   ============================================================================= */

:root{
  /* ── Surfaces: warm neutral ramp. Ported verbatim from the old style.css.
        These are WARM off-whites, not cold greys. Warm + gold = luxury;
        cold grey + gold = tarnished. DO NOT TOUCH THIS RAMP. ── */
  --paper:         #ffffff;
  --paper-raised:  #fafaf8;
  --paper-card:    #f5f3ef;
  --paper-sunk:    #eeebe4;
  --line:          #e0ddd6;   /* decorative hairlines, card and section edges */
  --line-strong:   #d4d0c8;   /* decorative, one step stronger */
  --control-line:  #8a857b;   /* INTERACTIVE borders only: inputs, secondary
                                 buttons. 3.67:1 on paper, 3.31:1 on card,
                                 3.08:1 on sunk. WCAG 2.2 AA 1.4.11 requires
                                 3:1 for the boundary of a form control, and
                                 --line-strong is 1.54:1, decorative only.
                                 Never use --line or --line-strong on a field. */

  /* ── Ink ── */
  --ink:           #111111;   /* headings            18.9:1 on paper */
  --ink-body:      #333333;   /* body                12.6:1 */
  --ink-muted:     #5a5a5a;   /* secondary            6.9:1 */
  --ink-faint:     #6b6b6b;   /* LOWEST PERMITTED     5.3:1 */
  /* #999999 (2.85:1) is BANNED. Nothing lighter than --ink-faint carries text. */

  /* ── Gold: TWO tokens with non-overlapping jobs. ── */
  --gold-surface:       #C9A84C;  /* FILLS ONLY. Black on this = 9.19:1 */
  --gold-surface-hover: #B8922E;
  --gold-ink:           #8B6914;  /* TEXT ONLY on light. 5.09:1 */
  --gold-tint-08:  rgba(201,168,76,0.08);
  --gold-tint-16:  rgba(201,168,76,0.16);
  --gold-tint-32:  rgba(201,168,76,0.32);
  --gold-tint-40:  rgba(201,168,76,0.40);
  /* HARD RULE: --gold-surface may NEVER appear on a `color:` property against a
     light background. #C9A84C on white is 2.29:1, so it fails AA normal (4.5:1)
     AND AA-large (3:1). Always write gold text as var(--gold-text). */

  /* ── Contextual pair. These two flip inside .stage so a component written
        once is correct on both surfaces with zero overrides. ── */
  --gold-text:  var(--gold-ink);   /* the ONLY approved gold `color:` value */
  --on-gold:    #111111;           /* text/icon colour on a gold fill (9.19:1) */
  --focus-ring: var(--gold-ink);
  --field-bg:   var(--paper);

  /* ── Dark stage: deliberate, not residue ── */
  --stage:          #0C0C0D;
  --stage-raised:   #141416;
  --stage-line:     #26262A;
  --stage-ink:      #F5F3EF;
  --stage-ink-muted:#A5A29B;
  /* On --stage, --gold-surface = 8.46:1 → gold IS permitted as text here,
     which is why .stage re-points --gold-text at it. */

  /* ── Semantic ── */
  --ok:     #15803D;   /* text-safe green on light (4.9:1) */
  --ok-fill:#22C55E;   /* fills and status dots only */

  /* ── WhatsApp brand green. FILL ONLY, always with --ink on top.
        #25D366 + #111111 = 9.52:1. White on it is 1.98:1 and is banned.
        The darker edge gives the pill a 4.14:1 boundary against white,
        satisfying WCAG 1.4.11 for a non-text UI component. ── */
  --wa:        #25D366;
  --wa-hover:  #1EBE5A;
  --wa-edge:   #128C7E;
  --warn:   #B45309;
  --err:    #B91C1C;

  /* ── Elevation: built for LIGHT surfaces. Layered, low-alpha, warm-tinted.
        Nothing above 0.10 alpha on white. ── */
  --elev-1: 0 1px 2px rgba(17,17,17,0.04);
  --elev-2: 0 1px 2px rgba(17,17,17,0.04), 0 8px 24px rgba(17,17,17,0.06);
  --elev-3: 0 1px 2px rgba(17,17,17,0.04), 0 8px 24px rgba(17,17,17,0.06),
            0 24px 48px rgba(17,17,17,0.05);
  --elev-gold: 0 8px 32px rgba(201,168,76,0.16);

  /* ── Type stacks. Three. Forever. ── */
  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body:    'Inter Tight', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ── Fluid type scale ── */
  --fs-xs:  clamp(0.75rem, 1.5vw, 0.80rem);
  --fs-sm:  clamp(0.85rem, 1.8vw, 0.90rem);
  --fs-base:clamp(0.95rem, 2.0vw, 1.00rem);
  --fs-lg:  clamp(1.05rem, 2.2vw, 1.15rem);
  --fs-xl:  clamp(1.20rem, 2.5vw, 1.35rem);
  --fs-2xl: clamp(1.40rem, 3.0vw, 1.75rem);
  --fs-3xl: clamp(1.80rem, 4.0vw, 2.50rem);
  --fs-4xl: clamp(2.20rem, 5.0vw, 3.50rem);
  --fs-5xl: clamp(2.80rem, 6.0vw, 4.60rem);
  --fs-hero:clamp(2.90rem, 7.0vw, 5.60rem);

  /* ── Tracking: one token per size step, no exceptions ── */
  --ls-display: -0.028em;  /* --fs-4xl and above */
  --ls-tight:   -0.028em;  /* alias of --ls-display, used by hero specs */
  --ls-heading: -0.018em;  /* --fs-2xl to --fs-3xl */
  --ls-body:    -0.005em;
  --ls-label:    0.14em;   /* mono eyebrows, uppercase only */

  /* ── Line heights ── */
  --lh-display:  1.05;
  --lh-heading:  1.18;
  --lh-body:     1.65;
  --lh-tight-ui: 1.35;

  /* ── Measures ── */
  --measure:        68ch;  /* body copy */
  --measure-narrow: 62ch;  /* editorial / founder copy */

  /* ── Spacing: 4px base. Component scale. ── */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-14: 56px;
  --space-16: 64px;
  /* Brief-compatibility aliases. BUILD-BRIEF.md names these --s-*.
     Both resolve identically; prefer --space-* in new code. */
  --s-1: var(--space-1); --s-2: var(--space-2);  --s-3: var(--space-3);
  --s-4: var(--space-4); --s-6: var(--space-6);  --s-8: var(--space-8);
  --s-12: var(--space-12);

  /* ── Section rhythm, exactly THREE values. Bias upward. ── */
  --section-sm: clamp(64px,  8vw,  96px);
  --section-md: clamp(96px, 12vw, 160px);
  --section-lg: clamp(140px,18vw, 220px);

  /* ── Containers, exactly THREE ── */
  --container:        1200px;
  --container-narrow: 720px;   /* founder story, editorial, legal */
  --container-wide:   1440px;  /* full-bleed visuals, dark stage */
  --gutter: clamp(20px, 5vw, 48px);

  /* ── Radii ── */
  --r-sm:  6px;    /* buttons, inputs, tags */
  --r-md:  10px;
  --r-lg:  16px;   /* cards */
  --r-xl:  24px;
  --r-pill:999px;
  /* Brief-compatibility aliases */
  --radius-sm: var(--r-sm); --radius-md: var(--r-md);
  --radius-lg: var(--r-lg); --radius-xl: var(--r-xl);

  /* ── Z-index scale. Nothing outside this list. ── */
  --z-base:      0;
  --z-raised:    1;
  --z-sticky:    100;  /* mobile sticky CTA bar */
  --z-float:     200;  /* WhatsApp pill, assistant launcher */
  --z-nav:       300;
  --z-progress:  350;  /* scroll progress rail, sits above nav hairline */
  --z-drawer:    400;  /* mobile nav drawer + assistant panel */
  --z-skip:      500;  /* skip link, always reachable */

  /* ── Motion ── */
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.30, 1);   /* reveals, drawers, panels */
  --ease-out-quart: cubic-bezier(0.22, 1, 0.36, 1);   /* hovers, small UI */
  --ease-back:      cubic-bezier(0.34, 1.56, 0.64, 1);/* one overshoot moment only */
  --d-fast:   180ms;
  --d-base:   320ms;
  --d-slow:   640ms;
  --d-reveal: 800ms;

  /* ── Furniture metrics ── */
  --nav-h: 72px;
}

/* The dark stage. Adding .stage to a section re-points the surface, ink,
   line and semantic tokens so every child component is correct with no
   per-child overrides. This is the only sanctioned way to go dark.
   The footer and the mobile drawer are permanently dark, so they inherit the
   same flip, which is why `color: var(--gold-text)` is safe inside them. */
.stage,
.footer,
.nav-drawer-panel{
  --paper:        var(--stage);
  --paper-raised: var(--stage-raised);
  --paper-card:   var(--stage-raised);
  --paper-sunk:   #1B1B1E;
  --line:         var(--stage-line);
  --line-strong:  #34343A;
  --control-line: #6a6a74;   /* 3.44:1 on --stage-raised, 3.66:1 on --stage */

  --ink:        var(--stage-ink);
  --ink-body:   #DDD9D1;
  --ink-muted:  var(--stage-ink-muted);
  --ink-faint:  #8E8B85;

  --gold-text:  var(--gold-surface);  /* 8.46:1 on #0C0C0D, permitted here */
  --focus-ring: var(--gold-surface);
  --field-bg:   var(--stage-raised);

  --ok:   #4ADE80;
  --warn: #FBBF24;
  --err:  #F87171;

  --elev-1: 0 1px 2px rgba(0,0,0,0.30);
  --elev-2: 0 1px 2px rgba(0,0,0,0.30), 0 8px 24px rgba(0,0,0,0.36);
  --elev-3: 0 1px 2px rgba(0,0,0,0.30), 0 8px 24px rgba(0,0,0,0.36),
            0 24px 48px rgba(0,0,0,0.32);

  background: var(--stage);
  color: var(--ink-body);
  color-scheme: dark;
}

@media (max-width: 767px){
  :root{ --nav-h: 64px; }
}

/* =============================================================================
   2. RESET
   ============================================================================= */

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

html{
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + var(--space-4));
  scrollbar-gutter: stable;
}

body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul{ margin: 0; }
ol[class], ul[class]{ list-style: none; padding: 0; }

body{
  min-height: 100svh;
  background: var(--paper);
  color: var(--ink-body);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
}

/* Body scroll lock for the drawer and any modal overlay.
   Set as a class, never as an inline style. */
html.is-locked, html.is-locked body{ overflow: hidden; }

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

input, button, textarea, select{ font: inherit; color: inherit; }
button{ background: none; border: 0; padding: 0; cursor: pointer; }

a{ color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.18em; }

table{ border-collapse: collapse; width: 100%; }
hr{ border: 0; border-top: 1px solid var(--line); margin: var(--space-12) 0; }

[hidden]{ display: none !important; }
[inert]{ cursor: default; }

::selection{ background: var(--gold-tint-32); color: var(--ink); }

/* =============================================================================
   3. FONTS: self-hosted, three faces, no third-party request.
   ascent/descent/line-gap overrides are matched to the fallback stack's line
   box so the swap does not move a single line of text (CLS budget is 0.02).
   ============================================================================= */

@font-face{
  font-family: 'Fraunces';
  src: url('../fonts/fraunces-var.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  ascent-override: 92%;
  descent-override: 22%;
  line-gap-override: 0%;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
                 U+2074, U+20AC, U+2122, U+2191, U+2192, U+2212, U+2215,
                 U+2713, U+FEFF, U+FFFD;
}

@font-face{
  font-family: 'Inter Tight';
  src: url('../fonts/inter-tight-var.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  ascent-override: 96%;
  descent-override: 24%;
  line-gap-override: 0%;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
                 U+2074, U+20AC, U+2122, U+2191, U+2192, U+2212, U+2215,
                 U+2713, U+FEFF, U+FFFD;
}

@font-face{
  font-family: 'JetBrains Mono';
  src: url('../fonts/jetbrains-mono.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  ascent-override: 93%;
  descent-override: 24%;
  line-gap-override: 0%;
  unicode-range: U+0000-00FF, U+2000-206F, U+2191, U+2192, U+2212, U+2713;
}

/* =============================================================================
   4. TYPOGRAPHY
   The three font-family declarations below are the ONLY ones in the codebase
   outside the @font-face block. Every element that needs the display or mono
   face is added to one of these two selector lists, and a page must NEVER
   write a fourth `font-family` declaration. build/check.mjs counts them.
   ============================================================================= */

body{ font-family: var(--font-body); }

h1, h2, h3, h4, h5, h6,
.display-hero, .display-1, .display-2, .display-3,
.quote blockquote,
.disclosure-trigger,
.nav-drawer-list a{ font-family: var(--font-display); }

.mono, .eyebrow, .tag, code, kbd, samp, pre,
.step-num,
.footer h2{ font-family: var(--font-mono); }

/* Heading defaults. Sizes are set by the .display-* classes; these are the
   sensible fallbacks so raw markup never looks unstyled. */
h1, h2, h3, h4, h5, h6{
  color: var(--ink);
  font-weight: 500;
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-heading);
  text-wrap: balance;
}

h1{ font-size: var(--fs-5xl); line-height: var(--lh-display); letter-spacing: var(--ls-display); }
h2{ font-size: var(--fs-3xl); }
h3{ font-size: var(--fs-2xl); }
h4{ font-size: var(--fs-xl); }
h5{ font-size: var(--fs-lg); }
h6{ font-size: var(--fs-base); }

.display-hero{
  font-size: var(--fs-hero);
  font-weight: 500;
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  text-wrap: balance;
}
.display-1{
  font-size: var(--fs-5xl);
  font-weight: 500;
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  text-wrap: balance;
}
.display-2{
  font-size: var(--fs-4xl);
  font-weight: 500;
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-display);
  color: var(--ink);
  text-wrap: balance;
}
.display-3{
  font-size: var(--fs-2xl);
  font-weight: 500;
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-heading);
  color: var(--ink);
  text-wrap: balance;
}

/* The one sanctioned gold emphasis inside a heading. */
h1 em, h2 em, .display-hero em, .display-1 em, .display-2 em{
  font-style: italic;
  color: var(--gold-text);
}

p{ max-width: var(--measure); }

.eyebrow{
  display: block;
  font-size: var(--fs-xs);
  font-weight: 500;
  line-height: var(--lh-tight-ui);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: var(--space-4);
}

.lede{
  font-size: var(--fs-lg);
  line-height: var(--lh-body);
  color: var(--ink-body);
  max-width: var(--measure-narrow);
}

.muted{ color: var(--ink-muted); }
.faint{ color: var(--ink-faint); }
.gold-text{ color: var(--gold-text); }

.measure{ max-width: var(--measure); }
.measure-narrow{ max-width: var(--measure-narrow); }

.mono{
  font-size: var(--fs-sm);
  font-weight: 500;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.prose > * + *{ margin-top: var(--space-6); }
.prose h2{ margin-top: var(--space-12); }
.prose h3{ margin-top: var(--space-10); }
.prose ul, .prose ol{ padding-left: var(--space-6); max-width: var(--measure); }
.prose li + li{ margin-top: var(--space-2); }
.prose a{ color: var(--ink); text-decoration: underline; text-decoration-color: var(--gold-tint-40); }
.prose a:hover{ text-decoration-color: var(--gold-surface); }

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

/* =============================================================================
   5. FOCUS: visible on paper and on stage. Never remove a focus outline
   without putting a visible replacement in its place.
   ============================================================================= */

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible{
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* On a gold fill, a gold ring is invisible, so use ink, which is dark on light
   and light on stage. */
.btn-primary:focus-visible,
.tag-live:focus-visible{ outline: 2px solid var(--ink); }

.skip-link{
  position: fixed;
  top: var(--space-2);
  left: var(--space-2);
  z-index: var(--z-skip);
  display: inline-block;
  padding: var(--space-3) var(--space-5);
  background: var(--gold-surface);
  color: var(--on-gold);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--r-sm);
  box-shadow: var(--elev-2);
  transform: translateY(calc(-100% - var(--space-4)));
  transition: transform var(--d-fast) var(--ease-out-quart);
}
.skip-link:focus-visible{ transform: translateY(0); }

/* =============================================================================
   6. LAYOUT PRIMITIVES
   ============================================================================= */

.wrap{
  width: 100%;
  max-width: calc(var(--container) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap-narrow{
  width: 100%;
  max-width: calc(var(--container-narrow) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap-wide{
  width: 100%;
  max-width: calc(var(--container-wide) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section{ padding-block: var(--section-md); }
.section-sm{ padding-block: var(--section-sm); }
.section-lg{ padding-block: var(--section-lg); }

.section-raised{
  background: var(--paper-raised);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.section-sunk{
  background: var(--paper-card);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* Section header block: eyebrow + h2 + lede. */
.section-head{ margin-bottom: var(--space-12); max-width: var(--measure-narrow); }
.section-head > * + *{ margin-top: var(--space-4); }
.section-head-wide{ max-width: var(--measure); }

.grid-2, .grid-3, .grid-4{
  display: grid;
  gap: var(--gap, var(--space-8));
}
.grid-2{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3{ grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4{ grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 1099px){
  .grid-4{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 979px){
  .grid-3{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 759px){
  .grid-2{ grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 559px){
  .grid-3, .grid-4{ grid-template-columns: minmax(0, 1fr); }
}

/* Vertical rhythm helper for simple stacks. */
.stack > * + *{ margin-top: var(--stack-gap, var(--space-6)); }
.cluster{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap, var(--space-4));
}

/* =============================================================================
   7. BUTTONS: min 44px touch target, real focus rings, transform-only motion.
   ============================================================================= */

.btn-primary, .btn-secondary, .btn-ghost{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: var(--space-3) var(--space-6);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: var(--lh-tight-ui);
  letter-spacing: 0;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--d-fast) var(--ease-out-quart),
              opacity var(--d-fast) var(--ease-out-quart),
              background-color var(--d-fast) var(--ease-out-quart),
              border-color var(--d-fast) var(--ease-out-quart),
              color var(--d-fast) var(--ease-out-quart);
}

.btn-primary{
  background: var(--gold-surface);
  color: var(--on-gold);
  border: 1px solid var(--gold-surface);
}
.btn-primary:hover{ background: var(--gold-surface-hover); border: 1px solid var(--gold-surface-hover); }
.btn-primary:active{ transform: scale(0.98); }

.btn-secondary{
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--control-line);
}
.btn-secondary:hover{ background: var(--paper-card); border: 1px solid var(--ink-faint); }
.btn-secondary:active{ transform: scale(0.98); }

.btn-ghost{
  background: transparent;
  color: var(--ink-body);
  border: 1px solid transparent;
  padding-inline: var(--space-3);
}
.btn-ghost:hover{ color: var(--ink); background: var(--paper-card); }
.btn-ghost:active{ transform: scale(0.98); }

.btn-sm{
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  font-size: var(--fs-sm);
}
.btn-block{ display: flex; width: 100%; }

.btn-primary[disabled], .btn-secondary[disabled], .btn-ghost[disabled],
.btn-primary[aria-disabled="true"]{
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/* Text link with a moving arrow, the secondary CTA pattern. */
.link-arrow{
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--gold-tint-40);
}
.link-arrow .arrow{
  display: inline-block;
  transition: transform var(--d-fast) var(--ease-out-quart);
}
.link-arrow:hover{ border-bottom: 1px solid var(--gold-surface); }
.link-arrow:hover .arrow{ transform: translateX(4px); }

/* The expectation-setter that always sits under the primary CTA. */
.cta-note{
  margin-top: var(--space-3);
  font-size: var(--fs-sm);
  color: var(--ink-muted);
  max-width: 44ch;
}
.btn-row{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

/* =============================================================================
   8. CARDS, TAGS, PANELS, FRAMES
   ============================================================================= */

.card{
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--space-8);
  box-shadow: var(--elev-1);
}
.card > * + *{ margin-top: var(--space-4); }
.card h3, .card h4{ color: var(--ink); }
.card p{ color: var(--ink-body); max-width: none; }

/* Interactive card. The whole card is clickable via a stretched link:
   <article class="card card-interactive"><h3><a class="card-link" href="…">…</a></h3>…</article> */
.card-interactive{
  position: relative;
  transition: transform var(--d-base) var(--ease-out-quart),
              border-color var(--d-base) var(--ease-out-quart),
              box-shadow var(--d-base) var(--ease-out-quart);
}
.card-link{ text-decoration: none; color: inherit; }
.card-link::after{
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.card-interactive:hover,
.card-interactive:focus-within{
  transform: translateY(-4px);
  border-color: var(--gold-tint-40);
  box-shadow: var(--elev-2);
}
.card-interactive:hover .arrow,
.card-interactive:focus-within .arrow{ transform: translateX(4px); }
.card-interactive .arrow{ transition: transform var(--d-base) var(--ease-out-quart); }
/* The ring is drawn around the WHOLE card, because the whole card is the target.
   The link's own ring is made transparent rather than removed, so the focus
   indicator is never absent; it has simply moved to the parent. */
.card-interactive:focus-within{ outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.card-interactive .card-link:focus-visible{ outline: 2px solid transparent; }

.card-quiet{ background: transparent; box-shadow: none; }

/* Panels: a quieter block for policy copy, comparisons, callouts. */
.panel{
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--space-8);
}
.panel > * + *{ margin-top: var(--space-4); }
.panel-gold{
  background: var(--gold-tint-08);
  border: 1px solid var(--gold-tint-32);
}

/* Tags. The honest taxonomy. Never invent a fourth variant. */
.tag-row{
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.tag{
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 5px var(--space-3);
  border-radius: var(--r-sm);
  font-size: var(--fs-xs);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  white-space: nowrap;
}
/* Live client project. The ONLY tag permitted on cuttingedgehairsalon.co.uk. */
.tag-live{
  background: var(--gold-surface);
  color: var(--on-gold);
  border: 1px solid var(--gold-surface);
}
/* Demo build. Neutral grey. Never green, never pulsing. */
.tag-demo{
  background: var(--paper-sunk);
  color: var(--ink-muted);
  border: 1px solid var(--line-strong);
}
/* System: in production / capability demo. */
.tag-system{
  background: transparent;
  color: var(--ink-body);
  border: 1px solid var(--line-strong);
}

/* Fixed-aspect image frame: zero CLS, warm skeleton, no third-party source. */
.img-frame{
  position: relative;
  overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: var(--paper-card);
  aspect-ratio: var(--ratio, 16 / 10);
}
/* A <picture> is a wrapper, not a box. Left inline it collapses to auto height
   and the img below stops resolving height:100% against the fixed ratio. */
.img-frame picture{
  display: block;
  width: 100%;
  height: 100%;
}
.img-frame img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--d-slow) var(--ease-out-quart);
}
.img-frame-zoom:hover img,
.card-interactive:hover .img-frame img{ transform: scale(1.03); }
/* Gold hairline that wipes in across the top of a hovered frame. */
.img-frame::after{
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--gold-surface);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--d-base) var(--ease-out-quart);
}
.img-frame-zoom:hover::after,
.card-interactive:hover .img-frame::after,
.card-interactive:focus-within .img-frame::after{ transform: scaleX(1); }

figure{ margin: 0; }
figcaption{
  margin-top: var(--space-3);
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

/* Checklist: "included in every build", "who this isn't for". */
.list-check{ display: grid; gap: var(--space-4); }
.list-check li{
  position: relative;
  padding-left: var(--space-8);
  color: var(--ink-body);
}
.list-check li::before{
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--gold-surface);
  transform: rotate(45deg);
}
.list-cross li::before{
  background: transparent;
  border: 1px solid var(--line-strong);
}

/* Pull quote / testimonial. Never ships without a real name and business. */
.quote{
  border-left: 2px solid var(--gold-surface);
  padding-left: var(--space-6);
}
.quote blockquote{
  font-size: var(--fs-xl);
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-heading);
  color: var(--ink);
  max-width: var(--measure-narrow);
}
.quote figcaption{
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}
.quote figcaption img{
  width: 44px;
  height: 44px;
  border-radius: var(--r-pill);
  object-fit: cover;
}

/* Numbered process steps. The connecting rule is drawn by --steps-p
   (0 → 1); it defaults to fully drawn so it is correct with JS off. */
.steps{ position: relative; display: grid; gap: var(--space-10); }
.steps::before{
  content: '';
  position: absolute;
  left: 19px;
  top: 12px;
  bottom: 12px;
  width: 1px;
  background: var(--line-strong);
  transform: scaleY(var(--steps-p, 1));
  transform-origin: top;
}
.step{
  position: relative;
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}
.step-num{
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  background: var(--paper);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-muted);
  transition: border-color var(--d-base) var(--ease-out-quart),
              color var(--d-base) var(--ease-out-quart);
}
.step.is-active .step-num{
  border: 1px solid var(--gold-surface);
  color: var(--gold-text);
}
.step-body > * + *{ margin-top: var(--space-2); }
.step-body h3{ font-size: var(--fs-xl); }

/* Status dot: the single permitted ambient loop per viewport. */
.dot-live{
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--ok-fill);
  animation: dot-pulse 2.4s var(--ease-out-quart) infinite;
}
@keyframes dot-pulse{
  0%, 100%{ opacity: 1; transform: scale(1); }
  50%     { opacity: 0.45; transform: scale(0.82); }
}

/* =============================================================================
   9. DISCLOSURE: automation catalogue rows and FAQ accordions.
   Real <button> + panel. Height animated via grid-template-rows, never
   max-height guessing. Open by default when JS is unavailable.
   ============================================================================= */

.disclosure{
  border-top: 1px solid var(--line);
}
.disclosure:last-of-type{ border-bottom: 1px solid var(--line); }

.disclosure-trigger{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  width: 100%;
  min-height: 64px;
  padding: var(--space-5) 0;
  text-align: left;
  font-size: var(--fs-xl);
  font-weight: 500;
  line-height: var(--lh-tight-ui);
  letter-spacing: var(--ls-heading);
  color: var(--ink);
  transition: color var(--d-fast) var(--ease-out-quart);
}
.disclosure-trigger:hover{ color: var(--gold-text); }

.disclosure-icon{
  flex: 0 0 auto;
  position: relative;
  width: 20px;
  height: 20px;
}
.disclosure-icon::before,
.disclosure-icon::after{
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 1.5px;
  margin-left: -7px;
  margin-top: -0.75px;
  background: var(--ink-muted);
  transition: transform var(--d-base) var(--ease-out-expo),
              opacity var(--d-base) var(--ease-out-expo);
}
.disclosure-icon::after{ transform: rotate(90deg); }
.disclosure-trigger[aria-expanded="true"] .disclosure-icon::after{
  transform: rotate(90deg) scaleX(0);
  opacity: 0;
}

.disclosure-panel{
  display: grid;
  grid-template-rows: 1fr;              /* open by default, no JS, no trap */
  transition: grid-template-rows var(--d-base) var(--ease-out-expo);
}
/* Deliberately gated on .js-ready (set by core.js itself), NOT on .js (set by
   boot.js). If core.js ever fails to load, an accordion gated on .js would trap
   its content closed forever with no way to open it. Gated this way, the panels
   simply stay open and readable. Reveals can gate on .js because a failure there
   only costs the animation; here it would cost the content. */
.js-ready .disclosure-panel{ grid-template-rows: 0fr; }
.js-ready .disclosure-panel.is-open{ grid-template-rows: 1fr; }
.disclosure-inner{ overflow: hidden; }
.disclosure-inner > *{ padding-bottom: var(--space-6); }
.disclosure-inner > * + *{ margin-top: var(--space-4); }

/* =============================================================================
   10. ENQUIRY FORM: one component, every placement.
   Every input carries a visible <label for>. Never label a field with hint
   text inside the box: it disappears on focus and leaves a screen reader an
   unlabelled edit box on the highest-intent form on the site.
   ============================================================================= */

.enquiry-form{
  display: grid;
  gap: var(--space-5);
}
/* Wrapper created by core.js so the success panel can height-transition in
   place. overflow is clipped only while the swap runs, so it can never clip a
   focus ring during normal use. */
.form-swap{ transition: height var(--d-base) var(--ease-out-expo); }
.form-swap.is-swapping{ overflow: hidden; }
.form-grid-2{
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 639px){
  .form-grid-2{ grid-template-columns: minmax(0, 1fr); }
}

.field{ display: grid; gap: var(--space-2); }
.field label{
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-tight-ui);
  color: var(--ink);
}
.field .req{ color: var(--gold-text); }
.field .hint{
  font-size: var(--fs-sm);
  color: var(--ink-muted);
}

.field input,
.field textarea,
.field select{
  width: 100%;
  min-height: 52px;
  padding: var(--space-3) var(--space-4);
  background: var(--field-bg);
  color: var(--ink);
  border: 1px solid var(--control-line);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
  line-height: var(--lh-tight-ui);
  transition: border-color var(--d-fast) var(--ease-out-quart),
              background-color var(--d-fast) var(--ease-out-quart);
}
.field textarea{
  min-height: 148px;
  resize: vertical;
  line-height: var(--lh-body);
}
.field input:hover,
.field textarea:hover,
.field select:hover{ border-color: var(--ink-faint); }

.field input:focus,
.field textarea:focus,
.field select:focus{ border-color: var(--ink); }

.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"],
.field select[aria-invalid="true"]{
  border-color: var(--err);
  background: color-mix(in srgb, var(--err) 5%, var(--field-bg));
}

.field-error{
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--err);
}

/* Select needs its own arrow because appearance:none removes the native one. */
.select-wrap{ position: relative; }
.select-wrap select{
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--space-10);
}
.select-wrap::after{
  content: '';
  position: absolute;
  right: var(--space-4);
  top: 50%;
  width: 10px;
  height: 6px;
  margin-top: -3px;
  background: var(--ink-muted);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  pointer-events: none;
}

/* Honeypot. Must be invisible to humans and to assistive tech. */
.hp{
  display: none !important;
}

.form-note{
  font-size: var(--fs-sm);
  color: var(--ink-muted);
  max-width: var(--measure-narrow);
}
.form-status{ font-size: var(--fs-sm); }
.form-status:empty{ display: none; }

.form-error{
  display: grid;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--err);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--err) 6%, var(--paper));
  color: var(--ink-body);
  font-size: var(--fs-sm);
}
.form-error a{ color: var(--ink); font-weight: 600; }

.enquiry-form[aria-busy="true"] .btn-primary{ opacity: 0.7; cursor: progress; }
.enquiry-form[aria-busy="true"] .field input,
.enquiry-form[aria-busy="true"] .field textarea,
.enquiry-form[aria-busy="true"] .field select{ opacity: 0.7; }

.form-success{
  display: grid;
  gap: var(--space-5);
  padding: var(--space-8);
  border: 1px solid var(--gold-tint-32);
  border-radius: var(--r-lg);
  background: var(--gold-tint-08);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--d-base) var(--ease-out-expo),
              transform var(--d-base) var(--ease-out-expo);
}
.form-success.is-in{ opacity: 1; transform: none; }
/* Guaranteed end state. core.js adds this on a timer, because a CSS transition
   stalls in a tab that is never painted and a confirmation panel is the one
   element on the site that must be visible no matter what. */
.form-success.is-settled{ opacity: 1; transform: none; transition: none; }
.form-success h3{ font-size: var(--fs-2xl); }
.form-success:focus-visible{ outline: 2px solid var(--focus-ring); outline-offset: 3px; }

/* =============================================================================
   11. NAV
   ============================================================================= */

.scroll-progress{
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: var(--z-progress);
  pointer-events: none;
}
.scroll-progress-bar{
  height: 100%;
  background: var(--gold-surface);
  transform: scaleX(var(--progress, 0));
  transform-origin: left;
}

.nav{
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  height: var(--nav-h);
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-bottom: 1px solid transparent;
  transition: border-color var(--d-base) var(--ease-out-quart);
}
.nav.is-scrolled{ border-bottom: 1px solid var(--line); }

.nav-inner{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  height: 100%;
  /* Transform-only condense: the header keeps a constant height so nothing
     below it ever reflows. --nav-p is 0 → 1 over the first 120px of scroll. */
  transform: scale(calc(1 - 0.06 * var(--nav-p, 0)));
  transform-origin: left center;
  /* No permanent will-change. A sticky, backdrop-filtered bar that is also a
     forced compositing layer for the whole session is exactly the kind of
     always-on layer promotion the performance budget forbids. */
}

.nav-logo{
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  color: var(--ink);
  text-decoration: none;
  view-transition-name: brand;
}
/* The logo file is 417x106, so 3.934:1. Height drives it and width follows,
   which keeps the ratio honest whatever the height. The max-width guard is a
   safety net for very narrow viewports: it stops the wordmark from ever
   crowding the hamburger out of the bar. */
.nav-logo-img{ display: block; height: 26px; width: auto; max-width: 44vw; }
@media (min-width: 1024px){
  .nav-logo-img{ height: 30px; }
}

.nav-links{ position: relative; display: none; }
@media (min-width: 1024px){
  .nav-links{ display: block; }
}
.nav-list{
  display: flex;
  align-items: center;
  gap: var(--space-6);
}
.nav-list a{
  display: inline-flex;
  align-items: center;
  height: 44px;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--ink-body);
  text-decoration: none;
  transition: color var(--d-fast) var(--ease-out-quart);
}
.nav-list a:hover{ color: var(--ink); }
.nav-list a[aria-current="page"]{ color: var(--ink); font-weight: 600; }

/* Single shared underline that slides between links. Transform only. */
.nav-underline{
  position: absolute;
  left: 0;
  bottom: 4px;
  width: 1px;
  height: 2px;
  background: var(--gold-surface);
  transform: translateX(var(--u-x, 0)) scaleX(var(--u-w, 0));
  transform-origin: left;
  transition: transform var(--d-base) var(--ease-out-expo);
  pointer-events: none;
}

.nav-actions{
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 0 0 auto;
}
.nav-cta{ display: none; }
@media (min-width: 1024px){
  .nav-cta{ display: inline-flex; }
}

/* Hamburger. Morphs to an X. Transform only. */
.nav-toggle{
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin-right: calc(var(--space-3) * -1);
  border-radius: var(--r-sm);
  color: var(--ink);
}
@media (min-width: 1024px){
  .nav-toggle{ display: none; }
}
.nav-toggle-bars{
  position: relative;
  width: 22px;
  height: 14px;
}
.nav-toggle-bars span{
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transition: transform var(--d-base) var(--ease-out-expo),
              opacity var(--d-fast) var(--ease-out-expo);
}
.nav-toggle-bars span:nth-child(1){ top: 0; }
.nav-toggle-bars span:nth-child(2){ top: 6.25px; }
.nav-toggle-bars span:nth-child(3){ top: 12.5px; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1){ transform: translateY(6.25px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2){ opacity: 0; transform: scaleX(0.2); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3){ transform: translateY(-6.25px) rotate(-45deg); }

/* =============================================================================
   12. MOBILE DRAWER: one component, one implementation.
   Closed state is `hidden` + `inert`, so it is gone from the pointer, the tab
   order and the accessibility tree. Never opacity-only.
   ============================================================================= */

.nav-drawer{
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.nav-drawer-backdrop{
  position: absolute;
  inset: 0;
  background: rgba(12,12,13,0.44);
  opacity: 0;
  transition: opacity var(--d-base) var(--ease-out-expo);
}
/* Full-width takeover. One implementation, every viewport under 1024px, which
   is why the header can lift above it and the hamburger stays operable. */
.nav-drawer-panel{
  position: relative;
  width: 100%;
  height: 100%;
  padding: calc(var(--nav-h) + var(--space-8)) var(--gutter) var(--space-10);
  background: var(--stage);
  color: var(--stage-ink);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateY(-12px);
  opacity: 0;
  transition: transform var(--d-base) var(--ease-out-expo),
              opacity var(--d-base) var(--ease-out-expo);
}
.nav-drawer-body{ max-width: 520px; }
.nav-drawer.is-open .nav-drawer-backdrop{ opacity: 1; }
.nav-drawer.is-open .nav-drawer-panel{ transform: none; opacity: 1; }

/* Header lifts above the open drawer so the hamburger can morph to an X and
   still be clicked. Its ink flips to stage ink because it now sits on --stage. */
.nav.is-drawer-open{
  z-index: calc(var(--z-drawer) + 1);
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 1px solid transparent;
}
.nav.is-drawer-open .nav-toggle{ color: var(--stage-ink); }
/* The bar is now sitting on the dark stage, so the wordmark swaps to the
   variant whose X stays readable there. The footer already pulls this file on
   every page, so it is warm in cache by the time the drawer opens. */
.nav.is-drawer-open .nav-logo-img{ content: url("/assets/img/oduex-logo-dark.png"); }

.nav-drawer-list{ display: grid; gap: var(--space-1); }
.nav-drawer-list a{
  display: block;
  padding: var(--space-4) 0;
  font-size: var(--fs-2xl);
  font-weight: 500;
  letter-spacing: var(--ls-heading);
  color: var(--stage-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--stage-line);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--d-base) var(--ease-out-expo),
              transform var(--d-base) var(--ease-out-expo),
              color var(--d-fast) var(--ease-out-quart);
  transition-delay: calc(var(--i, 0) * 50ms);
}
.nav-drawer.is-open .nav-drawer-list a{ opacity: 1; transform: none; }
/* Stagger set in CSS, not with an inline style attribute. */
.nav-drawer-list li:nth-child(1) a{ --i: 0; }
.nav-drawer-list li:nth-child(2) a{ --i: 1; }
.nav-drawer-list li:nth-child(3) a{ --i: 2; }
.nav-drawer-list li:nth-child(4) a{ --i: 3; }
.nav-drawer-list li:nth-child(5) a{ --i: 4; }
.nav-drawer-list li:nth-child(6) a{ --i: 5; }
.nav-drawer-list li:nth-child(7) a{ --i: 6; }
.nav-drawer-list li:nth-child(8) a{ --i: 7; }
/* --gold-text resolves to --gold-surface inside .nav-drawer-panel (8.46:1). */
.nav-drawer-list a:hover{ color: var(--gold-text); }
.nav-drawer-list a[aria-current="page"]{ color: var(--gold-text); }

.nav-drawer-foot{
  margin-top: var(--space-10);
  display: grid;
  gap: var(--space-4);
}
.nav-drawer-foot .btn-primary{ width: 100%; }
.nav-drawer-contact{
  display: grid;
  gap: var(--space-2);
  font-size: var(--fs-sm);
  color: var(--stage-ink-muted);
}
.nav-drawer-contact a{ color: var(--stage-ink); text-decoration: none; }
.nav-drawer-contact a:hover{ color: var(--gold-text); }

/* =============================================================================
   13. PERSISTENT FURNITURE: sticky CTA, WhatsApp pill
   ============================================================================= */

.sticky-cta{
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-sticky);
  display: flex;
  padding: var(--space-3) var(--gutter) calc(var(--space-3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-top: 1px solid var(--line);
  transform: translateY(110%);
  transition: transform var(--d-base) var(--ease-out-expo);
}
.sticky-cta.is-in{ transform: none; }
.sticky-cta .btn-primary{ width: 100%; }
@media (min-width: 1024px){
  .sticky-cta{ display: none; }
}

.wa-pill{
  position: fixed;
  left: var(--space-4);
  bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
  z-index: var(--z-float);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 48px;
  padding: 0 var(--space-4);
  border-radius: var(--r-pill);
  background: var(--wa);
  border: 1px solid var(--wa-edge);
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  box-shadow: var(--elev-2);
  transition: transform var(--d-base) var(--ease-out-quart),
              background var(--d-base) var(--ease-out-quart);
}
.wa-pill:hover{ transform: translateY(-2px); background: var(--wa-hover); }
.wa-pill svg{ width: 20px; height: 20px; flex: 0 0 auto; fill: currentColor; }
.wa-pill-label{
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  opacity: 0;
  transition: max-width var(--d-base) var(--ease-out-expo),
              opacity var(--d-base) var(--ease-out-expo);
}
.wa-pill:hover .wa-pill-label,
.wa-pill:focus-visible .wa-pill-label{ max-width: 160px; opacity: 1; }
@media (max-width: 1023px){
  /* Clear of the sticky CTA bar. */
  .wa-pill{ bottom: calc(var(--space-4) + 72px + env(safe-area-inset-bottom)); }
}

/* =============================================================================
   14. FOOTER
   ============================================================================= */

.footer{
  background: var(--stage);
  color: var(--stage-ink-muted);
  border-top: 1px solid var(--stage-line);
  padding-block: var(--section-sm) var(--space-8);
}
.footer-grid{
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1.2fr;
  gap: var(--space-12) var(--space-8);
}
@media (max-width: 979px){
  .footer-grid{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 559px){
  .footer-grid{ grid-template-columns: minmax(0, 1fr); }
}

/* Same 3.934:1 file, dark-stage variant, a little larger than the nav because
   nothing is competing with it down here. */
.footer-brand-img{ display: block; height: 34px; width: auto; max-width: 100%; }
.footer-line{
  margin-top: var(--space-5);
  max-width: 34ch;
  font-size: var(--fs-sm);
  color: var(--stage-ink-muted);
}
.footer-place{
  margin-top: var(--space-3);
  font-size: var(--fs-sm);
  color: var(--stage-ink);
}

.footer h2{
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--stage-ink-muted);
  margin-bottom: var(--space-5);
}
.footer-list{ display: grid; gap: var(--space-3); }
.footer-list a,
.footer-contact a{
  display: inline-block;
  font-size: var(--fs-sm);
  color: var(--stage-ink);
  text-decoration: none;
  transition: color var(--d-fast) var(--ease-out-quart);
}
.footer-list a:hover,
.footer-contact a:hover{ color: var(--gold-text); }
.footer-contact{ display: grid; gap: var(--space-3); }
.footer-social{
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-2);
}

.footer-bottom{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--section-sm);
  padding-top: var(--space-6);
  border-top: 1px solid var(--stage-line);
  font-size: var(--fs-xs);
  color: var(--stage-ink-muted);
}
.footer-bottom p{ max-width: none; }

/* =============================================================================
   15. REVEAL + MOTION
   Content is visible by DEFAULT. Animation is opted in by the `js` class, which
   /assets/js/boot.js sets before first paint. JS off → everything renders.
   ============================================================================= */

.js .reveal{
  opacity: 0;
  transform: translateY(28px);
}
.js .reveal.is-visible{
  opacity: 1;
  transform: none;
  transition: opacity var(--d-reveal) var(--ease-out-expo),
              transform var(--d-reveal) var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 70ms);
}

/* Line-mask heading reveal, the H1 treatment on every page.
   <h1 class="display-hero line-mask" data-line-mask>
     <span class="line"><span>…</span></span>
   </h1> */
.line-mask .line{
  display: block;
  overflow: hidden;
  padding-bottom: 0.08em;
  margin-bottom: -0.08em;
}
.js .line-mask .line > span{
  display: block;
  transform: translateY(110%);
}
.js .line-mask.is-in .line > span{
  transform: none;
  transition: transform 700ms var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 90ms);
}
/* Same guarantee as the form panel: an H1 that never un-masks is the worst
   failure this system could ship, so the end state is also set on a timer. */
.js .line-mask.is-settled .line > span{ transform: none; transition: none; }
.js .rise.is-settled{ opacity: 1; transform: none; transition: none; }

/* Fade-and-rise used by non-reveal furniture (hero eyebrow, CTA row). */
.js .rise{ opacity: 0; transform: translateY(16px); }
.js .rise.is-in{
  opacity: 1;
  transform: none;
  transition: opacity var(--d-slow) var(--ease-out-expo),
              transform var(--d-slow) var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 120ms);
}

/* Image-mask wipe, the founder portrait only. */
.js .mask-wipe{ clip-path: inset(0 0 100% 0); }
.js .mask-wipe.is-visible{
  clip-path: inset(0 0 0 0);
  transition: clip-path 700ms var(--ease-out-expo);
}

/* Cross-document view transitions. Same-origin only, no JS cost. */
@view-transition{ navigation: auto; }
main h1{ view-transition-name: page-title; }

/* No animation below 480px except reveals and the drawer. */
@media (max-width: 479px){
  .dot-live{ animation: none; }
  .img-frame img{ transition: none; }
}

/* =============================================================================
   16. PRINT
   ============================================================================= */

@media print{
  .nav, .nav-drawer, .sticky-cta, .wa-pill, .scroll-progress, .skip-link{ display: none !important; }
  body{ background: #ffffff; color: #000000; }
  .stage, .footer{ background: #ffffff; color: #000000; }
  .js .reveal, .js .rise, .js .line-mask .line > span{ opacity: 1 !important; transform: none !important; }
  a[href^="http"]::after{ content: ' (' attr(href) ')'; font-size: 0.8em; }
}

/* =============================================================================
   17. REDUCED MOTION: neutralises everything.
   Placed last so these !important rules beat every declaration above.
   Scroll-linked effects must also never INITIALISE in JS; core.js mirrors this.
   ============================================================================= */

@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  html{ scroll-behavior: auto !important; }

  .js .reveal,
  .js .rise,
  .js .line-mask .line > span{
    opacity: 1 !important;
    transform: none !important;
  }
  .js .mask-wipe{ clip-path: none !important; }

  .nav-inner{ transform: none !important; }
  .card-interactive:hover,
  .card-interactive:focus-within,
  .wa-pill:hover{ transform: none !important; }
  .img-frame-zoom:hover img,
  .card-interactive:hover .img-frame img{ transform: none !important; }
  .dot-live{ animation: none !important; }

  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){ animation: none !important; }
}
