/* Crypto War Room - public landing page.

   Linear system: void canvas, carbon cards bounded by a 1px inset
   hairline rather than a shadow, Inter at 400-590 with tight tracking,
   and one acid-lime action per view. Values come from the linear.app
   handoff and are declared once in :root below.

   The previous Factory/carbon revision is kept at home.css.pre-linear.bak.

   Deliberately does NOT load styles.css - that sheet belongs to the
   members' app, which inherits its tokens, and inheriting it here would mean
   fighting rules written for a design this one replaces. */

/* ============================================================
   LINEAR SYSTEM - token layer
   Values from the linear.app handoff, installed under their own names.
   The page's existing variables (--bg, --fg, --accent ...) are kept and
   re-pointed at them: 680 lines of this sheet already speak in those
   names, and a rename would have been a second, riskier change wearing
   the same commit.
   ============================================================ */

:root {
  /* -- Linear, as given -- */
  --color-void:        #08090a;
  --color-carbon:      #0f1011;
  --color-obsidian:    #161718;
  --color-graphite:    #23252a;
  --color-smoke:       #383b3f;
  --color-ash:         #62666d;
  --color-fog:         #8a8f98;
  --color-mist:        #d0d6e0;
  --color-bone:        #e5e5e6;
  --color-paper:       #ffffff;
  --color-acid-lime:   #e4f222;
  --color-pulse-green: #27a644;
  --color-coral-red:   #eb5757;
  --color-signal-teal: #02b8cc;
  --color-iris-violet: #6366f1;
  --color-lavender:    #8b5cf6;

  --shadow-sm:     rgba(0, 0, 0, 0.4) 0px 2px 4px 0px;
  --shadow-subtle: rgb(35, 37, 42) 0px 0px 0px 1px inset;
  --shadow-xl:     rgba(8, 9, 10, 0.6) 0px 4px 32px 0px;

  --radius-buttons: 6px;
  --radius-cards:   12px;
  --radius-pills:   9999px;
  --radius-badges:  4px;

  /* -- the page's own names, pointed at the system -- */
  --bg:        var(--color-void);
  --bg-card:   var(--color-carbon);
  --bg-lift:   var(--color-obsidian);
  --bg-panel:  var(--color-obsidian);
  --btn:       rgba(255,255,255,.05);
  --btn-hi:    rgba(255,255,255,.09);

  --line:      var(--color-graphite);
  --line-lift: var(--color-smoke);

  --fg:        var(--color-paper);   /* headings, max contrast */
  --chalk:     var(--color-paper);
  --fg-2:      var(--color-mist);    /* body copy */
  --fg-3:      var(--color-fog);     /* muted */
  --fg-dim:    var(--color-ash);     /* dim ON the bone card */
  --fg-quiet:  var(--color-fog);

  /* The one chromatic element. Acid lime fills the single primary action
     per view and nothing else - never a card, never a decoration. */
  --accent:      var(--color-acid-lime);
  --accent-hi:   #edfa5a;
  --accent-soft: rgba(228,242,34,.12);
  --accent-line: rgba(228,242,34,.34);

  /* The handoff calls pulse green and coral red supporting accents rather
     than status colours - but Linear is not a trading product and its
     palette carries no up/down pair. These are the only two candidates,
     and a screener with grey gains would be worse than the deviation. */
  --gain:      var(--color-pulse-green);
  --loss:      var(--color-coral-red);

  /* Berkeley Mono is not a webfont we can serve; the handoff names
     JetBrains Mono as the substitute. */
  --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  --sans: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --maxw: 1200px;
  /* The handoff says 1200. Kept at 1480 because that width was an explicit
     ask - 1200 left the rooms floating with the captures too small. One
     value to change if the narrower track is wanted. */
  --maxw-wide: 1480px;
  --ease: cubic-bezier(.4, 0, .2, 1);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-weight: 400;
  overflow-x: hidden;
  /* cv01, ss03 and zero are the alternate glyphs the handoff calls
     non-negotiable. They are what makes this read as Linear's Inter
     rather than as the default one. */
  font-feature-settings: "cv01" on, "ss03" on, "zero" on;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--fg); text-decoration: none; transition: color .15s var(--ease), border-color .15s var(--ease), background-color .15s var(--ease); }
a:hover { color: var(--chalk); }
::selection { background: var(--accent); color: var(--bg); }

img { max-width: 100%; height: auto; display: block; }

.wrap { max-width: var(--maxw-wide); margin: 0 auto; padding-inline: clamp(24px, 3.4vw, 48px); }

/* Caption: the system's one small-text size. 12px is the floor -- nothing
   on this page goes below it. */
.kicker {
  font-family: var(--mono); font-size: 12px; line-height: 1;
  letter-spacing: -.02em; text-transform: uppercase; color: var(--fg-2);
  display: flex; align-items: center; gap: 8px;
}
.dot { width: 6px; height: 6px; border-radius: 999px; background: var(--color-fog); display: inline-block; flex: none; }
.dot--gain { background: var(--gain); }
.dot--mute { background: var(--line-lift); }

/* Weight 510 for display and section heads; the scale stops at 590.
   Nothing here goes bold - the handoff caps the system below 700 and
   leans on tight tracking instead. */
h1, h2, h3 { font-weight: 510; margin: 0; }
.h-display { font-size: clamp(44px, 5.4vw, 72px); line-height: 1; letter-spacing: -.022em; text-wrap: balance; }
.h-section { font-size: clamp(32px, 3.6vw, 48px); line-height: 1; letter-spacing: -.022em; }
.h-loop    { font-size: clamp(32px, 3.6vw, 48px); line-height: 1; letter-spacing: -.022em; max-width: 24ch; }
.h-card    { font-size: 20px; line-height: 1.33; letter-spacing: -.012em; font-weight: 590; }

/* One body step, not three. This was 17px while .body-md ran 16 and
   .body-sm ran 15, all at weight 400 in the same grey -- three sizes a
   reader cannot tell apart, which is a flat ramp rather than a fine one.
   The handoff sets hero subtext at 16px/400 and differentiates the small
   end by weight and colour, not by one-pixel steps. */
.lede { font-size: 16px; line-height: 1.5; color: var(--fg-3); margin: 0; text-wrap: pretty; }
.body-sm { font-size: 15px; line-height: 1.6; letter-spacing: -.011em; color: var(--fg-3); margin: 0; }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--sans); font-size: 14px; line-height: 1; font-weight: 510;
  letter-spacing: -.011em;
  padding: 11px 16px; border-radius: var(--radius-buttons);
  border: 1px solid transparent;
  white-space: nowrap; cursor: pointer;
}
/* The acid-lime fill is the only chromatic element on the page, and there
   is exactly one of it per view: the hero's request-access. Every other
   action is neutral. */
.btn--solid { background: var(--accent); color: var(--bg); border-color: var(--accent); }
.btn--solid:hover { background: var(--accent-hi); border-color: var(--accent-hi); color: var(--bg); }

/* The nav sign-up: a white pill, the second-highest-contrast element
   after the lime. */
.btn--bone {
  background: var(--color-paper); color: var(--bg);
  border-radius: var(--radius-pills); padding: 9px 16px; font-size: 13px;
}
.btn--bone:hover { background: var(--color-bone); color: var(--bg); }

.btn--ink { background: var(--bg); color: var(--fg); }
.btn--ink:hover { background: var(--color-graphite); color: var(--chalk); }

.btn--ghost {
  border-color: var(--line); color: var(--fg-2);
  padding: 10px 14px; font-size: 13px; font-weight: 400;
}
.btn--ghost:hover { color: var(--chalk); border-color: var(--line-lift); background: rgba(255,255,255,.03); }

/* ---------- header ---------- */

.head {
  position: sticky; top: 0; z-index: 50;
  background: rgba(8, 9, 10, .72); backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.head__nav {
  max-width: var(--maxw-wide); margin: 0 auto; height: 64px; padding: 0 clamp(24px, 3.4vw, 48px);
  display: flex; align-items: center; gap: 40px;
}
.brand { display: flex; align-items: center; flex: none; }
/* Height-driven so the wordmark keeps its own 1935:393 proportions.

   Flattened to white in CSS rather than re-exported: brightness(0) drives
   every opaque pixel to black and invert(1) lifts them to white, leaving
   the alpha channel alone. That kills the orange mark the file still
   carries from the retired palette, which was the one warm thing left on
   the page. Replace the asset and this rule comes off. */
.brand__logo {
  height: 22px; width: auto; display: block;
  filter: brightness(0) invert(1);
}
@media (max-width: 900px) { .brand__logo { height: 19px; } }

/* Linear's nav: 13px at weight 400 in mist, sentence case, each item
   carrying its own padding so the gaps between them stay small. Ours was
   14px uppercase, which read as a section label rather than navigation. */
.head__links {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 400; letter-spacing: -.011em;
  white-space: nowrap;
}
.head__links a {
  color: var(--color-mist);
  padding: 8px 12px; border-radius: var(--radius-buttons);
}
.head__links a:hover { color: var(--color-paper); }
.head__spacer { flex: 1; }
.head__cta { display: flex; align-items: center; gap: 16px; flex: none; }

@media (max-width: 900px) {
  .head__links { display: none; }
  .head__nav { gap: 16px; }
}

/* ---------- hero ---------- */

.hero {
  max-width: var(--maxw-wide); margin: 0 auto;
  padding: clamp(64px, 7vw, 112px) clamp(24px, 3.4vw, 48px) clamp(72px, 8vw, 128px);
  position: relative;
}

/* The hero's atmospheric floor. The one gradient the system allows: a
   light wash rising under the product capture so it is grounded rather
   than floating on flat void. Full-bleed, because a floor that stops at
   the content track reads as a panel.

   The handoff's stop is opaque #d0d6e0; at that strength it puts a
   near-white band across the bottom of a hero this tall, so it is carried
   at low alpha and falls away before the edge -- the same gradient, tuned
   to this composition. */
.hero__glow {
  position: absolute;
  left: 50%; width: 100vw; transform: translateX(-50%);
  bottom: 0; height: 62%;
  z-index: 0; pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(8,9,10,0) 10%,
    rgba(208,214,224,.05) 58%,
    rgba(208,214,224,.13) 86%,
    rgba(208,214,224,0) 100%);
}
/* The copy and the capture sit above it. */
.hero > *:not(.hero__glow) { position: relative; z-index: 1; }

/* One column, not two, and centred. The description used to sit in a
   right-hand column beside the title; it reads under it now, cut to a
   slogan. The capture below stays where it is -- only the words centre. */
.hero__head { margin-bottom: clamp(48px, 5.4vw, 80px); text-align: center; }

/* No measure on the headline: capped at 15ch it wrapped into a narrow
   two-line block in the left half of the page. Left to run, it takes the
   whole track as one condensed line and only wraps when the viewport
   actually makes it. */
.hero h1 {
  margin: 0 auto;
  /* Tighter than the handoff's -.022em. That figure is the floor for
     display type, not a ceiling, and the brief here was explicitly for a
     more condensed setting. */
  letter-spacing: -.028em;
}

/* Just the description now -- the two hero buttons are gone, so there is
   nothing to sit opposite it. */
.hero__sub { margin-top: clamp(20px, 2.2vw, 28px); }
.hero__lede {
  font-size: 15px; line-height: 1.6; letter-spacing: -.011em;
  color: var(--fg-3); margin: 0 auto; max-width: 72ch;
  /* Centred, so the two lines are evened rather than left long-then-short. */
  text-wrap: balance;
}
.hero__actions { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }

/* The handoff pairs the hero headline with a link rather than a second
   button: one filled action per view, and it is the lime one. */
.hero__link {
  font-size: 14px; font-weight: 400; letter-spacing: -.011em;
  color: var(--fg-2); white-space: nowrap;
}
.hero__link:hover { color: var(--chalk); }

/* "A large product screenshot that bleeds beyond the max-width slightly."
   Wider than the track the words sit on, centred on the viewport, and
   still inside it -- the 100vw term keeps a gutter at every width, so the
   capture never runs off the edge of the screen. */
.hero__stage {
  left: 50%; transform: translateX(-50%);
  width: min(100% - 32px, 1120px);
}

@media (max-width: 900px) {
  .hero { padding-top: 56px; }
  .hero__sub { display: grid; gap: 24px; }
  .hero__stage { width: 100%; left: auto; transform: none; }
  .term { width: 100%; }
  .join { padding: 20px; }
}

/* ---------- terminal mock ---------- */

.term {
  background: var(--bg-card); border: 0; border-radius: var(--radius-cards);
  overflow: hidden; animation: frame-in .45s var(--ease) both;
  display: flex; flex-direction: column;
  position: relative;
  /* Ends on the page's own right edge, level with the chains strip
     underneath, rather than bleeding 292px off-screen -- which is what
     was putting a horizontal scrollbar on the page. Width is still the
     only definite dimension in this subtree; height comes from the image
     below it, never the other way round. Earlier versions drove height
     from the grid row and needed a percentage to resolve through
     .hero -> the stage -> .term -> img; that chain broke at a different
     link every time -- once squashing the 16:9 capture to 1.06:1, once
     leaving the frame 2342px tall. */
  width: 100%;
  max-width: none;
  box-shadow: var(--shadow-subtle);
}
@keyframes frame-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .term { animation: none; } }

.term__bar {
  height: 28px; display: flex; align-items: center; gap: 6px; padding: 0 11px;
  border-bottom: 1px solid var(--line); background: var(--bg-lift);
}
/* Traffic lights. The only place on the page these three colours appear
   together -- they read as "this is a window", which is the whole job of
   the frame, and they are chrome rather than product state so they carry
   no meaning the palette has to reserve. */
.term__light { width: 9px; height: 9px; border-radius: 999px; display: inline-block; flex: none; }
.term__light--red    { background: #ff5f57; }
.term__light--yellow { background: #febc2e; }
.term__light--green  { background: #28c840; }
.term__title {
  font-family: var(--mono); font-size: 12px; letter-spacing: -.02em;
  text-transform: uppercase; color: var(--fg-2); margin-left: 16px; white-space: nowrap;
}
.term__live { font-family: var(--mono); font-size: 12px; text-transform: uppercase; color: var(--fg-3); }
.term__grid { display: grid; grid-template-columns: 148px 1fr; }

/* Fills whatever height the text column set, cropping on the right rather
   than letterboxing. Cropping there costs nothing: that edge is already
   being faded out by the mask, so the cut is never visible. */
/* Absolutely positioned, and that is the load-bearing part: in flow, the
   image's own 2160px height fed back into the grid row, the row grew, the
   frame grew with it and the whole hero ballooned. Out of flow it
   contributes no height at all, so the row is sized by the paragraph alone
   and the frame simply matches it.

   `contain` rather than `cover` so the terminal is scaled down whole
   instead of cropped. The box is slightly wider than 16:9 needs, so the
   spare sits on the right -- inside the part the mask is fading out. */
.term__shot {
  /* 100% of a definite width, height from the image's own 16:9. No
     aspect-ratio, no object-fit, no percentage height -- nothing that can
     fail to resolve. max-width:none because the global `img` rule caps
     images at their container, which is what squashed this one before. */
  position: static;
  display: block;
  width: 100%;
  height: auto;
  max-width: none;
}

.term__list { border-right: 1px solid var(--line); padding: 16px 12px; display: flex; flex-direction: column; gap: 16px; }
.term__listhead, .term__row, .term__pool, .term__stats { font-family: var(--mono); font-size: 12px; letter-spacing: -.02em; }
.term__listhead { text-transform: uppercase; color: var(--fg-quiet); display: flex; justify-content: space-between; }
.term__coin { display: flex; flex-direction: column; gap: 4px; }
.term__row { display: flex; justify-content: space-between; gap: 8px; }
.term__pool { line-height: 1; color: var(--fg-quiet); }
.up { color: var(--gain); }
.down { color: var(--loss); }

.term__main { display: flex; flex-direction: column; min-width: 0; }
.term__price { padding: 16px; border-bottom: 1px solid var(--line); }
.term__pricerow { display: flex; align-items: baseline; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }
.term__sym { font-family: var(--mono); font-size: 14px; text-transform: uppercase; letter-spacing: -.02em; color: var(--fg-2); }
.term__big { font-size: 36px; line-height: 1.1; letter-spacing: -.031em; font-variant-numeric: tabular-nums; }
.term__chg { font-family: var(--mono); font-size: 12px; text-transform: uppercase; }

.term__chart { width: 100%; height: 140px; display: block; }
.term__stats { padding: 16px; display: grid; gap: 12px; text-transform: uppercase; }
.term__stat { display: flex; justify-content: space-between; gap: 16px; }
.term__stat span:first-child { color: var(--fg-quiet); }
.term__stat b { font-weight: 400; color: var(--fg-2); }
.term__stat--top { border-top: 1px solid var(--line); padding-top: 12px; }
.term__stat--top b { color: var(--fg); }
.term__buy { border-top: 1px solid var(--line); padding: 16px; display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.term__buybtn { font-size: 14px; background: var(--btn); color: var(--gain); border-radius: 3px; padding: 8px 14px; }
.term__note { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: -.02em; color: var(--fg-quiet); }
.term__squawk {
  border-top: 1px solid var(--line); background: var(--bg-lift); padding: 12px 16px;
  display: flex; align-items: center; gap: 12px; font-family: var(--mono);
  font-size: 12px; text-transform: uppercase; letter-spacing: -.02em;
}
.term__squawk .txt { color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-transform: none; }

@media (max-width: 560px) { .term__grid { grid-template-columns: 1fr; } .term__list { display: none; } }

/* ---------- marquee ---------- */

@media (prefers-reduced-motion: reduce) { .strip__track { animation: none; } }

/* ---------- sections ---------- */

.sect { max-width: var(--maxw-wide); margin: 0 auto; padding: 96px clamp(24px, 3.4vw, 48px); }
.sect--tight { padding-top: 0; }

/* The bone card: the one bright surface in the system, and the reason the
   page reads as a system rather than a dark template. */
.bone {
  background: var(--color-bone); color: var(--bg); border-radius: var(--radius-cards); padding: 24px;
  display: flex; flex-direction: column; gap: 24px;
}
.bone .kicker { color: var(--bg); }
.bone p { color: var(--fg-dim); }
.bone .btn--ink { align-self: flex-start; }

.guard__top { display: grid; grid-template-columns: 5fr 7fr; gap: 24px; align-items: start; margin-bottom: 24px; }
.card {
  border: 1px solid var(--line); border-radius: var(--radius-cards); padding: 24px;
  display: flex; flex-direction: column; gap: 16px;
  transition: border-color .15s var(--ease);
}
.card--hover:hover { border-color: var(--line-lift); }
.check {
  display: grid; grid-template-columns: 88px 1fr auto; gap: 16px; align-items: baseline;
  border-bottom: 1px solid var(--line); padding-bottom: 16px;
  font-family: var(--mono); font-size: 12px; letter-spacing: -.02em;
}
.check .sym { color: var(--fg); text-transform: uppercase; }
.check .detail { color: var(--fg-3); }
.check .verdict { text-transform: uppercase; white-space: nowrap; }
.rule {
  font-family: var(--mono); font-size: 12px; line-height: 1.2;
  text-transform: uppercase; letter-spacing: -.02em; color: var(--fg-quiet); margin: 0;
}
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }

@media (max-width: 900px) {
  .guard__top { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: 1fr; }
  .sect { padding: 56px 24px; }
}

/* ---------- the loop ---------- */

.loop__head { display: flex; align-items: end; justify-content: space-between; gap: 24px; margin-bottom: 40px; flex-wrap: wrap; }
.loop__grid { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--line); }
.step { padding: 32px 24px 32px 0; border-bottom: 1px solid var(--line); display: flex; flex-direction: column; gap: 16px; }
.step__n { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: -.02em; color: var(--color-fog); }

@media (max-width: 900px) { .loop__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .loop__grid { grid-template-columns: 1fr; } }

/* ---------- inside ---------- */

/* Fills the height the screener figure would otherwise leave empty beside the
   taller right-hand column. flex:1 makes it absorb whatever is left over, so
   the two columns finish level at any width. */
.join {
  flex: 1;
  border: 1px solid var(--line); border-radius: 10px; background: var(--bg-card);
  padding: 24px; display: flex; flex-direction: column; justify-content: center;
  gap: 16px;
}
.join .body-sm { max-width: 46ch; }
.join__actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 4px; }

@media (max-width: 900px) { .join { padding: 20px; } }


@media (max-width: 900px) { .shots { grid-template-columns: 1fr; } }

/* ---------- membership ---------- */

.member { display: flex; justify-content: center; }
.member .bone { width: 560px; max-width: 100%; }
.member__list { display: grid; gap: 16px; }
.member__item { font-size: 14px; line-height: 1.43; color: var(--fg-dim); display: flex; gap: 8px; }
.member__item .dash { color: var(--fg-dim); font-family: var(--mono); }
.member__actions { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
.link-ink { font-size: 14px; color: var(--fg-dim); border-bottom: 1px solid var(--fg-2); padding-bottom: 4px; }
.link-ink:hover { color: var(--bg); border-color: var(--bg); }

/* ---------- footer ---------- */

.foot { border-top: 1px solid var(--line); }
.foot__grid { max-width: var(--maxw-wide); margin: 0 auto; padding: 96px clamp(24px, 3.4vw, 48px) 56px; display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 32px; }
.foot__brand { display: flex; flex-direction: column; gap: 24px; }
.foot__wordmark { font-family: var(--mono); font-size: 12px; font-weight: 500; letter-spacing: .18em; text-transform: uppercase; }
.foot__status { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: -.02em; color: var(--fg-quiet); display: flex; align-items: center; gap: 8px; }
.foot__col { display: flex; flex-direction: column; gap: 16px; }
.foot__head { font-family: var(--mono); font-size: 12px; font-weight: 500; text-transform: uppercase; letter-spacing: -.02em; }
.foot__col a { font-size: 14px; color: var(--fg-3); }
.foot__col a:hover { color: var(--fg); }
.foot__legal { max-width: var(--maxw-wide); margin: 0 auto; padding: 0 clamp(24px, 3.4vw, 48px) 56px; }
.foot__legal p {
  font-size: 14px; line-height: 1.5; color: var(--fg-quiet);
  margin: 0; max-width: 84ch;
}

@media (max-width: 900px) { .foot__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .foot__grid { grid-template-columns: 1fr; } }

/* Mobile overrides live at the END of the sheet on purpose. The earlier
   @media block sits above the .term rules, and at equal specificity the
   later rule wins -- so the desktop bleed was beating it and pushing the
   frame off the side of a phone. */
@media (max-width: 900px) {
  .term { width: 100%; }
}


/* ---------- the venue stripe ---------- */

/* The same device as the app's gainers ticker: one row of marks running
   right to left on a loop, with no rule under it. It sits inside the hero,
   directly under the capture, so the hero ends on movement rather than on
   a border.

   Full-bleed rather than held to the content track -- a stripe that stops
   short of the edges reads as a list that happens to slide. */
.chains {
  margin-top: clamp(40px, 4.4vw, 64px);
  padding: 0;
  overflow: hidden;
  /* Faded at both ends so marks arrive and leave rather than popping
     against a hard cut. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}

.marquee { display: flex; align-items: center; width: max-content; }
/* The class only goes on once the second copy has been cloned in. Until
   then -- and with JS off -- the row simply sits still. */
.marquee--ready { animation: stripe-run 52s linear infinite; will-change: transform; }
/* No pause on hover. The app's ticker pauses because you click coins in
   it; these marks are not interactive, so all the pause did was stop the
   stripe dead whenever the cursor happened to rest near it -- which reads
   exactly like the loop breaking. */

/* The track carries the same list twice, so -50% lands exactly on the
   start of the second copy and the loop repeats with no seam to see. */
@keyframes stripe-run {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

.chains__list {
  list-style: none; margin: 0; padding: 0;
  flex: none;
  display: flex; align-items: center;
  gap: clamp(40px, 4.6vw, 72px);
  padding-inline-end: clamp(40px, 4.6vw, 72px);
}
.chain {
  display: flex; align-items: center; gap: 12px;
  color: var(--color-paper);
  flex: none;
}
/* Sized off the wordmark so mark and name stay optically level. */
.chain svg { width: 28px; height: 28px; flex: none; fill: currentColor; display: block; }
.chain span {
  font-size: 20px; line-height: 1;
  letter-spacing: -.02em; white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .marquee--ready { animation: none; }
}


/* Footer lockup: bigger than the header's, quieter, since it is a sign-off
   rather than navigation. */
/* Same flattening as the header lockup -- a white mark up top and an
   orange one in the footer would just look like an oversight. */
.foot__logo {
  width: 148px; height: auto; display: block; margin-bottom: 12px;
  filter: brightness(0) invert(1); opacity: .75;
}

/* ============================================================
   THE ROOMS
   One band per room: what it is on one side, the real screen on
   the other, sides alternating down the page. Bands rather than a
   grid of cards -- the screenshots are the argument, and a card
   grid would shrink all eight to thumbnails that prove nothing.
   ============================================================ */

/* Centred: the section opener addresses the whole page rather than the
   left column, and the bands underneath already carry the left edge. */
/* Sits higher than the section's own 96px top padding would put it, with
   the space moved to the other side -- the opener belongs near the stripe
   above it, and the gap before Terminal is what separates the intro from
   the first room. */
.rooms { padding-top: clamp(40px, 4.4vw, 64px); }
/* ---------- the figures ---------- */

.figures {
  max-width: var(--maxw-wide); margin: 0 auto;
  /* The band closes itself rather than leaning on the rooms' top padding
     for the gap -- with none of its own, the numbers sat right on top of
     the section opener below. */
  padding: clamp(72px, 7.6vw, 112px) clamp(24px, 3.4vw, 48px) clamp(56px, 6vw, 88px);
}
/* Hairline dividers drawn by the grid's own gap over a line-coloured
   background, so the cells share one continuous rule instead of each
   carrying a border that doubles up against its neighbour. */
.figures__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px; background: var(--line);
  box-shadow: var(--shadow-subtle);
  border-radius: var(--radius-cards);
  overflow: hidden;
}
.figure {
  background: var(--bg);
  padding: clamp(24px, 2.4vw, 32px);
  display: grid; gap: 12px; align-content: start;
}
.figure__n {
  font-size: clamp(32px, 3.4vw, 48px); line-height: 1;
  letter-spacing: -.022em; font-weight: 510; color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.figure__l {
  font-size: 14px; line-height: 1.45; letter-spacing: -.011em;
  color: var(--fg-3);
}

@media (max-width: 900px) {
  .figures__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 520px) {
  .figures__grid { grid-template-columns: 1fr; }
}

.rooms__head {
  margin: 0 auto clamp(56px, 6.4vw, 96px);
  display: grid; gap: 18px; justify-items: center;
  text-align: center;
}
/* The 62ch cap used to sit on this block, measured at the 16px body size
   -- about 450px -- which was squeezing a 48px heading onto two lines.
   The measure belongs on the paragraph, which is the only thing here that
   needs one. */
.rooms__head .lede { max-width: 56ch; }
/* Drawn as a grid item, so justify-items:center already places it. */
.rooms__head::after {
  content: ""; width: min(100%, 420px); height: 1px;
  background: var(--line); margin-top: clamp(12px, 1.6vw, 22px);
}
.rooms__head .h-loop { max-width: none; }
@media (min-width: 901px) {
  /* One line. Below this the clamp has shrunk the heading enough that it
     fits anyway, and forcing it would overflow a phone. */
  .rooms__head .h-loop { white-space: nowrap; }
}

.room {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 4.4vw, 68px);
  align-items: center;
  padding-block: clamp(44px, 5.2vw, 80px);
  border-top: 1px solid var(--line);
}
/* The opener carries its own short rule; a full-width one here as well
   would draw the same separator twice. */
.room:first-of-type { border-top: 0; }

/* Alternating sides. Reordering alone was not enough: the columns are
   asymmetric, so moving the copy to second put the SCREENSHOT in the
   narrow 432px track and every flipped room rendered its capture two
   thirds the size of the one above it. The template mirrors too, so the
   image keeps the wide track whichever side it sits on. */
/* Equal columns, so the capture stops towering over the words that
   explain it: at 30rem/1fr the panel ran 488px against 368px of
   copy. The flip only reorders now. */
.room--flip .room__copy { order: 2; }

.room__copy { display: grid; gap: 16px; align-content: start; }
.h-room {
  font-size: clamp(24px, 2.4vw, 32px); line-height: 1.13; letter-spacing: -.022em;
  font-weight: 510; margin: 0; color: var(--fg);
}
.body-md {
  font-size: 16px; line-height: 1.5; color: var(--fg-3);
  margin: 0; max-width: 58ch; text-wrap: pretty;
}
.body-md strong { color: var(--fg-2); font-weight: 590; }
.body-md em { font-style: normal; color: var(--fg-2); }

/* The concrete numbers, set in the data face because that is what they
   are. Not a bullet list of features -- three facts that can be checked. */
.facts {
  list-style: none; margin: 6px 0 0; padding: 0;
  display: grid; gap: 9px;
  border-top: 1px solid var(--line); padding-top: 16px;
}
.facts li {
  font-family: var(--mono); font-size: 13px; line-height: 1.4;
  color: var(--fg-3);
  padding-inline-start: 18px; position: relative;
}
.facts li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: .52em;
  width: 7px; height: 1px; background: var(--color-smoke);
  /* Scaled rather than widened: animating width lays the row out again
     on every frame, where a transform stays on the compositor. */
  transform-origin: left center;
}
.facts b { color: var(--fg); font-weight: 400; }

/* ---- the window frame around each capture ---- */

.frame {
  margin: 0; min-width: 0;
  border: 0; border-radius: var(--radius-cards);
  background: var(--bg-card);
  overflow: hidden;
  /* Elevation is the inset hairline, not a cast shadow -- the handoff is
     explicit that cards get no outer shadow and no glow. */
  box-shadow: var(--shadow-subtle);
}
.frame__bar {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 11px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-lift);
}
.frame__light { width: 9px; height: 9px; border-radius: 999px; flex: none; }
/* Window chrome, never product state. */
.frame__light--red   { background: #ff5f57; }
.frame__light--amber { background: #febc2e; }
.frame__light--green { background: #28c840; }
.frame__title {
  font-family: var(--mono); font-size: 12px; letter-spacing: -.02em;
  color: var(--fg-3); margin-inline-start: 6px;
}
.frame img { display: block; width: 100%; height: auto; }

@media (max-width: 900px) {
  .room { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  /* Stacked, the screen should follow the words that explain it, so the
     flip has to stop applying. */
  .room--flip .room__copy { order: 0; }
  .body-md { max-width: none; }
}

/* ============================================================
   MOTION
   One gesture, used everywhere: content rises 16px and fades in as it
   is reached, over half a second on a decelerating curve. Linear's
   motion is uniform and quiet -- it never tells you which direction a
   section came from, and nothing bounces, wipes or scales.

   That replaces a per-section entrance (the captures used to wipe in
   from whichever side they sat on, with a clip-path). It read as
   choreography, and choreography is the opposite of this system.
   ============================================================ */

/* Everything the reader touches sits above the hero floor.

   .head is deliberately not in this list. It declares position:sticky
   further up the sheet, and naming it here re-declared it as relative --
   same specificity, later rule wins - which quietly un-stuck the nav.
   It keeps its own stacking, above the page rather than level with it. */
main, .foot { position: relative; z-index: 1; }

/* ---- the nav ----
   Transparent over the canvas at the top of the page, and only when the
   page has moved does it earn a surface and a hairline under it. */
.head {
  background: transparent; border-bottom-color: transparent;
  transition: background-color .3s var(--ease), border-color .3s var(--ease);
}
.head--scrolled {
  background: rgba(8, 9, 10, .72);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom-color: var(--line);
}

/* ---- the reveal ----
   Default is the finished state: with no JS, an old browser or a
   crawler, the page is simply the page. .js-motion only goes on once the
   observer is actually running. */
.js-motion [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity .5s cubic-bezier(.16, 1, .3, 1) var(--reveal-delay, 0s),
    transform .5s cubic-bezier(.16, 1, .3, 1) var(--reveal-delay, 0s);
}
.js-motion [data-reveal].is-in { opacity: 1; transform: none; }

/* ---- hover ----
   The capture lifts on a brighter edge rather than a heavier shadow,
   which is how this system shows elevation everywhere else. */
.room .frame {
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.room .frame:hover {
  transform: translateY(-3px);
  box-shadow: rgb(56, 59, 63) 0 0 0 1px inset, var(--shadow-sm);
}

/* The rule under each fact draws itself across on hover. Scaled rather
   than widened: animating width lays the row out again every frame. */
.facts li::before { transition: transform .3s var(--ease); }
.room:hover .facts li::before { transform: scaleX(1.9); }

@media (prefers-reduced-motion: reduce) {
  .js-motion [data-reveal] { opacity: 1; transform: none; transition: none; }
  .head { transition: none; }
  .room .frame, .facts li::before { transition: none; }
  .room .frame:hover { transform: none; }
}
