/* ==========================================================================
   Vaughn Automation — design system  (branch: design/amp)

   Architecture derived from ampcode.com, teardown in
   career-agent/docs/research/2026-09-05-ampcode-design-teardown.md

   What is taken from Amp:
     - one fluid unit driving the whole type scale
     - shell / surface colour split, tinted rather than neutral
     - a single accent spent no more than ~6 times per page
     - display serif + mono labels + SYSTEM sans for body copy
     - layered atmosphere as composable named layers with modifiers
     - product UI rebuilt natively (.app-mock) rather than screenshotted

   Palette: the ground is a dark saturated orange — the colour itself,
   not a neutral carrying an accent. Deep blue is the counterweight, used
   for every instrument surface (.app-mock, the Safety band), so the page
   reads as warm room / cool instruments. Amp runs warm-on-warm; this runs
   warm-against-cool, which is a different thing rather than a clone.

   What is deliberately not taken:
     - three licensed typefaces (no budget, no need)
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Typefaces

   Amp pays for a display serif and a mono and then sets body copy in
   system-ui. That is the right split: the faces that carry character are
   licensed, the face doing the most bytes is free and already installed.

   Substitutions here are all free and self-hostable:
     Instrument Serif  — display, has a real italic (the italic matters,
                         see the one-italic-word rule below)
     system-ui         — body, exactly as Amp does
     Geist Mono        — labels, code, terminal chrome

   Drop woff2 into /fonts. Until then the fallbacks below render correctly.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Instrument Serif";
  src: url("/fonts/InstrumentSerif-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Serif";
  src: url("/fonts/InstrumentSerif-Italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/GeistMono-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --font-display: "Instrument Serif", "Iowan Old Style", Georgia,
    "Times New Roman", serif;
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono",
    Menlo, Consolas, monospace;
}

/* --------------------------------------------------------------------------
   2. Fluid unit + type scale   (Amp's --lens-fluid-unit)

   One unit — 1vi, capped so it stops growing past the clamp width — drives
   every step. Each step is the same formula with a different multiplier, so
   the scale stays proportional at every viewport by construction. No media
   queries, no per-step maximum hacks, no parallel -mobile classes.
   -------------------------------------------------------------------------- */

:root {
  --clamp-width: 1440px;
  --u: min(1vi, calc(var(--clamp-width) / 100));

  --type-xs:   clamp(0.69rem, 0.65rem + 0.11 * var(--u), 0.88rem);
  --type-sm:   clamp(0.83rem, 0.77rem + 0.15 * var(--u), 1.00rem);
  --type-base: clamp(1.00rem, 0.93rem + 0.20 * var(--u), 1.25rem);
  --type-lg:   clamp(1.20rem, 1.06rem + 0.36 * var(--u), 1.60rem);
  --type-xl:   clamp(1.44rem, 1.19rem + 0.62 * var(--u), 2.20rem);
  --type-2xl:  clamp(1.73rem, 1.28rem + 1.10 * var(--u), 3.20rem);
  --type-3xl:  clamp(2.07rem, 1.32rem + 1.85 * var(--u), 4.60rem);
  --type-4xl:  clamp(2.49rem, 1.29rem + 2.95 * var(--u), 6.50rem);

  /* Amp's line-height set. 1.414 is √2 and is the body default. */
  --lh-tight: 1.1;
  --lh-snug: 1.25;
  --lh-normal: 1.414;
  --lh-relaxed: 1.65;
  --lh-loose: 1.8;

  --track-tight: -0.02em;
  --track-label: 0.08em;
}

/* --------------------------------------------------------------------------
   3. Colour — shell / surface, tinted, dark-first

   Amp's key structural idea: shell (the chrome, the page ground) is a
   different token family from surface (raised content, mockups, cards).
   That separation is what lets an .app-mock sit inside the page without
   looking pasted on.

   Nothing here is neutral grey. The ground is blue-black, the text is a
   cool white. Tint everything or tint nothing — half-tinted is what reads
   as a mistake.
   -------------------------------------------------------------------------- */

:root {
  /* Ground is a dark, saturated orange — not a neutral with an orange
     accent. Everything warm-shifts off it. The whole page lives in one
     hue family; the only cool thing on the site is inside an .app-mock,
     which carries its own scoped palette (see §12) and therefore reads
     as a different surface entirely. That contrast is the point. */
  --shell:      #2a1206;
  --shell-2:    #351809;
  --surface:    #3d1d0c;
  --surface-2:  #4a2510;

  --ink:        #fdf1e6;   /* 14.9:1 on shell */
  --ink-2:      #ecceb4;   /* 10.4:1 — body copy, secondary */
  --ink-3:      #c1977a;   /* 5.6:1  — quiet labels, captions */

  /* Hairlines are ALPHA, never hex. Amp's rules are rgba(...,0.12) so the
     same token works on shell, on surface, and inside a mock without a
     per-context override. Copy the technique, not the colour. */
  --rule:       rgba(255, 214, 180, 0.14);
  --rule-2:     rgba(255, 214, 180, 0.26);
  --tick:       #d8a882;   /* the 1px bar before headings */

  /* The accent cannot be orange any more — the ground already is. Same
     hue family, far higher luminance, so it reads as light rather than
     as another colour. Still capped at ~6 uses per page. */
  --accent:     #ffc061;
  --accent-ink: #2a1206;

  /* Signal lives inside .app-mock only, against that mock's near-black.
     Putting a red on this orange ground would be unreadable and is why
     the mocks keep their own palette. */
  --stop:       #ff6b6b;
  --ok:         #6ee7a8;

  /* Deep blue is the counterweight. The page is one warm hue, so the cool
     panels read as a genuinely different surface rather than a tinted card
     — shell vs surface done with hue, not just lightness. Orange ground,
     blue instruments. Signal red and green also sit far better on navy
     than they ever could on orange. */
  --deep:       #0a1420;
  --deep-2:     #101c2b;
  --deep-ink:   #e6eef7;
  --deep-ink-2: #93a8c0;
  --deep-rule:  rgba(150, 190, 235, 0.16);

  /* Elevation as alpha, not as hex. Amp raises surfaces with white at
     4% / 6% / 8% (.tile, .circle-button, .tab--active), which means one
     ladder works on every ground — warm, cool, or inside a mock. */
  --lift-1:     rgba(255, 246, 238, 0.04);
  --lift-2:     rgba(255, 246, 238, 0.06);
  --lift-3:     rgba(255, 246, 238, 0.08);

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   4. Grid + spacing   (Amp's "lens")
   -------------------------------------------------------------------------- */

:root {
  /* Text is inset from a rail by this much; the CELL EDGE stays on the
     rail. Same value the bento cells use, so an inset heading in an open
     section lines up with an inset heading inside a bento cell. Amp calls
     this cell-pad and it is why their grid reads as drawn rather than as
     text jammed against a line. */
  --cell-pad: var(--space-40);

  --container: 76rem;
  --measure: 36rem;
  --gutter: clamp(1.25rem, 0.9rem + 1.6vw, 2.5rem);

  --space-10: clamp(0.31rem, 0.21rem + 0.46vw, 0.63rem);
  --space-20: clamp(0.63rem, 0.42rem + 0.93vw, 1.25rem);
  --space-30: clamp(0.94rem, 0.63rem + 1.39vw, 1.88rem);
  --space-40: clamp(1.25rem, 0.83rem + 1.85vw, 2.50rem);
  --space-60: clamp(1.88rem, 1.25rem + 2.78vw, 3.75rem);
  --space-80: clamp(2.50rem, 1.67rem + 3.70vw, 5.00rem);
  --space-100: clamp(3.13rem, 2.08rem + 4.63vw, 6.25rem);
  --space-160: clamp(5.00rem, 3.33rem + 7.41vw, 10.0rem);
}

/* --------------------------------------------------------------------------
   4b. The bento grid   (Amp's .grid-container / .bento-cell)

   Amp's sections are a 12-unit field expressed as five columns — 3:3:2:2:2.
   Column one holds a .bento-header that spans every row, so the section
   label sits out in the margin beside its content rather than stacked on
   top of it. Nested blocks use subgrid to stay on the same tracks.

   The hairlines between cells are not borders. The grid has a 1px gap and
   the container paints --rule behind it, so every seam is exactly 1px and
   they never double up where two cells meet. This is the detail that makes
   a grid look drawn rather than assembled.
   -------------------------------------------------------------------------- */

.bento {
  display: grid;
  grid-template-columns: 3fr 3fr 2fr 2fr 2fr;
  gap: 1px;
  background: var(--rule);   /* shows through the gaps as hairlines */
  border-block: 1px solid var(--rule);
}

.bento > * { background: var(--bento-cell-bg, var(--shell)); padding: var(--space-40); }

/* Column one, full height: the label rail.

   `grid-row: 1 / -1` does NOT work here — -1 resolves against the explicit
   grid, and these rows are implicit, so the header collapses to row 1 and
   a cell slides in underneath it. The span has to be stated. Amp hard-codes
   theirs the same way (grid-row: span 6). Set --bento-rows to the number of
   rows the cells will occupy: ceil(cells / 2) for span-2 cells. */
.bento { --bento-rows: 3; }
.bento__header { grid-column: 1; grid-row: 1 / span var(--bento-rows); }

/* Content cells span pairs of the 2fr tracks. */
.bento__cell { grid-column: span 2; }
.bento__cell--wide { grid-column: span 3; }

/* Fills to the end of the row. The container paints --rule and only cells
   cover it, so ANY empty grid area shows up as a solid block of seam
   colour. Every row has to be filled — use this on the last cell when the
   count is odd. */
.bento__cell--full { grid-column: 2 / -1; }

@media (max-width: 62rem) {
  .bento { grid-template-columns: 1fr 1fr; }
  .bento__header { grid-column: 1 / -1; grid-row: auto; }
  .bento__cell, .bento__cell--wide { grid-column: span 1; }
}
@media (max-width: 38rem) {
  .bento { grid-template-columns: 1fr; }
  .bento__cell, .bento__cell--wide { grid-column: 1; }
}

/* --------------------------------------------------------------------------
   5. Base
   -------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--shell);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--type-base);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
}

/* Headings are the display serif. Weight stays 400 — the face is already
   high-contrast, and bolding it destroys what it is for. */
h1, h2, h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  text-wrap: balance;
}
h4 {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--type-base);
  font-weight: 500;
  line-height: var(--lh-snug);
}

h1 { font-size: var(--type-4xl); }
h2 { font-size: var(--type-3xl); }
h3 { font-size: var(--type-xl); }

/* The tick. Amp puts a 1px bar in the margin beside a heading — small
   enough to read as drafting rather than decoration, and it is the thing
   that makes the type feel placed on a grid instead of floating.

   Correction to an earlier note here: this is a RAIL MARKER, not a
   highlight. Amp puts one on every bento-header heading, and it is
   consistent for that reason. Use it on every section heading or none. */
.tick { position: relative; padding-left: var(--space-30); }
.tick::before {
  content: "";
  position: absolute;
  left: 0; top: 0.18em; bottom: 0.18em;
  width: 1px;
  background: var(--tick);
}

p { margin: 0 0 var(--space-20); line-height: var(--lh-relaxed); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }
:is(h1, h2, h3, h4) + p { margin-top: var(--space-20); }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
img, video, svg { display: block; max-width: 100%; height: auto; }
.tabular, .price, table { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   6. Layout
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--space-100); }
.section--tall { padding-block: var(--space-160); }
.prose { max-width: var(--measure); }

/* Section modifiers, Amp's --dense / --spacious. They change spacing only,
   never type or colour, so they compose with everything else. */
.section--dense { padding-block: var(--space-60); }

/* Label rail: the section eyebrow sits out in the margin on the same
   3:3:2:2:2 field, beside its content rather than stacked above it.
   Amp's .bento-header, without needing the full bento.

   GAP MUST BE ZERO. A gap is taken out of the tracks, so any gap here
   pushes the heading off the rail it is supposed to sit on — measured at
   318px against a rail at 293px before this was fixed. Separation comes
   from padding inside the cell, which moves the text without moving the
   track edge. Same rule applies to every grid on this page that is meant
   to align: pad, never gap. */
.rail {
  display: grid;
  grid-template-columns: 3fr 9fr;
  gap: 0;
  align-items: start;
}
/* The inset goes on LEAF cells, never on a wrapper. Padding a wrapper
   shrinks its content box, so any grid inside it starts inboard of the
   rail and every nested boundary drifts — measured 12px out before this
   was corrected. Wrappers stay flush to the track; leaves inset. */
.rail > :first-child { padding-inline: var(--cell-pad); }
.rail > :last-child { padding-inline: 0; }
/* Depth-tolerant on purpose: written as `.rail > :last-child > h2` this
   silently did nothing in any section whose heading was a direct child of
   the rail rather than wrapped in a div, and that section hugged the line
   while every other one insetted. Match the leaf wherever it sits. */
.rail :is(h2, h3, .prose) { padding-inline: var(--cell-pad); }
.rail > :last-child > p { padding-inline: var(--cell-pad); }
/* ...but never double-pad a leaf that already inset itself as a grid cell */
.rail .field-grid :is(h2, h3, .prose),
.rail .bento :is(h2, h3, .prose) { padding-inline: 0; }

/* Prose inside the rail body ends on the next rail rather than at an
   arbitrary measure. The body spans units 3-12; 5/9 of it lands on unit 8. */
@media (min-width: 62rem) {
  .rail > :last-child .prose { max-width: 56%; }
}

@media (max-width: 62rem) {
  .rail { grid-template-columns: 1fr; }
  .rail > :first-child { padding-inline: 0; padding-bottom: var(--space-30); }
  .rail > :last-child > h2,
  .rail > :last-child > h3,
  .rail > :last-child > p,
  .rail > :last-child > .prose { padding-inline: 0; }

  /* ...except the tick, whose gap IS padding-left. Zeroing padding-inline
     above put the bar hard against the text on every rail heading, while
     the Safety heading kept its gap because it sits in a bento header and
     the selector never reached it. That was the only visible difference
     between them on mobile. */
  .rail > :last-child > .tick { padding-left: var(--space-30); }
}

/* The content field: the four tracks to the right of the label rail.
   Anything laid out inside a rail body uses these so its edges land on
   the same lines the rails are drawn on. */
.field-grid {
  display: grid;
  grid-template-columns: 3fr 2fr 2fr 2fr;
  gap: 0;
}
.field-grid > * { padding-inline: var(--cell-pad); }

/* Two across. Each child takes two tracks, so the split falls on unit 8
   (page rail 727) and the row ends on unit 12 (rail 1075) — the same
   arithmetic that makes the Safety bento line up. */
.field-grid--2up > * { grid-column: span 2; }

/* Takes the whole field. For a panel that needs the width to stay legible
   rather than one that merely wants it. */
.field-grid > .span-all { grid-column: 1 / -1; }

@media (max-width: 62rem) {
  .field-grid { grid-template-columns: 1fr; }
  .field-grid > * { grid-column: 1 !important; padding-inline: 0; padding-bottom: var(--space-40); }
}

/* A proof row: flavour text in the margin track, the evidence beside it.
   The margin column is ~210px of text after padding, which is a caption
   measure — roughly 30 characters. Copy for it has to be written short;
   a paragraph meant for full width becomes a thin tower here. */
/* The frames sit on the same inset as the body copy, not flush to the
   track. Padding the wrapper is safe HERE — unlike elsewhere on the page,
   nothing inside a proof body is a grid that has to line up with the page
   rails, so shrinking the content box costs nothing. The per-element
   padding on captions is switched off so they are not inset twice. */
.proof-row { margin-top: var(--space-160); align-items: start; }
.proof-row > :last-child { padding-inline: var(--cell-pad); }
.proof-row > :last-child > p { padding-inline: 0; }

@media (max-width: 62rem) {
  .proof-row > :last-child { padding-inline: 0; }
}
.proof-row h4 + p { color: var(--ink-2); font-size: var(--type-sm); }

/* A hairline above a section, the way Amp opens each grid-section. */
.section--ruled { border-top: 1px solid var(--rule); }

.grid-3 {
  display: grid;
  gap: var(--space-40);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

/* Raised content sits on --surface, the way Amp separates shell from
   surface. Use for anything that should read as a distinct plane. */
.section--surface { background: var(--surface); }

/* The Safety section is the one place the page goes cool. It is the
   section about restraint, and the temperature shift does more than a
   heading could. */
.section--deep {
  background: var(--deep);
  --ink: var(--deep-ink);
  --ink-2: var(--deep-ink-2);
  --ink-3: var(--deep-ink-2);
  --rule: var(--deep-rule);
  --bento-cell-bg: var(--deep);
  color: var(--deep-ink);
}

/* A mock inside a cool section would otherwise sit navy-on-navy and
   disappear. Lift it one step and strengthen its edge. Mock-bearing
   sections are warm by preference; this is the fallback. */
.section--deep .app-mock {
  --m-bg: var(--deep-2);
  border-color: rgba(150, 190, 235, 0.26);
}

/* The alternation is the page's rhythm: warm room, cool instrument,
   warm room. Sections carrying an .app-mock stay warm so the navy panels
   read against their ground. */

/* --------------------------------------------------------------------------
   7. Mono micro-labels

   Amp's eyebrows, link CTAs and handles are all mono, uppercase, ~11px,
   tracked out. It is the cheapest way to get an engineering texture that a
   sans label cannot give you.
   -------------------------------------------------------------------------- */

.label {
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--ink-3);
}

.label--accent { color: var(--accent); }

/* A term the reader may not know. Amp marks "orb" this way in their hero.
   Theirs is a tooltip; this is a link to the FAQ answer instead — tooltips
   do not exist on touch and are unreliable for screen readers, and the
   explanation already lives on a page worth sending someone to. */
.term {
  text-decoration: underline dotted;
  text-decoration-color: var(--ink-3);
  text-underline-offset: 0.24em;
  color: inherit;
}
.term:hover, .term:focus-visible { text-decoration-color: var(--accent); }

/* Amp's inline meta divider: 1px wide, 16px tall, 10% ink. */
.sep {
  display: inline-block;
  width: 1px;
  height: 1em;
  vertical-align: -0.15em;
  background: currentColor;
  opacity: 0.28;
}

.link-mono {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-2);
  padding-bottom: 0.2em;
}
.link-mono:hover, .link-mono:focus-visible { border-color: var(--accent); color: var(--accent); }

/* --------------------------------------------------------------------------
   8. Atmosphere — composable named layers   (Amp's field + ember)

   A base `field` plus accent `glow` layers with modifiers, rather than one
   monolithic gradient. The accent bleeds through the dark ground as
   separately tunable layers.
   -------------------------------------------------------------------------- */

.field { position: relative; isolation: isolate; overflow: hidden; }
.field > * { position: relative; z-index: 1; }

.field::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(120% 130% at 50% 115%, #4a2510 0%, transparent 58%),
    linear-gradient(var(--shell), var(--shell-2));
}

/* Accent glows. Keep them subtle — the accent is a budget, and a glow
   spends some of it. */
.glow {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  filter: blur(28px);
}
.glow--column {
  inset: -10% auto -10% -5%;
  width: 55%;
  background: radial-gradient(55% 70% at 30% 45%,
    rgba(255, 176, 74, 0.22), rgba(214, 106, 24, 0.10) 55%, transparent 80%);
}
.glow--corner {
  inset: -20% -10% auto auto;
  width: 40%;
  aspect-ratio: 1;
  background: radial-gradient(closest-side,
    rgba(255, 200, 130, 0.14), rgba(214, 106, 24, 0.05) 55%, transparent);
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes glow-drift {
    0%, 100% { transform: translate3d(0,0,0) scale(1); }
    50%      { transform: translate3d(3%, -2%, 0) scale(1.08); }
  }
  .glow--column { animation: glow-drift 30s ease-in-out infinite; }
  .glow--corner { animation: glow-drift 38s ease-in-out infinite reverse; }
}

/* --------------------------------------------------------------------------
   8b. Atmosphere, part two: grain, rails, vignette

   Three more layers lifted from Amp. All three are page furniture — they
   sit behind content, never take pointer events, and carry no meaning.
   -------------------------------------------------------------------------- */

/* GRAIN. Amp's exact filter: fractal noise, desaturated, tiled at 160px,
   7% opacity, screen blend. About 300 bytes and no network request. It is
   what stops a big dark gradient reading as flat CSS — the page gets the
   tooth of a printed surface. Do not raise the opacity; at 0.07 you should
   not consciously see it.

   Page-wide and fixed rather than per-section: the flat navy bands are
   exactly where an untextured surface shows, and one element beats four. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.07;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%' height='100%' filter='url(#n)'/></svg>");
}

/* VIGNETTE. Darkens the corners so the eye falls to the middle. */
.vignette {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(115% 100% at 50% 45%,
    transparent 55%, rgba(10, 4, 0, 0.55) 100%);
}

/* RAILS. Amp draws the column boundaries as faint fixed lines running the
   whole page. It is the single biggest reason their layout reads as
   constructed rather than assembled — the grid is visible, like drafting
   paper under the type.

   Theirs is a generated SVG at computed x positions. This is the same
   thing in CSS: a fixed grid on the identical tracks, with a hairline on
   each track edge, so the rails cannot drift out of step with .bento.
   Faint on purpose — --rail is roughly a third of --rule. */
:root { --rail: rgba(255, 214, 180, 0.05); }

.rails { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.rails > .container { height: 100%; }
.rails__grid {
  display: grid;
  grid-template-columns: 3fr 3fr 2fr 2fr 2fr;
  height: 100%;
}
.rails__grid > i { border-left: 1px solid var(--rail); }
.rails__grid > i:last-child { border-right: 1px solid var(--rail); }

/* Content has to sit above the fixed rails. */
.section, .band, .footer { position: relative; z-index: 1; }

@media (max-width: 62rem) { .rails { display: none; } }

/* --------------------------------------------------------------------------
   9. Nav
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  inset-block-start: var(--space-20);
  inset-inline: 0;
  z-index: 100;
  display: flex;
  justify-content: center;
  pointer-events: none;
}

/* LIQUID GLASS.
   Built in layers, because the one-line version (blur + translucent fill)
   reads as frosted plastic. What makes Apple's look like glass is the rim:
   a bright specular line along the top edge, a dimmer one along the
   bottom, and a lift shadow underneath so it sits above the page rather
   than in it.

   Layer 1  backdrop-filter  blur + saturate + brightness on what is behind
   Layer 2  a barely-there fill so the glass has body
   Layer 3  inset box-shadows — the rim light, top bright, bottom faint
   Layer 4  ::after specular sweep, screen-blended, top-left to bottom-right
   Layer 5  outer shadow for lift

   The pill is 59px tall on purpose. The rim fold band is a fraction of
   the height, so at the original 47px the vertical mirror had ~5px to
   work in and was invisible. Height is what makes that layer legible.

   Layer 6 (Chrome only) is real edge refraction via an SVG displacement
   map — see .nav__pill below and the <filter> in index.html. Safari and
   Firefox do not support SVG filters inside backdrop-filter, so it sits
   behind @supports and its absence costs nothing: layers 1-5 are the
   whole look, and 6 is the flourish.
   Sources: kube.io/blog/liquid-glass-css-svg, blog.logrocket.com */

.nav__pill {
  pointer-events: auto;
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-30);
  padding: 0.83rem 0.6rem 0.83rem 1.05rem;
  border: 1px solid rgba(255, 236, 214, 0.14);
  border-radius: 14px;
  background: rgba(255, 238, 220, 0.020);
  font-size: var(--type-sm);
  isolation: isolate;
  box-shadow:
    inset 0 1px 0 rgba(255, 245, 235, 0.34),
    inset 0 -1px 0 rgba(255, 245, 235, 0.05),
    inset 1px 0 0 rgba(255, 245, 235, 0.10),
    inset -1px 0 0 rgba(255, 245, 235, 0.10),
    0 10px 30px rgba(0, 0, 0, 0.40),
    0 2px 8px rgba(0, 0, 0, 0.25);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .nav__pill {
    /* The milkiness is the FILL, not the blur. Dropping the fill and
       easing brightness clears the frost while keeping the refraction,
       which is what should be doing the work. */
    background: rgba(255, 238, 220, 0.004);
    -webkit-backdrop-filter: blur(6px) saturate(165%) brightness(1.02);
    backdrop-filter: blur(6px) saturate(165%) brightness(1.02);
  }
}

/* Layer 6, Chrome only. An SVG displacement map bends what is behind the
   pill at its edges — the actual refraction, not a simulation of it. The
   map encodes x position in red and y in green, so neutral sits at the
   centre and pixels are pushed hardest at the rim, which is a lens.

   @supports cannot feature-detect "SVG filter inside backdrop-filter" —
   the property parses everywhere, it just does nothing in Safari and
   Firefox. So this is scoped to engines known to support it rather than
   probed, and the fallback is layers 1-5, which is the whole look. */
@supports (backdrop-filter: url(#x)) and (not (-webkit-hyphens: none)) {
  .nav__pill {
    -webkit-backdrop-filter: blur(4px) saturate(165%) brightness(1.02) url(#liquid-glass);
    backdrop-filter: blur(4px) saturate(165%) brightness(1.02) url(#liquid-glass);
  }
}

/* The specular sweep. Screen blend so it adds light rather than fogging
   the content, and pointer-events off so it never eats a click. */
.nav__pill::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(
    135deg,
    rgba(255, 250, 245, 0.20) 0%,
    rgba(255, 250, 245, 0.04) 32%,
    transparent 55%,
    rgba(255, 250, 245, 0.07) 100%
  );
  mix-blend-mode: screen;
}

/* At this transparency the page's own text passes behind the labels and
   competes with them. A dark halo separates them without touching the
   glass. If legibility ever needs more, raise BLUR — it removes competing
   detail while staying transparent. Raising the fill would only fog it. */
.nav__pill { text-shadow: 0 1px 3px rgba(10, 5, 0, 0.85), 0 0 10px rgba(10, 5, 0, 0.55); }
.nav__cta { text-shadow: none; }

.nav__brand {
  font-family: var(--font-display);
  font-size: var(--type-lg);
  letter-spacing: var(--track-tight);
  text-decoration: none;
  white-space: nowrap;
}
.nav__links { display: flex; gap: var(--space-30); }
.nav__link {
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  text-decoration: none;
  color: var(--ink-2);
}
.nav__link:hover, .nav__link:focus-visible { color: var(--ink); }

.nav__cta {
  padding: 0.4rem 0.8rem;
  border-radius: 7px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--type-xs);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.nav__cta:hover, .nav__cta:focus-visible { filter: brightness(1.08); }

@media (max-width: 32rem) { .nav__links { display: none; } }

/* --------------------------------------------------------------------------
   10. Button
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.8rem 1.4rem;
  border: 0;
  border-radius: 9px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-sans);
  font-size: var(--type-base);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: filter 0.15s ease, transform 0.15s ease;
}
.btn:hover, .btn:focus-visible { filter: brightness(1.08); transform: translateY(-1px); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule-2);
  font-weight: 500;
}
.btn--ghost:hover { border-color: var(--ink-3); }

.btn-note { margin-top: var(--space-20); font-size: var(--type-sm); color: var(--ink-3); }

/* --------------------------------------------------------------------------
   11. Band — full bleed, dotted rules

   Amp separates major sections with a fine dotted rule rather than a solid
   one. Costs nothing, reads as drafting rather than decoration.
   -------------------------------------------------------------------------- */

.band {
  padding-block: var(--space-160);
  border-block: 1px solid var(--rule);
}
.band__line {
  font-family: var(--font-display);
  font-size: var(--type-3xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--track-tight);
  max-width: 16ch;
  margin: 0;
}
.band__note { margin-top: var(--space-40); color: var(--ink-2); max-width: var(--measure); }

/* --------------------------------------------------------------------------
   12. .app-mock — product UI rebuilt natively, never screenshotted

   This is the single most useful pattern on ampcode.com. Their product
   screens are HTML with a scoped palette, so they are sharp at any
   resolution, themeable, and never go stale.

   Scoped palette: everything inside a .app-mock reads its colours from
   these locals, so a mock can be dropped on any ground and stay coherent.
   -------------------------------------------------------------------------- */

/* The scoped instrument palette, shared by every instrument surface.
   These tokens are defined HERE, not globally, so a component using --m-*
   without being in this selector list resolves every one of them to
   nothing and falls back to transparent.

   That has now bitten twice: the phone rendered with the page showing
   through it, and the browser's toggles rendered at the right size in the
   right place with no colour at all. ANY NEW INSTRUMENT SURFACE MUST BE
   ADDED TO THIS LIST. Symptom to recognise: correct geometry, invisible
   paint. */
.app-mock,
.device,
.browser {
  --m-bg: var(--deep);
  --m-panel: var(--deep-2);
  --m-rule: var(--deep-rule);
  --m-ink: var(--deep-ink);
  --m-dim: var(--deep-ink-2);
  --m-stop: var(--stop);
  --m-ok: var(--ok);
}

.app-mock {
  border: 1px solid var(--m-rule);
  border-radius: 12px;
  background: var(--m-bg);
  color: var(--m-ink);
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  line-height: var(--lh-snug);
  overflow: hidden;
}



.app-mock__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-20);
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--m-rule);
  background: var(--m-panel);
  color: var(--m-dim);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
}

.app-mock__body { padding: 0.5rem 0.85rem 0.85rem; overflow-x: auto; }

.app-mock table { width: 100%; border-collapse: collapse; }
.app-mock th {
  text-align: left;
  font-weight: 400;
  color: var(--m-dim);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  padding: 0.55rem 0.6rem 0.55rem 0;
  border-bottom: 1px solid var(--m-rule);
  white-space: nowrap;
}
.app-mock td {
  padding: 0.5rem 0.6rem 0.5rem 0;
  border-bottom: 1px solid var(--m-rule);
  vertical-align: top;
}
.app-mock tr:last-child td { border-bottom: 0; }

.badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 0.92em;
  white-space: nowrap;
}
.badge--stop { color: var(--m-stop); }
.badge--ok { color: var(--m-ok); }

.dim { color: var(--m-dim); }
.app-mock__note {
  margin-top: var(--space-20);
  font-family: var(--font-sans);
  font-size: var(--type-sm);
  color: var(--ink-3);
  max-width: var(--measure);
}

/* Mail window. One message you can actually read, with the decision
   underneath as a system banner. A summarised list asks the reader to take
   the attack on trust; showing the message lets them see it for
   themselves, which is the whole point of the panel.

   The typefaces carry the authorship. The message — header values and
   body — is SANS, because a person wrote it and a mail client is not a
   terminal. The chrome and the decision stay MONO, because the machine
   wrote those. Keep that split: it is why the verdict reads as something
   the system added rather than as more of the email. */

/* No max-width. The frame fills its column exactly like the other two
   examples; capping it at 46rem left it short of the right rail while the
   tick log and permissions panel ran the full width. */
.mail { width: 100%; }

.mail__chrome {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.85rem;
  border-bottom: 1px solid var(--m-rule);
  background: var(--m-panel);
  color: var(--m-dim);
  font-size: var(--type-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
}
/* Window dots, deliberately neutral. The real ones are red/amber/green;
   importing those would spend the accent budget on chrome. */
.mail__dots { display: inline-flex; gap: 0.3rem; }
.mail__dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--m-rule); }

.mail__head { padding: 0.75rem 0.85rem; border-bottom: 1px solid var(--m-rule); }
.mail__line { display: grid; grid-template-columns: 4.5rem 1fr; gap: 0.6rem; padding: 0.16rem 0; }
.mail__k { color: var(--m-dim); font-size: var(--type-xs); text-transform: uppercase; letter-spacing: var(--track-label); }
.mail__v { font-family: var(--font-sans); font-size: var(--type-sm); color: var(--m-ink); }
.mail__v strong { font-weight: 600; }

.mail__body {
  padding: 0.85rem;
  font-family: var(--font-sans);
  font-size: var(--type-sm);
  line-height: var(--lh-relaxed);
  color: var(--m-ink);
}
.mail__body p { margin: 0 0 0.7rem; }
.mail__body p:last-child { margin: 0; }
.mail__acct { font-family: var(--font-mono); color: var(--m-dim); }

/* The decision. Reads as something inserted BELOW the message by the
   system, not as part of it — hence the tinted ground and the top rule. */
.mail__decision {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  align-items: start;
  padding: 0.85rem;
  border-top: 1px solid var(--m-rule);
  background: rgba(255, 107, 107, 0.07);
}
.mail__verdict { color: var(--m-ink); font-weight: 600; }
.mail__because { list-style: none; margin: 0.4rem 0 0; padding: 0; color: var(--m-dim); }
.mail__because li { padding: 0.12rem 0 0.12rem 0.9rem; position: relative; }
.mail__because li::before { content: "\2014"; position: absolute; left: 0; }
.mail__because b { color: var(--m-stop); font-weight: 400; }

/* A message that passed. Same anatomy, different verdict — showing only
   blocks would read as a system whose talent is refusing things. */
.mail__decision--ok { background: rgba(110, 231, 168, 0.07); }
.mail__because b.ok { color: var(--m-ok); }

@media (max-width: 34rem) {
  .mail__line { grid-template-columns: 1fr; gap: 0; }
  .mail__decision { grid-template-columns: 1fr; }
}

/* Three examples, driven by arrows. Stacked in a single grid cell so the
   frame is as tall as the tallest message and nothing reflows on the swap.

   State is JS rather than keyframes because the reader steers it now; a
   pure-CSS cycle cannot be interrupted. Auto-advance is a convenience and
   stops the moment anyone touches a control — taking the wheel back off a
   reader who just used it is the rudest thing a carousel can do.
   See scripts/carousel.js. */

/* The frame takes the full column, edge to edge, like the other two
   examples. Controls sit underneath so nothing eats into the width — the
   arrows flank the dots, which is also where a reader looks for them. */
.mail-carousel { display: block; }

.mail-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-30);
  margin-top: var(--space-30);
}

.mail-stack { display: grid; }
.mail-stack > .mail {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s ease;
}
.mail-stack > .mail[data-active] { opacity: 1; visibility: visible; }

/* Arrows flank the dots in the control row beneath the frame. */
.mail-arrow {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border: 1px solid var(--rule-2);
  border-radius: 50%;
  background: var(--lift-2);
  color: var(--ink-2);
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.mail-arrow:hover, .mail-arrow:focus-visible { color: var(--accent); border-color: var(--accent); }

.mail-dots { display: flex; gap: 0.4rem; }
.mail-dots button {
  width: 22px; height: 3px; padding: 0;
  border: 0; border-radius: 2px;
  background: var(--rule-2);
  cursor: pointer;
  transition: background 0.2s ease;
}
.mail-dots button[aria-current="true"] { background: var(--accent); }
.mail-dots button:hover { background: var(--ink-3); }

/* No motion: no cross-fade and no auto-advance. The arrows still work, and
   carousel.js leaves the timer off entirely. */
@media (prefers-reduced-motion: reduce) {
  .mail-stack > .mail { transition: none; }
}

/* Lock screen. The owner never opens a log; they pick up a device. The
   written record used to sit beside this in a second panel, which said
   the same thing twice — the notifications ARE the record.

   Landscape on the web, as an iPad: even bezel, camera in the top edge,
   home indicator, status glyphs. Portrait returns at the mobile
   breakpoint, where a phone is what you are holding anyway. */

.device {
  position: relative;
  width: 100%;
  padding: 16px 18px 18px;
  border-radius: 28px;
  background: #07090d;
  color: var(--m-ink);
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.10),
    inset 0 0 0 1px rgba(255,255,255,0.04),
    0 22px 48px rgba(0,0,0,0.5);
}

.device__camera {
  position: absolute;
  top: 5px;
  left: 50%;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #0a0c10;
  box-shadow: inset 0 0 0 1.5px #1a2230, inset 1px 1px 0 rgba(80,140,200,0.35);
  transform: translateX(-50%);
}

.device__screen {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-60);
  align-items: center;
  padding: var(--space-60) var(--space-40) calc(var(--space-60) + 10px);
  border-radius: 12px;
  background:
    radial-gradient(ellipse 80% 60% at 30% 0%, rgba(255, 160, 70, 0.10), transparent 55%),
    var(--m-bg);
  overflow: hidden;
}

.device__status {
  position: absolute;
  top: 12px;
  right: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--m-ink);
}
.device__wifi { display: block; opacity: 0.85; }
.device__battery {
  display: block;
  width: 22px;
  height: 10px;
  border: 1.5px solid currentColor;
  border-radius: 3px;
  padding: 1.5px;
  opacity: 0.85;
  position: relative;
}
.device__battery::after {
  content: "";
  position: absolute;
  right: -3px;
  top: 2px;
  width: 2px;
  height: 4px;
  border-radius: 0 1px 1px 0;
  background: currentColor;
}
.device__battery i {
  display: block;
  height: 100%;
  width: 70%;
  border-radius: 1px;
  background: currentColor;
}

.device__home {
  position: absolute;
  left: 50%;
  bottom: 7px;
  width: 88px;
  height: 5px;
  border-radius: 999px;
  background: rgba(255,255,255,0.28);
  transform: translateX(-50%);
}

.device__clock { padding-top: var(--space-20); }
.device__day {
  color: var(--m-dim);
  font-size: var(--type-sm);
  letter-spacing: var(--track-label);
  white-space: nowrap;
}
.device__time {
  font-family: var(--font-sans);
  font-size: clamp(3rem, 1.6rem + 3.4vw, 5rem);
  font-weight: 200;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--m-ink);
  margin-top: 0.25rem;
}

.device__stack { display: grid; gap: var(--space-30); }

.notif {
  padding: var(--space-30);
  border-radius: 18px;
  background: var(--lift-3);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.notif__head { display: flex; justify-content: space-between; gap: var(--space-20); color: var(--m-dim); font-size: var(--type-xs); text-transform: uppercase; letter-spacing: var(--track-label); }
.notif__body { margin-top: 0.55rem; font-family: var(--font-sans); font-size: var(--type-sm); line-height: var(--lh-relaxed); color: var(--m-ink); }
.notif--stop { box-shadow: inset 3px 0 0 var(--m-stop); }
.notif--stop b { color: var(--m-stop); font-weight: 600; }

@media (max-width: 52rem) {
  /* Phone: thicker bezel, island camera, home bar, portrait stack. */
  .device {
    max-width: 300px;
    margin-inline: auto;
    border-radius: 40px;
    padding: 14px 12px 16px;
  }
  .device__camera {
    top: 22px;
    width: 68px;
    height: 18px;
    border-radius: 999px;
    background: #05070a;
    box-shadow: inset 0 0 0 1px #1a2230;
  }
  .device__screen {
    grid-template-columns: 1fr;
    gap: var(--space-40);
    border-radius: 28px;
    /* Top padding has to clear the status band, not just the bezel. The
       date is nowrap and centred, so at 300px it spans nearly the full
       screen and ran under the absolutely-positioned wifi/battery —
       measured 44x14px of overlap, hiding the day number. */
    padding: calc(var(--space-40) + 32px) 0.85rem calc(var(--space-40) + 14px);
  }
  .device__clock { padding-top: 0; text-align: center; }
  .device__stack { gap: var(--space-20); }
  .device__status { top: 36px; }
  .notif { padding: var(--space-20); border-radius: 15px; }
}

/* Browser window. The permissions live in a file you own, so the honest
   frame for them is something you can open — a page in a browser, not an
   OS settings pane you would have to take my word for. */

.browser { border: 1px solid var(--m-rule); border-radius: 12px; background: var(--m-bg); overflow: hidden; }

.browser__chrome {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--space-30);
  align-items: center;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--m-rule);
  background: var(--m-panel);
}
.browser__dots { display: inline-flex; gap: 0.35rem; }
.browser__dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--m-rule); }
.browser__url {
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  background: var(--lift-1);
  color: var(--m-dim);
  font-size: var(--type-xs);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.browser__url b { color: var(--m-ink); font-weight: 400; }
.browser__spacer { width: 34px; }
.browser__body { padding: var(--space-40); }

.dash__title { font-family: var(--font-sans); font-size: var(--type-lg); color: var(--m-ink); }
.dash__sub { color: var(--m-dim); margin-top: 0.3rem; }

/* Permissions. A may/may-not list reads as a claim; a settings screen
   reads as a thing. The locked rows matter most: "off" looks like a
   switch somebody could flip at 3am, "locked" looks structural. */

.perm { display: grid; grid-template-columns: 1fr auto; gap: var(--space-30); align-items: center; padding: 0.55rem 0; }
.perm + .perm { border-top: 1px solid var(--m-rule); }
.perm__label { font-family: var(--font-sans); font-size: var(--type-sm); color: var(--m-ink); }
.perm--off .perm__label { color: var(--m-dim); }
.perm__lock { color: var(--m-stop); font-size: var(--type-xs); margin-right: 0.4rem; }

.switch {
  position: relative;
  width: 38px; height: 22px;
  border-radius: 999px;
  background: var(--m-rule);
  flex: none;
}
.switch::after {
  content: "";
  position: absolute; top: 3px; left: 3px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--m-dim);
}
.switch--on { background: color-mix(in srgb, var(--m-ok) 55%, transparent); }
.switch--on::after { left: auto; right: 3px; background: var(--m-ok); }

.perm-group { padding-top: 0.4rem; }
.perm-group + .perm-group { margin-top: var(--space-20); padding-top: var(--space-20); border-top: 1px solid var(--m-rule); }
.perm-group__title { color: var(--m-dim); font-size: var(--type-xs); text-transform: uppercase; letter-spacing: var(--track-label); }

/* --------------------------------------------------------------------------
   13. Gate — the hero product panel (two panels, 18s cycle)
   -------------------------------------------------------------------------- */

.gate { max-width: 30rem; }
.gate__panel { padding: 0.5rem 0.85rem 0.85rem; }
.gate__panel + .gate__panel { border-top: 1px solid var(--m-rule); }

.gate__steps { list-style: none; margin: 0; padding: 0.4rem 0 0; }
.gate__steps li { display: flex; align-items: baseline; gap: 0.6rem; padding: 0.35rem 0; }
.gate__steps li::before {
  content: ""; flex: none; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; transform: translateY(-0.15em);
}
.gate__alert { color: var(--m-stop); }

.gate__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-20); margin-top: 0.5rem; padding-top: 0.6rem;
  border-top: 1px solid var(--m-rule);
}
.gate__btn {
  flex: none; padding: 0.3rem 0.7rem; border: 1px solid var(--m-rule);
  border-radius: 6px; color: var(--m-dim); background: var(--lift-2);
}

@media (prefers-reduced-motion: no-preference) {
  /* Three panels, 27s cycle, 9s each. Stacked in one grid cell so the card
     is as tall as the tallest panel and nothing reflows when they swap.

     Order is deliberate: the working case leads. Panels two and three are
     both the system stopping — showing only those would read as a machine
     that mostly refuses to do things. */
  .gate { display: grid; }
  .gate__panel { grid-area: 1 / 1; }
  .gate__panel + .gate__panel { border-top: 0; }

  @keyframes gate-a { 0%, 31% { opacity: 1 } 34%, 97% { opacity: 0 } 100% { opacity: 1 } }
  @keyframes gate-b { 0%, 31% { opacity: 0 } 34%, 64% { opacity: 1 } 67%, 100% { opacity: 0 } }
  @keyframes gate-c { 0%, 64% { opacity: 0 } 67%, 97% { opacity: 1 } 100% { opacity: 0 } }
  @keyframes gate-step { 0% { opacity: .22 } 2%, 30% { opacity: 1 } 33%, 100% { opacity: .22 } }
  @keyframes gate-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .4 } }

  .gate__panel:nth-child(1) { animation: gate-a 27s infinite both; }
  .gate__panel:nth-child(2) { animation: gate-b 27s infinite both; }
  .gate__panel:nth-child(3) { animation: gate-c 27s infinite both; }

  .gate__steps li, .gate__foot { animation: gate-step 27s infinite both; }

  .gate__panel:nth-child(1) .gate__steps li:nth-child(1) { animation-delay: .3s; }
  .gate__panel:nth-child(1) .gate__steps li:nth-child(2) { animation-delay: 1.2s; }
  .gate__panel:nth-child(1) .gate__steps li:nth-child(3) { animation-delay: 2.1s; }
  .gate__panel:nth-child(1) .gate__foot { animation-delay: 3.2s; }

  .gate__panel:nth-child(2) .gate__steps li:nth-child(1) { animation-delay: 9.3s; }
  .gate__panel:nth-child(2) .gate__steps li:nth-child(2) { animation-delay: 10.2s; }
  .gate__panel:nth-child(2) .gate__steps li:nth-child(3) { animation-delay: 11.1s; }
  .gate__panel:nth-child(2) .gate__foot { animation-delay: 12.2s; }

  .gate__panel:nth-child(3) .gate__steps li:nth-child(1) { animation-delay: 18.3s; }
  .gate__panel:nth-child(3) .gate__steps li:nth-child(2) { animation-delay: 19.2s; }
  .gate__panel:nth-child(3) .gate__steps li:nth-child(3) { animation-delay: 20.1s; }
  .gate__panel:nth-child(3) .gate__foot { animation-delay: 21.2s; }

  .gate__dot { animation: gate-pulse 1.4s infinite ease-in-out; }
}

/* --------------------------------------------------------------------------
   14. Ladder + steps + estimator
   -------------------------------------------------------------------------- */

.ladder { width: 100%; border-collapse: collapse; margin-top: var(--space-40); }
.ladder td { padding: var(--space-30) var(--cell-pad); border-top: 1px solid var(--rule); vertical-align: top; }
/* The ladder now sits inside a rail body, which spans 9 of the 12 units.
   Column widths are fractions of THAT, not of the container: 3/9 lands on
   page rail 554, and 3/9 + 2/9 lands on 727. rem widths landed on neither. */
.ladder td:first-child { width: 33.333%; }
.ladder td:nth-child(2) { width: 22.222%; font-family: var(--font-mono); }
@media (max-width: 44rem) {
  .ladder, .ladder tbody, .ladder tr, .ladder td { display: block; width: auto !important; }
  .ladder tr { border-top: 1px solid var(--rule); padding-block: var(--space-30); }
  .ladder td { border: 0; padding: 0 0 0.35rem; }
}

.steps { counter-reset: s; list-style: none; padding: 0; margin: var(--space-60) 0 0; }
/* Each step is itself a rail: the number sits in track 1 alongside the
   section labels, the text starts on rail 293 alongside every heading and
   body on the page. A rem-width number column put the text at 114px,
   which is on no rail at all. */
.steps li { counter-increment: s; padding-block: var(--space-30);
  border-top: 1px solid var(--rule); display: grid; gap: 0;
  grid-template-columns: 3fr 9fr; }
.steps li > * { padding-inline: var(--cell-pad); }
.steps li::before { padding-inline: var(--cell-pad); }
.steps li::before {
  content: counter(s, decimal-leading-zero);
  font-family: var(--font-mono); font-size: var(--type-xs);
  letter-spacing: var(--track-label); color: var(--accent);
}

.estimator { display: grid; gap: 0; grid-template-columns: 5fr 4fr; align-items: start; }
/* The grid stays flush so its 5/9 split lands on a rail; the CELLS inset,
   which is what puts the input bars and the readout off the line. */
.estimator > * { padding-inline: var(--cell-pad); }
@media (max-width: 52rem) {
  .estimator { grid-template-columns: 1fr; }
  .estimator > * { padding-inline: 0; }
  .estimator > :first-child { padding-bottom: var(--space-40); }
}

.field-row { display: block; margin-bottom: var(--space-30); }
.field-row > span { display: block; margin-bottom: 0.4rem;
  font-family: var(--font-mono); font-size: var(--type-xs);
  text-transform: uppercase; letter-spacing: var(--track-label); color: var(--ink-3); }
.field-row input {
  width: 100%; padding: 0.7rem 0.85rem;
  border: 1px solid var(--rule-2); border-radius: 8px;
  background: var(--surface); color: var(--ink);
  font: inherit; font-family: var(--font-mono); font-size: var(--type-base);
  font-variant-numeric: tabular-nums;
}
.field-row input:focus-visible { border-color: var(--accent); outline-offset: 1px; }

.readout { margin: 0; }
.readout dt { font-family: var(--font-mono); font-size: var(--type-xs);
  text-transform: uppercase; letter-spacing: var(--track-label); color: var(--ink-3);
  padding-top: var(--space-30); border-top: 1px solid var(--rule); }
.readout dd { margin: 0.3rem 0 var(--space-30);
  font-family: var(--font-display); font-size: var(--type-2xl);
  line-height: var(--lh-tight); letter-spacing: var(--track-tight);
  font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   15. FAQ + footer
   -------------------------------------------------------------------------- */

.qa { border-top: 1px solid var(--rule); padding-block: var(--space-40); }
.qa h2 { font-size: var(--type-xl); }
/* The heading+paragraph rule cannot fire through a wrapper element, so
   restore the gap explicitly wherever prose follows a heading. */
:is(h1, h2, h3, h4) + .prose { margin-top: var(--space-30); }
.qa .prose { color: var(--ink-2); }

.footer { padding-block: var(--space-80) var(--space-60); border-top: 1px solid var(--rule); }
.footer.section--deep { background: var(--deep); }
.footer__grid { display: flex; flex-wrap: wrap; gap: var(--space-60);
  justify-content: space-between; align-items: start; }
.footer__brand { font-family: var(--font-display); font-size: var(--type-lg); }
.footer__tagline { color: var(--ink-3); margin-top: 0.15rem; }
.footer__contact p + p { margin-top: 0.35rem; }
.footer__legal { display: flex; flex-wrap: wrap; gap: var(--space-30);
  margin-top: var(--space-60); padding-top: var(--space-30);
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono); font-size: var(--type-xs);
  text-transform: uppercase; letter-spacing: var(--track-label); color: var(--ink-3); }
.footer__legal a { color: var(--ink-3); }
.footer__legal a:hover { color: var(--accent); }

/* --------------------------------------------------------------------------
   16. Focus, motion, print
   -------------------------------------------------------------------------- */

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

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

@media print {
  .nav, .btn, .glow { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .app-mock { background: #fff; color: #000; border-color: #999; }
  .section { padding-block: 1.5rem; }
  a::after { content: " (" attr(href) ")"; font-size: 9pt; color: #444; }
}
