/* ═══════════════════════════════════════════════════════════════════
   River~Code /build — "Descent"

   The page falls from the surface to the abyss. Ground darkens
   monotonically, section by section; the river widens as it goes.
   One accent hue (the logo cyan), one expanded grotesque, one mono.

   Deliberately NOT the parent site's paper-document look — this is a
   separate property with a separate job. Business rules from the spec
   (published prices, real URLs only, no fabricated proof) are unchanged.
   ═══════════════════════════════════════════════════════════════════ */

:root {
  /* Ground — the descent. Blue-biased, never neutral grey. */
  --paper:      #F4F3EF;
  --paper-2:    #ECEAE4;
  --deep-1:     #0A1218;
  --deep-2:     #080F14;
  --deep-3:     #060C10;
  --deep-4:     #050A0D;
  --deep-5:     #04080B;
  --abyss:      #03070A;

  --ink:        #101416;
  --ink-mute:   #55626A;   /* 5.1:1 on --paper-2, the lighter of the two */
  --white:      #F2F7FA;
  /* Both are measured against the darkest ground they are used on
     (--deep-1 #0A1218): --mist lands ~8.5:1, --mist-2 ~4.7:1. The
     second one is small mono, so it has to clear AA on its own. */
  --mist:       rgba(216,232,240,0.82);   /* body copy on water */
  --mist-2:     rgba(216,232,240,0.58);   /* mono labels on water */

  --flow:       #44C1F6;                  /* the one accent */
  --flow-dim:   rgba(68,193,246,0.34);
  --flow-glow:  rgba(68,193,246,0.10);
  --deep-blue:  #006C90;                  /* the accent, legible on paper */

  --hair:       rgba(216,232,240,0.14);
  --hair-paper: #DEDCD6;

  /* Type. Two sans faces with one job each: Archivo, cut expanded, is
     architecture; Saira is running text. Saira is the squarer of the two
     — flat-sided bowls, straight terminals — so the body copy sits on the
     same geometry as the display instead of reading round beside it. */
  --f-display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --f-sans: "Saira", "Archivo", "Helvetica Neue", Arial, sans-serif;
  --f-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* The display lines are set nowrap so the designed breaks are the only
     breaks — which means the type must be guaranteed to fit the column,
     or overflow:hidden clips it instead. 11.2 is the width-to-size ratio
     of the longest line ("AND THE SYSTEM" in Archivo at wdth 125) plus
     headroom, so the second term is the largest size that still fits.
     Re-measure this if the display copy ever changes. */
  --t-arch:   min(clamp(28px, 8.2vw, 84px),
                  calc((100vw - 2 * var(--outer) - 8px) / 11.2));
  --t-arch-m: clamp(22px, 3.2vw, 40px);
  --t-arch-s: clamp(24px, 3.4vw, 40px);
  --t-h2:     clamp(26px, 3.4vw, 42px);
  --t-h3:     clamp(20px, 2vw, 26px);
  --t-lede:   clamp(17px, 1.5vw, 20px);
  --t-body:   clamp(15px, 1.25vw, 17px);
  --t-small:  clamp(13px, 1.1vw, 15px);
  --t-label:  clamp(11px, 0.85vw, 12.5px);

  /* Layout */
  --max:    1440px;
  --outer:  22px;
  --gap-1:  8px;  --gap-2: 16px; --gap-3: 24px; --gap-4: 32px;
  --gap-5:  48px; --gap-6: 72px; --gap-7: 112px; --gap-8: 160px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}
@media (min-width: 768px)  { :root { --outer: 40px; } }
@media (min-width: 1280px) { :root { --outer: 64px; } }

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

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--abyss);          /* explicit: the page owns its ground */
  color: var(--white);
  font-family: var(--f-sans);
  font-size: var(--t-body);
  line-height: 1.55;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ── River canvas + grain ──────────────────────────────────────── */
#river {
  position: fixed; inset: 0; width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
}
.grain {
  position: fixed; inset: 0; z-index: 1; pointer-events: none; opacity: 0.5;
  background-image: radial-gradient(rgba(255,255,255,0.035) 1px, transparent 1px);
  background-size: 3px 3px;
  mix-blend-mode: overlay;
}
main, .topbar, .foot, .dock, .gauge { position: relative; z-index: 2; }

/* ── Depth gauge ───────────────────────────────────────────────── */
.gauge {
  position: fixed; left: var(--outer); bottom: 16px; z-index: 4;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.08em;
  color: var(--mist-2);
  transition: opacity 300ms var(--ease);
}
.gauge__num { color: var(--flow); font-variant-numeric: tabular-nums; }
/* Above the waterline the gauge sits on paper and needs the dark ink pair. */
.gauge.dry { color: var(--ink-mute); }
.gauge.dry .gauge__num { color: var(--deep-blue); }
.gauge.dry .gauge__bar { background: var(--hair-paper); }
.gauge.dry .gauge__bar i { background: var(--deep-blue); }
.gauge__unit { color: var(--mist-2); }
.gauge__bar {
  display: block; width: 76px; height: 1px; background: var(--hair);
  position: relative; overflow: hidden;
}
.gauge__bar i {
  position: absolute; inset: 0 auto 0 0; width: 0%;
  background: var(--flow); display: block;
}
/* On a short viewport the gauge lands on the hero's primary button. */
@media (max-width: 700px), (max-height: 820px) { .gauge { display: none; } }

/* ── Skip link, focus ──────────────────────────────────────────── */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 20;
  background: var(--flow); color: #04080B; padding: 12px 18px;
  font-family: var(--f-mono); font-size: 13px; text-decoration: none;
}
.skip:focus { left: 0; }
:focus-visible { outline: 2px solid var(--flow); outline-offset: 3px; border-radius: 2px; }
.s--surface :focus-visible, .s--shallow :focus-visible { outline-color: var(--deep-blue); }

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

/* ── Topbar ────────────────────────────────────────────────────── */
.topbar {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px var(--outer); z-index: 5;
}
.topbar__mark { color: var(--ink); text-decoration: none; display: block; }
.topbar__mark img { display: block; width: 132px; height: auto; }
.topbar__tag {
  font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.18em;
  color: var(--deep-blue); border: 1px solid currentColor;
  padding: 4px 10px; border-radius: 2px; text-decoration: none;
  transition: background-color 180ms var(--ease), color 180ms var(--ease);
}
.topbar__tag:hover { background: var(--deep-blue); color: var(--paper); }

/* ── Section grounds — the descent ─────────────────────────────── */
.wrap { max-width: var(--max); margin-inline: auto; padding-inline: var(--outer); }
.s { padding-block: clamp(64px, 8vh, 104px); position: relative; }
.s--surface { background: var(--paper); color: var(--ink); padding-top: 92px; }
.s--shallow { background: var(--paper-2); color: var(--ink); }
.s--deep    { background: var(--deep-1); }
.s--deeper  { background: var(--deep-2); }
.s--deeper2 { background: var(--deep-3); }
.s--deeper3 { background: var(--deep-4); }
.s--deeper4 { background: var(--deep-5); }
.s--abyss   { background: transparent; padding-block: clamp(80px, 11vh, 132px) clamp(48px, 6vh, 72px); }

.s--surface, .s--shallow { --acc: var(--deep-blue); --mut: var(--ink-mute); --hr: var(--hair-paper); }
.s--deep, .s--deeper, .s--deeper2, .s--deeper3, .s--deeper4, .s--abyss {
  --acc: var(--flow); --mut: var(--mist); --hr: var(--hair);
}

/* The waterline: the one seam where the page goes under. */
.waterline { position: relative; height: 0; z-index: 3; }
.waterline span {
  position: absolute; left: 0; right: 0; top: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--flow) 22%, var(--flow) 78%, transparent);
  opacity: 0.55;
  box-shadow: 0 0 24px 2px var(--flow-glow);
}

/* ── Type ──────────────────────────────────────────────────────── */
.label {
  font-family: var(--f-mono); font-size: var(--t-label);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--mut); margin: 0 0 var(--gap-3);
}
.mono { font-family: var(--f-mono); font-size: var(--t-small); letter-spacing: 0.03em; }
.h2 {
  font-size: var(--t-h2); font-weight: 600; line-height: 1.12;
  letter-spacing: -0.02em; text-wrap: balance;
  max-width: 20ch; margin: 0 0 var(--gap-6);
}
.lede {
  font-size: var(--t-lede); line-height: 1.5; color: var(--mut);
  max-width: 34em; margin: 0 0 var(--gap-5);
}
.fineprint { font-size: var(--t-small); color: var(--mut); max-width: 40em; margin: var(--gap-4) 0 0; }

/* Architectural type: Archivo at width 125. Signage, not prose. */
.arch {
  font-family: var(--f-display);
  font-weight: 700; font-stretch: 125%;
  font-variation-settings: "wdth" 125, "wght" 700;
  text-transform: uppercase; letter-spacing: -0.035em;
  line-height: 0.88; margin: 0;
}
.arch--lg { font-size: var(--t-arch); }
.arch--md { font-size: var(--t-arch-m); }
.arch--sm { font-size: var(--t-arch-s); line-height: 1; }
.arch__l { display: block; overflow: hidden; }
.arch__l > span { display: block; white-space: nowrap; }

/* ── Hero, on load ────────────────────────────────────────────────
   The hero animates on LOAD, not on scroll. A scroll-triggered word
   swap is only seen by people who scroll slowly at the right moment;
   this plays for everyone, and it needs no JavaScript to do it. */
.boot {
  opacity: 0; transform: translateY(16px);
  animation: boot 720ms var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * 90ms);
}
@keyframes boot { to { opacity: 1; transform: none; } }

.hero h1 { position: relative; font-size: var(--t-arch); margin: 0 0 var(--gap-4); }
.hero .lede { margin-bottom: var(--gap-4); }

/* The eyebrow above the two product frames, with a drawn rule that
   runs out to the right so the fold has a horizontal line in it. */
.frames__eyebrow {
  display: flex; align-items: center; gap: 10px;
  color: var(--mut); letter-spacing: 0.14em; font-size: var(--t-label);
  margin: 0 0 var(--gap-3);
}
.frames__eyebrow::after {
  content: ""; flex: 1; height: 1px; background: var(--hr);
  transform-origin: left; animation: rule 900ms var(--ease) 700ms both;
}
@media (max-width: 760px) {
  .frames__eyebrow { align-items: flex-start; }
  .frames__eyebrow .tick { margin-top: 5px; }
  .frames__eyebrow::after { display: none; }
}
@keyframes rule { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.tick {
  width: 6px; height: 6px; flex: none; border-radius: 50%;
  background: var(--acc); box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 18%, transparent);
}

@media (prefers-reduced-motion: reduce) {
  .boot { opacity: 1; transform: none; animation: none; }
  .frames__eyebrow::after { animation: none; }
}

/* ── Reveals ───────────────────────────────────────────────────────
   Scoped to html.js, which build.js adds. Nothing is hidden until
   something is guaranteed to un-hide it: with JavaScript off the page
   renders complete, which is the build gate the spec puts first. */
.js .rv {
  opacity: 0; transform: translateY(18px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.js .rv.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .rv { opacity: 1; transform: none; transition: none; }
  .hero .arch__stack--two { display: none; }
}

/* The second headline stack is a motion device. Without JS it would sit
   on top of the first, so it only exists when JS is there to move it. */
.arch__stack--two { display: none; }
/* Must restate flex, not block: the layout rule below relies on it to
   push the replacement onto lines two and three. */
.js .arch__stack--two { display: flex; }

/* ── Buttons ───────────────────────────────────────────────────── */
.cta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-3); margin-bottom: var(--gap-6); }
.cta__quiet {
  color: var(--acc); text-decoration: none; letter-spacing: 0.08em;
  text-transform: uppercase; font-size: 11.5px;
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
}
.cta__quiet:hover { text-decoration: underline; text-underline-offset: 4px; }
/* Stacked buttons that hug their own labels read as a mistake. */
@media (max-width: 599px) { .cta .btn { width: 100%; } }
.btn {
  display: inline-flex; align-items: center; gap: 8px; justify-content: center;
  min-height: 52px; padding-inline: 26px; border-radius: 2px;
  font-size: var(--t-body); font-weight: 600; line-height: 1;
  text-decoration: none; cursor: pointer; border: 1.5px solid transparent;
  transition: transform 180ms var(--ease), background-color 180ms var(--ease),
              color 180ms var(--ease), box-shadow 180ms var(--ease);
}
.s--surface .btn--primary, .s--shallow .btn--primary { background: var(--ink); color: var(--paper); }
.btn--primary { background: var(--flow); color: #04080B; }
.btn--primary:hover { transform: translateY(-2px); box-shadow: 0 8px 30px -12px var(--flow); }
.btn--ghost { border-color: currentColor; color: inherit; background: transparent; }
.btn--ghost:hover { background: rgba(128,180,205,0.10); transform: translateY(-2px); }

/* ── 01 Hero ───────────────────────────────────────────────────── */
/* The hero shares the page measure. It used to sit inside a narrower
   1180px box, so its left edge lined up with nothing below it. */
.hero h1 { margin-bottom: var(--gap-4); }
.hero .lede { margin-bottom: var(--gap-4); }
.more { color: var(--mut); margin: var(--gap-2) 0 0; letter-spacing: 0.1em; }

.frames { display: grid; gap: var(--gap-4); }
@media (min-width: 900px) { .frames { grid-template-columns: 1fr 1fr; gap: var(--gap-5); } }

/* Browser frames. The URL is the proof; nothing may decorate around it. */
.frame { margin: 0; }
.frame__chrome {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 12px; border: 1px solid var(--hr); border-bottom: 0;
  border-radius: 3px 3px 0 0; background: rgba(128,170,195,0.06);
}
.s--surface .frame__chrome, .s--shallow .frame__chrome { background: #fff; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--hr); flex: none; }
.frame__url {
  font-family: var(--f-mono); font-size: 11.5px; letter-spacing: 0.02em;
  color: var(--acc); margin-left: 10px;
}
.frame__shot {
  border: 1px solid var(--hr); border-radius: 0 0 3px 3px;
  display: grid; place-items: center;
  background:
    linear-gradient(135deg, rgba(68,193,246,0.05), transparent 60%),
    repeating-linear-gradient(45deg, rgba(128,170,195,0.05) 0 1px, transparent 1px 9px);
  box-shadow: 0 24px 60px -30px rgba(0,0,0,0.75);
  overflow: hidden;
}
/* The image sizes its own box. A fixed ratio here cropped 2-5% off the
   sides of every screenshot — and on a UI screenshot the sides are the
   navigation and the buttons, which is the part that must survive. Each
   img carries real width/height attributes, so the space is still
   reserved before it loads and nothing shifts. */
.frame__shot img { width: 100%; height: auto; display: block; }
/* Only used if a shot is ever missing, so the frame is not zero-height. */
.frame__shot:not(:has(img)) { aspect-ratio: 1440 / 751; }
.shot__note {
  font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.14em;
  color: var(--mut); text-align: center; padding: 0 12px;
}
.frame figcaption {
  font-size: var(--t-small); color: var(--mut); margin-top: var(--gap-2);
}
.frame figcaption a { color: var(--acc); text-decoration: none; border-bottom: 1px solid currentColor; }
/* These two are a client's production tools. The URL is the evidence;
   an "open it" button would only send strangers into them. */
/* Sits on the same line as the caption it qualifies — one word is enough. */
.frame__note {
  display: inline-block; font-family: var(--f-mono); font-size: 10px;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--mist-2);
  border: 1px solid var(--hr); border-radius: 2px;
  padding: 1px 6px; margin-left: 6px; white-space: nowrap;
  vertical-align: 1px;
}
.s--surface .frame__note, .s--shallow .frame__note { color: var(--ink-mute); }
.facts__note { color: var(--mist-2); }
.frame figcaption a:hover { color: inherit; }

/* ── 02 Rows ───────────────────────────────────────────────────── */
.rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--hr); }
.row {
  display: grid; gap: var(--gap-1) var(--gap-5);
  padding-block: var(--gap-4);
  border-bottom: 1px solid var(--hr);
  position: relative;
}
/* Draws in accent, then settles to the neutral rule underneath. Left at
   full accent, every row read as a selected state. */
.row::after {
  content: ""; position: absolute; left: 0; bottom: -1px; height: 1px;
  width: 0; background: var(--acc);
  transition: width 520ms var(--ease), opacity 400ms var(--ease) 560ms;
}
.row.in::after { width: 100%; opacity: 0; }
html:not(.js) .row::after { width: 100%; }
.row h3 { font-size: var(--t-h3); font-weight: 600; margin: 0; letter-spacing: -0.015em; }
.row p { margin: 0; color: var(--mut); max-width: 46ch; }
.row__link {
  color: var(--acc); text-decoration: none; justify-self: start;
  display: inline-flex; align-items: center; gap: 7px;
  letter-spacing: 0.06em; text-transform: uppercase; font-size: 11.5px;
}
.row__link:hover { text-decoration: underline; text-underline-offset: 4px; }
@media (min-width: 900px) {
  .row { grid-template-columns: 5fr 7fr auto; align-items: baseline; }
}

/* ── 03 Exhibits ───────────────────────────────────────────────── */
.ex {
  display: grid; gap: var(--gap-5);
  padding-block: var(--gap-6);
  border-top: 1px solid var(--hr);
}
@media (min-width: 1000px) {
  .ex { grid-template-columns: 7fr 5fr; align-items: start; gap: var(--gap-6); }
  .ex--flip .ex__media { order: 2; }
}
.ex__media { position: relative; }
/* A designed overlap at two columns. At one column it covered the
   desktop shot and the heading under it. */
.frame--phone { display: none; }
@media (min-width: 1000px) {
  .frame--phone {
    display: block;
    position: absolute; right: -6px; bottom: -34px; width: 26%; max-width: 150px;
  }
}
.frame--phone .frame__shot { border-radius: 6px; }
.ex h3 { font-size: var(--t-h3); font-weight: 600; margin: 0 0 var(--gap-2); letter-spacing: -0.015em; }
.ex__body { color: var(--mut); margin: 0 0 var(--gap-4); max-width: 42ch; }
.facts { margin: 0; display: grid; gap: 10px; font-size: 12px; }
.facts > div { display: grid; gap: 4px; align-items: start; }
@media (min-width: 560px) {
  .facts > div { grid-template-columns: 92px 1fr; gap: 12px; }
}
.facts dt { color: var(--mist-2); letter-spacing: 0.12em; }
.facts dd { margin: 0; color: var(--mut); }
.facts a { color: var(--acc); text-decoration: none; border-bottom: 1px solid var(--flow-dim); }
.facts a:hover { border-bottom-color: currentColor; }

.pair__label { padding-top: var(--gap-6); border-top: 1px solid var(--hr); margin-bottom: var(--gap-4); }
.pair { display: grid; gap: var(--gap-5); padding-bottom: var(--gap-6); }
@media (min-width: 820px) { .pair { grid-template-columns: 1fr 1fr; gap: var(--gap-6); } }
.mini h3 { font-size: 18px; font-weight: 600; margin: var(--gap-3) 0 4px; }
.mini p { margin: 0 0 4px; color: var(--mut); }
.mini a { color: var(--acc); text-decoration: none; border-bottom: 1px solid var(--flow-dim); }

.thispage { padding-top: var(--gap-6); border-top: 1px solid var(--hr); }
.thispage h3 { margin-bottom: var(--gap-4); color: var(--white); }
.stats { display: grid; gap: var(--gap-3); margin: 0; }
@media (min-width: 720px) { .stats { grid-template-columns: repeat(3, 1fr); } }
.stats dt { font-size: 10.5px; letter-spacing: 0.16em; color: var(--mist-2); margin-bottom: 6px; }
.stats dd {
  margin: 0; font-size: clamp(20px, 2.2vw, 28px); color: var(--flow);
  font-variant-numeric: tabular-nums;
}

/* ── 04 Automation bridge ──────────────────────────────────────────
   The build work is the smaller half of the company. Someone who reads
   this far and recognises a process problem rather than a build problem
   should be handed to the main site rather than lost. */
.bridge { display: grid; gap: var(--gap-5); }
@media (min-width: 1000px) {
  .bridge { grid-template-columns: 7fr 5fr; gap: var(--gap-7); align-items: start; }
}
.bridge__main .h2 { margin-bottom: var(--gap-3); }
.bridge__main .lede { margin-bottom: var(--gap-3); }
.bridge__p { color: var(--mut); max-width: 34em; margin: 0 0 var(--gap-4); }
.bridge__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gap-2); }
.bridge__list li {
  display: grid; grid-template-columns: 6px 1fr; gap: 14px; align-items: center;
  padding-block: var(--gap-2); border-bottom: 1px solid var(--hr);
  color: var(--mut); letter-spacing: 0.02em;
}
.bridge__list li:first-child { border-top: 1px solid var(--hr); }

/* ── 05 Stage → result ────────────────────────────────────────────
   Each row is one trade: what happens on the left, what the client is
   holding when it is over on the right. The old version listed the
   stages down one column and repeated the outcomes in a panel beside
   them, which said everything twice and still left the screen lopsided. */
.flow { list-style: none; margin: 0; padding: 0; display: grid; }
.flow__row {
  display: grid; gap: var(--gap-3);
  padding-block: var(--gap-5);
  border-top: 1px solid var(--hr);
}
.flow__row:last-child { border-bottom: 1px solid var(--hr); }
@media (min-width: 900px) {
  .flow__row {
    grid-template-columns: minmax(0, 5fr) 44px minmax(0, 6fr);
    align-items: stretch; gap: var(--gap-4);
  }
  /* The rule runs the full height of the row, so it reads as a divider
     between the two halves rather than as a bracket around the result. */
  .flow__stage { align-self: start; }
  .flow__out { align-self: stretch; display: flex; flex-direction: column; justify-content: center; }
}
.flow__stage { display: grid; grid-template-columns: 46px 1fr; gap: 0 var(--gap-2); }
.flow__n {
  grid-row: 1 / span 2; color: var(--acc); font-size: 13px;
  letter-spacing: 0.1em; padding-top: 7px;
}
.flow__stage h3 { font-size: var(--t-h3); font-weight: 600; margin: 0 0 6px; letter-spacing: -0.015em; }
.flow__stage p { margin: 0; color: var(--mut); max-width: 34ch; }

/* The arrow is the whole diagram: stage on one side, result on the other. */
.flow__arrow {
  display: none; align-self: center; justify-self: center;
  color: var(--acc); font-size: 18px; opacity: 0.55;
}
@media (min-width: 900px) { .flow__arrow { display: block; } }
.flow__row.in .flow__arrow { animation: nudge 2.4s var(--ease) 400ms 2; }
@keyframes nudge {
  0%, 100% { transform: translateX(0); opacity: 0.55; }
  40%      { transform: translateX(6px); opacity: 1; }
}

.flow__out { border-left: 1px solid var(--hr); padding-left: var(--gap-3); }
.flow__k { color: var(--mist-2); letter-spacing: 0.16em; font-size: 10.5px; margin: 0 0 8px; }
.flow__out p { margin: 0; color: var(--mut); max-width: 40ch; }
.flow__out strong { color: var(--white); font-weight: 600; }
.s--surface .flow__out strong, .s--shallow .flow__out strong { color: var(--ink); }

/* ── 06 Price table ────────────────────────────────────────────────
   Published numbers, set as data: the name reads as a heading, the
   figure and the duration as facts, all on one baseline. */
.price { width: 100%; border-collapse: collapse; }
.price th, .price td {
  text-align: left; padding: var(--gap-3) 0;
  border-bottom: 1px solid var(--hr); vertical-align: baseline;
}
.price tr:first-child th, .price tr:first-child td { border-top: 1px solid var(--hr); }
.price th { font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.015em; padding-right: var(--gap-4); }
.qual {
  display: block; font-family: var(--f-mono); font-size: 11.5px;
  color: var(--mut); letter-spacing: 0.06em; font-weight: 400; margin-top: 4px;
}
.num {
  color: var(--flow); font-size: clamp(17px, 1.8vw, 22px);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  padding-right: var(--gap-4);
}
.dur { color: var(--mut); white-space: nowrap; text-align: right; }
@media (max-width: 700px) {
  .price th, .price td { display: block; border: 0; padding: 6px 0; }
  .price tr { display: block; padding-block: var(--gap-3); border-bottom: 1px solid var(--hr); }
  .price tr:first-child { border-top: 1px solid var(--hr); }
  .dur { text-align: left; }
}

/* ── 06 Billing models ─────────────────────────────────────────────
   A fixed price only covers a fixed scope. Saying so, next to the
   published numbers, is what stops the first change request becoming
   an argument. */
.billing { display: grid; gap: var(--gap-5); margin-top: var(--gap-6); }
@media (min-width: 820px) { .billing { grid-template-columns: 1fr 1fr; gap: var(--gap-6); } }
.billing__col { padding-top: var(--gap-3); border-top: 1px solid var(--acc); }
.billing__k {
  margin: 0 0 var(--gap-2); font-size: 11.5px; letter-spacing: 0.18em;
  color: var(--acc); font-weight: 400;
}
.billing__col p { margin: 0; color: var(--mut); font-size: var(--t-small); max-width: 40em; }

/* ── 07 Commitments ────────────────────────────────────────────── */
.commits { list-style: none; margin: 0 0 var(--gap-6); padding: 0; display: grid; gap: var(--gap-4); }
.commits li { display: grid; gap: 6px; }
@media (min-width: 900px) {
  .commits li {
    grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
    align-items: baseline; gap: var(--gap-5);
  }
}
@media (min-width: 1200px) {
  .commits li { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); }
}
.commit__k { color: var(--white); }
.commit__v { color: var(--mut); font-size: var(--t-body); max-width: 46ch; }
/* Set below the commitments it qualifies, so it reads as a footnote. */
.commits__foot {
  font-size: var(--t-small); color: var(--mut); max-width: 52ch;
  margin: 0; padding-top: var(--gap-3); border-top: 1px solid var(--hr);
}

/* ── 07 Configurator ───────────────────────────────────────────── */
.s--abyss .arch--lg { margin-bottom: var(--gap-6); color: var(--white); }
.s--abyss .arch--lg .arch__l > span { text-shadow: 0 0 60px rgba(68,193,246,0.22); }
.cfg { display: grid; gap: var(--gap-5); }
/* All four questions are visible at once. Revealing them one at a time
   meant a scroll after every answer — a cost, and no benefit.

   The two columns are equal by construction rather than by luck: each is a
   flex column, and the textarea in the right one grows to take up whatever
   slack is left. Laid out as a plain 2x2 grid, the short timing question
   left a hole under itself the height of the textarea beside it. */
.cfg__grid { display: grid; gap: var(--gap-5); }
.cfg__col { display: flex; flex-direction: column; gap: var(--gap-5); }
@media (min-width: 900px) {
  .cfg__grid { grid-template-columns: 1fr 1fr; gap: var(--gap-6); align-items: stretch; }
  .step--grow { flex: 1; }
  .field--grow { flex: 1; display: grid; grid-template-rows: auto 1fr; }
  .field--grow textarea { height: 100%; }
}
.cfg textarea { min-height: 118px; resize: vertical; }

/* The send row keeps the same two-column rhythm: the field sits over the
   left column, the button starts on the right column's edge. */
.cfg__send { display: grid; gap: var(--gap-3); align-items: end; }
@media (min-width: 700px) {
  .cfg__send { grid-template-columns: 1fr 1fr; gap: var(--gap-6); }
}
.cfg__email { min-width: 0; }
.btn--send { justify-self: start; }
@media (max-width: 699px) { .btn--send { width: 100%; } }
.step { border: 0; padding: 0; margin: 0; display: grid; gap: var(--gap-3); }
.step legend {
  padding: 0; font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.015em;
  margin-bottom: var(--gap-2);
}
.step legend .mono {
  display: inline-block; font-size: 11px; letter-spacing: 0.16em;
  color: var(--flow); font-weight: 400; margin-right: 10px;
}
.chips { display: flex; flex-wrap: wrap; gap: 10px; }
.chip { position: relative; }
.chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip span {
  display: inline-flex; align-items: center; min-height: 46px; padding: 0 18px;
  border: 1px solid var(--hair); border-radius: 2px; cursor: pointer;
  color: var(--mist); font-size: var(--t-small);
  transition: border-color 160ms var(--ease), color 160ms var(--ease),
              background-color 160ms var(--ease);
}
.chip span:hover { border-color: var(--flow-dim); color: var(--white); }
.chip input:checked + span {
  border-color: var(--flow); color: var(--flow);
  background: var(--flow-glow);
}
.chip input:focus-visible + span { outline: 2px solid var(--flow); outline-offset: 3px; }
.field { display: grid; gap: 8px; }
.field__l { font-family: var(--f-mono); font-size: 11.5px; letter-spacing: 0.1em; color: var(--mist-2); text-transform: uppercase; margin: 0; }
.cfg textarea, .cfg input[type="email"] {
  width: 100%; max-width: 100%; background: rgba(128,170,195,0.05); color: var(--white);
  border: 1px solid var(--hair); border-radius: 2px; padding: 14px 16px;
  font-family: inherit; font-size: var(--t-body); resize: vertical;
}
.cfg textarea:focus, .cfg input[type="email"]:focus { border-color: var(--flow); outline: none; }
.cfg textarea::placeholder, .cfg input::placeholder { color: var(--mist-2); }
.btn--send { justify-self: start; }
.cfg__err { color: #FF8A6B; margin: 0; }
.cfg__done {
  max-width: 640px; border-left: 2px solid var(--flow); padding-left: var(--gap-3);
  font-size: var(--t-lede); color: var(--white);
}

/* Nothing is hidden and nothing is gated, so the form behaves the same
   with JavaScript on or off. */

/* ══ LIFE ══════════════════════════════════════════════════════════
   Movement that reports something, rather than movement for its own
   sake: a live signal that pulses, controls that answer the pointer,
   a section marker that blinks like a caret. All of it stops dead
   under prefers-reduced-motion. */

/* The signal dot: the page is looking at something running. */
.tick { position: relative; }
.tick::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 1px solid var(--acc); opacity: 0;
  animation: ping 2.8s var(--ease) infinite;
}
@keyframes ping {
  0%   { transform: scale(0.6); opacity: 0.7; }
  70%  { transform: scale(1.9); opacity: 0; }
  100% { transform: scale(1.9); opacity: 0; }
}

/* Rows answer the pointer: the rule lights and the row steps right. */
.row { transition: transform 260ms var(--ease); }
.row:hover { transform: translateX(6px); }
.row:hover::after { width: 100%; opacity: 1; transition: opacity 160ms var(--ease); }
.row:hover h3 { color: var(--acc); transition: color 200ms var(--ease); }

/* Frames lift and pick up the accent when pointed at. */
.frame__shot { transition: transform 380ms var(--ease), box-shadow 380ms var(--ease); }
.frame:hover .frame__shot {
  transform: translateY(-4px);
  box-shadow: 0 30px 70px -30px rgba(0,0,0,0.85), 0 0 0 1px var(--flow-dim);
}
.frame__chrome { transition: border-color 300ms var(--ease); }
.frame:hover .frame__chrome { border-color: var(--flow-dim); }

/* Chips answer before they are chosen. */
.chip span { transition: border-color 160ms var(--ease), color 160ms var(--ease),
                         background-color 160ms var(--ease), transform 160ms var(--ease); }
.chip span:hover { transform: translateY(-2px); }

/* The price rows count as data: the number brightens on approach. */
.price tr { transition: background-color 220ms var(--ease); }
.price tr:hover { background: rgba(128,170,195,0.04); }
.price tr:hover .num { color: var(--white); transition: color 220ms var(--ease); }

/* The waterline breathes, so the page reads as running rather than printed. */
.waterline span { animation: seam 6s var(--ease) infinite; }
@keyframes seam { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.75; } }

/* Commitments sweep in from the left edge of their line box. */
.commits li .commit__k > span { display: inline-block; }
.commits li.in .commit__k > span { animation: sweep 620ms var(--ease) both; }
@keyframes sweep {
  from { transform: translateX(-14px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* The published statistics arrive as a masked wipe, not a counter. */
.stats dd { position: relative; overflow: hidden; }
.thispage.in .stats dd { animation: wipe 700ms var(--ease) both; }
.thispage.in .stats > div:nth-child(2) dd { animation-delay: 90ms; }
.thispage.in .stats > div:nth-child(3) dd { animation-delay: 180ms; }
@keyframes wipe {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

@media (prefers-reduced-motion: reduce) {
  .tick::after, .waterline span,
  .commits li.in .commit__k > span, .thispage.in .stats dd,
  .flow__row.in .flow__arrow { animation: none; }
  .row, .frame__shot, .chip span, .price tr { transition: none; }
  .row:hover { transform: none; }
  .frame:hover .frame__shot { transform: none; }
}

/* ── Footer / FAB ──────────────────────────────────────────────── */
.foot { border-top: 1px solid var(--hair); padding-block: var(--gap-4); background: var(--abyss); }
.foot__in { display: flex; flex-wrap: wrap; gap: var(--gap-2) var(--gap-5); align-items: center; }
.foot p { margin: 0; color: var(--mist-2); letter-spacing: 0.08em; }
.foot a { color: var(--mist); text-decoration: none; }
.foot a:hover { color: var(--flow); }
.foot__c { margin-left: auto !important; }

.dock {
  position: fixed; right: var(--outer); bottom: 28px; z-index: 6;
  display: flex; align-items: center; gap: 10px;
  transition: transform 220ms var(--ease);
}
@media (prefers-reduced-motion: reduce) { .dock { transition: none; } }
.dock__top, .dock__cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; text-decoration: none; border-radius: 2px;
  opacity: 0; transform: translateY(12px); pointer-events: none;
  transition: opacity 240ms var(--ease), transform 240ms var(--ease),
              background-color 160ms var(--ease);
}
.dock__top {
  width: 44px; font-size: 16px; line-height: 1;
  background: rgba(128,170,195,0.10); color: var(--flow);
  border: 1px solid var(--hair);
}
.dock__top:hover { background: rgba(128,170,195,0.20); }
.dock__cta {
  padding: 0 16px; background: var(--flow); color: #04080B;
  letter-spacing: 0.06em; font-size: 12.5px;
}
.dock__top.on, .dock__cta.on { opacity: 1; transform: none; pointer-events: auto; }
@media (max-width: 599px) {
  .dock__cta { font-size: 11.5px; padding: 0 14px; min-height: 42px; }
  .dock__top { width: 42px; min-height: 42px; }
}
