/* ============================================================
   FBGM24 — components.css
   Hero, cards, data surfaces, charts, tables, tiers, tools, FAQ.
   ============================================================ */

@layer components;

@layer components {

/* ---------------- Hero ---------------- */
.hero {
  position: relative;
  display: grid;
  align-content: center;
  padding-block: clamp(2.2rem, 5vh, 4rem) clamp(1.6rem, 3.5vh, 3rem);
}
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
@media (max-width: 980px) { .hero-grid { grid-template-columns: 1fr; } }

.hero h1 { margin-block: 0.9rem 1.2rem; }
.accent:not(:has(.split-word)), .accent-word {
  background: linear-gradient(96deg, var(--ice-200), var(--ice-400) 40%, var(--sig-400));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 1.6rem; }

.hero-meta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, max-content)); gap: 1.4rem 2.2rem;
  margin-top: clamp(1.6rem, 3vw, 2.4rem);
  padding-top: 1.3rem;
  border-top: 1px solid var(--hairline);
}
.hero-meta div { display: grid; gap: 0.18rem; }
.hero-meta dt, .hero-meta .k {
  font-family: var(--font-mono); font-size: var(--step--2);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg-faint);
}
.hero-meta .v { font-family: var(--font-display); font-size: var(--step-2); color: var(--paper-100); letter-spacing: -0.03em; }

/* ---------------- Decorative section media ----------------------
   Footage on a black plate, composited with `screen` so the plate
   disappears and only the object floats over the page.
------------------------------------------------------------------ */
.band.has-sec-bg { position: relative; }
.band.has-sec-bg > .shell,
.band.has-sec-bg > .shell-narrow { position: relative; z-index: 1; }

.sec-bg {
  position: absolute;
  /* Bleeds upward by the whole inter-section gap. The clip belongs to the
     seam between two bands — that gap is the empty space it is meant to
     occupy — so the frame it lives in has to reach into it. --sec-rise buys
     the extra headroom the double-size object needs: scaling it from its top
     edge alone would walk its centre of mass down onto the card grid, so the
     frame is lifted by roughly half the growth plus the shoulder's own
     offset, which puts the object back above where it used to sit. */
  --sec-rise: clamp(150px, 23.7vw, 360px);
  top: calc(var(--flow) * -1 - var(--sec-rise));
  left: 0;
  right: 0;
  bottom: auto;
  height: calc(100% + var(--flow) + var(--sec-rise));
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
/* Sits in the open shoulder beside the section heading. Anchored to the
   shell's own right edge rather than to a share of the viewport: a plain
   percentage keeps drifting out into the dead margin on wide screens, and
   bleeding it off-canvas (as `right: -2%` did) lets the viewport slice a
   piece off it. The max() keeps the 5% inset once the shell stops growing. */
.sec-bg video {
  position: absolute;
  top: 0;
  /* The object is parked in the pocket the layout leaves open between the end
     of the section above and the first card row: past the right edge of the
     last quote in that row, and clear of the grid below. The frame carries a
     wide skirt of empty plate on its left, so anchoring it a little OUTSIDE
     the shell rather than inside is what puts the object itself — not its
     padding — in that pocket. */
  right: max(1rem, calc((100% - var(--shell)) / 2 - 3.5rem));
  /* Sized off the viewport, not off the frame it sits in: the frame now
     carries --sec-rise, so a percentage here would compound with it. */
  height: min(62vw, 900px);
  width: auto;
  max-width: none;
  /* The supplier's payment-provider artwork is burned into the face of the
     highlighted card for half the loop, top and bottom, so no crop clears it.
     Blur is the only treatment that keeps the object and kills the lettering —
     the same reason the head film carried one. */
  filter: blur(5px) saturate(1.05);
  mix-blend-mode: screen;
  opacity: 0.55;
  /* Warm pastel source pulled into the site's blue register. */
  filter: hue-rotate(198deg) saturate(0.8) brightness(0.95);
  mask-image: radial-gradient(closest-side, #000 58%, transparent 100%);
  -webkit-mask-image: radial-gradient(closest-side, #000 58%, transparent 100%);
}
.sec-bg[data-side='left'] video { left: max(1rem, calc((100% - var(--shell)) / 2 - 3.5rem)); right: auto; transform: scaleX(-1); }
.sec-bg[data-side='center'] video { left: 50%; right: auto; transform: translateX(-50%); opacity: 0.36; }

/* Between 1101 and 1365 the quote board above runs four to a row, so its last
   row is full width and the shoulder the object parks in on a wide screen does
   not exist. All that is left is the horizontal strip between that row and the
   card grid — about 265px — so the object is sized to clear it and lifted less,
   since a smaller object needs less lift to sit in the same strip. */
@media (min-width: 1101px) and (max-width: 1365px) {
  .sec-bg { --sec-rise: 9vw; }
  .sec-bg video { height: min(41vw, 560px); }
}
@media (max-width: 1100px) { .sec-bg video { opacity: 0.32; height: min(56vw, 660px); right: var(--gutter); } }
/* Phones have no shoulder beside the heading — the grid collapses to one
   column and the copy runs the full width — so the clip takes the seam
   instead of being dropped. The band lends it a little extra top padding,
   the clip hangs in that lane offset to the right, and the radial mask puts
   its bright core clear of the headline. Width-driven here, not
   height-driven: the frame is 1.4:1, so sizing by height overflows a phone. */
@media (max-width: 700px) {
  .band.has-sec-bg { padding-top: calc(var(--flow) * 0.5 + 3.2rem); }
  /* No shoulder to grow into on a phone, so the object keeps its old size and
     its old seam: the rise would only push it up into the section above. */
  .sec-bg { --sec-rise: 0px; }
  .sec-bg video,
  .sec-bg[data-side='left'] video,
  .sec-bg[data-side='center'] video {
    top: 0;
    left: auto;
    /* Flush to the viewport edge, never past it: the mask is an *ellipse*
       (closest-side with no shape keyword), so it only reaches zero exactly
       at the box edge — bleed the box and the frame gets cut off square. */
    right: 0;
    width: min(68vw, 290px);
    height: auto;
    transform: none;
    opacity: 0.46;
  }
}
@media (prefers-reduced-motion: reduce) { .sec-bg video { display: none; } }

/* A single line the page cannot afford to have skipped, carried inside the
   page head rather than in a band of its own. It used to be a `note` card in
   its own section, which read as a second opening after the first one; here
   it is the tail of the head's own copy — same column, same rhythm — marked
   by a rule and a mono label instead of by a box. */
.head-callout {
  margin-top: clamp(1.1rem, 2.4vw, 1.7rem);
  padding-left: clamp(0.9rem, 1.6vw, 1.25rem);
  border-left: 2px solid var(--ice-400);
  max-width: 62ch;
  color: var(--fg);
  font-size: var(--step-0);
}
.head-callout b {
  display: block;
  margin-bottom: 0.3rem;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ice-400);
}

/* ---------------- Rotating headline tail ---------------- */
.rot {
  display: inline-block;
  text-align: left;
  vertical-align: top;
  transition: opacity 0.36s var(--ease-out), transform 0.36s var(--ease-out), filter 0.36s var(--ease-out);
}
.rot.is-out { opacity: 0; transform: translate3d(0, -0.28em, 0); filter: blur(6px); }
.rot.is-in-fresh { animation: rot-in 0.6s var(--ease-out) both; }
@keyframes rot-in {
  from { opacity: 0; transform: translate3d(0, 0.34em, 0); filter: blur(7px); }
  to   { opacity: 1; transform: none; filter: none; }
}
@media (prefers-reduced-motion: reduce) { .rot, .rot.is-in-fresh { animation: none; transition: none; } }

/* ---------------- Hero with full-bleed background video ----------
   The clip covers the whole hero band and is faded out on every edge so
   it dissolves into the page field instead of reading as a boxed panel.
------------------------------------------------------------------- */
.hero.has-bg { min-height: clamp(620px, 90vh, 960px); align-content: center; }
.hero.has-bg .hero-grid { grid-template-columns: minmax(0, 1fr); }
.hero.has-bg h1 { max-width: 17ch; }
.hero.has-bg .lede { max-width: 46ch; }
.hero.has-bg .hero-meta { max-width: 46rem; }

.hero-bg {
  position: absolute;
  inset: -80px 0 0 0;           /* run under the floating header */
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
/* Opaque plate under the clip: the engineering grid and light wells of the
   page field must not read through the footage. It fades out at the very
   bottom so the hero still dissolves into the rest of the page. */
.hero-bg::before {
  content: '';
  position: absolute;
  inset: 0;
  background: #02060f;
  mask-image: linear-gradient(to bottom, #000 0, #000 92%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 92%, transparent 100%);
}
.hero-bg video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 96% 42%;      /* push the device cluster clear of the copy */
  /* The clip is a light UI on white; knock it back so our own type stays
     the brightest thing on the screen. */
  filter: brightness(0.82) contrast(1.02) saturate(0.98);
  display: block;
  /* Soften the clip into the surrounding field: no hard rectangle edge. */
  /* Fade on every edge, with a long tail at the bottom so the clip never
     ends on a visible bright fragment. */
  /* Only the bottom edge is feathered — the sides now sit on the opaque
     plate above, so the clip can run the full width of the band. */
  /* The fade used to start at 66% and finish at 97%, which left the bottom
     third of the hero as a flat dark band with no footage in it — the clip
     died well before the section did. It now holds to 90% and dissolves right
     on the section's own edge, so the picture runs the full height. */
  mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 90%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 90%, transparent 100%);
}
@media (prefers-reduced-motion: reduce) {
  .hero-bg video { display: none; }
  .hero-bg { background: #04101f url('/assets/img/hero-poster.jpg') center / cover no-repeat; }
}

.hero-scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(2, 6, 15, 0.96) 0%, rgba(2, 6, 15, 0.93) 34%, rgba(2, 6, 15, 0.78) 48%, rgba(2, 6, 15, 0.34) 66%, rgba(2, 6, 15, 0.04) 84%, transparent 100%),
    linear-gradient(180deg, rgba(2, 6, 15, 0.7) 0%, transparent 22%, transparent 60%, rgba(2, 6, 15, 0.92) 94%),
    radial-gradient(90% 70% at 82% 42%, rgba(46, 109, 255, 0.14), transparent 72%);
}
@media (max-width: 980px) {
  /* The band used to stop at 78vh, which on a 844px phone put its floor at
     768 — and the plate above starts dissolving at 92% of its own box, so the
     footage was already giving way to the blue field about 130px before the
     fold. The first screen ended on a strip of page background instead of on
     the clip. Filling the viewport moves that dissolve below the fold: the
     opening screen is the clip and nothing else, and the hand-off to the
     field happens once you are scrolling. `svh` and not `vh` so the browser
     chrome on a phone cannot push the seam back up into view. */
  .hero.has-bg { min-height: 100svh; }
  .hero-bg video { object-position: 70% 38%; }
  /* The scrim is here to keep type legible, and on a phone the type stops
     after the stat pairs — roughly three quarters down. Holding it at 0.95 to
     the floor blacked out the last stretch of the band, which is why the clip
     looked like it ended early. It now eases back off below the copy so the
     footage carries to the bottom edge. */
  .hero-scrim { background: linear-gradient(180deg, rgba(2, 6, 15, 0.82) 0%, rgba(2, 6, 15, 0.42) 30%, rgba(2, 6, 15, 0.66) 62%, rgba(2, 6, 15, 0.5) 82%, rgba(2, 6, 15, 0.34) 100%); }
  .hero.has-bg h1, .hero.has-bg .lede { max-width: none; }
}

/* ---------------- Hero video (inline, framed) ---------------- */
.hero-video { position: relative; aspect-ratio: 16 / 10; isolation: isolate; }
.hero-video video { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-video.is-framed { border-radius: var(--radius-l); overflow: hidden; border: 1px solid var(--hairline-2); box-shadow: var(--glow), 0 40px 90px -40px rgba(0, 0, 0, 0.9); }
.hero-video.is-framed::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(2, 6, 15, 0) 60%, rgba(2, 6, 15, 0.55));
  mix-blend-mode: multiply;
}
@media (max-width: 980px) { .hero-video { aspect-ratio: 16 / 9; } }

/* ---------------- Glass surface (the only "card" primitive) ---------------- */
.pane {
  position: relative;
  border-radius: var(--radius-l);
  border: 1px solid var(--hairline);
  background: var(--surface-1);
  backdrop-filter: blur(16px) saturate(130%);
  overflow: hidden;
  isolation: isolate;
  transition: border-color 0.4s var(--ease-out), transform 0.5s var(--ease-out), background 0.4s;
}
.pane::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(
    460px 300px at calc(var(--mx, 50%) ) calc(var(--my, 0%) ),
    rgba(111, 212, 255, 0.13), transparent 68%);
  opacity: 0; transition: opacity 0.45s var(--ease-out);
}
.pane:hover { border-color: var(--hairline-2); background: var(--surface-2); }
.pane:hover::before { opacity: 1; }
.pane-pad { padding: clamp(1.35rem, 2.4vw, 2.1rem); }
.pane-lift:hover { transform: translateY(-5px); }

/* Top hairline highlight — reads as machined edge, not a border box */
.pane-edge::after {
  content: '';
  position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(180, 225, 255, 0.42) 30%, rgba(180, 225, 255, 0.42) 70%, transparent);
}

/* ---------------- Feature grids ---------------- */
.grid { display: grid; gap: clamp(0.9rem, 1.6vw, 1.5rem); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-auto { grid-template-columns: repeat(auto-fit, minmax(min(100%, 262px), 1fr)); }
@media (max-width: 980px) { .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* Asymmetric editorial grid — deliberately un-boxy */
.grid-mosaic {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(0.9rem, 1.6vw, 1.5rem);
}
.grid-mosaic > .span-4 { grid-column: span 4; }
.grid-mosaic > .span-3 { grid-column: span 3; }
.grid-mosaic > .span-2 { grid-column: span 2; }
.grid-mosaic > .span-6 { grid-column: span 6; }
@media (max-width: 900px) {
  .grid-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-mosaic > * { grid-column: span 2 !important; }
}

/* Rule-marked entries instead of glass cards. Four panes in a row read as a
   spec sheet — a table of boxes — which is the opposite of what a page of
   commitments wants. This is the page-head callout's treatment applied to a
   grid: no box, no plate, just a lit rule down the left and the copy set
   against it. The rule is a gradient rather than a flat border so it reads as
   light falling off rather than as a drawn line, and it is a pseudo-element
   rather than `border-left` so it can carry that gradient at all. */
/* `.feature.feature-rule`, not `.feature-rule`: this block sits above
   `.feature` in the file and the two carry the same specificity, so a bare
   class lost the `display` declaration to it — the cell stayed a grid, and a
   grid packed with `align-content: start` leaves no free space for the
   `margin-top: auto` below to absorb. */
.feature.feature-rule {
  position: relative;
  /* Flex, not the grid `.feature` uses, so the link can be pushed to the foot
     of its cell with `margin-top: auto`. Entries in a row hold different
     amounts of text, and with the links floating at the end of each one the
     row read as ragged; on a common baseline it reads as a row. */
  display: flex;
  flex-direction: column;
  align-content: stretch;
  gap: 0.72rem;
  padding-left: clamp(1.05rem, 1.9vw, 1.6rem);
  padding-block: 0.2rem 0.4rem;
}
.feature-rule .feature-link { margin-top: auto; padding-top: 0.35rem; }
.feature-rule::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.1rem;
  bottom: 0.1rem;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--ice-400) 0%, var(--sig-500) 55%, rgba(46, 109, 255, 0) 100%);
}
.feature-rule h4 { letter-spacing: -0.015em; }
.feature-rule p { max-width: 54ch; }
/* Same treatment on the even grids. A pill inside a ruled entry puts the box
   straight back in — one per card — so in this mode the link is type with a
   rule of its own that grows on hover, and the arrow does the pointing. */
.grid:has(.feature-rule) { gap: clamp(1.7rem, 3.2vw, 2.9rem) clamp(1.6rem, 3.4vw, 3.2rem); }

.feature-link {
  display: inline-flex;
  /* Both of these, and not `align-self` alone: a flex child is blockified, so
     the `inline-` half of `inline-flex` is dropped and the item stretches to
     the column. `align-self: start` computed correctly and still left the
     underline running the full width — `fit-content` is what actually sizes
     it to the label. */
  align-self: flex-start;
  width: fit-content;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.05em;
  color: var(--ice-300);
  padding-bottom: 0.18rem;
  border-bottom: 1px solid rgba(111, 212, 255, 0.28);
  transition: color 0.26s var(--ease-out), border-color 0.26s var(--ease-out);
}
.feature-link::after {
  content: '\2192';
  transition: transform 0.26s var(--ease-out);
}
.feature-link:hover { color: var(--paper-100); border-bottom-color: var(--ice-400); }
.feature-link:hover::after { transform: translateX(3px); }

/* The mosaic spans exist to stop four equal boxes reading as a table. Without
   boxes there is nothing to break up, and uneven column widths just make the
   measure jump from one entry to the next — so a ruled grid is even.

   Wider gaps than the boxed grid: a pane's own padding is what separates one
   card from the next, and with the panes gone that space has to come from the
   grid or the entries run together. The `!important` is not decoration — the
   mosaic forces `grid-column: span 2 !important` below 900px, and without
   matching it every ruled entry spans both tracks. */
.grid-mosaic:has(.feature-rule) {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.7rem, 3.2vw, 2.9rem) clamp(1.6rem, 4vw, 4rem);
}
.grid-mosaic > .feature-rule { grid-column: auto !important; }
@media (max-width: 760px) {
  .grid-mosaic:has(.feature-rule) { grid-template-columns: minmax(0, 1fr); }
}

.feature { display: grid; gap: 0.72rem; align-content: start; }
.feature h4 { color: var(--paper-100); }
.feature p { font-size: var(--step--1); color: var(--fg-dim); line-height: 1.66; }

.feature-idx {
  font-family: var(--font-mono); font-size: var(--step--2);
  color: var(--ice-400); letter-spacing: 0.16em;
}

/* ---------------- Stat strip ---------------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr)); gap: 1px; background: var(--hairline); border-block: 1px solid var(--hairline); }
.stat { padding: clamp(1.4rem, 2.6vw, 2.3rem) clamp(1rem, 2vw, 1.8rem); background: rgba(3, 8, 18, 0.42); backdrop-filter: blur(10px); display: grid; gap: 0.35rem; transition: background 0.4s; }
.stat:hover { background: rgba(12, 32, 62, 0.6); }
.stat .v { font-family: var(--font-display); font-size: var(--step-3); letter-spacing: -0.04em; color: var(--paper-100); line-height: 1; }
.stat .v em { color: var(--ice-400); font-style: normal; }
.stat .k { font-size: var(--step--2); color: var(--fg-faint); letter-spacing: 0.06em; }
/* Phones: the auto-fit track bottoms out at one column, so five figures became
   five near-full-height rows and the strip ran a thousand pixels deep. Two to a
   row at a smaller step keeps every figure legible in about a fifth of that; an
   odd last one takes the full width rather than leaving a hole. */
@media (max-width: 640px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat { padding: 0.95rem 1.05rem; gap: 0.2rem; }
  .stat .v { font-size: var(--step-1); }
  .stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* ---------------- Market widgets ---------------- */
.quote-board { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 232px), 1fr)); gap: clamp(0.7rem, 1.3vw, 1.1rem); }
.quote {
  position: relative;
  padding: 1.1rem 1.15rem 0.55rem;
  border-radius: var(--radius-m);
  border: 1px solid var(--hairline);
  background: var(--surface-1);
  backdrop-filter: blur(14px);
  overflow: hidden;
  transition: border-color 0.35s, transform 0.4s var(--ease-out), background 0.35s;
}
.quote:hover { border-color: var(--hairline-2); transform: translateY(-4px); background: var(--surface-2); }
.quote-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; }
.quote-sym { font-family: var(--font-display); font-size: var(--step-0); font-weight: 600; letter-spacing: -0.01em; color: var(--paper-100); }
.quote-name { font-size: var(--step--2); color: var(--fg-faint); }
.quote-px { font-family: var(--font-mono); font-size: var(--step-1); color: var(--paper-100); margin-top: 0.5rem; font-variant-numeric: tabular-nums; transition: color 0.6s; }
.quote-px.flash-up { color: var(--up); }
.quote-px.flash-down { color: var(--down); }
.quote-chg { font-family: var(--font-mono); font-size: var(--step--2); margin-top: 0.1rem; }
.quote-chg[data-dir='up'] { color: var(--up); }
.quote-chg[data-dir='down'] { color: var(--down); }
.quote-spark { width: 100%; height: 46px; margin-top: 0.45rem; display: block; }
.quote-badge {
  position: absolute; top: 0.85rem; right: 0.9rem;
  font-family: var(--font-mono); font-size: 0.58rem; letter-spacing: 0.14em;
  padding: 0.14rem 0.44rem; border-radius: 999px;
  border: 1px solid var(--hairline); color: var(--fg-faint);
}

/* Live pulse dot */
.live-dot { display: inline-flex; align-items: center; gap: 0.45rem; font-family: var(--font-mono); font-size: var(--step--2); color: var(--fg-faint); letter-spacing: 0.1em; }
.live-dot::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--up); box-shadow: 0 0 0 0 rgba(62, 232, 165, 0.6);
  animation: pulse 2.4s var(--ease-out) infinite;
}
@keyframes pulse { 70% { box-shadow: 0 0 0 9px rgba(62, 232, 165, 0); } 100% { box-shadow: 0 0 0 0 rgba(62, 232, 165, 0); } }

/* ---------------- Chart shell ---------------- */
.chart-shell {
  border-radius: var(--radius-l);
  border: 1px solid var(--hairline);
  background: linear-gradient(175deg, rgba(10, 24, 48, 0.5), rgba(3, 8, 18, 0.62));
  backdrop-filter: blur(20px);
  overflow: hidden;
}
.chart-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 1rem;
  padding: 0.9rem 1.15rem;
  border-bottom: 1px solid var(--hairline);
}
.chart-title { display: grid; gap: 0.1rem; margin-right: auto; }
.chart-title b { font-family: var(--font-display); font-size: var(--step-0); letter-spacing: -0.02em; }
.chart-title span { font-size: var(--step--2); color: var(--fg-faint); }
.seg { display: inline-flex; padding: 2px; border: 1px solid var(--hairline); border-radius: 999px; background: var(--surface-1); }
.seg button {
  padding: 0.32rem 0.72rem; border-radius: 999px;
  font-family: var(--font-mono); font-size: var(--step--2); letter-spacing: 0.06em;
  color: var(--fg-faint); transition: background 0.24s, color 0.24s;
}
.seg button[aria-pressed='true'] { background: var(--surface-3); color: var(--paper-100); }
.seg button:hover { color: var(--paper-100); }
.chart-canvas-wrap { position: relative; height: clamp(280px, 44vh, 480px); }
.chart-canvas-wrap canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.chart-tip {
  position: absolute; pointer-events: none; z-index: 3;
  padding: 0.55rem 0.7rem; border-radius: 10px;
  border: 1px solid var(--hairline-2);
  background: rgba(4, 10, 22, 0.94);
  font-family: var(--font-mono); font-size: var(--step--2); line-height: 1.6;
  opacity: 0; transition: opacity 0.18s; white-space: nowrap;
}
.chart-tip.on { opacity: 1; }
.chart-foot {
  display: flex; flex-wrap: wrap; gap: 0.8rem 1.6rem; align-items: center;
  padding: 0.75rem 1.15rem; border-top: 1px solid var(--hairline);
  font-family: var(--font-mono); font-size: var(--step--2); color: var(--fg-faint);
}
.chart-status[data-state='live'] { color: var(--up); }
.chart-status[data-state='error'] { color: var(--warn); }

/* Embedded third-party chart frames blend into the field.
   The frame owns the height: the provider's container chain and its iframe
   stretch to fill it, so the embed always reaches the bottom hairline
   instead of leaving a gap where its own preset height ran out. */
.tv-frame { position: relative; display: flex; border-radius: var(--radius-l); overflow: hidden; border: 1px solid var(--hairline); background: rgba(3, 8, 18, 0.5); }
.tv-frame > .tradingview-widget-container { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.tv-frame .tradingview-widget-container__widget { flex: 1 1 auto; min-height: 0; }
.tv-frame iframe { display: block; border: 0; width: 100% !important; height: 100% !important; }

/* ---------------- Data tables ---------------- */
.table-wrap { overflow-x: auto; border-radius: var(--radius-l); border: 1px solid var(--hairline); background: rgba(4, 10, 22, 0.42); backdrop-filter: blur(16px); }
.table-wrap table { min-width: 640px; font-size: var(--step--1); }
/* Every cell carries its own edge, so the table reads as a grid rather than as
   a stack of rows. The borders sit on the cells (not on the row) and the last
   column and last row drop theirs, so nothing doubles up against the rounded
   container edge. border-collapse from the reset keeps them a single hairline. */
.table-wrap th, .table-wrap td {
  padding: 0.85rem 1.05rem; text-align: left;
  border-right: 1px solid rgba(150, 195, 255, 0.06);
  border-bottom: 1px solid rgba(150, 195, 255, 0.06);
}
.table-wrap th:last-child, .table-wrap td:last-child { border-right: 0; }
.table-wrap tbody tr:last-child td { border-bottom: 0; }
.table-wrap thead th {
  position: sticky; top: 0; z-index: 2;
  font-family: var(--font-mono); font-size: var(--step--2); font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ice-400);
  background: rgba(6, 16, 32, 0.94);
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}
.table-wrap tbody tr { transition: background 0.24s; }
.table-wrap tbody tr:hover { background: rgba(46, 109, 255, 0.07); }
.table-wrap td:first-child { color: var(--paper-100); font-weight: 500; }
.table-wrap .num { color: var(--paper-200); }
.table-filter {
  display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center;
  padding: 0.85rem 1.05rem; border-bottom: 1px solid var(--hairline);
}
.field-input {
  flex: 1 1 190px; min-width: 0;
  padding: 0.55rem 0.9rem; border-radius: 999px;
  border: 1px solid var(--hairline); background: var(--surface-1);
  font-size: var(--step--1); color: var(--paper-100);
  transition: border-color 0.26s, background 0.26s;
}
.field-input::placeholder { color: var(--fg-faint); }
.field-input:focus { border-color: var(--ice-400); background: var(--surface-2); outline: none; }
select.field-input { flex: 0 0 auto; appearance: none; padding-right: 2.2rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-faint) 50%), linear-gradient(135deg, var(--fg-faint) 50%, transparent 50%);
  background-position: calc(100% - 17px) 52%, calc(100% - 12px) 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
select.field-input option { background: #071527; color: #e8f1ff; }

/* ---------------- Account tiers ---------------- */
.tier-rail { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(0.8rem, 1.4vw, 1.25rem); align-items: start; }
@media (max-width: 1000px) { .tier-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .tier-rail { grid-template-columns: 1fr; } }
.tier {
  position: relative;
  display: grid;
  gap: 1rem;
  padding: 1.6rem 1.5rem 1.7rem;
  border-radius: var(--radius-l);
  border: 1px solid var(--hairline);
  background: linear-gradient(180deg, rgba(255,255,255,0.045), rgba(255,255,255,0.012));
  backdrop-filter: blur(18px);
  overflow: hidden;
  transition: transform 0.5s var(--ease-out), border-color 0.4s, box-shadow 0.5s;
}
.tier:hover { transform: translateY(-6px); border-color: var(--hairline-2); box-shadow: var(--glow); }
.tier[data-featured='true'] {
  border-color: rgba(111, 212, 255, 0.4);
  background: linear-gradient(180deg, rgba(46,109,255,0.16), rgba(111,212,255,0.05) 45%, rgba(255,255,255,0.015));
  box-shadow: var(--glow);
}
.tier-flag {
  position: absolute; top: 0; right: 0;
  padding: 0.3rem 0.85rem 0.34rem;
  border-bottom-left-radius: 12px;
  background: linear-gradient(110deg, var(--ice-300), var(--sig-500));
  color: #02060f;
  font-family: var(--font-mono); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.tier-name { font-family: var(--font-display); font-size: var(--step-1); letter-spacing: -0.025em; color: var(--paper-100); }
.tier-sub { font-size: var(--step--2); color: var(--fg-faint); line-height: 1.55; min-height: 2.6em; }
.tier-price { display: flex; align-items: baseline; gap: 0.4rem; }
.tier-price .amount { font-family: var(--font-display); font-size: var(--step-3); letter-spacing: -0.045em; color: var(--paper-100); line-height: 1; }
.tier-price .per { font-size: var(--step--2); color: var(--fg-faint); }
.tier ul { list-style: none; padding: 0; display: grid; gap: 0.52rem; margin: 0; }
.tier li { position: relative; padding-left: 1.55rem; font-size: var(--step--1); line-height: 1.5; color: var(--fg-dim); }
.tier li::before {
  content: ''; position: absolute; left: 0; top: 0.42em;
  width: 15px; height: 9px;
  border-left: 1.7px solid var(--ice-400); border-bottom: 1.7px solid var(--ice-400);
  transform: rotate(-45deg) scale(0.7); transform-origin: left bottom;
}
.tier li[data-off='true'] { color: var(--fg-faint); opacity: 0.62; }
.tier li[data-off='true']::before {
  border: 0; width: 11px; height: 11px; top: 0.5em; transform: none;
  background:
    linear-gradient(45deg, transparent 44%, currentColor 44%, currentColor 56%, transparent 56%),
    linear-gradient(-45deg, transparent 44%, currentColor 44%, currentColor 56%, transparent 56%);
  color: var(--fg-faint);
}

/* Comparison matrix */
.matrix-wrap { overflow-x: auto; }
.matrix { min-width: 900px; font-size: var(--step--1); }
.matrix th, .matrix td {
  padding: 0.8rem 0.9rem; text-align: center;
  border-right: 1px solid rgba(150, 195, 255, 0.07);
  border-bottom: 1px solid rgba(150, 195, 255, 0.07);
}
.matrix th:last-child, .matrix td:last-child { border-right: 0; }
.matrix tbody tr:last-child th, .matrix tbody tr:last-child td { border-bottom: 0; }
.matrix thead th {
  position: sticky; top: 0; z-index: 2;
  /* Opaque, not 95%: the columns slide *under* these cells, and the last 5%
     was enough to show the values ghosting through as the table scrolls. */
  background: #0a1728;
  font-family: var(--font-display); font-size: var(--step--1); letter-spacing: -0.01em;
  color: var(--paper-100); border-bottom: 1px solid var(--hairline);
}
.matrix tbody th {
  text-align: left; color: var(--fg-dim); font-weight: 400;
  position: sticky; left: 0; z-index: 1;
  /* Same reason as the header row, plus a tone: at rgba(4,10,22,.94) over the
     glass this column composited to #050b18 against #0d1e34 cells, so it read
     as a black slab bolted to the side of the table. */
  background: #0a1628;
  box-shadow: 12px 0 16px -12px rgba(0, 0, 0, 0.8);
  min-width: 220px;
}
/* The corner has to hold both axes, otherwise it slides out from above the
   label column the moment the table is dragged sideways. */
.matrix thead th:first-child { left: 0; z-index: 3; }
.matrix td { font-family: var(--font-mono); font-size: var(--step--2); color: var(--paper-200); }
.matrix tbody tr:hover td { background: rgba(46,109,255,0.07); }
.matrix .yes { color: var(--up); }
.matrix .no { color: var(--fg-faint); opacity: 0.6; }

/* ---------------- Steps / timeline ---------------- */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); gap: clamp(1rem, 2vw, 2rem); counter-reset: st; }
.step { position: relative; display: grid; gap: 0.6rem; padding-top: 2.6rem; }
.step::before {
  counter-increment: st; content: counter(st, decimal-leading-zero);
  position: absolute; top: 0; left: 0;
  font-family: var(--font-mono); font-size: var(--step-1);
  color: transparent; -webkit-text-stroke: 1px rgba(111, 212, 255, 0.6);
}
.step::after {
  content: ''; position: absolute; top: 1.55rem; left: 3.1rem; right: -1rem; height: 1px;
  background: linear-gradient(90deg, var(--hairline-2), transparent);
}
.step:last-child::after { display: none; }
@media (max-width: 720px) { .step::after { display: none; } }

.timeline { display: grid; gap: 0; position: relative; }
.timeline::before { content: ''; position: absolute; left: 7px; top: 8px; bottom: 8px; width: 1px; background: linear-gradient(180deg, var(--ice-400), rgba(46,109,255,0.16)); }
.tl-item { position: relative; padding: 0 0 1.9rem 2.6rem; }
.tl-item::before {
  content: ''; position: absolute; left: 0; top: 0.42em;
  width: 15px; height: 15px; border-radius: 50%;
  border: 1px solid var(--ice-400); background: var(--ink-1000);
  box-shadow: 0 0 0 4px rgba(111, 212, 255, 0.09);
}
.tl-year { font-family: var(--font-mono); font-size: var(--step--2); letter-spacing: 0.16em; color: var(--ice-400); }
.tl-item h4 { margin-block: 0.25rem 0.3rem; }
.tl-item p { font-size: var(--step--1); color: var(--fg-dim); }

/* ---------------- FAQ ---------------- */
.faq { display: grid; gap: 0.6rem; }
.faq-item {
  border-radius: var(--radius-m);
  border: 1px solid var(--hairline);
  background: var(--surface-1);
  backdrop-filter: blur(14px);
  overflow: hidden;
  transition: border-color 0.34s, background 0.34s;
}
.faq-item[open] { border-color: var(--hairline-2); background: var(--surface-2); }
.faq-q {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.1rem 1.3rem; cursor: pointer; list-style: none;
  font-family: var(--font-display); font-size: var(--step-0); font-weight: 500;
  letter-spacing: -0.015em; color: var(--paper-100);
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q .sign { position: relative; flex: 0 0 auto; width: 16px; height: 16px; }
.faq-q .sign::before, .faq-q .sign::after {
  content: ''; position: absolute; inset: 50% 0 auto 0; height: 1.5px;
  background: var(--ice-400); transition: transform 0.36s var(--ease-out);
}
.faq-q .sign::after { transform: rotate(90deg); }
.faq-item[open] .faq-q .sign::after { transform: rotate(0deg); }
.faq-a { padding: 0 1.3rem 1.3rem; color: var(--fg-dim); font-size: var(--step--1); line-height: 1.75; max-width: 82ch; }
.faq-a p + p { margin-top: 0.8em; }

/* ---------------- Tools / calculators ---------------- */
.tool { display: grid; gap: 1.2rem; }
.tool-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 165px), 1fr)); gap: 0.85rem; }
/* Both of these were inline grid-template-columns, which no media query can
   outrank — the converter kept its four columns on a phone and pushed the
   `to` field off the pane. Classes instead, so the narrow layout can win. */
.tool-grid.is-convert { grid-template-columns: 2fr 1fr auto 1fr; align-items: end; }
.tool-grid.is-pair { grid-template-columns: 1fr 1fr; }
@media (max-width: 620px) {
  /* Amount takes its own row; from / swap / to share the next one. */
  .tool-grid.is-convert { grid-template-columns: 1fr auto 1fr; }
  .tool-grid.is-convert > .tool-field:first-child { grid-column: 1 / -1; }
  .tool-grid.is-pair { grid-template-columns: 1fr; }
}
.tool-field { display: grid; gap: 0.35rem; }
.tool-field label { font-family: var(--font-mono); font-size: var(--step--2); letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.tool-field input, .tool-field select {
  padding: 0.62rem 0.85rem; border-radius: var(--radius-s);
  border: 1px solid var(--hairline); background: rgba(2, 6, 15, 0.5);
  font-family: var(--font-mono); font-size: var(--step--1); color: var(--paper-100);
  transition: border-color 0.24s, background 0.24s;
}
.tool-field input:focus, .tool-field select:focus { border-color: var(--ice-400); background: rgba(10, 24, 48, 0.6); outline: none; }
.tool-out { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); border-radius: var(--radius-m); overflow: hidden; }
.tool-out div { padding: 0.95rem 1.05rem; background: rgba(4, 10, 22, 0.72); display: grid; gap: 0.2rem; }
.tool-out .k { font-family: var(--font-mono); font-size: var(--step--2); letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.tool-out .v { font-family: var(--font-mono); font-size: var(--step-1); color: var(--ice-300); font-variant-numeric: tabular-nums; }
.range-row { display: grid; gap: 0.4rem; }
input[type='range'] { -webkit-appearance: none; appearance: none; width: 100%; height: 3px; border-radius: 999px; background: linear-gradient(90deg, var(--ice-400) var(--fill, 40%), rgba(150,195,255,0.16) var(--fill, 40%)); }
input[type='range']::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: var(--paper-100); border: 3px solid var(--sig-500); cursor: grab; }
input[type='range']::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%; background: var(--paper-100); border: 3px solid var(--sig-500); cursor: grab; }

/* ---------------- Method / payment rails ---------------- */
.rail-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: 0.9rem; }
.method { display: grid; gap: 0.55rem; padding: 1.15rem 1.2rem; border-radius: var(--radius-m); border: 1px solid var(--hairline); background: var(--surface-1); backdrop-filter: blur(12px); transition: transform 0.42s var(--ease-out), border-color 0.34s; }
.method:hover { transform: translateY(-4px); border-color: var(--hairline-2); }
.method b { font-family: var(--font-display); font-size: var(--step-0); letter-spacing: -0.015em; }
.method dl { display: grid; grid-template-columns: auto 1fr; gap: 0.28rem 0.7rem; font-size: var(--step--2); margin: 0; }
.method dt { color: var(--fg-faint); font-family: var(--font-mono); letter-spacing: 0.06em; }
.method dd { margin: 0; color: var(--paper-200); font-family: var(--font-mono); text-align: right; }

/* ---------------- Pull-quote / CTA closer ---------------- */
/* ---------------- Page-head film ----------------
   A clip behind a page head. Unlike the closing scenery this footage is an
   opaque, warm-lit scene, so `screen` is wrong twice over: it would keep the
   bright half and it would drag the band out of the site's blue. It is
   composited normally instead, pushed hard into the blue register and laid
   under an ink well that keeps the breadcrumb and the h1 on a clean ground.
   The band grows a floor so the picture has room that the copy does not sit in.
--------------------------------------------------- */
/* The frame is 1.41:1 and the handset fills 68% of its height, so `cover`
   beheads the device on any band shorter than ~0.52 x its own width. The
   band therefore holds that ratio open itself; everything below it — the
   tier cards included — simply starts lower. */
.band.has-head-film {
  position: relative;
  padding-bottom: clamp(4rem, 14vw, 10.5rem);
  min-height: min(52vw, 1000px);
}
.band.has-head-film > .shell { position: relative; z-index: 2; }
.head-film {
  position: absolute;
  /* Not `inset: 0`. The band begins ~150-185px down the page, below the rail
     and the floating header, so a film that started at the band's own top
     edge left the whole first strip of the screen painted in page background:
     a visible seam right under the menu, which is the first thing on the
     page. It now starts above the document and runs the band's full height,
     so the art is the entire first screen. The header's pills are z-index 50
     against this layer's 0, so they stay on top of it. */
  --head-bleed: 200px;
  top: calc(var(--head-bleed) * -1);
  left: 0;
  right: 0;
  height: calc(100% + var(--head-bleed));
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.head-film video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 64% 50%;
  opacity: 0.78;
  /* sepia+hue-rotate is the pair that actually moves warm footage to blue —
     hue-rotate alone barely touches near-neutral wood. The blur is not only
     depth of field: it keeps the screen in shot from being read as UI. */
  filter: blur(3px) grayscale(0.4) brightness(0.82) contrast(1.04) sepia(0.45) hue-rotate(172deg) saturate(1.45);
  /* No mask here any more. The frame fades itself — ink well included — so a
     second one on the clip would only compound it. */
}
/* A still behind the page head. It gets none of the film's colour work: that
   filter exists to drag warm footage into the palette, and this art arrives
   in the palette already, sharp and dark on the left where the copy goes. Only
   the level comes down, so the headline stays the brightest thing in the band.
   Same bottom fade as the film, so a page can swap one for the other. */
/* Both stills put their subject in the right half and leave the left dark,
   which is where the copy goes. The lede used to ride in a second column on
   that right half, straight over the subject; the heads no longer split, so
   it runs under the headline instead — held to a measure that keeps it on
   the dark side of the frame. */
.band.has-head-art .band-head { max-width: min(52ch, 56%); }
@media (max-width: 1120px) { .band.has-head-art .band-head { max-width: min(52ch, 68%); } }
/* Full width only on a phone. Between 620 and 860 the layout is the tall one
   but the column is still wide enough to run past the dark side of the frame
   and onto the subject: at 820px the lede measured 3.0:1 against the plate
   there, and no crop position fixed it because the problem was the measure,
   not the framing. Holding the constraint to 620 puts it back at 8:1. */
@media (max-width: 620px)  { .band.has-head-art .band-head { max-width: none; } }

/* The fade belongs to the whole layer, not to the picture inside it. The ink
   well below is `inset: 0` and carried no bottom fade of its own, so its
   left-hand wash stopped dead on the film's bottom edge: a hard line across
   the full width with a darker page above it and a lighter one below. Masking
   the container takes the wash out together with the art.

   The stops approximate a cosine falloff rather than ramping straight to
   zero. A straight ramp ends on a kink and the eye reads that kink as a band
   — the same trap the light wells hit, and the reason they were rewritten
   this way. */
.head-film {
  --film-fade:
    linear-gradient(180deg,
      #000 0%,
      #000 50%,
      rgba(0, 0, 0, 0.97) 60%,
      rgba(0, 0, 0, 0.9) 68%,
      rgba(0, 0, 0, 0.78) 75%,
      rgba(0, 0, 0, 0.6) 82%,
      rgba(0, 0, 0, 0.4) 88%,
      rgba(0, 0, 0, 0.22) 93%,
      rgba(0, 0, 0, 0.08) 97%,
      transparent 100%);
  mask-image: var(--film-fade);
  -webkit-mask-image: var(--film-fade);
}
/* The wide stills are ~3:1 and the band is nearer 1.5:1, so `cover` was
   pulling them up by a third of their own height to fill it — the single
   biggest source of softness here, worse than any compression setting.
   Capping the frame holds the crop closer to the art's own proportions: less
   magnification, more of the picture in shot.

   Only above the phone breakpoint. Below it the art is a portrait crop in a
   tall box, which needs no cap — and 46vw of a 390px screen is 179px, which
   cut the frame off level with the first line of copy. */
@media (min-width: 861px) {
  .head-film { max-height: calc(var(--head-bleed) + 46vw); }
}

.head-film.is-still img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 50%;
  opacity: 0.88;
  filter: brightness(0.92) saturate(1.04);
}

/* Ink well under the breadcrumb and headline; the film is composited
   normally, so this can simply sit on top of it. */
.head-film::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(2, 6, 15, 0.92) 0%, rgba(2, 6, 15, 0.6) 38%, rgba(2, 6, 15, 0.04) 70%, rgba(2, 6, 15, 0) 100%),
    linear-gradient(180deg, rgba(2, 6, 15, 0.55) 0%, rgba(2, 6, 15, 0) 38%);
}
/* The object rides in the flow, under the headline; the film behind the copy
   is a desktop-only treatment. Only one of the two is ever rendered. */
.head-object { display: none; }
@media (max-width: 860px) {
  .band.has-head-film { min-height: 0; padding-bottom: 0; }
  .head-film { display: none; }

  /* The film is dropped on a phone because a full-bleed clip behind copy at
     this width is unreadable and costs a video decode; a still is neither, so
     it stays. The 900px variant is 47KB. The crop moves right and up: the
     subject sits in the right half of the frame and a narrow `cover` box
     would otherwise centre on empty plate. */
  /* The desktop ink well is a left-to-right wash: the copy occupies the left
     half of a wide band and the art the right, so the two never meet. A phone
     has one column, so that wash leaves the end of every line sitting on the
     bull. Here the band instead reserves a strip of its own below the copy and
     the scrim runs top-to-bottom: solid where the words are, clearing where
     the picture is. */
  .band.has-head-art { min-height: 0; padding-bottom: clamp(11rem, 46vw, 17rem); }
  .band.has-head-art .head-film { display: block; }
  .head-film.is-still img { opacity: 1; filter: brightness(1.04) saturate(1.06); }
  /* This used to hold 0.92-0.95 down to 42%, which is exactly where the
     subject's head sits — it was scrimmed out of existence while the legs
     below read fine. Pulled back to the least that still carries the copy:
     the headline is 3rem white on this, and the lede has the densest part of
     the ramp behind it. */
  /* Not a plain top-down ramp: it thins at the very top and thickens again
     through the middle. The two stills need opposite things in those two
     places. The bull's head sits in the top third and was being scrimmed out
     of existence at 0.92; the commodities gold sits behind the lede, and
     measured against that copy the worst tenth of the plate came back at
     1.7:1 — unreadable. Light where a subject needs to show, dense where the
     words are, clear below both. */
  .head-film::after {
    background: linear-gradient(
      180deg,
      rgba(2, 6, 15, 0.7) 0%,
      rgba(2, 6, 15, 0.68) 24%,
      rgba(2, 6, 15, 0.8) 42%,
      rgba(2, 6, 15, 0.82) 60%,
      rgba(2, 6, 15, 0.62) 72%,
      rgba(2, 6, 15, 0.2) 86%,
      rgba(2, 6, 15, 0) 100%);
  }
  /* Centred only where the portrait crop is served. Between 620 and 860 this
     layout gets the wide file instead, and that one still needs its subject
     pulled in from the right. */
  @media (max-width: 620px) { .head-film.is-still img { object-position: 50% 50%; } }

  /* Centre of the croppable range, which is where the handset sits. The
     desktop 64% points at the subject in a wide frame; in a phone's tall
     `cover` box the same value walks it off the right edge.

     This briefly lived at 36% to keep the lit screen away from the copy, back
     when it measured 2.7:1 there. That reading was a symptom of a stale
     duplicate `::after` further down this file overriding the dense scrim —
     with that gone the centred crop measures 6.9:1, so the compromise is not
     needed and the whole device is in shot again. */
  .head-film video { object-position: 52% 50%; }

  .head-object {
    display: block;
    /* Right edge runs off the screen and the bottom edge lands exactly on the
       first tier card, so neither cut is visible: the frame ends where the
       viewport and the card already draw a line. */
    margin: clamp(1.4rem, 5vw, 2.6rem) calc(var(--gutter) * -1) calc(var(--flow) * -0.5) 0;
  }
  .head-object video {
    display: block;
    width: 100%;
    height: auto;
    /* The source carries its own alpha, so nothing needs compositing here —
       it simply floats on the page surface. */
  }
}
@media (prefers-reduced-motion: reduce) { .head-film { display: none; } }

/* ---------------- Closing scenery ----------------
   A full-bleed clip behind the last band on the page. The footage is a peak
   on a pale dawn sky — the opposite plate to the ambient object clips — so it
   cannot simply be screened in: the sky is the brightest thing in the frame
   and would wash the whole band out. It is crushed and pushed into the site's
   blue register first, so that under `screen` only the lit ridge and the
   snow survive as light and the sky falls back into the page's own ink.
   Edge to edge horizontally, so only the top and bottom need a fade.
--------------------------------------------------- */
.band.has-scape { position: relative; }
.band.has-scape > .shell { position: relative; z-index: 1; }
.scape {
  position: absolute;
  top: calc(var(--flow) * -1);
  left: 0;
  right: 0;
  bottom: calc(var(--flow) * -0.5);
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.scape video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 62%;
  mix-blend-mode: screen;
  opacity: 0.55;
  filter: brightness(0.46) contrast(1.28) saturate(0.5) hue-rotate(186deg);
  mask-image: linear-gradient(180deg, transparent 0%, #000 30%, #000 74%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 30%, #000 74%, transparent 100%);
}
/* A well of the page's own ink under the copy, painted over the clip and
   under the text. The clip is screen-blended, so nothing below it can darken
   it; this layer has to sit on top to give the centred headline a clean
   ground while leaving the ridge on the right untouched. */
.scape::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Eased to zero over the whole radius instead of stopping dead at 74%:
     that stop was a kink, and a kink in a wash this wide reads as an edge
     running across the band. Radii scaled by the old cut so the darkened
     area stays the size it was. */
  background: radial-gradient(41% 40% at 46% 54%, rgba(2, 6, 15, 0.66) 0%, rgba(2, 6, 15, 0.561) 22%, rgba(2, 6, 15, 0.363) 44%, rgba(2, 6, 15, 0.182) 64%, rgba(2, 6, 15, 0.059) 82%, rgba(2, 6, 15, 0.0) 100%);
}
/* Narrow viewports: the band turns portrait while the frame stays 1.4:1, so
   `cover` crops in to a wall of rock and the peak — the whole point of the
   shot — is gone. Show the frame whole instead, stood on the band's floor,
   and fade only its top. */
@media (max-width: 860px) {
  /* Not `object-fit: contain`: the mask is applied to the element box, and
     contain letterboxes the picture inside it, so the fade lands above the
     frame's real top edge and that edge draws as a hard grey line. Sizing the
     element itself to the frame's own ratio puts the two back in register. */
  .scape video {
    top: auto;
    bottom: 0;
    width: 100%;
    height: auto;
    /* Declared, not inherited from the file: an unloaded <video> reports the
       HTML default 300x150, so `height: auto` alone sizes the box wrong until
       metadata lands — and this clip is preload="none". */
    aspect-ratio: 1600 / 1138;
    object-fit: fill;
    opacity: 0.5;
    /* Radial, not a top fade: with the whole frame on screen the pale sky is
       most of it, and a vertical fade leaves its bottom edge as a hard line
       across the band. This keeps the peak and dissolves everything around it. */
    mask-image: radial-gradient(62% 72% at 72% 76%, #000 36%, transparent 84%);
    -webkit-mask-image: radial-gradient(62% 72% at 72% 76%, #000 36%, transparent 84%);
  }
  .scape::after {
    background: radial-gradient(74% 47% at 46% 40%, rgba(2, 6, 15, 0.74) 0%, rgba(2, 6, 15, 0.629) 22%, rgba(2, 6, 15, 0.407) 44%, rgba(2, 6, 15, 0.204) 64%, rgba(2, 6, 15, 0.067) 82%, rgba(2, 6, 15, 0.0) 100%);
  }
}
@media (prefers-reduced-motion: reduce) { .scape { display: none; } }

.closer { position: relative; text-align: center; display: grid; gap: 1.2rem; justify-items: center; padding-block: clamp(1.5rem, 4vw, 3.5rem); }
.closer h2 { max-width: 18ch; }
.closer .lede { text-align: center; }

/* No rules and no gap: the two hairlines drew the strip as a boxed band with
   a lit edge top and bottom, which on a flat ground is the most visible seam
   on the page. It now butts straight onto the same tone the hero ends on, so
   the strip reads as the dark carrying on with type running through it. */
.marquee { overflow: hidden; padding-block: 0.9rem; }
.marquee-track { display: flex; gap: 3.2rem; width: max-content; animation: rail-scroll 40s linear infinite; }
.marquee span {
  font-family: var(--font-display); font-size: var(--step-2);
  letter-spacing: -0.03em; color: transparent;
  -webkit-text-stroke: 1px rgba(150, 200, 255, 0.28);
  white-space: nowrap;
}
.marquee span:nth-child(even) { -webkit-text-stroke-color: rgba(111, 212, 255, 0.55); }

/* ---------------- Key-value spec list ---------------- */
.specs { display: grid; gap: 0; }
.spec-row {
  /* The value column was a bare `auto`, i.e. max-content: one long value (the
     language list) took the whole row and squeezed the label track to 34px,
     so the label spilled out from under it. Floor the label, and let the value
     wrap instead of claiming its full max-content width. */
  display: grid; grid-template-columns: minmax(7.5rem, 1fr) minmax(0, auto); gap: 1rem;
  padding: 0.95rem 0; border-bottom: 1px solid rgba(150,195,255,0.08);
  font-size: var(--step--1);
}
.spec-row:last-child { border-bottom: 0; }
.spec-row dt { color: var(--fg-dim); }
.spec-row dd { margin: 0; font-family: var(--font-mono); color: var(--paper-100); text-align: right; overflow-wrap: anywhere; }

/* ---------------- Notes / callouts ---------------- */
.note {
  display: grid; gap: 0.45rem;
  padding: 1.15rem 1.35rem;
  border-radius: var(--radius-m);
  border-left: 2px solid var(--ice-400);
  background: linear-gradient(90deg, rgba(111,212,255,0.08), transparent 70%);
  font-size: var(--step--1); color: var(--fg-dim);
}
.note b { color: var(--paper-100); font-family: var(--font-display); letter-spacing: -0.01em; }

.pill-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pill {
  padding: 0.34rem 0.8rem; border-radius: 999px;
  border: 1px solid var(--hairline); background: var(--surface-1);
  font-family: var(--font-mono); font-size: var(--step--2); letter-spacing: 0.05em;
  color: var(--fg-dim); transition: border-color 0.26s, color 0.26s, background 0.26s;
}
a.pill:hover, .pill:hover { border-color: var(--ice-400); color: var(--paper-100); background: var(--surface-2); }

/* ---------------- Split feature (image/visual + copy) ---------------- */
.split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.6rem, 4vw, 4.5rem); align-items: center; }
.split.is-reverse > *:first-child { order: 2; }
@media (max-width: 880px) { .split { grid-template-columns: 1fr; } .split.is-reverse > *:first-child { order: 0; } }

/* ---------------- Anchor nav (on this page) ---------------- */
.anchors { position: sticky; top: 88px; display: grid; gap: 0.35rem; align-content: start; }
.anchors b { font-family: var(--font-mono); font-size: var(--step--2); letter-spacing: 0.16em; text-transform: uppercase; color: var(--ice-400); margin-bottom: 0.5rem; }
.anchors a { font-size: var(--step--1); color: var(--fg-faint); padding: 0.3rem 0 0.3rem 0.8rem; border-left: 1px solid var(--hairline); transition: color 0.24s, border-color 0.24s; }
.anchors a:hover, .anchors a.is-active { color: var(--paper-100); border-left-color: var(--ice-400); }
.doc-layout { display: grid; grid-template-columns: 230px minmax(0, 1fr); gap: clamp(2rem, 5vw, 4.5rem); }
/* The FAQ pairs a headline column with the accordion instead of a fixed-width
   anchor rail. It has to be a class, not an inline grid-template-columns: an
   inline declaration outranks the collapse rule below, which is what kept the
   two columns side by side on a phone and clipped the long words out of the
   heading. */
.doc-layout.is-faq { grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.25fr); }
@media (max-width: 900px) {
  .doc-layout,
  .doc-layout.is-faq { grid-template-columns: 1fr; }
  .anchors { position: static; }
}

/* ---------------- Heatmap ---------------- */
.heat { display: grid; grid-template-columns: repeat(auto-fit, minmax(78px, 1fr)); gap: 3px; }
.heat-cell {
  aspect-ratio: 1.35;
  display: grid; place-content: center; gap: 0.1rem;
  border-radius: 8px;
  font-family: var(--font-mono); text-align: center;
  transition: transform 0.3s var(--ease-out), filter 0.3s;
}
.heat-cell:hover { transform: scale(1.07); z-index: 2; filter: brightness(1.25); }
.heat-cell b { font-size: var(--step--2); color: #fff; font-weight: 600; }
.heat-cell span { font-size: 0.62rem; color: rgba(255,255,255,0.82); }

/* ---------------- Misc ---------------- */
.hr-fade { height: 1px; border: 0; background: linear-gradient(90deg, transparent, var(--hairline-2), transparent); margin-block: calc(var(--flow) * 0.5); }
.badge-row { display: flex; flex-wrap: wrap; gap: 0.8rem 1.6rem; align-items: center; }
.badge {
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-family: var(--font-mono); font-size: var(--step--2);
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg-faint);
}
.badge svg { width: 16px; height: 16px; color: var(--ice-400); }
}


/* ---------------- Glass cost control ----------------
   Every backdrop-filter forces the browser to snapshot what is behind the
   element, blur it and recomposite — and behind these sits a WebGL canvas
   that never stops moving, so no snapshot is ever reusable. On a capable
   GPU that is affordable and the glass is worth it. js/app.js flags weaker
   machines with .perf-lite; there the surfaces keep their translucency and
   hairlines and simply stop blurring. Set it by hand on <html> to compare. */
.perf-lite .pane,
.perf-lite .quote,
.perf-lite .stat,
.perf-lite .chart-shell,
.perf-lite .table-wrap,
.perf-lite .tier,
.perf-lite .faq-item,
.perf-lite .method {
  backdrop-filter: none;
}
