/* =========================================================
   ATREIDES — site-atreides
   Two acts: black and bone, joined by a plunge through the
   brand blue. Amber is the working accent; blue only where
   intelligence is denoted. The flare gradient appears in the
   hero grade, the mark, the contact button and the plate
   under "engineering" — nowhere else. Nothing moves but the
   reel, the two flares, the riders on the turn's outer ring,
   and the stack laying its layers as the reader reaches them.
   ========================================================= */

/* woff2, not the .otf originals: 536KB -> 208KB across the four faces,
   and the web format is not an installable desktop binary of a licensed
   typeface. The .otf files stay in the repo and are kept out of the
   deployment by .vercelignore.

   ?v=6 — every URL under fonts/ and img/ carries this, because
   vercel.json now serves both directories `immutable` for a year. The
   query IS the cache key: a repeat visitor holds these files without
   ever asking about them again, and the ONLY way to hand them a new
   one is to bump the number here, in index.html and in site.js
   together. Replacing a file in place ships nothing. */
@font-face{ font-family:"Alliance No2"; src:url("fonts/alliance-regular.woff2?v=6") format("woff2");  font-weight:400; font-style:normal; font-display:swap; }
@font-face{ font-family:"Alliance No2"; src:url("fonts/alliance-semibold.woff2?v=6") format("woff2"); font-weight:600; font-style:normal; font-display:swap; }
@font-face{ font-family:"Alliance No2"; src:url("fonts/alliance-bold.woff2?v=6") format("woff2");     font-weight:700; font-style:normal; font-display:swap; }
@font-face{ font-family:"Alliance No2"; src:url("fonts/alliance-extrabold.woff2?v=6") format("woff2");font-weight:800; font-style:normal; font-display:swap; }

:root{
  --ink:#0A0A0B;
  --ink-72:rgba(10,10,11,.72);
  --bone:#EDE9DF;
  --bone-70:rgba(237,233,223,.70);
  --bone-45:rgba(237,233,223,.45);
  --bone-30:rgba(237,233,223,.30);
  --hair:rgba(237,233,223,.16);
  --amber:#FF9F00;
  --gold:#FFCF3C;
  --blue:#168ADF;
  --blue-deep:#0078D6;
  --font-d:"Alliance No2",system-ui,-apple-system,sans-serif;
  --font-m:"IBM Plex Mono",ui-monospace,"SF Mono",Menlo,monospace;
  --pad:48px;
}

*{ margin:0; padding:0; box-sizing:border-box; }
html,body{ overscroll-behavior:none; }
body{
  background:var(--ink);
  color:var(--bone);
  font-family:var(--font-d);
  font-feature-settings:"ss04";
  font-size:16px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  -webkit-user-select:none; user-select:none;   /* nothing on the page selects, so nothing highlights */
}
img{ max-width:100%; }
a{ color:inherit; text-decoration:none; }
a:focus-visible{ outline:1px solid var(--amber); outline-offset:3px; }

.wrap{ max-width:1200px; margin:0 auto; padding:0 var(--pad); }
.wrap-c{ max-width:820px; margin:0 auto; padding:0 var(--pad); text-align:center; }

/* the one button on the page — small, carrying the softened flare */
.btn{
  display:inline-block;
  appearance:none; border:0; cursor:pointer;
  font-family:var(--font-m); font-size:11px; font-weight:600;
  letter-spacing:.12em; text-transform:uppercase;
  background:linear-gradient(135deg,
    #E09B29 0%, #E7C355 42%,
    #2E86C6 74%, #1B75BB 100%);
  background-size:250% 250%;
  color:var(--ink);
  padding:11px 22px;
  animation:btn-flare 3.2s ease-in-out infinite alternate;
}
@keyframes btn-flare{
  from{ background-position:0% 0%; }
  to{ background-position:100% 100%; }
}

/* ================= BRAND ================= */
#nav{
  position:fixed; inset:0 0 auto 0; z-index:10;
  padding:22px var(--pad);
}
.nav-brand{ display:inline-flex; align-items:center; gap:14px; }
.mark{ height:24px; width:auto; display:block; }
.mark .chev, .mark .star{ fill:var(--bone); }
/* rounded wing tips ride a same-colour 4u round stroke; the star stays sharp */
.mark .chev, .mark-g .chev{ stroke:var(--bone); stroke-width:4; stroke-linejoin:round; }
.nav-word{
  font-weight:600; font-size:15px; letter-spacing:.34em;
  transform:translateY(1px);
  transition:opacity .35s ease;
}
body.scrolled .nav-word{ opacity:0; }
/* over the bone act the mark renders flat ink (gradient is dark-ground only) */
#nav.on-light .chev, #nav.on-light .star{ fill:var(--ink); }
#nav.on-light .chev{ stroke:var(--ink); }

/* ================= HERO ================= */
/* 100vh on a phone is the LARGE viewport — the screen as it would be with
   the browser's toolbars already retracted — so a body pinned to the bottom
   of the box sits behind them, and how much is swallowed is per-browser:
   Safari's one compact bar hides a sliver, Chrome's address bar plus toolbar
   hide the whole sub. svh is the viewport with all that chrome showing, so
   the nameplate clears it everywhere, and unlike dvh it does not move when
   the toolbars retract — it just lets the next section peek over the fold.
   The vh line stays first as the fallback for engines without svh. */
.hero{
  position:relative;
  height:100vh;
  height:100svh;
  min-height:560px;
  overflow:hidden;
}
.hero-img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:center 38%;
  filter:saturate(1.12) contrast(1.06) brightness(.92);
}
/* the one flare-gradient moment: the grade itself */
.hero-grade{ position:absolute; inset:0; pointer-events:none; }
.hero-grade::before{
  content:""; position:absolute; inset:0;
  background:linear-gradient(112deg,
    rgba(0,120,214,.20) 0%, rgba(22,138,223,.09) 26%,
    rgba(10,10,11,0) 48%,
    rgba(255,159,0,.09) 72%, rgba(255,159,0,.18) 100%);
  mix-blend-mode:overlay;
}
.hero-grade::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(180deg,
    rgba(10,10,11,.62) 0%, rgba(10,10,11,.10) 32%,
    rgba(10,10,11,.10) 52%, rgba(10,10,11,.78) 84%, var(--ink) 100%);
}
.hero-body{
  position:absolute; inset:auto 0 0 0;
  padding:0 var(--pad) 40px;
}
.hero-line{
  font-weight:800; text-transform:uppercase;
  letter-spacing:-.012em; line-height:.94;
  /* Sized for the face that will actually render it. In Alliance ExtraBold
     at this tracking the line measures 13.896x its own font-size, dead
     linear, so the size that fills the frame is the frame / 13.896 — the
     very number site.js converges on, which means the face landing changes
     the letterforms and nothing else. It used to say 7vw, calibrated to
     nothing: fitted to the fallback the line came out 30.4px, and Alliance
     at 30.4px is 422px wide in a 384px frame, 38px of it clipped, until
     fonts.ready snapped it back. The 2px is fitLine's own margin; site.js
     still has the last word, and still needs it if this copy ever changes. */
  font-size:calc((100vw - 2 * var(--pad) - 2px) * 0.071963);
  white-space:nowrap;
}
.hero-sub{
  max-width:44ch; margin:26px 0 0 auto; text-align:right;
  font-size:16px; line-height:1.55; color:rgba(237,233,223,.8);
}

/* ================= PROBLEM ================= */
.problem{ padding:190px var(--pad) 8px; }
.stat{ max-width:980px; margin:0 auto; }
/* the number is the figure; the rest of the sentence stands beside it,
   its last line lifted a little off the numeral's baseline */
.stat-row{ display:flex; align-items:baseline; gap:34px; }
/* 200, not 230: the operator adds ~120px to the block at the old size,
   which leaves the claim 496px where its designed first line needs 577,
   and drops it to four lines. At 200 with the operator at .62em the
   block is 351px and the two-line break clears by 15px. 205 also fits,
   by 6px — not enough to survive a face or a rounding change. */
.stat-num{
  font-weight:800; font-size:clamp(110px, 13.9vw, 200px);
  line-height:.78; letter-spacing:-.045em;
}
/* the qualifier, as notation — set below the figure's size so it reads as
   an operator on the number rather than a character in it */
.stat-num .lt{ font-size:.62em; margin-right:.04em; letter-spacing:-.02em; }
/* the glyph is decorative; these are the words behind it, for anything
   that reads the page rather than looks at it */
.vh{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
.stat-claim{
  position:relative; top:-1.65em;
  font-weight:600; font-size:clamp(24px, 2.8vw, 40px);
  line-height:1.25; letter-spacing:-.012em;
}
.ref-mark{
  font-family:var(--font-m); font-weight:500; font-size:.42em; line-height:0;
  vertical-align:.95em; margin-left:.16em; color:var(--bone-70);
}
.ref{
  margin-top:-8px;
  font-family:var(--font-m); font-size:11px; line-height:1.7; letter-spacing:.04em;
  color:var(--bone-30);
}
.ref .ref-no{ display:inline-block; min-width:1.8em; color:var(--bone-45); }
.ref cite{ font-style:normal; color:var(--bone-45); white-space:nowrap; }

/* ================= THE TURN ================= */
/* the turn rides over the head of the plunge: the band starts behind
   the globe's lower wires, so there is no black left under the planet */
.turn{ position:relative; z-index:1; padding:0 var(--pad); overflow:hidden; }
.orbit{ width:min(1200px, 92vw); aspect-ratio:15/7; margin:0 auto; }
.orbit svg{ width:100%; height:100%; display:block; overflow:visible; }
/* the statement sits straight on the wires: no halo behind it */
.turn .line{
  font-size:40px; font-weight:600; letter-spacing:-.012em;
  fill:var(--bone); text-anchor:middle;
}
.turn .line .w{ fill:var(--ink); font-weight:700; }
.turn .plate{ fill:url(#plate-flare); }
/* the globe: near lines firm, far lines faint */
.turn .globe{ fill:none; stroke:var(--bone-45); stroke-width:1; }
.turn .wire-near path{ fill:none; stroke:var(--bone-30); stroke-width:1; }
.turn .wire-far path{ fill:none; stroke:var(--hair); stroke-width:1; }
.turn .ring{ fill:none; stroke:url(#ring-fade); stroke-width:1; }
/* the riders: survey markers, labelled */
/* no ink behind the riders: they cross the head of the plunge, and an
   ink halo would read as a black box once the ground turns navy */
.turn .mk{ fill:none; stroke:var(--bone); stroke-width:1.2; }
.turn .tag{
  font-family:var(--font-m); font-size:17px; font-weight:500; letter-spacing:.14em;
  fill:var(--bone); text-anchor:middle;
}
.turn .far .mk{ stroke:var(--bone-45); }
.turn .far .tag{ fill:var(--bone-45); }

/* ================= THE PLUNGE ================= */
.band{
  height:42vh;
  /* ride up behind the globe's lower wires — 95 of the figure's 1200
     units, so the overlap scales with it and always clears the
     statement's last line (which ends at unit 509 of 660). Lowered
     from 140 on 2026-08-29: the ink now runs past the two lower
     riders, and only the globe's bottom cap sits on the blue */
  margin-top:calc(min(1200px, 92vw) * -.0792);
  /* Paced in CIE L*, not in sRGB values. The old curve accelerated
     hardest at the TOP — 3.8 L* per 10px out of the black against 1.2
     into the bone — and the eye reads a given step as far harsher in
     near-black than in pale blue, so the plunge broke right under the
     globe. The rate is now a skewed bell, r(t) = t^2 (1-t)^1.5: it
     leaves the ink at 0.2 L* per 10px, peaks at 3.5 around L* 52 (a mid
     blue, where the eye is least sensitive) and eases out at 0.7 into
     the bone. Both ends still reach ZERO slope, or the band's own
     edges read as stripes against the flat ink above and bone below.
     Same colour path as before, only re-paced: the hues are resampled
     at even L* off the original ramp, and any gap wider than 3.5% is
     split so the long dark shoulder keeps its shape. Regenerate with
     tools/band-ramp.py (a=2.0 b=1.5) rather than hand-nudging a stop. */
  background:linear-gradient(180deg,
    var(--ink) 0%, #0A0B0C 7.656%, #090D11 11.484%,
    #090F14 13.398%, #081017 15.313%, #08121C 17.481%,
    #081521 19.65%, #07192A 22.812%, #061C32 25.4%,
    #051F3A 27.65%, #052340 29.663%, #042747 31.512%,
    #042B4D 33.212%, #042E54 34.825%, #04325B 36.35%,
    #043662 37.8%, #043A69 39.188%, #033E70 40.538%,
    #034278 41.837%, #03467F 43.1%, #024A85 44.337%,
    #024E8C 45.55%, #025293 46.725%, #02579A 47.888%,
    #025BA2 49.038%, #025FA9 50.175%, #0163B1 51.287%,
    #0168B8 52.4%, #016CC0 53.5%, #0070C8 54.6%,
    #0275CF 55.688%, #087AD3 56.775%, #0E7FD7 57.862%,
    #1A84D9 58.95%, #2589DA 60.038%, #308EDB 61.138%,
    #3A93DC 62.238%, #4498DD 63.35%, #4D9DDE 64.475%,
    #57A2DF 65.612%, #61A6E1 66.775%, #6BABE2 67.95%,
    #75AFE3 69.15%, #7FB4E4 70.388%, #88B8E3 71.65%,
    #92BDE3 72.95%, #9AC2E3 74.312%, #A4C6E2 75.725%,
    #AECAE2 77.225%, #B7CFE2 78.812%, #BFD3E1 80.525%,
    #C9D8E1 82.4%, #D1DCE0 84.525%, #DBE0E0 87.05%,
    #E4E5DF 90.388%, #EBE8DF 95.194%, var(--bone) 100%);
}

/* ================= APPROACH ================= */
.approach{ background:var(--bone); color:var(--ink); padding:56px 0 0; }
.approach h2{
  font-weight:700; font-size:clamp(30px, 4.2vw, 56px);
  line-height:1.12; letter-spacing:-.02em;
}
.approach .wrap-c p{
  margin-top:34px; font-size:17px; line-height:1.75; color:var(--ink-72);
}
/* the laptop on the light ground; the bone band threads across it */
.console-melt{ position:relative; margin-top:96px; }
/* three tabs, named for what each screen enables; the live one carries the amber rule */
.tabs{ display:flex; justify-content:center; gap:40px; margin-bottom:44px; }
.tab{
  appearance:none; background:none; border:0; cursor:pointer;
  font-family:var(--font-m); font-size:11px; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase;
  color:rgba(10,10,11,.42); padding:0 2px 9px;
  border-bottom:2px solid transparent;
}
.tab:hover{ color:var(--ink-72); }
.tab.on{ color:var(--ink); border-bottom-color:var(--amber); }
.tab:focus-visible{ outline:1px solid var(--amber); outline-offset:6px; }
.laptop{ position:relative; width:min(1180px, 74vw); margin:0 auto; }
.screen{
  position:relative; overflow:hidden;
  aspect-ratio:16/10;
  background:#101012;
  border:14px solid #101012; border-bottom-width:22px;
  border-radius:20px 20px 0 0;
}
/* the magma as the machine's wallpaper — one ground under all three
   screens (dusty amber → orange → deep red, plus the grain below) */
.wallpaper{
  position:absolute; inset:0;
  background:
    radial-gradient(115% 70% at 16% 44%, rgba(255,184,92,.62), transparent 58%),
    radial-gradient(110% 70% at 74% 56%, rgba(255,126,40,.3), transparent 58%),
    linear-gradient(180deg,
      #E2A468 0%, #EC7E33 14%, #E0511D 30%,
      #B22C0E 52%, #7E1A09 76%, #48100A 100%);
}
.wallpaper::after{
  content:""; position:absolute; inset:0;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='300' height='300' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode:overlay; opacity:.38;
}
.notch{
  position:absolute; top:0; left:50%; transform:translateX(-50%);
  width:13%; height:13px; background:#101012;
  border-radius:0 0 7px 7px;
}
/* the console as a real window on the wallpaper: a unified title bar
   over the app's own header, a bevelled rim, and macOS's stacked
   shadow instead of one flat drop. The .display is only a sizing
   container — 100cqw is the screenshot's own 2000px, so every number
   in the chrome below is written in the app's pixels (macOS's 28px
   bar, 12px lights on an 8px pitch, 20px in from the edge) and holds
   that scale at any viewport. The ghost holds the outgoing screen and
   fades off the new one — the one dissolve on the page, short enough
   to read as a change of state, not an effect. */
.display{
  position:relative;
  width:88%; margin:4.5% auto 0;
  container-type:inline-size;
}
.win{
  position:relative; overflow:hidden;
  border-radius:.75cqw;
  box-shadow:
    0 .1cqw .35cqw rgba(10,10,11,.34),
    0 .8cqw 1.9cqw rgba(10,10,11,.26),
    0 2.4cqw 5cqw rgba(10,10,11,.36);
}
/* the bevel: a hairline rim all round, brightest along the top edge
   where the glass catches the light */
.win::after{
  content:""; position:absolute; inset:0; pointer-events:none; z-index:2;
  border-radius:inherit;
  box-shadow:
    inset 0 0 0 .1cqw rgba(237,233,223,.13),
    inset 0 .1cqw 0 rgba(237,233,223,.13);
}
.chrome{
  display:flex; align-items:center; gap:.4cqw;
  height:1.4cqw; padding-left:1cqw;
  background:linear-gradient(180deg, #171A21 0%, #0E1116 100%);
}
.light{
  width:.6cqw; height:.6cqw; border-radius:50%;
  box-shadow:inset 0 0 0 .05cqw rgba(10,10,11,.24);
}
.light.r{ background:#FF5F57; }
.light.y{ background:#FEBC2E; }
.light.g{ background:#28C840; }
.pane{ position:relative; }
.console{ display:block; width:100%; }
.ghost{
  position:absolute; inset:0;
  width:100%; height:100%;
  opacity:0; pointer-events:none;
  transition:opacity .45s ease;
}
.deck{
  position:relative;
  width:calc(100% + 72px); margin-left:-36px;
  height:24px;
  background:linear-gradient(180deg, #3E3E43 0%, #232326 55%, #141416 100%);
  border-radius:0 0 18px 18px;
}
.deck::before{
  content:""; position:absolute; inset:0 0 auto 0; height:2px;
  background:rgba(10,10,11,.55);
}
.lip{
  position:absolute; top:0; left:50%; transform:translateX(-50%);
  width:15%; height:9px; background:#131315;
  border-radius:0 0 9px 9px;
}

/* ================= STACK ================= */
/* One screen holds the whole section. The head, the figure and the rail
   window are ONE sticky stage of exactly one screen; the rail slides inside it
   by --p block-lengths, set from scroll progress by site.js.

   Why a stage rather than a sticky head + sticky figure: with the head
   stuck to the section it can only release once the section's bottom
   reaches the head — a full band AFTER the fourth block centres — so the
   title would hang over the close section. Pinning the whole composition
   and giving the section 3 block-lengths of travel means it releases at
   the exact moment the fourth block lands in the middle. Nothing drifts
   afterwards, which is the point.

   --head is the head's measured height (site.js), --band the strip under it
   the blocks and the figure live in, --block one layer's travel.

   --foot is what centres the pinned frame. The head sits at the top, so
   without a matching space beneath it the band's middle — where the current
   block and the figure sit — lands --head/2 BELOW the middle of the screen.
   --foot puts that space back, capped at 16vh so a short screen keeps its
   room. --air is the black after the bone act: it belongs to the section,
   NOT to the head, or it becomes air at the top of every pinned frame and
   pushes the whole composition down again. */
.stack{
  background:var(--ink);
  /* the black after the bone act. The stage now carries --foot/2 above the
     head, so the section gives that much back: the run of black between
     the bone act and the heading stays the 150 it has always been. */
  --air:calc(150px - var(--foot) / 2);
  --head:220px;
  /* The stage stands on the SMALL viewport, for the reason the hero does:
     100vh on a phone is the screen as it would be with the toolbars already
     retracted, so a stage cut to it hides its own foot — and the bottom of
     the rail window with it — behind the bar the reader is actually looking
     at. svh is the viewport with that chrome showing, and unlike dvh it does
     not move when the toolbars go. --vh carries the plain vh for engines
     without svh; a custom property is always valid at parse time, so the
     two-declaration fallback the hero uses cannot work here. */
  --vh:1vh;
  --foot:min(var(--head), calc(16 * var(--vh)));
  --band:calc(100 * var(--vh) - var(--head) - var(--foot));
  --block:clamp(260px, calc(var(--band) * .66), 560px);
  /* The dissolve at the head of the rail, in two parts: a solid lid that
     wants at least one line of the paragraph (--cover), and the ramp under
     it — 34% of the rail window, held between --fade-min and --fade-max.
     Both are wishes; --room is the ceiling and the solid is served first, so
     where the space runs out the scrim keeps the part that hides the cut and
     spends what is left on the ramp. The arithmetic itself is done in
     site.js, which hands back --solid and --ramp as plain px: written here
     it nested var() five deep inside clamp()/min()/max(), and an engine that
     cannot resolve that drops the ramp WITHOUT dropping the lid — a hard
     edge straight through the type, which is the bug this replaces. */
  --cover:32px;
  --fade-min:120px;
  --fade-max:240px;
}
@supports (height:100svh){ .stack{ --vh:1svh; } }
.stack-head{ padding:56px 0 22px; }   /* 56 clears the fixed mark in the corner */

/* everything below is gated on JS: without it the section is simply a
   tall column of four blocks beside the finished stack */
.stack.build{
  padding:var(--air) 0 0;
  /* border-box, so the content box stays one screen + three block-lengths and
     the stage's travel is unchanged; the air just delays the pin */
  height:calc(100 * var(--vh) + 3 * var(--block) + var(--air));
}
/* The head sits at the top of the stage and --foot is left empty under the
   band, so the whole composition rides high in the frame. Half the foot
   goes back above the head: head, figure and block then centre in the
   screen the stage pins for. border-box keeps the stage exactly that tall,
   so the band and the travel are untouched — only where they sit moves. */
.stack.build .stack-stage{ position:sticky; top:0; height:calc(100 * var(--vh)); padding-top:calc(var(--foot) / 2); }
.stack.build .stack-grid{ height:var(--band); }
.stack.build .stack-fig{ position:static; height:var(--band); }
.stack.build .stack-rail{ height:var(--band); overflow:hidden; position:relative; padding:0; }
.stack.build .rail-track{
  padding-top:calc((var(--band) - var(--block)) / 2);
  transform:translate3d(0, calc(var(--p, 0) * var(--block) * -1), 0);
}
.stack.build .layer{ height:var(--block); min-height:0; }
/* Blocks dissolve on the way up instead of being guillotined by the head.
   The window's top edge IS a guillotine, so the scrim opens with --cover of
   solid ink — a whole line of the paragraph — and only then starts its ramp:
   a gradient already half spent by the time the first line reaches the edge
   shows the cut straight through the letters, which is what the old 12%
   plateau did. The two are laid as separate background layers rather than
   percentage stops so the solid head keeps its measure whatever height the
   ramp is given. The ramp eases to zero rather than running out linearly —
   a gradient that meets flat ground with a slope leaves its own edge
   visible. Whatever depth the two ask for, --room is the ceiling: the scrim
   stops at the resting block's heading, so it can never veil the block it
   is handing over to — the last one least of all, which has nothing above
   it left to dissolve. */
.rail-fade{ display:none; }
.stack.build .rail-fade{
  display:block; position:absolute; left:0; right:0; top:0; z-index:2;
  height:calc(var(--solid, 0px) + var(--ramp, 0px)); pointer-events:none;
  /* longhands, not the shorthand: a var() in the shorthand's `/ size` slot
     is the other thing that has been known to take the whole declaration
     down, and the scrim failing silently is the one failure mode it cannot
     afford — it is the only thing standing between the type and the
     window's hard edge. */
  background-image:
    linear-gradient(180deg,
      #0A0A0B 0%,
      rgba(10,10,11,.777) 10%, rgba(10,10,11,.585) 20%,
      rgba(10,10,11,.425) 30%, rgba(10,10,11,.293) 40%,
      rgba(10,10,11,.189) 50%, rgba(10,10,11,.111) 60%,
      rgba(10,10,11,.056) 70%, rgba(10,10,11,.021) 80%,
      rgba(10,10,11,.004) 90%, transparent 100%),
    linear-gradient(#0A0A0B, #0A0A0B);
  background-size:100% var(--ramp, 0px), 100% var(--solid, 0px);
  background-position:0 100%, 0 0;
  background-repeat:no-repeat, no-repeat;
}
.stack h2{
  font-weight:700; font-size:clamp(30px, 4vw, 52px);
  line-height:1.1; letter-spacing:-.02em;
}
.stack-grid{
  display:grid; grid-template-columns:minmax(0,10fr) minmax(0,11fr);
  gap:48px; /* items stretch: the side column must span the whole rail
  so its sticky figure has room to travel */
}
/* 72ch keeps the sub to two lines, which is what buys the band its height */
.stack-sub{
  margin-top:16px; max-width:72ch;
  font-size:16.5px; line-height:1.7; color:var(--bone-70);
}
.stack-rail{ order:1; max-width:calc(46ch + 90px); }
.stack-side{ order:2; }
/* the number takes its own gutter and spans both rows, so the heading and
   the paragraph keep one measure beside it */
.layer{
  min-height:0; padding:34px 0;
  display:grid; align-content:center;
  grid-template-columns:66px minmax(0,1fr); column-gap:24px;
}
.layer .no{
  grid-row:1 / span 2; align-self:start; margin-top:7px;   /* caps line up with the heading's */
  font-weight:800; font-size:56px; line-height:.78; letter-spacing:-.05em;
}
/* the two data layers wear the gold, the two intelligence layers the blue —
   the same split the slabs use */
.layer:nth-child(-n+2) .no{ color:#E7C355; }
.layer:nth-child(n+3)  .no{ color:#2E86C6; }
.layer h3{ grid-column:2; font-weight:600; font-size:23px; letter-spacing:-.012em; }
.layer p{ grid-column:2; margin-top:14px; font-size:16.5px; line-height:1.72; color:var(--bone-70); }
/* blocks light as they cross mid-viewport — only once JS is on the job */
.stack.build .layer{ opacity:.32; transition:opacity .45s ease; }
.stack.build .layer.on{ opacity:1; }
/* the figure holds the frame while the rail scrolls past it — pinned
   under the head, not at the viewport top */
.stack-fig{ display:grid; place-items:center; }
.stack-fig svg{ width:100%; height:min(calc(var(--band) - 44px), 620px); display:block; }
/* the slabs: laid one at a time; a new slab settles from above
   and takes the face art over from the layer it covers */
.slab polygon{ fill:var(--ink); stroke:var(--bone-45); stroke-width:1; }
/* two flare shades only: the data layers wear the gold, the
   intelligence layers the mid blue */
.slab-1 polygon, .slab-2 polygon{ stroke:#E7C355; }
.slab-3 polygon, .slab-4 polygon{ stroke:#2E86C6; }
.stack.build .slab{ transition:opacity .5s ease, transform .7s cubic-bezier(.22,.61,.36,1); }
.stack.build .slab.off{ opacity:0; transform:translateY(-46px); }
/* ?stack=N holds the forced state still for captures */
.stack.hold *{ transition:none !important; }
.face{ opacity:0; transition:opacity .45s ease .15s; }
.slab.top .face{ opacity:1; }
/* the faint ground ruling belongs to its slab's hue, like everything else */
.fgrid{ stroke-width:1; fill:none; }
.slab-1 .fgrid, .slab-2 .fgrid{ stroke:rgba(231,195,85,.16); }
.slab-3 .fgrid{ stroke:rgba(46,134,198,.16); }
.fic{ fill:none; stroke-width:.9; stroke-linecap:round; stroke-linejoin:round; }
.fic-d{ stroke:#E7C355; }
.fic-p{ stroke:#E7C355; }
.forbit{ fill:none; stroke:rgba(231,195,85,.4); stroke-width:1; stroke-dasharray:3 6; }
.fchip{ fill:none; stroke:#2E86C6; stroke-width:1; stroke-linecap:round; }
.fchip circle{ fill:var(--ink); }
.fchip .gate{ fill:#2E86C6; stroke:none; }
/* the industry volumes standing on the autonomous slab: ink-filled
   bodies so nearer walls occlude farther ones, blue line work */
.find{ fill:var(--ink); stroke:#2E86C6; stroke-width:.8; stroke-linejoin:round; stroke-linecap:round; }
.find .fo{ fill:none; }
.find .fn{ fill:none; stroke-width:.5; }      /* panes, ribs, bands */
.find .fplan{ fill:none; stroke:rgba(46,134,198,.32); stroke-width:.6; }
.crop path{ stroke:var(--hair); stroke-width:1; fill:none; }
.fd-b{ stroke:rgba(46,134,198,.5); stroke-width:1; fill:none; }
.fd-d{ stroke:rgba(231,195,85,.5); stroke-width:1; fill:none; }
.fd-dot{ fill:#E7C355; opacity:.85; }

/* ================= CLOSE ================= */
.close{
  background:var(--ink); text-align:center;
  padding:190px var(--pad) 64px;
}
.close h2{
  font-weight:700; font-size:clamp(26px, 3.4vw, 44px);
  letter-spacing:-.018em; line-height:1.14;
}
.close .btn{ margin-top:36px; }
.mark-g{
  display:block; width:min(320px, 58vw); height:auto;
  margin:130px auto 0;
  filter:drop-shadow(0 0 26px rgba(237,233,223,.5)) drop-shadow(0 0 90px rgba(237,233,223,.22));
}
.mark-g path{ fill:var(--bone); }
.close-tag{
  font-family:var(--font-d); font-weight:600; font-size:15px;
  letter-spacing:.34em; text-indent:.34em; /* recentre despite tracking */
  color:var(--bone-70); margin-top:110px;
}

/* ================= CONTACT =================
   The form stands over the page on the same ink ground, so opening it
   reads as the page continuing rather than a panel arriving. Nothing
   is boxed but the long field: labels sit on hairlines, the required
   mark and the error stand a shade brighter than the label rather than
   changing hue, and the send button carries the flare it was opened
   by. ================= */
.cmodal{
  /* pinned rather than sized: the visual viewport is what it must fill,
     so a phone's shrinking address bar never opens a seam at the foot */
  position:fixed; inset:0;
  width:100%; max-width:none; height:100%; max-height:none;
  border:0; padding:0; margin:0;
  background:var(--ink); color:var(--bone);
  color-scheme:dark;                 /* the country list opens dark too */
}
.cmodal::backdrop{ background:var(--ink); }
.cmodal .cm-scroll{
  height:100%; overflow-y:auto; overscroll-behavior:contain;
  padding-bottom:120px;
}
/* the mark to dismiss rides at the top of the ink, always in reach,
   on the same left edge as everything under it */
.cm-bar{
  position:sticky; top:0; z-index:2;
  background:var(--ink);
  padding:26px var(--pad) 18px;
}
.cm-bar, .cm-wrap{ max-width:calc(1080px + var(--pad) * 2); margin:0 auto; }
.cm-x{
  appearance:none; background:none; border:0; cursor:pointer;
  display:block; width:30px; height:30px; padding:0;
  color:var(--bone-70);
}
.cm-x:hover{ color:var(--bone); }
.cm-x:focus-visible{ outline:1px solid var(--amber); outline-offset:5px; }
.cm-x svg{ width:100%; height:100%; display:block; }
.cm-x path{ stroke:currentColor; stroke-width:1.4; fill:none; }

.cm-wrap{ padding:0 var(--pad); }
/* the routing line */
.cm-route{
  margin-top:34px;
  font-family:var(--font-m); font-size:11px; font-weight:500;
  letter-spacing:.14em; text-transform:uppercase; line-height:1.4;
  color:var(--bone-45);
}

/* ---- the fields ---- */
.cm-form{ margin-top:96px; }
.cm-grid{ display:grid; grid-template-columns:1fr 1fr; gap:56px 60px; }
.fld.wide{ grid-column:1 / -1; }
.fld{ display:flex; flex-direction:column; }
.fld label{
  font-family:var(--font-m); font-size:11px; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--bone-45);
}
.fld .req{ color:var(--bone); margin-left:.5em; }
.fld input, .fld select, .fld textarea{
  -webkit-user-select:text; user-select:text;   /* the page selects nothing; these must */
  width:100%; margin-top:14px;
  font-family:var(--font-d); font-size:17px; font-weight:400; line-height:1.5;
  color:var(--bone); background:none;
  border:0; border-bottom:1px solid var(--hair); border-radius:0;
  padding:0 0 12px;
}
/* addresses and numbers are machine strings, and Alliance's @ is a
   squared glyph that reads as a missing character — both go to the mono */
.fld input[type="email"], .fld input[type="tel"]{
  font-family:var(--font-m); font-size:15px; letter-spacing:.01em;
}
.fld input:hover, .fld select:hover, .fld textarea:hover{ border-color:var(--bone-30); }
.fld input:focus, .fld select:focus, .fld textarea:focus{
  outline:none; border-color:var(--bone);
}
.fld:focus-within label{ color:var(--bone-70); }
/* the long field is the one boxed thing on the page */
.fld textarea{
  border:1px solid var(--hair); padding:14px 16px;
  min-height:132px; resize:vertical;
}
/* what is wrong is said in bone, in the label's own voice: the marked
   field is the one whose rule and message stand at full strength */
.fld .err{
  font-family:var(--font-m); font-size:11px; letter-spacing:.06em;
  color:var(--bone); margin-top:9px; min-height:0;
}
.fld.bad input, .fld.bad select, .fld.bad textarea{ border-color:var(--bone); }

/* the honeypot: off-screen, not display:none — see the note in index.html */
.cm-hp{
  position:absolute; left:-9999px; top:auto;
  width:1px; height:1px; overflow:hidden;
}

.cm-foot{ margin-top:76px; }

/* ---- after sending ---- */
.cm-done{ margin-top:120px; text-align:center; }
.cm-mark{
  display:block; width:min(180px, 42vw); height:auto; margin:0 auto;
  filter:drop-shadow(0 0 22px rgba(237,233,223,.42)) drop-shadow(0 0 70px rgba(237,233,223,.18));
}
.cm-mark path{ fill:var(--bone); }
.cm-mark .chev{ stroke:var(--bone); stroke-width:4; stroke-linejoin:round; }
.cm-done-h{
  margin-top:64px;
  font-weight:700; font-size:clamp(26px, 3.4vw, 44px);
  letter-spacing:-.018em; line-height:1.14;
}
.cm-done-p{
  margin:22px auto 0; max-width:46ch;
  font-size:17px; line-height:1.75; color:var(--bone-70);
}

/* the one motion the form is allowed: it arrives, it does not slide about */
.cmodal[open]{ animation:cm-in .2s ease-out both; }
@keyframes cm-in{ from{ opacity:0; transform:translateY(10px); } }
html.cm-open{ overflow:hidden; }

/* capture mode — screenshots want final state, not transitions */
.cap *{ transition:none !important; animation:none !important; }
.cap .hero-video{ display:none; }

/* ================= RESPONSIVE ================= */
@media (max-width:900px){
  :root{ --pad:22px; }
  .hero-sub{ text-align:right; max-width:34ch; }
  .problem{ padding:120px var(--pad) 24px; }
  .stat-row{ flex-direction:column; gap:10px; }
  /* The break is cut for the measure the claim gets beside the numeral.
     Stacked under it there is less room, and between 375 and 429px the
     forced line runs 348px into 331 — which drops "AI" onto a line of
     its own on every iPhone from the SE to the 14. Let it wrap to the
     column instead. The claim was hard-spaced here while it ended in the
     two-word "chat windows"; "simple chatbots" binds worse than it wraps
     — held together it strands a short "AI beyond" mid-claim at 360 —
     so it takes an ordinary space and rags. */
  .stat-claim{ top:0; }
  .stat-claim br{ display:none; }
  .ref{ margin-top:12px; }
  /* the globe stays near full scale, the rings bleed off both sides */
  .turn{ padding:0; }
  .orbit{ width:200vw; margin-left:-50vw; }
  .band{ height:32vh; margin-top:calc(200vw * -.0792); }
  .approach{ padding-top:40px; }
  /* the machine stays near full scale: anchored left, bleeding off the right */
  .console-melt{ margin-top:56px; padding-bottom:0; overflow:hidden; }
  .tabs{ gap:28px; margin-bottom:32px; }
  .laptop{ width:175vw; margin:0 0 0 10vw; }
  .screen{ border-width:9px; border-bottom-width:14px; border-radius:14px 14px 0 0; }
  .deck{ width:calc(100% + 40px); margin-left:-20px; height:16px; border-radius:0 0 12px 12px; }
  /* Mobile runs the SAME pinned stage, stacked instead of side by side:
     head, then the figure, then the rail window the blocks slide through.
     The band is split figure/rail rather than shared by two columns, and
     --block is the rail window itself, so one block shows at a time.
     Everything below is type coming down to make room — a phone screen
     has to hold the head, the figure and a whole paragraph at once. */
  .stack{
    padding:0;
    --air:calc(110px - var(--foot) / 2);   /* the same air after the bone act, to scale */
    --figh:clamp(130px, calc(24 * var(--vh)), 290px);
    --railh:calc(var(--band) - var(--figh));
    --block:var(--railh);
    /* One block fills the window here, so the headroom above its heading is
       whatever the window has spare — small on a short phone, generous on a
       tall one. --cover is one line of the smaller body size and is never
       given up; the ramp is what tracks the slack, so the whole scrim lands
       just short of the current block's heading. */
    --cover:26px;
    --fade-min:30px;
    --fade-max:120px;
  }
  .stack-head{ padding:56px 0 14px; }      /* the air moved to --air; 56 clears the mark */
  .stack h2{ font-size:clamp(24px, 6.6vw, 30px); }
  .stack-sub{ margin-top:12px; max-width:none; font-size:14px; line-height:1.5; }
  .stack.build .stack-grid{ display:block; height:var(--band); }
  .stack.build .stack-side{ height:var(--figh); }
  .stack.build .stack-fig{ height:var(--figh); }
  .stack-fig svg{ height:calc(var(--figh) - 14px); }
  .stack.build .stack-rail{ height:var(--railh); }
  .stack.build .rail-track{ padding-top:0; }
  .stack.build .layer{ height:var(--railh); }
  .stack-rail{ max-width:none; }
  .layer{ padding:0; grid-template-columns:52px minmax(0,1fr); column-gap:18px; }
  /* the number keeps its own row beside the heading, and the paragraph runs
     the full width under both — a 52px indent is a lot of a phone screen */
  .layer .no{ grid-row:1; font-size:40px; margin-top:4px; }
  .layer p{ grid-column:1 / -1; }
  .layer h3{ font-size:20px; }
  .layer p{ margin-top:10px; font-size:14.5px; line-height:1.6; }
  .close{ padding:130px var(--pad) 48px; }
  .mark-g{ margin-top:90px; }
  .close-tag{ margin-top:70px; }
  /* the form goes to one column and closes its gaps */
  .cm-form{ margin-top:56px; }
  .cm-grid{ grid-template-columns:1fr; gap:40px; }
  .cm-foot{ margin-top:52px; }
  .cm-done{ margin-top:80px; }
  .cm-done-h{ margin-top:48px; }
}
/* the shortest phones (SE-class): the tallest block all but fills the rail
   window, so trim the head and the body copy to buy the scrim some room —
   --room is all it gets here, and hiding the cut is what the little there
   is has to pay for first. */
@media (max-width:900px) and (max-height:720px){
  .stack{ --cover:24px; }
  .stack-head{ padding:56px 0 10px; }
  .stack-sub{ font-size:13.5px; line-height:1.45; }
  .layer h3{ font-size:19px; }
  .layer p{ margin-top:8px; font-size:13.5px; line-height:1.55; }
  .layer .no{ font-size:36px; }
}

/* ================= REDUCED MOTION ================= */
@media (prefers-reduced-motion: reduce){
  *{ transition:none !important; }
  .hero-video{ display:none; }
  .cmodal[open]{ animation:none; }
}
