/* =========================================================================
   0G Singapore — 0gm.ai
   Hand-written stylesheet. Palette + type scale extracted from the real
   0gfoundation.ai Webflow CSS, rebuilt clean. Mobile-first, 8px rhythm.
   ========================================================================= */

/* ------------------------------ Design variables ------------------------------ */
:root {
  /* Surfaces */
  --bg:            #ffffff;
  --bg-subtle:     #f6f6f6;   /* section alt background (from real site) */
  --bg-muted:      #fafafa;
  --surface:       #ffffff;
  --bg-dark:       #0b0b10;   /* flagship + CTA dark sections */
  --bg-dark-2:     #14141b;
  --surface-dark:   #191924;  /* dark-section radial highlight */
  --surface-dark-2: #201f39;  /* CTA-card radial highlight */

  /* Ink */
  --ink:           #111111;   /* headings */
  --ink-body:      #33373b;   /* body copy (~#333) */
  --ink-muted:     rgba(17,17,17,.64);  /* real site's #111111a3 */
  --ink-faint:     rgba(17,17,17,.62);  /* passes 4.5:1 on #fff and #f6f6f6 */
  --ink-invert:    #ffffff;
  --ink-invert-muted: rgba(255,255,255,.68);

  /* Accent — the 0G blue-violet */
  --accent:        #4b36ff;
  --accent-strong: #402bff;
  --accent-deep:   #100097;   /* badge/eyebrow ink */
  --accent-soft:   rgba(75,54,255,.07);
  --accent-line:   rgba(75,54,255,.22);
  --accent-tint:   #a99bff;             /* light violet — invert eyebrow */
  --accent-tint-2: #cfc9ff;             /* paler violet — dark-section lead */
  --accent-glow:   rgba(75,54,255,.35); /* derived from --accent (#4b36ff) */

  /* Borders */
  --border:        rgba(17,17,17,.12);
  --border-strong: rgba(17,17,17,.20);
  --border-invert: rgba(255,255,255,.14);

  /* Placeholder / TBC amber */
  --amber-bg:      rgba(245,158,11,.14);
  --amber-border:  rgba(217,119,6,.45);
  --amber-ink:     #92600a;

  /* Type */
  --font-sans: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  /* Fluid type scale */
  --fs-h1:  clamp(2.5rem, 1.4rem + 4.6vw, 5rem);
  --fs-h2:  clamp(1.75rem, 1.15rem + 2.4vw, 2.75rem);
  --fs-h3:  1.125rem;
  --fs-lead: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem);
  --fs-body: 1rem;
  --fs-sm:  0.875rem;
  --fs-eyebrow: 0.75rem;

  /* Spacing — 8px rhythm */
  --sp-1: 8px;  --sp-2: 16px; --sp-3: 24px; --sp-4: 32px;
  --sp-5: 40px; --sp-6: 48px; --sp-7: 56px; --sp-8: 64px;
  --sp-10: 80px; --sp-12: 96px; --sp-16: 128px;

  /* Layout */
  --maxw: 1120px;
  --gutter: 24px;

  /* Shape */
  --radius:    12px;
  --radius-lg: 24px;
  --radius-sm: 8px;
  --pill: 999px;

  /* Signature offset shadow (from real .button) */
  --shadow-accent:   -4px 4px 0 var(--accent);
  --shadow-accent-h: -6px 6px 0 var(--accent);
  --shadow-soft: 0 1px 2px rgba(17,17,17,.04), 0 8px 24px rgba(17,17,17,.05);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--ink-body);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  -webkit-tap-highlight-color: rgba(75,54,255,.15);
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul, ol { margin: 0; padding: 0; list-style: none; }
h1, h2, h3 { margin: 0; color: var(--ink); font-weight: 600; line-height: 1.15; letter-spacing: -0.02em; }
p { margin: 0; }
dl, dd { margin: 0; }

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

/* Anchor offset so sticky nav doesn't cover section tops */
section[id], span[id] { scroll-margin-top: 88px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: #fff; padding: 10px 16px; border-radius: 0 0 8px 0;
  font-size: var(--fs-sm);
}
.skip-link:focus { left: 0; }

.wrap {
  width: 100%; max-width: var(--maxw); margin-inline: auto;
  padding-left: max(var(--gutter), env(safe-area-inset-left));
  padding-right: max(var(--gutter), env(safe-area-inset-right));
}

/* ------------------------------ Typography helpers ------------------------------ */
.eyebrow {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-deep);
}
.eyebrow--invert { color: var(--accent-tint); }

.h1 { font-size: var(--fs-h1); font-weight: 600; letter-spacing: -0.035em; }
/* Hero headline: two deliberate lines (break in markup); sized to fit them. */
.hero__header .h1 { font-size: clamp(2.25rem, 1.2rem + 3.1vw, 4rem); }
@media (max-width: 560px) { .hero__br { display: none; } }
.h2 { font-size: var(--fs-h2); }

.gradient-text {
  background-image: linear-gradient(122deg, var(--accent) 0%, var(--ink) 78%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ------------------------------ Buttons ------------------------------ */
.btn {
  --btn-shadow: var(--shadow-accent);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.005em;
  padding: 13px 22px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--btn-shadow);
  transition: transform .18s ease, box-shadow .18s ease, background .18s ease, color .18s ease;
  white-space: nowrap;
}
.btn:hover { transform: translate(2px, -2px); box-shadow: var(--shadow-accent-h); }
/* Distinct pressed state — touch has no hover phase, so give taps real feedback. */
.btn:active { transform: translate(1px, -1px); box-shadow: -2px 2px 0 var(--accent); }
.btn--sm { padding: 9px 16px; font-size: 0.8125rem; }

.btn--primary { background: var(--ink); color: #fff; border-color: var(--ink); }
.btn--ghost   { background: var(--surface); color: var(--ink); }

/* dark-section buttons */
.btn--light { background: #fff; color: var(--ink); border-color: #fff; }

/* ------------------------------ Nav ------------------------------ */
.nav {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255,255,255,.82);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  /* 1.5px so the hairline never rounds to 0 device px at fractional zoom
     (a 1px border drops out in segments at e.g. 90%/110% zoom) */
  border-bottom: 1.5px solid rgba(17,17,17,.10);
}
.nav__inner { display: flex; align-items: center; justify-content: space-between; height: 64px; gap: var(--sp-3); }

.nav__brand { display: inline-flex; align-items: center; gap: 10px; }
.nav__logo { height: 30px; width: auto; } /* 281x120 wordmark, never force square */

.nav__links { display: none; gap: var(--sp-4); }
.nav__links a { font-size: var(--fs-sm); font-weight: 500; color: var(--ink-muted); transition: color .15s ease; }
.nav__links a:hover { color: var(--ink); }

.nav__actions { display: flex; align-items: center; gap: var(--sp-2); }
.nav__actions .btn { display: none; }

.nav__burger {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 5px;
  width: 44px; height: 44px; padding: 0 10px;
  background: transparent; border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer;
}
.nav__burger span { display: block; height: 2px; width: 100%; background: var(--ink); border-radius: 2px; transition: transform .25s ease, opacity .2s ease; }
.nav__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.nav-mobile {
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--sp-2) var(--gutter) var(--sp-3);
  border-top: 1px solid var(--border);
  background: rgba(255,255,255,.96);
  backdrop-filter: blur(14px);
}
.nav-mobile[hidden] { display: none; }

/* Dim scrim behind the open mobile menu (below the sticky header, above page). */
.nav-scrim {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(17,17,17,.32);
}
.nav-scrim[hidden] { display: none; }
.nav-mobile a:not(.btn) {
  padding: 12px 4px; font-size: 1rem; font-weight: 500; color: var(--ink);
  border-bottom: 1px solid var(--border);
}
.nav-mobile .btn { margin-top: var(--sp-2); }

/* ------------------------------ Hero ------------------------------ */
.hero {
  position: relative;
  overflow: hidden;
  min-height: min(760px, 92vh);
  display: flex; align-items: center;
  padding-block: var(--sp-12) var(--sp-10);
  background: linear-gradient(180deg, #ffffff 0%, var(--bg-subtle) 100%);
}
.hero__inner { position: relative; z-index: 3; }

.hero__animation {
  position: absolute; z-index: 2; pointer-events: none;
  right: -6%; bottom: -4%;
  width: 68%; max-width: 1100px;
  aspect-ratio: 16 / 9;
}
.hero__animation canvas { width: 100% !important; height: 100% !important; }

/* Network globe hero (the shipping hero): same slot as .hero__animation. */
.hero__network {
  position: absolute; z-index: 2; pointer-events: none;
  right: -6%; bottom: -4%;
  width: 68%; max-width: 1100px;
  aspect-ratio: 16 / 9;
}
.hero__network canvas { width: 100% !important; height: 100% !important; opacity: 0; transition: opacity .8s ease; }
.hero__network.is-ready canvas { opacity: 1; }
/* Soft radial accent wash grounding the globe zone; on phones (no canvas)
   this IS the hero art. */
.hero--net { position: relative; }
.hero--net::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(42% 55% at 79% 66%, rgba(75,54,255,.06), transparent 70%),
    radial-gradient(60% 75% at 79% 66%, rgba(207,201,255,.05), transparent 75%);
}

.hero__grid {
  position: absolute; z-index: 1; pointer-events: none;
  right: -3%; bottom: 0;
  width: 66%; max-width: 1000px;
  opacity: 0.9;
  -webkit-user-select: none; user-select: none;
}

.hero__header { max-width: 720px; display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-3); }
.hero__header .eyebrow { margin-bottom: 2px; }
.hero__sub { font-size: var(--fs-lead); color: var(--ink-muted); max-width: 34ch; }
.hero__cta { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-1); }

/* ------------------------------ Section scaffold ------------------------------ */
.section { padding-block: clamp(56px, 8vw, 112px); position: relative; }
.section--alt { background: var(--bg-subtle); }

.section__head { max-width: 720px; display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-8); }
/* Section eyebrows read as a pill badge (the hero eyebrow stays plain). */
.section__head .eyebrow {
  align-self: flex-start;
  margin-bottom: var(--sp-1);
  padding: 6px 12px;
  border: 1px solid var(--accent-line);
  border-radius: 20px;
}
.section__head .eyebrow--invert { border-color: var(--border-invert); }
.section__sub { font-size: var(--fs-lead); color: var(--ink-muted); }

/* Grids */
.grid { display: grid; gap: var(--sp-3); }
.grid--3 { grid-template-columns: 1fr; }
.grid--4 { grid-template-columns: 1fr; }
.grid--5 { grid-template-columns: 1fr; }

/* ------------------------------ Trusted-by strip ------------------------------ */
.trusted { padding-block: var(--sp-8); border-bottom: 1px solid var(--border); }
.trusted__lead { text-align: center; color: var(--ink-muted); font-size: var(--fs-lead); margin-bottom: var(--sp-5); }
/* Partner logo marquee — slow, seamless, pauses on hover.
   Logos render grayscale/dimmed and regain color on hover. */
.marquee {
  --marquee-gap: var(--sp-7);
  position: relative;
  overflow: hidden;
  padding-block: var(--sp-1);
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee 60s linear infinite;
}
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track { animation-play-state: paused; }
.marquee__group {
  display: flex; align-items: center;
  gap: var(--marquee-gap);
  flex: 0 0 auto;
  list-style: none;
  margin: 0; padding: 0 var(--marquee-gap) 0 0;
}
.marquee__item { display: flex; align-items: center; }
.marquee__item img {
  display: block;
  height: 26px; width: auto;
  filter: grayscale(1);
  opacity: .62;
  transition: filter .25s ease, opacity .25s ease;
}
.marquee__item:hover img { filter: grayscale(0); opacity: 1; }
.marquee__item--md img { height: 34px; }
.marquee__item--seal img { height: 40px; }
@keyframes marquee { to { transform: translateX(-50%); } }

/* Hero canvas fades in once lottie has rendered its first frame */
.hero__animation canvas { opacity: 0; transition: opacity .8s ease; }
.hero__animation.is-ready canvas { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .marquee { mask-image: none; -webkit-mask-image: none; }
  .marquee__track { animation: none; width: auto; flex-wrap: wrap; justify-content: center; }
  .marquee__group { width: 100%; flex-wrap: wrap; justify-content: center; padding-right: 0; }
  .marquee__group[aria-hidden="true"] { display: none; }
}

/* ------------------------------ Cards ------------------------------ */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-3);
  display: flex; flex-direction: column; gap: 10px;
  transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
/* Hover lift echoes the button offset-shadow language, softened. */
.card:hover { border-color: var(--accent-line); transform: translate(2px, -2px); box-shadow: -6px 6px 0 var(--accent-soft); }
.card__title { font-size: var(--fs-h3); color: var(--ink); }
.card p { color: var(--ink-muted); font-size: var(--fs-sm); }

/* Feature cards — "blueprint" treatment: corner brackets + a mono index. */
.card--feature { position: relative; padding: var(--sp-4) var(--sp-3); }
/* Two L-shaped corner brackets (top-left + bottom-right) built from borders. */
.card--feature::before,
.card--feature::after {
  content: ""; position: absolute;
  width: 14px; height: 14px; pointer-events: none;
  transition: width .22s ease, height .22s ease, border-color .22s ease;
}
.card--feature::before {
  top: 10px; left: 10px;
  border-top: 1.5px solid var(--accent-line);
  border-left: 1.5px solid var(--accent-line);
}
.card--feature::after {
  bottom: 10px; right: 10px;
  border-bottom: 1.5px solid var(--accent-line);
  border-right: 1.5px solid var(--accent-line);
}
.card--feature:hover::before,
.card--feature:hover::after { width: 20px; height: 20px; border-color: var(--accent); }
/* Mono index (01–04) in the top-right corner. */
.card__index {
  position: absolute; top: var(--sp-3); right: var(--sp-3);
  font-family: var(--font-mono); font-size: var(--fs-eyebrow); font-weight: 500;
  letter-spacing: 0.08em; color: var(--ink-faint);
  transition: color .22s ease;
}
.card--feature:hover .card__index { color: var(--accent-deep); }
.card__mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin-bottom: var(--sp-1);
  border-radius: var(--radius-sm); background: var(--accent-soft); color: var(--accent);
}
.card__mark svg { width: 24px; height: 24px; }

/* ------------------------------ Principles ------------------------------ */
/* Accent rule above the title — grows + deepens on hover (quietest tier). */

/* ------------------------------ How it works — isometric stack (>=900px) ------------------------------
   An exploded iso layer-stack that replaces the flow list on wide screens.
   Six rounded near-white slabs stack with violet cubes sandwiched in the gaps;
   HTML labels with hairline connectors sit in the left/right columns. Hover,
   focus, or the auto-cycle lights one layer at a time via .stack[data-active].
   The default (no active layer) renders every layer full + lit — so the no-JS
   diagram is complete. Slab pieces are styled here (CSS custom properties do
   not work in SVG presentation attributes). */
/* The stack renders at ALL widths now (the old <900px .flow list is gone).
   Desktop keeps the gutter-label totem; mobile (styles in the max-width:899px
   block far below) shows the same totem full-width above a static badge-list. */
.stack { display: block; position: relative; --expand: 1; }

@media (min-width: 900px) {
  .stack {
    max-width: 900px;
    margin-inline: auto;
  }
}

/* --- scroll-driven expansion (desktop >=900px only). Each plate group is
   wrapped in a .stack__lift inner group carrying a per-layer --dyk (the signed
   distance from its resting Yk to the collapsed convergence target near the
   vault level). The container drives --expand 0..1; at 0 every plate slides to
   its collapsed slot (one combined multi-plate block), at 1 the plates sit at
   rest. Transform-only scrub — no layout/paint in the animated path. JS adds
   .stack--scrub while a desktop pointer session is live and toggles will-change
   only during motion; labels/thread/guides fade in as --expand rises. --- */
.stack__lift { transform: translateY(calc((1 - var(--expand)) * var(--dyk) * 1px)); }

/* central thread + guide dots/lines: hidden while collapsed, fade in as the
   stack expands (continuous, clamped by the browser to [0,1]). Default
   --expand:1 keeps them fully visible with JS off / on mobile / reduced motion. */
.stack__svg .stack__guide { opacity: calc((var(--expand) - 0.45) * 2); }

.stack__svg { display: block; width: 100%; max-width: 500px; height: auto; margin-inline: auto; overflow: visible; }

/* --- totem state machinery (transplanted from the approved prototype; the
   per-element .is-active/.is-dim classes the prototype JS set are re-expressed
   here as .stack[data-active="k"] attribute selectors, since the production
   initStack only toggles data-active on the container). Dimming is uniform:
   the active unit lifts + glows, EVERY other unit dims to .45 — one rule. --- */
.t     { transition: opacity .35s ease, transform .35s cubic-bezier(.22,.9,.34,1), filter .35s ease; }
.glow  { opacity: 0; transition: opacity .35s ease; pointer-events: none; }
.vmilk { transition: opacity .35s ease; }
.vglass{ opacity: 0; transition: opacity .35s ease; pointer-events: none; }
#memTile { transition: fill .3s ease, stroke .3s ease; }
#memTile .dotA { transition: fill .3s ease; }
.thseg { opacity: 0; transition: opacity .3s ease; }

/* hit shapes are the ONLY pointer targets; painted art never intercepts */
.stack__svg .t, .stack__svg .glow, .stack__svg .vglass, .stack__svg .vmilk { pointer-events: none; }
.stack__hit { fill: rgba(0,0,0,0); pointer-events: all; cursor: default; }
/* The SVG slab groups are pointer-only targets (keyboard focus lives on the
   gutter labels, which carry their own focus ring). Suppress the global
   :focus-visible outline so a plain mouse click never scars the diagram with a
   persistent accent rectangle around the invisible hit polygon's bounds. */
.stack__svg .stack__layer { outline: none; }

/* dim every unit while a layer is active, then re-assert the active unit */
.stack:not([data-active=""]) .t { opacity: .45; }
.stack[data-active="0"] #L0,
.stack[data-active="1"] #Bplate, .stack[data-active="1"] #Bcube,
.stack[data-active="2"] #L2,
.stack[data-active="3"] #L3,
.stack[data-active="4"] #Bplate, .stack[data-active="4"] #gBack, .stack[data-active="4"] #gFront,
.stack[data-active="5"] #L5 { opacity: 1; transform: translateY(-7px); }

/* active-unit glow */
.stack[data-active="0"] #L0 .glow,
.stack[data-active="1"] #Bplate .glow, .stack[data-active="1"] #Bcube .glow,
.stack[data-active="2"] #L2 .glow,
.stack[data-active="3"] #L3 .glow,
.stack[data-active="4"] #Bplate .glow, .stack[data-active="4"] #gFront .glow,
.stack[data-active="5"] #L5 .glow { opacity: .17; }

/* vault shell: k=1 (compute) vs k=4 (verification). The cube and shell are one
   physical object — whichever half is dimmed still travels with the lifted half. */
.stack[data-active="4"] .vglass { opacity: 1; }
.stack[data-active="4"] #gFront .glow { opacity: .08; } /* vglass carries the tint */
.stack[data-active="4"] #Bcube { transform: translateY(-7px); }
.stack[data-active="1"] #gBack,
.stack[data-active="1"] #gFront { transform: translateY(-7px); }
.stack[data-active="1"] #Bcube { filter: saturate(1.12); }

/* memory tile joins its plate's active state */
.stack[data-active="2"] #memTile rect { fill: #d9d0ff; stroke: rgba(75,54,255,.42); }
.stack[data-active="2"] #memTile .dotA { fill: #5b45ff; }

/* thread hot segments light up when their level is active */
.stack[data-active="0"] #th0,
.stack[data-active="1"] #th0, .stack[data-active="1"] #th1,
.stack[data-active="2"] #th1, .stack[data-active="2"] #th2,
.stack[data-active="3"] #th2, .stack[data-active="3"] #th3,
.stack[data-active="4"] #th0, .stack[data-active="4"] #th1,
.stack[data-active="5"] #th3 { opacity: .9; animation: flow 1.1s linear infinite; }
@keyframes flow { to { stroke-dashoffset: -24; } }

@media (prefers-reduced-motion: reduce) {
  .stack .t, .stack .glow, .stack .vmilk, .stack .vglass, .stack .thseg,
  .stack #memTile, .stack #memTile .dotA { transition: none !important; animation: none !important; }
  /* scroll-expansion is disabled: lock the stack fully expanded (static-complete
     diagram) and neutralise the scrub transforms regardless of any --expand. */
  .stack { --expand: 1 !important; }
  .stack__lift { transform: none !important; transition: none !important; }
}

/* --- labels: title + description in the left/right gutters of the centered
   totem; a hairline leader + tip dot points from the title toward its node.
   (Step-number badges removed — the leader/dot alone anchor each label.) --- */
.stack__label {
  position: absolute; width: 214px; transform: translateY(-50%);
  display: flex; align-items: flex-start; cursor: default;
  /* transform included so the 14px re-gather drop eases out on exit instead of
     popping; the .is-expanded rule overrides with its own longer entrance curve. */
  outline: none; transition: opacity .35s ease, transform .35s ease;
}
.stack__label-body { min-width: 0; }
.stack__title { font-size: var(--fs-h3); color: var(--ink); margin: 0 0 3px; letter-spacing: -0.01em; transition: color .3s ease; }
.stack__desc  { font-size: 0.8125rem; line-height: 1.45; color: var(--ink-muted); }

/* right column (label box pinned right, text left) */
.stack__label[data-k="0"], .stack__label[data-k="1"], .stack__label[data-k="3"], .stack__label[data-k="5"] { right: 0; text-align: left; }
/* left column (label box pinned left; body pushed to the inner edge toward the
   totem via row-reverse, text right-aligned) */
.stack__label[data-k="2"], .stack__label[data-k="4"] { left: 0; flex-direction: row-reverse; text-align: right; }

.stack__label[data-k="0"] { top: 8.6%; }
.stack__label[data-k="4"] { top: 23.7%; }
.stack__label[data-k="1"] { top: 31.0%; }
.stack__label[data-k="2"] { top: 56.5%; }
.stack__label[data-k="3"] { top: 71.4%; }
.stack__label[data-k="5"] { top: 88.5%; }

/* leader hairline (::before) + node tip dot (::after), running from the inner
   edge of the label box (where the step badge used to sit) toward the node. */
.stack__label::before {
  content: ""; position: absolute; top: 11px; height: 1px;
  background: var(--border-strong); transition: background-color .3s ease;
}
.stack__label::after {
  content: ""; position: absolute; top: 9px; width: 5px; height: 5px;
  border-radius: 50%; background: var(--border-strong); transition: background-color .3s ease;
}
/* right column: leader runs left from the label box inner (left) edge */
.stack__label[data-k="0"]::before { right: 100%; width: 116px; }
.stack__label[data-k="0"]::after  { right: calc(100% + 114px); }
.stack__label[data-k="1"]::before { right: 100%; width: 184px; }
.stack__label[data-k="1"]::after  { right: calc(100% + 182px); }
.stack__label[data-k="3"]::before { right: 100%; width: 72px; }
.stack__label[data-k="3"]::after  { right: calc(100% + 70px); }
.stack__label[data-k="5"]::before { right: 100%; width: 54px; }
.stack__label[data-k="5"]::after  { right: calc(100% + 52px); }
/* left column: leader runs right from the label box inner (right) edge */
.stack__label[data-k="2"]::before { left: 100%; width: 100px; }
.stack__label[data-k="2"]::after  { left: calc(100% + 98px); }
.stack__label[data-k="4"]::before { left: 100%; width: 140px; }
.stack__label[data-k="4"]::after  { left: calc(100% + 138px); }

/* label state: focus ring, dim, active title + leader */
.stack__label:focus-visible .stack__label-body { outline: 2px solid rgba(75,54,255,.55); outline-offset: 6px; border-radius: 3px; }
.stack:not([data-active=""]) .stack__label { opacity: .55; }
.stack[data-active="0"] .stack__label[data-k="0"],
.stack[data-active="1"] .stack__label[data-k="1"],
.stack[data-active="2"] .stack__label[data-k="2"],
.stack[data-active="3"] .stack__label[data-k="3"],
.stack[data-active="4"] .stack__label[data-k="4"],
.stack[data-active="5"] .stack__label[data-k="5"] { opacity: 1; }

.stack[data-active="0"] .stack__label[data-k="0"] .stack__title,
.stack[data-active="1"] .stack__label[data-k="1"] .stack__title,
.stack[data-active="2"] .stack__label[data-k="2"] .stack__title,
.stack[data-active="3"] .stack__label[data-k="3"] .stack__title,
.stack[data-active="4"] .stack__label[data-k="4"] .stack__title,
.stack[data-active="5"] .stack__label[data-k="5"] .stack__title { color: var(--accent-deep); }

.stack[data-active="0"] .stack__label[data-k="0"]::before, .stack[data-active="0"] .stack__label[data-k="0"]::after,
.stack[data-active="1"] .stack__label[data-k="1"]::before, .stack[data-active="1"] .stack__label[data-k="1"]::after,
.stack[data-active="2"] .stack__label[data-k="2"]::before, .stack[data-active="2"] .stack__label[data-k="2"]::after,
.stack[data-active="3"] .stack__label[data-k="3"]::before, .stack[data-active="3"] .stack__label[data-k="3"]::after,
.stack[data-active="4"] .stack__label[data-k="4"]::before, .stack[data-active="4"] .stack__label[data-k="4"]::after,
.stack[data-active="5"] .stack__label[data-k="5"]::before, .stack[data-active="5"] .stack__label[data-k="5"]::after { background: var(--accent); }

/* --- scroll-expansion label choreography (desktop only). JS adds .stack--scrub
   once the desktop scroll handler is installed; before the stack has expanded
   (no .is-expanded) the gutter labels are hidden and dropped, then rise in
   staggered when JS flips .is-expanded at --expand >= .92. The stagger delay is
   carried on transform only (not opacity), so the later data-active dim/active
   opacity changes stay instant. With JS off / reduced motion / mobile the
   .stack--scrub class is never present, so labels render fully visible. */
@media (min-width: 900px) {
  .stack--scrub:not(.is-expanded) .stack__label {
    opacity: 0; transform: translateY(calc(-50% + 14px)); pointer-events: none;
  }
  .stack--scrub.is-expanded .stack__label {
    transform: translateY(-50%);
    transition: opacity .45s ease, transform .55s cubic-bezier(.22,.9,.34,1);
  }
  /* Delays follow VERTICAL position (top -> bottom), not DOM order, so the
     entrance reads as a single top-to-bottom ripple. DOM order is k0,k1,k2,k3,
     k4,k5 (nth-child 1..6); vertical order is k0,k4,k1,k2,k3,k5. */
  .stack--scrub.is-expanded .stack__label:nth-child(1) { transition-delay: 0s, .02s; } /* k0 — top */
  .stack--scrub.is-expanded .stack__label:nth-child(5) { transition-delay: 0s, .09s; } /* k4 */
  .stack--scrub.is-expanded .stack__label:nth-child(2) { transition-delay: 0s, .16s; } /* k1 */
  .stack--scrub.is-expanded .stack__label:nth-child(3) { transition-delay: 0s, .23s; } /* k2 */
  .stack--scrub.is-expanded .stack__label:nth-child(4) { transition-delay: 0s, .30s; } /* k3 */
  .stack--scrub.is-expanded .stack__label:nth-child(6) { transition-delay: 0s, .37s; } /* k5 — bottom */
}

/* ------------------------------ AI Safety (dark) ------------------------------ */
.section--dark {
  background: radial-gradient(120% 120% at 80% 0%, var(--surface-dark) 0%, var(--bg-dark) 55%);
  color: var(--ink-invert);
}
.section--dark .h2 { color: #fff; }
.section--dark .section__sub--invert { color: var(--ink-invert-muted); }
.safety__angle {
  margin-top: var(--sp-2);
  font-size: var(--fs-lead); color: var(--accent-tint-2); max-width: 60ch;
}
.pillar {
  border: 1px solid var(--border-invert); border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-3);
  background: rgba(255,255,255,.02);
  display: flex; flex-direction: column; gap: 10px;
}
.pillar__title { color: #fff; font-size: var(--fs-h3); position: relative; padding-left: 18px; }
.pillar__title::before {
  content: ""; position: absolute; left: 0; top: 0.35em;
  width: 8px; height: 8px; border-radius: 2px; background: var(--accent); transform: rotate(45deg);
}
.pillar p { color: var(--ink-invert-muted); font-size: var(--fs-sm); }

/* ------------------------------ Research / ecosystem mini-cards ------------------------------ */
.mini {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: var(--sp-3);
  display: flex; flex-direction: column; gap: 6px;
  transition: border-color .2s ease, transform .2s ease;
}
.mini:hover { border-color: var(--accent-line); transform: translate(2px, -2px); }
.mini h3 { font-size: 1.0625rem; color: var(--ink); }
.mini p { color: var(--ink-muted); font-size: var(--fs-sm); }

/* ------------------------------ Deployment patterns ------------------------------ */
.pattern {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: var(--sp-3);
  display: flex; flex-direction: column; gap: var(--sp-2);
}
/* .pattern is a native <details>; .pattern__title is its <summary>. These
   props make the summary render exactly like the former <h3> (the base
   h1,h2,h3 rule does not reach it) and remove the disclosure marker. */
.pattern__title {
  font-size: 1.125rem; color: var(--ink);
  font-weight: 600; letter-spacing: -0.02em; line-height: 1.15;
  padding-bottom: var(--sp-2); border-bottom: 1px solid var(--border);
  list-style: none;
}
.pattern__title::-webkit-details-marker { display: none; }
/* Desktop: all patterns forced open (JS); summary is inert, matching the
   original static card titles. */
@media (min-width: 940px) {
  .pattern__title { pointer-events: none; cursor: default; }
}
.pattern dl { display: flex; flex-direction: column; gap: 12px; }
.pattern dt {
  font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent-deep); margin-bottom: 2px;
}
.pattern dd { color: var(--ink-muted); font-size: var(--fs-sm); }

/* Singapore proof-point band — full width, distinct accent treatment. */
.proof {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--accent-soft) 0%, var(--surface) 62%);
  box-shadow: var(--shadow-soft);
  padding: var(--sp-5);
  margin-bottom: var(--sp-6);
  display: flex; flex-direction: column; gap: var(--sp-2);
}
.proof::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: var(--accent);
}
.proof__eyebrow {
  font-family: var(--font-mono); font-size: var(--fs-eyebrow); font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent-deep);
}
.proof__title { font-size: 1.375rem; color: var(--ink); }
.proof__body { color: var(--ink-muted); font-size: var(--fs-sm); }

/* ------------------------------ Resources ------------------------------ */
.resource {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: var(--sp-3);
  display: flex; flex-direction: column; gap: 8px;
  transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
.resource:hover { border-color: var(--accent-line); transform: translateY(-2px); box-shadow: var(--shadow-soft); }
.resource h3 { font-size: var(--fs-h3); color: var(--ink); display: flex; align-items: center; gap: 8px; }
.resource h3::after { content: "\2197"; color: var(--accent); font-weight: 400; font-size: 0.95em; }
.resource p { color: var(--ink-muted); font-size: var(--fs-sm); flex: 1; }
.resource__host { font-family: var(--font-mono); font-size: 0.75rem; color: var(--ink-faint); letter-spacing: 0.02em; }

/* ------------------------------ Company / About ------------------------------ */
.company__grid { display: grid; gap: var(--sp-6); grid-template-columns: 1fr; }
.facts { display: flex; flex-direction: column; }
.facts__row {
  display: grid; grid-template-columns: 1fr; gap: 4px;
  padding: var(--sp-3) 0; border-top: 1px solid var(--border);
}
.facts__row:last-child { border-bottom: 1px solid var(--border); }
.facts dt {
  font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-faint);
}
.facts dd { color: var(--ink); font-size: var(--fs-body); }
.facts dd a { color: var(--accent-strong); }
.facts dd a:hover { text-decoration: underline; }
.mgmt { list-style: none; display: flex; flex-direction: column; gap: 6px; }

.company__role-title { font-size: var(--fs-h3); color: var(--ink); margin-bottom: var(--sp-2); }
.role__list { display: flex; flex-direction: column; gap: 10px; margin-bottom: var(--sp-3); }
.role__list li { position: relative; padding-left: 22px; color: var(--ink-body); font-size: var(--fs-sm); }
.role__list li::before {
  content: ""; position: absolute; left: 0; top: 0.5em;
  width: 8px; height: 8px; border-radius: 2px; background: var(--accent); transform: rotate(45deg);
}
.company__note { color: var(--ink-muted); font-size: var(--fs-sm); }

/* TBC placeholder chip */
.tbc {
  display: inline-block;
  font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 500;
  color: var(--amber-ink);
  background: var(--amber-bg);
  border: 1px dashed var(--amber-border);
  border-radius: var(--radius-sm);
  padding: 3px 9px;
}
.tbc--sm { font-size: 0.75rem; padding: 2px 8px; }

/* ------------------------------ Final CTA ------------------------------ */
.final-cta { padding-block: clamp(48px, 7vw, 96px); }
.cta-card {
  position: relative; overflow: hidden;
  border-radius: var(--radius-lg);
  background: radial-gradient(130% 130% at 15% 0%, var(--surface-dark-2) 0%, var(--bg-dark) 60%);
  color: #fff;
  text-align: center;
  padding: clamp(48px, 7vw, 88px) var(--sp-4);
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
}
.cta-card::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 90% at 85% 110%, var(--accent-glow) 0%, transparent 60%);
}
.cta-card > * { position: relative; z-index: 1; }
.cta-card__title { color: #fff; }
.cta-card__sub { color: var(--ink-invert-muted); font-size: var(--fs-lead); max-width: 46ch; }
.cta-card__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-1); }

/* ------------------------------ Footer ------------------------------ */
.footer { background: var(--bg-subtle); border-top: 1px solid var(--border); padding-block: var(--sp-8) var(--sp-4); }
.footer__inner { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
.footer__brand { display: flex; flex-direction: column; gap: var(--sp-2); }
.footer__tagline { color: var(--ink-muted); font-size: var(--fs-sm); max-width: 30ch; }
.footer__col { display: flex; flex-direction: column; gap: 12px; }
.footer__heading {
  font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-faint);
}
.footer__col a { color: var(--ink-muted); font-size: var(--fs-sm); transition: color .15s ease; }
.footer__col a:hover { color: var(--ink); }
.footer__base {
  display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: space-between; align-items: center;
  margin-top: var(--sp-8); padding-top: var(--sp-3); border-top: 1px solid var(--border);
  color: var(--ink-faint); font-size: var(--fs-sm);
}

/* ------------------------------ Scroll reveal ------------------------------ */
.reveal { opacity: 0; transform: translateY(12px); transition: opacity .6s ease, transform .6s ease; }
.reveal.is-visible { opacity: 1; transform: none; }

/* ============================================================
   Responsive
   ============================================================ */
@media (min-width: 560px) {
  .grid--5 { grid-template-columns: repeat(2, 1fr); }
  .grid--4 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(2, 1fr); }
  .company__grid { grid-template-columns: 1fr; }
  .facts__row { grid-template-columns: 200px 1fr; align-items: baseline; gap: var(--sp-3); }
}

@media (min-width: 768px) {
  .footer__inner { grid-template-columns: 2fr 1fr 1fr 1fr; }
  .company__grid { grid-template-columns: 1.1fr 1fr; gap: var(--sp-8); align-items: start; }
  .hero__sub { max-width: 40ch; }
}

@media (min-width: 940px) {
  .nav__links { display: flex; }
  .nav__actions .btn { display: inline-flex; }
  .nav__burger { display: none; }
  .nav-mobile { display: none !important; }

  .grid--3 { grid-template-columns: repeat(3, 1fr); }
  .grid--4 { grid-template-columns: repeat(4, 1fr); }
  .grid--5 { grid-template-columns: repeat(5, 1fr); }
  .safety__grid { grid-template-columns: repeat(3, 1fr); }
}

/* Hero: keep text readable where the animation overlaps on small screens */
@media (max-width: 768px) {
  .hero { min-height: auto; padding-block: var(--sp-8) 0; }
  .hero__inner { padding-bottom: 38vw; }        /* room below text for the art */
  .hero__animation { width: 118%; right: -18%; bottom: 0; }
  .hero--net::before {
    background:
      radial-gradient(52% 44% at 62% 80%, rgba(75,54,255,.10), transparent 70%),
      radial-gradient(72% 60% at 62% 80%, rgba(207,201,255,.08), transparent 75%);
  }
  /* Make the fallback grid earn the band it keeps — denser corner, full opacity. */
  .hero__grid { opacity: 1; width: 150%; right: -30%; bottom: -6%; }
  .hero__header { gap: var(--sp-2); }
}

@media (max-width: 420px) {
  :root { --gutter: 18px; }
  .hero__inner { padding-bottom: 44vw; }
}

/* ============================================================
   MOBILE REFACTOR — everything below is scoped to max-width media
   queries; the desktop layout (>=940px, and the >=900px totem) is
   untouched. Ordered widest breakpoint first, then the tightest.
   ============================================================ */

/* --- Section rhythm: desktop-sized head margins are too airy on phones --- */
@media (max-width: 768px) {
  .section__head { margin-bottom: var(--sp-5); }   /* 64 -> 40 */
}

/* --- Below the desktop nav breakpoint (899px) --- */
@media (max-width: 899px) {
  /* Instant anchor jumps — the reveal system shows everything up-front on
     mobile, so a smooth fly-through past 10 sections is pure cost. */
  html { scroll-behavior: auto; }

  /* Cheaper sticky nav; no per-frame backdrop re-filter over a white page. */
  .nav {
    backdrop-filter: none; -webkit-backdrop-filter: none;
    background: rgba(255,255,255,.97);
  }

  /* Comfortable footer link pitch (>=44px). */
  .footer__col a { padding-block: 11px; }

  /* Larger tap target for the contact mailto (layout-neutral). */
  .facts dd a { display: inline-block; padding-block: 8px; margin-block: -8px; }

  /* ---- #how: totem full-width above a static tappable row-list ---- */
  .stack__svg { max-width: 330px; margin-inline: auto; }
  .stack__labels { margin-top: var(--sp-4); max-width: 480px; margin-inline: auto; }
  .stack__label,
  .stack__label[data-k="0"], .stack__label[data-k="1"], .stack__label[data-k="2"],
  .stack__label[data-k="3"], .stack__label[data-k="4"], .stack__label[data-k="5"] {
    position: relative; transform: none; width: auto;
    top: auto; left: auto; right: auto;
    display: flex; flex-direction: row; text-align: left;
    align-items: flex-start;
    padding: 12px 0 12px 16px; border-top: 1px solid var(--border);
  }
  .stack__label::after { display: none; }  /* no tip dot in the row list */
  /* active-row cue: a 3px accent inset bar down the left of the row (::before,
     full row height), replacing the old filled number badge. Rest rows show no
     bar. The active title also turns accent (shared desktop rule). The
     .stack__labels scope lifts specificity to override the desktop per-k leader
     geometry (.stack__label[data-k="n"]::before) so the bar geometry wins. */
  .stack__labels .stack__label::before {
    content: ""; display: block; position: absolute;
    left: 3px; top: 9px; bottom: 9px; right: auto;
    width: 3px; height: auto; border-radius: 2px;
    background: var(--accent); opacity: 0; transition: opacity .3s ease;
  }
  .stack[data-active="0"] .stack__label[data-k="0"]::before,
  .stack[data-active="1"] .stack__label[data-k="1"]::before,
  .stack[data-active="2"] .stack__label[data-k="2"]::before,
  .stack[data-active="3"] .stack__label[data-k="3"]::before,
  .stack[data-active="4"] .stack__label[data-k="4"]::before,
  .stack[data-active="5"] .stack__label[data-k="5"]::before { opacity: 1; }
}

/* --- Below the desktop content breakpoint (940px) --- */
@media (max-width: 939px) {
  /* #research: two-up program catalog; feature cards keep full-width emphasis */
  .ecosystem__grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  /* Odd trailing mini spans both cols so the 2-col grid has no orphan. */
  .ecosystem__grid .mini:nth-child(odd):last-child { grid-column: 1 / -1; }

  /* #patterns: native <details> accordions */
  .pattern { padding: 0; gap: 0; }
  .pattern__title {
    display: flex; align-items: center; justify-content: space-between;
    min-height: 56px; margin: 0; padding: 0 var(--sp-3);
    border-bottom: 0; cursor: pointer;
  }
  .pattern__title::after {
    content: "+"; margin-left: var(--sp-2);
    font-family: var(--font-mono); font-size: 1.375rem; font-weight: 400;
    line-height: 1; color: var(--accent);
  }
  details[open].pattern > .pattern__title::after { content: "\2013"; }  /* – */
  details[open].pattern > .pattern__title { border-bottom: 1px solid var(--border); }
  .pattern dl { padding: var(--sp-3); }
}

/* --- Phone (<=560px): compress the desktop-sized air --- */
@media (max-width: 560px) {
  /* Global grid gap 24 -> 16. */
  .grid { gap: var(--sp-2); }

  /* #problem two-up at the narrowest widths. */
  .problem__grid { grid-template-columns: repeat(2, 1fr); }
  .problem__grid { gap: 12px; }
  /* Odd trailing card spans the full row instead of orphaning in the left col. */
  .problem__grid > .card:nth-child(odd):last-child { grid-column: 1 / -1; }

  /* Card padding compression. */
  .card { padding: var(--sp-2) 18px; }
  .card--feature, .pillar { padding: var(--sp-3) var(--sp-2); }

  /* #how badge rows: keep the (compressed) card padding off the labels. */
  .stack__label { padding: 12px 0 12px 16px; }

  /* Hero: drop the redundant eyebrow (nav wordmark already says it); stack CTAs. */
  .hero__header > .eyebrow { display: none; }
  .hero__cta { flex-direction: column; align-items: stretch; width: 100%; }
  .hero__cta .btn { width: 100%; }

  /* Trusted marquee: tighter gap + smaller logos so ~3 are visible per frame. */
  .trusted { padding-block: var(--sp-6); }
  .marquee { --marquee-gap: var(--sp-4); }
  .marquee__item img { height: 20px; }
  .marquee__item--md img { height: 26px; }
  .marquee__item--seal img { height: 30px; }

  /* #research: compact mini cards. */
  .mini { padding: var(--sp-2); }
  .mini h3 { font-size: 0.9375rem; }
  .mini p { font-size: 0.8125rem; }
  .ecosystem__grid { gap: 12px; }   /* re-assert over the global grid gap */

  /* #patterns: tighten the proof band (40 -> 24). */
  .proof { padding: var(--sp-3); }

  /* #resources: collapse link cards into a single bordered link list. */
  .resources__grid {
    gap: 0; border: 1px solid var(--border);
    border-radius: var(--radius); overflow: hidden;
  }
  .resource { border: 0; border-radius: 0; padding: 14px var(--sp-2); gap: 2px; }
  .resource:hover { transform: none; box-shadow: none; }
  .resource + .resource { border-top: 1px solid var(--border); }
  .resource p { font-size: 0.8125rem; }   /* keep the description, just smaller */

  /* Footer: two-up the columns; brand spans the row. */
  .footer__inner { grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
  .footer__brand { grid-column: 1 / -1; }
}

/* ============================================================
   Reduced motion — skip reveals + smooth scroll; art shown static
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
  .btn:hover, .card:hover, .resource:hover, .mini:hover { transform: none; }
}

/* ============================================================
   How it works: ambient micro-animations inside the totem layers
   ============================================================
   Five whisper-quiet loops, one per layer, that say what each layer
   does while it sits at rest. Everything animates opacity, transform
   or stroke-dashoffset only; no filters, no permanent will-change,
   no JavaScript. Every animated element lives INSIDE its layer group,
   so the data-active dimming subordinates it for free.

   All five share one 9s master cycle (the vault glow uses a 4.5s
   alternate, which is the same 9s round trip), so the whole picture
   repeats exactly and the events can be placed by hand. Map of the
   cycle, in seconds, with the running count of visible events:

     0.00-0.80  chain: link 1 draws, block 1 lands            (1)
     0.90-2.00  memory: pattern A lit                         (1-2)
     1.10-1.80  storage: ping 1                               (2)
     1.95-2.75  chain: link 2 draws, block 2 lands            (2)
     2.00-3.10  memory: pattern B lit                         (1-2)
     2.85-3.55  storage: ping 2                               (2)
     3.30-5.30  app: request dot drifts to the front corner   (1-2)
     4.15-4.85  storage: ping 3                               (2)
     4.90-6.00  memory: pattern C lit                         (1-2)
     5.70-6.40  storage: ping 4                               (2)
     6.20-7.60  vault: glint sweeps the lid                   (1-2)
     6.75-7.85  memory: pattern D lit                         (2)
     8.10-9.00  chain: growth end resets, gently              (1-2)
     8.20-8.90  storage: ping 5                               (2)

   Peak concurrency is 2 at every instant. The two chain append
   moments (0.00-0.80 and 1.95-2.75) are the loudest events and are
   given the clearest air; the chain's reset is scheduled opposite a
   single storage ping so the eye is elsewhere while it happens. The
   vault glow breathes continuously underneath all of it at an
   amplitude too small to count as an event.

   Gating: the ambients run only when the totem is open. On desktop
   that is .stack.is-expanded, set by the scroll scrub. Below 900px
   the scrub is never installed and the script never adds that class
   on a fresh load, so the small-screen gate is .stack:not(.stack--scrub)
   inside the same breakpoint the script uses. With JavaScript off at
   desktop width neither gate matches, the loops never start, and the
   chain's growth end renders complete and solid. Invisibility for the
   growth blocks and links lives INSIDE the keyframes, never on the
   element, so the default state of every chain piece is fully drawn.
   Both gates work by handing the animation NAME to the elements
   through custom properties; the timing on each element is inert
   until a name arrives.  */

.stack.is-expanded {
  --amb-app: ambAppDot;
  --amb-breathe: ambVaultGlow;
  --amb-glint: ambVaultGlint;
  --amb-mem: ambMemStep;
  --amb-shard: ambShardPing;
  --amb-link: ambChainHold;
  --amb-draw1: ambChainDraw1;
  --amb-draw2: ambChainDraw2;
  --amb-block1: ambChainBlock1;
  --amb-block2: ambChainBlock2;
}
@media (max-width: 899px) {
  .stack:not(.stack--scrub) {
    --amb-app: ambAppDot;
    --amb-breathe: ambVaultGlow;
    --amb-glint: ambVaultGlint;
    --amb-mem: ambMemStep;
    --amb-shard: ambShardPing;
    --amb-link: ambChainHold;
    --amb-draw1: ambChainDraw1;
    --amb-draw2: ambChainDraw2;
    --amb-block1: ambChainBlock1;
    --amb-block2: ambChainBlock2;
  }
}

/* 1. App plate: one request leaves the chip and drifts 26px toward the
   plate's front corner, fading as it goes. 2s of travel, then a long
   silent tail inside the keyframes. */
.ambApp {
  animation: var(--amb-app, none) 9s linear infinite 3.3s;
}
@keyframes ambAppDot {
  0%      { opacity: 0;  transform: translate(0, 0); }
  2%      { opacity: .5; }
  22.222% { opacity: 0;  transform: translate(-19px, 17px); }
  100%    { opacity: 0;  transform: translate(-19px, 17px); }
}

/* 2a. Vault: the compute cube's glow breathes. A second copy of the
   glow ellipse carries this so the layer's own active-state glow (which
   the state machine drives) is left alone. */
.ambBreathe {
  animation: var(--amb-breathe, none) 4.5s ease-in-out infinite alternate;
}
@keyframes ambVaultGlow {
  from { opacity: .07; }
  to   { opacity: .105; }
}

/* 2b. Vault: a glint crosses the sealed lid. The line lives in the lid's
   own rotated space, so a plain sideways slide reads as a sweep along the
   lid, and the lid clip keeps it inside the panel. */
.ambGlint {
  animation: var(--amb-glint, none) 9s linear infinite 6.2s;
}
@keyframes ambVaultGlint {
  0%      { opacity: 0;   transform: translateX(-84px); }
  3%      { opacity: .26; }
  12%     { opacity: .26; }
  15.556% { opacity: 0;   transform: translateX(84px); }
  100%    { opacity: 0;   transform: translateX(84px); }
}

/* 3. Memory plate: the frosted tile steps through lit dot patterns in
   discrete states, 1.1s apart, no fading between them. Four patterns per
   cycle; the second follows the first immediately so the step is seen. */
.ambMem {
  animation: var(--amb-mem, none) 9s steps(1, end) infinite;
}
.ambMem--1 { animation-delay: .9s; }
.ambMem--2 { animation-delay: 2s; }
.ambMem--3 { animation-delay: 4.9s; }
.ambMem--4 { animation-delay: 6.75s; }
@keyframes ambMemStep {
  0%              { opacity: .8; }
  12.222%         { opacity: .8; }
  12.223%, 100%   { opacity: 0; }
}

/* 4. Storage plate: possession pings. Five scattered, never adjacent
   shards brighten to accent in turn, about one every two seconds. */
.ambShard {
  animation: var(--amb-shard, none) 9s ease-in-out infinite;
}
.ambShard--1 { animation-delay: 1.1s; }
.ambShard--2 { animation-delay: 2.85s; }
.ambShard--3 { animation-delay: 4.15s; }
.ambShard--4 { animation-delay: 5.7s; }
.ambShard--5 { animation-delay: 8.2s; }
@keyframes ambShardPing {
  0%     { opacity: 0; }
  3.9%   { opacity: .75; }
  7.778% { opacity: 0; }
  100%   { opacity: 0; }
}

/* 5. Chain base: new blocks are added. A link extends from the settled
   run, a block lands on it with the house arrival curve, and 1.6s later
   the pair repeats. The finished growth end holds for five seconds and
   then resets softly, well away from the appends. The links are drawn
   by their own dash offset; their group opacity only handles the reset,
   so a link is hidden by its dash offset until its turn comes. */
.ambLink { animation: var(--amb-link, none) 9s linear infinite; }
.ambLink path { animation-duration: 9s; animation-timing-function: ease-out; animation-iteration-count: infinite; }
.ambLink--1 path { animation-name: var(--amb-draw1, none); }
.ambLink--2 path { animation-name: var(--amb-draw2, none); }
.ambBlock { transform-box: fill-box; transform-origin: 50% 50%; }
.ambBlock--1 { animation: var(--amb-block1, none) 9s linear infinite; }
.ambBlock--2 { animation: var(--amb-block2, none) 9s linear infinite; }

@keyframes ambChainHold {
  0%, 90% { opacity: 1; }
  100%    { opacity: 0; }
}
@keyframes ambChainDraw1 {
  0%             { stroke-dashoffset: 72; }
  3.889%, 100%   { stroke-dashoffset: 0; }
}
@keyframes ambChainDraw2 {
  0%, 21.667%    { stroke-dashoffset: 72; }
  25.556%, 100%  { stroke-dashoffset: 0; }
}
@keyframes ambChainBlock1 {
  0%      { opacity: 0; transform: scale(.65); }
  3.889%  { opacity: 0; transform: scale(.65); animation-timing-function: cubic-bezier(.22,.9,.34,1); }
  8.889%  { opacity: 1; transform: scale(1); }
  90%     { opacity: 1; transform: scale(1); animation-timing-function: ease; }
  100%    { opacity: 0; transform: scale(1); }
}
@keyframes ambChainBlock2 {
  0%      { opacity: 0; transform: scale(.65); }
  25.556% { opacity: 0; transform: scale(.65); animation-timing-function: cubic-bezier(.22,.9,.34,1); }
  30.556% { opacity: 1; transform: scale(1); }
  90%     { opacity: 1; transform: scale(1); animation-timing-function: ease; }
  100%    { opacity: 0; transform: scale(1); }
}

/* Reduced motion: every ambient stops. The chain's growth end has no
   base-style invisibility, so stopping the loops leaves the record
   complete rather than short two blocks. */
@media (prefers-reduced-motion: reduce) {
  .ambApp, .ambBreathe, .ambGlint, .ambMem, .ambShard,
  .ambLink, .ambLink path, .ambBlock { animation: none !important; }
  .ambLink, .ambBlock { opacity: 1 !important; transform: none !important; }
  .ambLink path { stroke-dashoffset: 0 !important; }
}
