/* World is Fair — event page grid.
 *
 * Structure and dark palette follow ponsfamily.com/launchpad. Every value
 * marked [pons] was read out of their stylesheet, not approximated:
 *   _next/static/chunks/254yg1tqykifa.css, html[data-theme=dark]
 *
 * We diverge in four places, each marked [ours]: the card surface, the measure,
 * the corner ornament on the top three, and fixed column counts instead of
 * Pons's auto-fill.
 *
 * Settled decisions, no longer switchable:
 *   surface   — the warm translucent card that was "Bloom", now the base
 *   top three — a descending laurel printed into the image corner, and
 *               nothing else: all sixty cards are otherwise identical
 * The presets, the toggle bar and the alternative rank treatments are gone.
 */

/* ==========================================================================
   The card surface
   ========================================================================== */

:root {
  /* Two values, still independent so the surface can be retuned without
   * touching anything else: what colour the white is, and how much black comes
   * through it. The tint is the engraving's own paper, sampled from the asset. */
  --card-tint: 237 234 227;
  --card-opacity: 0.16;
  --card-hover-opacity: 0.23;

  /* Ink and furniture for the base card. Alphas here are not eyeballed — they
   * are the minimum that clears 4.5:1 for text and 3:1 for the measure against
   * the resolved card colour (#31302f), with a little headroom. */
  --card-ink: #f7f5f1;
  --card-ink-muted: #f7f5f1a8;    /* handle        6.2:1 */
  --card-ink-subtle: #f7f5f18f;   /* address       4.8:1 */
  --card-border: #ffffff1f;
  /* The site's warm ink, #f4efe4, as channels so it can be tinted. Everything
   * in the G.O.A.T. band that used to be pure white is drawn from this. */
  --warm-ink: 244 239 228;
  --card-hover-border: #ffffff3d;
  /* The track the share is measured across. Warm and dark so it reads as
   * unfilled ground rather than as a second bar. 1.9:1 against the card —
   * present, but never competing with the fill on top of it. */
  --measure-track: #ffffff1a;
  /* Dimmed from #faf8f4 to make room for the travelling sheen. At 250,248,244
     the fill sat 5-11 values off pure white, so a highlight "brighter than the
     fill" had nowhere to go and was literally invisible. 234,230,222 leaves the
     sheen about 21 values to lift through, and costs almost nothing where it
     matters: 15.62:1 against the page, against a 3:1 requirement. */
  --measure-fill: #eae6de;
  /* The reference marks. Brighter than the track so they read, far dimmer than
     the fill so they never compete with it -- and they sit UNDER the fill, so a
     bar that has passed a tick simply covers it. What stays visible is the
     ticks still ahead, which is the useful half. */
  --measure-tick: #ffffff45;

  /* The corner ornament: gold, cut in.
   *
   * Measured against what it actually sits on. That corner is mostly the
   * engraving's dark hatching, not its paper — the paper frame is ~24px of a
   * 900px asset, a 5px border at card size. So:
   *
   *     ink  #1e160e   14.9:1 on paper   1.0:1 on hatching
   *     gold #ddb95e    1.6:1 on paper  10.0:1 on hatching
   *
   * Neither wins outright, and no flat colour can: the footprint spans luma 2
   * to 255. Gold takes the ground it mostly stands on, and the incised edge
   * takes the pale patches where gold alone would vanish — a dark shadow above
   * and a light catch below, which is also what makes it read as cut into the
   * plate rather than laid on it. */
  --gold: #ddb95e;
  /* Pons's own accent, read off their launchpad rather than sampled from a
     screenshot: their stylesheet names it --accent, --live-dot and --chart-line,
     all #d4fc50. A flat single value, not a gradient, so there is no mid to
     take. The only colour in this palette and it will get reused. */
  --pons-green: #d4fc50;
  --incised-shadow: #14100ae6;
  --incised-light: #fff8e59e;
}

/* --- the top three -------------------------------------------------------
 *
 * Nothing. Deliberately nothing.
 *
 * All sixty cards carry the same surface, the same ink, the same measure and
 * the same size. The only thing that separates the top three is the ornament
 * cut into the corner — and it *descends*, so first is genuinely singular
 * rather than three cards being vaguely special together:
 *
 *   first   numeral inside a closed laurel wreath
 *   second  numeral flanked by two laurel branches
 *   third   numeral alone
 *
 * Which is how period engraving did hierarchy. It also means there is no second
 * ink set to keep in contrast, no surface that changes mid-animation, and no
 * card that has to be legible two different ways.
 *
 * If you are about to add a border, a tint or a coloured measure here: that is
 * the thing that was tried and removed. */

/* ==========================================================================
   Tokens taken from Pons
   ========================================================================== */

:root {
  /* [pons] html[data-theme=dark] */
  --bg-page: #0d0d0d;
  --bg-panel: #161616;        /* their --bg-white */
  --bg-elevated: #1c1c1c;     /* the raised segment in a pill */
  --panel-fill: #1f1f1f;      /* their --panel-blue, the flat card we replace */
  --text-primary: #f2f2f2;
  --text-muted: #f2f2f294;
  --text-subtle: #f2f2f266;
  --panel-border: #ffffff1f;
  --shadow-soft: 0 4px 24px #00000059;

  /* [pons] motion */
  --duration-fast: 0.18s;
  --duration-normal: 0.28s;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);

  /* [pons] radii */
  --radius-panel: 28px;       /* .launch-explore */
  --radius-card: 20px;        /* .launch-card */
  --radius-media: 14px;       /* .launch-card-logo */
  --radius-pill: 999px;

  /* WIF Par v1.1, our own face, and now the only one that renders anything on
   * this page. The v1 file was a 49-glyph display face with no lowercase, so
   * Fraunces sat behind it rendering every lowercase letter; v1.1 adds the
   * lowercase and Fraunces is gone from the stack.
   *
   * What is left behind it is a fallback in the true sense — reached only by a
   * character the face does not contain. The set is exact:
   *
   *     A-Z  a-z  0-9  $ . , : - – / ( ) % # \' ’ @ … _  space
   *
   * Note what is NOT in it: accented characters, the em dash (– en dash only)
   * and the middle dot ·. A string containing one renders that character in
   * Georgia at Georgia's proportions, mid-word. Check new copy against the set
   * rather than against a screenshot. */
  --font-display: "WIF Par", Georgia, "Times New Roman", serif;
}

/* ==========================================================================
   The typeface
   ========================================================================== */

/* WIF Par v1.1, the variable font at FLAT=0 — the shipping instance. The axis
 * exists (0 crushes to 100) but the face was spaced, kerned and colour-checked
 * at 0 only, so 0 is what is set. Advances are identical at every axis position,
 * so animating it later cannot reflow text.
 *
 * The version string in the font's own name table still reads 0.001 in v1.1, so
 * it does not identify the build. Glyph count does: v1 is 49, v1.1 is 80. */
@font-face {
  font-family: "WIF Par";
  src: url("../static/fonts/WIFPar-VF.woff2?v=1787060778") format("woff2"),
       url("../static/fonts/WIFPar-VF.ttf?v=1787060778") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   Floating navigation — after titan.com, palette inverted
   ==========================================================================

   Read off their live page rather than approximated. What they actually do:

     outer      header, position: fixed, top 0, full width, 88px tall, z 100,
                fully transparent — it is a frame, not a surface
     pill       max-width 1280px, centred, border-radius 160px
     gutter     56px each side at >=992px, 20px below it
     at rest    no background, no blur, and a 1px INSET ring at zero alpha
     scrolled   background rgba(255,255,255,.7), backdrop-filter blur(14px),
                the same ring raised to rgba(0,0,0,.05)
     size       40px tall -> 56px, padding 0 -> 0 16px; top 24 -> 16, so the
                pill's centre stays on the same line while it grows
     trigger    a class added at scrollY ~51px
     motion     .3s cubic-bezier(.83, 0, .17, 1) on background, colour,
                box-shadow, backdrop-filter and padding
     scroll     it never hides and never translates. No hide-on-down.

   Two things worth stating because they are easy to get wrong by eye:

   THERE IS NO GRADIENT MASK. Not on the pill, not on either pseudo-element,
   not on the header. The soft edge is a translucent fill over a blur, and the
   blur sampling a light page is what reads as a gradient.

   THERE IS NO BORDER AND NO DROP SHADOW. The only edge is a 1px inset ring
   drawn with box-shadow, which is why it sits inside the radius cleanly instead
   of fighting it.

   Inverting for dark is not simply swapping the two colours. Their ring is
   black at 5% on near-white; white at 5% on near-black is invisible, because
   the eye's contrast sensitivity is not symmetric about mid-grey. The ring is
   raised to 12% and the fill to 66% so the pill separates from the artwork
   behind it by the same amount theirs separates from the page. */

.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* Well clear of everything on the page. The highest z-index anywhere else is
     the copy toast at 200; the cards, the chart canvas and the stretched card
     links are all at 2 or below. 900 is not a magic number so much as a
     statement that nothing is expected to sit above the bar. */
  z-index: 900;
  height: 88px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 56px;
  /* The frame passes clicks through; only the pill takes them. */
  pointer-events: none;

  /* The FRAME carries the background, not just the pill, and that is the fix.
     The pill is a centred 1280px box 40-56px tall inside an 88px full-width
     band; anything scrolling through the band but outside the pill -- which is
     most of it -- had nothing in front of it at all and read straight through.
     Backgrounding the frame is what makes content pass BEHIND the bar rather
     than through it.
     
     Worth recording: the reference does not do this. Pons's bar is
     rgba(10,10,10,0.06) over blur(3px), and its own price heading and range
     buttons are plainly legible through it while scrolling. Copying it would
     have reproduced the defect rather than fixed it. */
  background: rgb(13 13 13 / 0);
  transition: background 0.3s cubic-bezier(0.83, 0, 0.17, 1);
}

.topbar.is-scrolled {
  /* Opaque, not translucent. A blur alone still passes luminance through, and
     engraved white line work on near-black stays legible through a surprising
     amount of it -- which is exactly what was showing. */
  background: rgb(13 13 13);
  box-shadow: 0 1px 0 rgb(255 255 255 / 0.06);
}

.topbar-pill {
  pointer-events: auto;
  width: 100%;
  max-width: 1280px;
  height: 40px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 0;
  border-radius: 160px;
  background: transparent;
  box-shadow: inset 0 0 0 1px #ffffff00;
  transition:
    height 0.3s cubic-bezier(0.83, 0, 0.17, 1),
    padding 0.3s cubic-bezier(0.83, 0, 0.17, 1),
    background 0.3s cubic-bezier(0.83, 0, 0.17, 1),
    box-shadow 0.3s cubic-bezier(0.83, 0, 0.17, 1),
    backdrop-filter 0.3s cubic-bezier(0.83, 0, 0.17, 1);
}

.topbar.is-scrolled .topbar-pill {
  height: 56px;
  padding: 0 16px;
  /* The frame is opaque now, so the pill no longer needs to do the hiding --
     it only has to read as a raised surface on top of it. Lifted slightly
     above the frame rather than tinted darker than the page. */
  background: rgb(26 25 23);
  box-shadow: inset 0 0 0 1px #ffffff1f;      /* 12% white, their 5% black */
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 40px;                                  /* theirs */
  min-width: 0;
}

.topbar-mark {
  /* A <button> on both pages: it copies the contract address. It was an <a>
     home on kol.html and a bare <span> on the grid; the copy action cannot
     share a click with a navigation, so the mark means one thing everywhere
     now and the grid is still reachable from kol.html through the nav.

     Everything below the reset exists to make a button render exactly as the
     span did -- measured at 42.24 x 16 before the change and after it. */
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font-family: inherit;
  font-weight: inherit;
  line-height: inherit;
  text-align: inherit;
  cursor: pointer;
  text-decoration: none;
  position: relative;
  display: inline-flex;
  align-items: center;
  font-size: 16px;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  white-space: nowrap;
  font-variation-settings: "FLAT" 0;
}

/* Two pseudo-elements rather than padding on the button, because padding would
   move the wordmark and the bar's left edge is measured from it. Both are
   absolutely positioned, so neither costs a pixel of layout.

   ::before is the hover pill -- the same #ffffff0f and 160px radius the nav
   items use, at their 32px height, so the mark highlights like everything else
   in the bar rather than in a way of its own.

   ::after is the tap target. The wordmark is 42.24 x 16, which is under the
   44px minimum in both axes on a phone, and the bar is where it lives at every
   width. */
.topbar-mark::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: calc(100% + 24px);
  height: 32px;
  border-radius: 160px;
  background: transparent;
  transition: background var(--duration-fast) ease;
}
.topbar-mark:hover::before { background: #ffffff0f; }

.topbar-mark::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: calc(100% + 24px);
  min-width: 44px;
  height: 44px;
}

/* The radius paints nothing -- the button's background is transparent -- but an
   outline follows it, so the focus ring comes out as a lozenge like the rest of
   the bar's controls instead of a rectangle around the letters. */
.topbar-mark { border-radius: 160px; }
.topbar-mark:focus-visible { outline: 1px solid #ffffff59; outline-offset: 3px; }

/* Their nav items sit in a pill of their own inside the bar. Kept, because it
   is the part of the shape language that makes the bar read as Titan's rather
   than as a generic rounded header. */
.topbar-nav {
  display: flex;
  align-items: center;
  padding: 0 5px;
  border-radius: 160px;
  background: #ffffff0a;
}

.topbar-nav button,
.topbar-nav a {
  appearance: none;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
  /* The items were <button> when this was written, so nothing needed to say
     either of these. They are anchors now -- they all go somewhere -- and an
     anchor brings an underline with it, and does NOT centre its own contents
     the way a button does. Measured before this: 0px above the text and 18px
     below it in a 32px box, so the labels sat hard against the top of the
     pill. */
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  padding: 0 18px;
  border-radius: 160px;
  font-size: 14px;
  letter-spacing: 0.02em;                     /* theirs: 0.28px at 14px */
  color: var(--text-muted);
  white-space: nowrap;
  transition: color var(--duration-fast) ease, background var(--duration-fast) ease;
}

.topbar-nav button:hover,
.topbar-nav a:hover { color: var(--text-primary); background: #ffffff0f; }
.topbar-nav button:focus-visible,
.topbar-nav a:focus-visible { outline: 1px solid #ffffff59; outline-offset: 1px; }

.topbar-centre {
  margin: 0;
  font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  white-space: nowrap;
  font-variation-settings: "FLAT" 0;
}

/* Load-bearing even when empty: it is the third grid column, and the centre is
   centred by the two 1fr columns being equal rather than by anything in them.
   It now carries the outbound links, and that is unchanged either way. */
.topbar-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

/* THE OUTBOUND GROUP IS PLAIN TEXT, NOT A SECOND PILL.
 *
 * It carried the full .topbar-nav treatment at first, and the bar then read as
 * two pill containers with the wordmark loose in the gap between them -- two
 * objects rather than one bar with a nav in it. The left pill is the primary
 * nav; these are links out of the site and should not compete with it.
 *
 * The container's surface and padding come off; the ITEM rules stay, so the
 * type, size, colour, transition and focus ring are still the nav's and there
 * is no second treatment to keep in step. The hover loses its background and
 * becomes colour alone, because a hover pill on a plain link is the pill
 * arriving late. */
.topbar-nav-plain {
  background: none;
  padding: 0;
  gap: 22px;
}

.topbar-nav-plain a {
  height: auto;
  padding: 0;
  border-radius: 0;
}

/* The X mark, not a word. Sized to the cap height of the words beside it
   rather than to the 14px type size -- a 14px square glyph reads bigger than
   14px text, because the text only fills about 70% of its em. currentColor so
   it takes the link's hover and focus like the words do. */
.topbar-nav-plain svg {
  display: block;
  width: 13px;
  height: 13px;
}

.topbar-nav-plain a:hover { background: none; color: var(--text-primary); }

/* The ring stays. It is the only thing that shows a keyboard user where they
   are, and it does not need a background to read. */
.topbar-nav-plain a:focus-visible { outline: 1px solid #ffffff59; outline-offset: 3px; }

/* Their gutter breakpoint: 56px each side above 992, 20px below it. */
@media (max-width: 991px) {
  .topbar { padding: 0 20px; }
}

/* HOLDING MEASURE, not a decision.
 *
 * Because the grid is 1fr/auto/1fr the two outer columns are always equal, so
 * the left group has only (pill - centre - gaps) / 2 to live in whatever is on
 * the right. It outgrows that and runs under the centre.
 *
 * Note the failure mode, because it is not the obvious one: the centre does
 * NOT drift. It stays exactly centred and the left group overflows its column
 * and runs underneath it, so measuring the COLUMN boxes reports everything as
 * fine right through the break -- the content edges are what to measure.
 *
 * THE BREAK MOVED WHEN "Events" BECAME "Who is the G.O.A.T.". That label is
 * 176px against the old 54, so the left group grew by 122 and the break went
 * from a 749px viewport to 885. Re-measured on the real type: clean at 890,
 * 5px under the centre at 880, 15px at 860, 61px at 769. Hidden at 899 for a
 * little margin on font rendering.
 *
 * Hiding the centre instead was tried and is worse: with it gone the auto
 * column collapses, each 1fr column takes half the pill, and the two navs
 * overlap EACH OTHER by 60px at 899 and 125px at 769. The content is simply
 * wider than the bar below this point.
 *
 * State the cost plainly, because it went up: the nav items now go somewhere,
 * so hiding them costs more than it did when neither linked anywhere. Below
 * 899px The Vault, the event and the three outbound links are all unreachable
 * from the bar. This is where the menu goes when there is one. */
@media (max-width: 899px) {
  /* THE BAR BELOW 900: $WIF, the goat pill, the menu button. Nothing else.
     It used to be `.topbar-nav { display: none }`, which took BOTH navs out and
     left a bar with no navigation at all -- neither the event nor the vault was
     reachable from it on a phone.

     `display: contents` on the left group so the mark and the nav become grid
     items of the pill directly. That is what lets the pill sit in the middle
     column rather than trailing the mark, without a second markup shape for
     mobile. */
  .topbar-left { display: contents; }
  .topbar-pill { grid-template-columns: auto 1fr auto; }

  /* The wordmark goes. It is identity, not a destination, and $WIF already
     carries it two inches to the left. */
  .topbar-centre { display: none; }

  /* The outbound links move into the menu. THE WRAPPER, not just the nav
     inside it: `.topbar-right` is a real grid child, so hiding only its
     contents left an empty fourth item in a three-column grid and the menu
     button wrapped onto a second row directly under the $WIF mark. Written
     before the rule below that brings the primary nav back, because both
     match `.topbar-nav`. */
  .topbar-right { display: none; }
  .topbar-nav-plain { display: none; }

  .topbar-nav:not(.topbar-nav-plain) {
    display: flex;
    justify-self: center;
    /* The container's own pill goes: with a single filled item inside it, the
       faint ground read as a ring around the pill rather than as a bar. */
    background: none;
    padding: 0;
  }
  /* The Vault is in the menu now. The goat pill stays in the bar at every
     width: it is the primary destination and putting it behind a button is
     the thing this change exists to stop. */
  .topbar-nav:not(.topbar-nav-plain) a:not(.is-live) { display: none; }

  .topbar-pill .topbar-menu-btn { display: inline-flex; }
}

/* ==========================================================================
   The mobile menu
   ========================================================================== */

/* 44x44 of TAP AREA whatever the mark inside measures. The rule mark is 18px
   wide and the button is more than twice that in both directions, which is the
   whole point of separating them. */
.topbar-menu-btn {
  /* Hidden by default and shown in the max-width:899 block ABOVE. That block
     comes first in this file, so this declaration would otherwise win on source
     order at equal specificity and the button would never appear -- which is
     exactly what happened the first time. Hence :not(:defined)-free, plain, and
     overridden by a rule that is more specific rather than merely earlier. */
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 160px;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  justify-self: end;
}
.topbar-menu-btn:hover { color: var(--text-primary); }
/* The focus ring is the bar's own, and it is not removed. */
.topbar-menu-btn:focus-visible {
  outline: 1px solid #ffffff59;
  outline-offset: 1px;
  color: var(--text-primary);
}

/* Two rules and the gap between them, drawn with a border rather than three
   elements. No icon font, no SVG set: there is no icon vocabulary in this
   project to be consistent with. */
.topbar-menu-mark {
  width: 18px;
  height: 12px;
  border-top: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
}
.topbar-menu-btn[aria-expanded="true"] .topbar-menu-mark {
  /* Closed state of the same mark: the two rules meet. Not a rotation into an
     X, which needs two absolutely positioned children and a transform each. */
  height: 2px;
  border-bottom-color: transparent;
}

/* A panel under the bar, full width. Not a side drawer: a drawer fights the
   fixed bar for the same edge and needs a transform to exist anywhere. */
.topbar-menu {
  position: fixed;
  top: 88px;
  left: 0;
  right: 0;
  z-index: 899;                  /* under the bar's 900, over everything else */
  /* THE PANEL HAS TO TAKE ITS OWN CLICKS. .topbar sets pointer-events: none so
     the frame passes them through to the page, and .topbar-pill restores it for
     itself -- this panel is the pill's sibling and inherited the none, so every
     tap went straight through to whatever was scrolled behind it. The links
     were correct the whole time; nothing could reach them. */
  pointer-events: auto;
  display: grid;
  padding: 8px 20px 16px;
  background: var(--bg-panel);
  border-bottom: 1px solid var(--panel-border);
}
.topbar-menu[hidden] { display: none; }

/* One per row, 44 minimum, left aligned, the muted treatment the nav already
   uses. No new colour, and the goat pill is not repeated here: it is in the
   bar two inches above. */
.topbar-menu a {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 4px;
  color: var(--text-muted);
  font-size: 15px;
  letter-spacing: 0.02em;
  text-decoration: none;
}
.topbar-menu a + a { border-top: 1px solid #ffffff14; }
.topbar-menu a:hover { color: var(--text-primary); }
.topbar-menu a:focus-visible {
  outline: 1px solid #ffffff59;
  outline-offset: -2px;
  color: var(--text-primary);
}

/* Above the breakpoint the real nav is back and neither of these exists. */
@media (min-width: 900px) {
  .topbar-menu { display: none; }
}

/* The long half of the label. "Who is the G.O.A.T." does not fit beside the
   mark and the button on a narrow bar, so the prefix goes and the label reads
   "G.O.A.T." -- shortened rather than shrunk or wrapped. It is a real span
   rather than a CSS text swap so the accessible name shortens with it instead
   of announcing something the screen does not show. */
/* THE TRAILING SPACE IS REAL AND MUST SURVIVE. The markup is
   `<span class="topbar-nav-long">Who is the </span>G.O.A.T.` and that space sits
   at the end of an inline element, where normal white-space processing collapses
   it away -- the label rendered "Who is theG.O.A.T." at every width from 430 up.

   `pre` on the span rather than a margin on it. A margin would put a gap there
   visually and leave the DOM string unchanged, so the accessible name and
   anything copied off the page would still read as one word. The space is a
   character and it should stay one. `pre` is safe on a span with no newlines
   in it, and when the span is hidden the space goes with it, so the short label
   is exactly "G.O.A.T." with nothing in front.

   Not &nbsp;: that survives collapsing but is a different character, and it
   would leave two spaces the moment somebody puts a normal one back. */
.topbar-nav-long { white-space: pre; }

@media (max-width: 429px) {
  .topbar-nav-long { display: none; }
}

/* Nothing moves. */
@media (prefers-reduced-motion: reduce) {
  .topbar-menu, .topbar-menu-btn, .topbar-menu-mark { transition: none; }
}

/* ==========================================================================
   The world — wireframe globe and the fee tracker
   ========================================================================== */

/* Anchored scrolls from the bar have to land below it, the same 104px the
   goat section uses. Without this, #vault puts the plinth's top edge under a
   72px bar. */
.hero { scroll-margin-top: 104px; }

/* The "Who is the G.O.A.T." target. Same 104px as .hero and .goat -- the bar is
   88px and this clears it by 16 -- so a click lands the sort bar under the bar
   rather than behind it, with the first row of cards below. */
.controls { scroll-margin-top: 104px; }

/* A band between the plinth and the explanation, not an object sitting in the
 * gap. Mollweide is 2:1 by construction, so the height follows the width and is
 * never set directly -- squashing it further would flatten the projection into
 * a lie about equal area.
 *
 * Centred on the facade with the same -1.29% the paragraph uses: the hands are
 * 29.31% and 31.89%, so the vault's centre is at 48.71% of the stage. */
.world-band {
  position: relative;
  flex: 0 0 auto;
  /* The aspect is the projection's, not a styling choice: 2:1 is what Mollweide
   * is, so width alone sets the size. */
  width: 34%;
  aspect-ratio: 2 / 1;
  /* No pull needed: the stage's bottom sits on the plinth, so in flow the band
   * lands directly under the building. An earlier -10.5% here was tuned while
   * the band was AFTER the explanation in the markup -- the 179px it appeared
   * to be closing was the paragraph sitting between them, not dead space, and
   * once the order was fixed that margin drove the band 154px up into the
   * facade. Order first, then spacing. */
  margin: 0;
}

.world-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Over the globe, not beside it. Small on purpose -- it is a figure in a
 * composition rather than the headline of one. */
/* A soft dark well behind the figure. Without it the number sits on the grid
 * and the grid reads straight through the counters -- legibility first, and it
 * also pushes the globe back behind the thing it is there to frame. */
/* Clearance for the figure and its label: the mesh fades out behind them
 * rather than being covered by anything. No border, no radius to catch the eye,
 * no glow -- an ellipse of page colour that is opaque under the text and gone
 * by its edge, so there is no line anywhere for the eye to read as a panel.
 *
 * Wider and taller than the type it clears, and held solid to 42% before it
 * starts falling away, because a gradient that begins fading immediately leaves
 * grid lines showing through the middle of the words. */
.world-fees::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 11.5em;
  /* Grown from 5.6em with the block. Four lines now instead of two, and the
     clearance has to cover the link at the bottom as well or the mesh runs
     through it. Measured against the rendered block, not guessed -- see the
     note on .world-fees. */
  height: 8.4em;
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side ellipse,
    var(--bg-page) 42%,
    rgb(13 13 13 / 0.72) 68%,
    rgb(13 13 13 / 0) 100%);
  z-index: -1;
}

.world-fees {
  isolation: isolate;
  /* The figure's size lives here so the well behind it, sized in em, scales
   * with the number instead of with the inherited body size. */
  font-size: clamp(20px, 2.05vw, 30px);
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  text-align: center;
  /* The block itself stays click-through so it never steals a drag from the
   * globe behind it. Only the link takes pointer events back. */
  pointer-events: none;

  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Source order is usd, label, eth, link. With a rate, that is already the order
   wanted. Without one, the ETH figure has to jump above the label to become the
   headline -- order 0 does that without touching the DOM, so the swap costs no
   re-render and the reading order stays label-then-figure in both states. */
.world-fees-usd    { order: 1; }
.world-fees-value  { order: 3; }
.world-fees-verify { order: 4; }
.world-fees:not(.has-usd) .world-fees-value { order: 0; }

/* NOTHING UNTIL BOTH FIGURES HAVE SETTLED. The dollar amount needs a rate as
   well as a balance and those are two separate requests, so painting on the
   first to return showed ETH as the headline and then jumped when the rate
   arrived. `visibility`, not `display`: the block is absolutely positioned over
   the globe, so it costs no layout either way, and visibility leaves the well
   behind it composited rather than making it appear from nothing.

   The default is hidden and the class is what reveals it, so a script that
   never runs shows no figure at all rather than a stale one. */
.world-fees:not(.is-resolved) { visibility: hidden; }

/* Hidden until a rate has actually been read. Never shows a stale or zero
   dollar amount: this rule is the whole failure behaviour, and it is the
   default rather than something a handler has to remember to apply. */
.world-fees-usd { display: none; }
.world-fees.has-usd .world-fees-usd { display: block; }

/* .world-fees-label and its rules are gone with the "claimable now" line. */

.world-fees-value {
  position: relative;
  margin: 0;
  font-size: 1em;
  line-height: 1;
  color: #f4efe4;
  font-variant-numeric: tabular-nums;
  font-variation-settings: "FLAT" 0;
}

.world-fees-unit {
  margin-left: 0.34em;
  /* Proportional to the figure it suffixes, but never out of legibility. */
  font-size: max(12px, 0.42em);
  letter-spacing: 0.12em;
  color: var(--text-muted);
  vertical-align: 0.5em;
}

/* The headline. Same size, weight and ink the ETH figure had when it held this
   position, so the block does not change shape when the rate arrives -- only
   which number is in the big slot. */
.world-fees-usd {
  position: relative;
  margin: 0;
  font-size: 1em;
  line-height: 1;
  color: #f4efe4;
  font-variant-numeric: tabular-nums;
  font-variation-settings: "FLAT" 0;
}

/* Demoted, but deliberately NOT down to the label's weight. This is the figure
   anyone checking the link will be comparing against, so it has to stay
   legible; --text-muted reads clearly and still sits below the headline. */
.world-fees.has-usd .world-fees-value {
  margin-top: 9px;
  /* Floored like the unit. Demoted this is 0.4em of a root that is 20px on a
     phone, which put the ETH figure itself at 8px. At desktop the root is 30
     and 0.4em is already 12, so nothing moves there. */
  font-size: max(12px, 0.4em);
  color: var(--text-muted);
}

/* The unit is sized against its own line, so demoting the figure would take it
   to 0.17em and vanish it. Re-proportioned, and dropped back to the baseline --
   a superscript unit is a headline mannerism and this is no longer a headline. */
.world-fees.has-usd .world-fees-unit {
  margin-left: 0.3em;
  font-size: max(12px, 0.78em);
  vertical-align: 0;
}

/* The quietest thing on the block, and the first <a href> on the page -- there
   were none before this, so this is the site's link treatment rather than a
   match for an existing one. Kept in the page's own vocabulary: no button, no
   icon, no colour of its own. A 1px rule at low opacity instead of
   text-decoration, so the underline sits clear of the descenders and can
   brighten with the ink on hover. */
.world-fees-verify {
  order: 4;
  position: relative;
  pointer-events: auto;
  margin-top: 11px;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
  /* No rule at rest and none on hover. It reads as a link from its position and
     its wording; the underline was the loudest thing on the quietest element. */
  text-decoration: none;
  opacity: 0.85;
  font-variation-settings: "FLAT" 0;
  transition: color var(--duration-fast) var(--ease-out),
              opacity var(--duration-fast) var(--ease-out);
}

.world-fees-verify:hover { color: var(--text-primary); opacity: 1; }
.world-fees-verify:focus-visible {
  outline: 2px solid var(--text-muted);
  outline-offset: 3px;
  border-radius: 2px;
  color: var(--text-primary);
}

@media (max-width: 760px) {
  /* Centred on the FACADE, not on the section -- the right hand is the wider
     asset, so the building sits at 48.81% and the footnote has to match it. The
     desktop rule does the same thing with padding-right: 2.58%; that rule is
     ~2000 lines further down and was half-overriding this one on source order,
     leaving 24px one side and 9.7px the other. Scoped here so it wins. */
  .hero .vault-footnote {
    flex-direction: column;
    left: 0;
    padding: 0 calc(24px + 2.38%) 0 24px;
  }
  .world-band { width: 62%; }
  /* flex-basis is the MAIN size, so `flex: 0 1 600px` stopped being a column
     width here and became a 600px column height -- ~600px of dead space under
     the copy. Scoped to the parent because the base rule is ~2000 lines further
     down and would otherwise win on source order. */
  .vault-footnote .vault-explain { flex-basis: auto; }
}

/* ==========================================================================
   The first event — who is the G.O.A.T.
   ========================================================================== */

/* One row, two columns. The label and the tube share the right column and are
 * sized together to the card's height. */
.goat {
  max-width: 1360px;
  /* Clearance under the fixed topbar. The bar is 88px tall with its pill
   * bottom at 72px, and the heading was landing at 33px from the section top --
   * squarely behind it.
   *
   * The spacing goes on the CARD's top padding, not here as padding: .goat
   * carries the travelling shimmer ring as an absolutely-positioned ::before at
   * inset:-1px, so padding on the section would stretch the ring up over the
   * empty space above the card. Margin here, padding there.
   *
   * Be straight about the limit: a position:fixed bar covers whatever is
   * passing under it, so no spacing makes the heading clear at literally every
   * scroll offset -- mid-scroll it will always pass behind. What this buys is
   * that the heading clears whenever the section comes to rest at the top of
   * the viewport, and scroll-margin-top makes anchored and keyboard scrolls
   * land below the bar rather than behind it. */
  margin: 64px auto 0;
  scroll-margin-top: 104px;
  display: grid;
  /* The right column's width is FIXED rather than derived from the tube. When
   * it was `auto`, the tube's width came from the card's height and the card's
   * width came from what the tube left over -- a genuine circular dependency.
   * It converged, but on a much smaller composition every time.
   *
   * 30%. The card gives up 173px and the column takes it.
   *
   * Two things changed what this number should be. The stack now starts at the
   * card's top edge instead of the bubble block's, which added ~96px to the
   * span the images divide; and the CRT asset was cropped to its own ink, which
   * had been floating in a 900x900 square and filling only 71% of its width.
   * Between them the visible monitor went from about 176x200 to 268x305 -- half
   * again as large -- with no extra column width involved.
   *
   * Past 30% the images stop growing, because they are limited by the height
   * the card gives them rather than by the column:
   *
   *     col%   monitor   goat      fills col   bubble   max chars
   *      26%   268x305   248x317      72%       470        54
   *      30%   268x305   248x317      62%       441        50
   *      34%   268x305   248x317      55%       413        50
   *      38%   280x318   259x331      51%       384        48
   *      42%   303x345   280x359      50%       355        45
   *
   * 34% buys nothing at all -- identical images, narrower bubbles, more empty
   * column. 38% and above buy about 5% more image for 57px of measure and
   * another 11% of the column left empty. 30% is where the card is meaningfully
   * narrower, the measure is a comfortable 50 characters, and the images are at
   * the largest the span allows.
   *
   * The column is the card's only source of extra width,
   * and the card wants it more than the monitor does: the tube was drawing
   * "$10,000" and a five-word caption at 475px, which is more room than that
   * needs, while the bubbles beside it were breaking to narrow measures and
   * paying for it in height. Seven points of width moves ~100px from a picture
   * that does not need it to text that does. */
  grid-template-columns: minmax(0, 1fr) 30%;
  align-items: stretch;
  gap: 28px;
}

.goat-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;                    /* clips the dither to the radius */
  border-radius: var(--radius-card);
  /* Sampled off the rendered KOL panel: background rgb(22,22,22), border
   * rgba(255,255,255,0.12) which composites to rgb(42,42,42). All the large
   * cards on the page now share one surface value.
   *
   * This also restores the correct nesting. Matching the bubbles put the card
   * at rgb(31,31,31) -- the same value as its own contents -- and before that
   * it was lighter than them. At 22 it is darker than the bubbles at 31, which
   * is the same relationship the how-it-works panel has with the bubbles inside
   * it. Container below contents. */
  border: 1px solid rgb(255 255 255 / 0.12);
  background: rgb(22 22 22);
}

/* The column is only a positioning context now; the stack inside it is
 * absolutely positioned, and that is what lets it fill.
 *
 * Two things had to be true at once: the stack's top must line up with the
 * bubble block's top and its foot with the card's bottom edge, AND the monitor
 * and portrait must divide that span between them rather than floating in it.
 * The second needs a definite height to divide. A flow-layout column cannot
 * give one -- its height is the grid row's, the row's is the max of its items',
 * and an item sized from the row is circular; that is exactly what blew the
 * card from 759px to 1511px last time.
 *
 * Taking the stack out of flow breaks the loop. It no longer contributes to the
 * row, so the row is the card's height alone, and `top`/`bottom` against the
 * stretched column give the stack a definite height for its children to share. */
.goat-right {
  position: relative;
}

.goat-stack {
  position: absolute;
  left: 0;
  right: 0;
  /* The card's own top and bottom edges. The stack used to start level with
     the bubble block, which put the label ~96px down the column and left the
     monitor and goat dividing a span shorter than the card by that much. Level
     with the card's top edge instead: the label is the first thing in the
     column, as the heading is the first thing in the card. */
  top: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 9px. At 3 the label was touching the chassis -- the CRT asset is cropped
     to its own ink now, so there is no transparent margin above it absorbing
     the gap the way there used to be. Still close enough to belong to the
     monitor rather than float above it. */
  gap: 9px;
}

.goat-label {
  margin: 0;
  flex: 0 0 auto;
  /* The same heading as "Who is the G.O.A.T.?", scaled down for its position:
   * same face, same 500, same line-height, same letter-spacing, same
   * --text-primary. No small caps and no tracking, because that heading uses
   * neither -- it should read as the same kind of heading, just smaller. */
  font-size: clamp(17px, 1.5vw, 22px);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--text-primary);
  font-variation-settings: "FLAT" 0;
}

/* Takes the height the label leaves, and the FULL column width -- without
 * align-self:stretch it shrank to its own canvas, so the canvas was sizing
 * itself from its own width and always came out flush to the column edges with
 * no margin either side. */
/* Takes the height the label leaves, and the FULL column width -- without
 * align-self:stretch it shrank to its own canvas, so the canvas was sizing
 * itself from its own width and always came out flush to the column edges with
 * no margin either side. */
/* flex:0 0 auto, NOT the 1 1 auto it had. That mattered only once the copy
 * grew. When the tube filled the column exactly there was no slack for the
 * stage to absorb and the two were identical; now there is 275px of it, and a
 * growing stage swallowed all of it BETWEEN the label and the tube -- the
 * label stayed pinned to the top of the column and the monitor sank 141px
 * below it, breaking the one relationship the 3px gap exists to hold. Sized to
 * its content instead, so the label keeps sitting on the monitor and the slack
 * goes outside the pair, where justify-content:center splits it evenly. */
/* Takes whatever height the label and the portrait leave, and the tube is a
 * square of min(that height, the column width). Sizing the monitor from the
 * LEFTOVER is what makes "shrink the monitor to fit both" automatic rather
 * than a number to maintain: the portrait's share is declared, the label's is
 * its own line box, and the tube gets the rest. */
/* flex-basis 0 on both the stage and the portrait, so the space left after the
 * label is split between them in the ratio of their grow factors rather than
 * by their content. `auto` would have let each take its natural size first and
 * only share the remainder, which is what left them small with dead space
 * around them. 1 : 1.04 gives the monitor and the goat the same visual weight
 * once the goat's 680x871 aspect is accounted for. */
/* Takes the flex share .goat-stage used to take, and divides it between the
 * monitor and its link. startTube still measures .goat-stage and not this, so
 * the tube is sized by what the stage gets rather than by a number written
 * here; the link's height simply comes off the top of what is available. */
.goat-monitor {
  flex: 1 1 0;
  align-self: stretch;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* The stage no longer grows to fill this box (see below), so the leftover has
     to go somewhere. It goes BELOW the pair, not around it: centring here put
     29.8px above the tube at 901px wide and pulled the label off the monitor,
     which is the one relationship the stack's 9px gap exists to hold. Below the
     link it is empty column above the goat, where it costs nothing. */
  justify-content: flex-start;
}

/* THE STAGE IS THE TUBE'S SHAPE, and that is what keeps the link under the
 * monitor rather than under the monitor's empty space.
 *
 * startTube fits the canvas inside this box by aspect: min(width / aspect,
 * height). When the box was simply "all the leftover height", a column that was
 * narrower than it was tall made the canvas WIDTH-limited -- it filled the box
 * sideways and left vertical slack, which place-items: center then split above
 * and below it. The link is 11px below the STAGE, so that slack pushed it 40.8px
 * below the tube at 901px wide while reading 11px at 1180. Giving the stage the
 * tube's own ratio removes the case: the height is now width / aspect exactly,
 * so the canvas is never width-limited, and if the leftover is shorter than that
 * the stage shrinks and the canvas is height-limited. Either way it fills
 * vertically and the 11px is measured from the tube's own edge.
 *
 * flex: 0 1 auto, not 1 1 0 -- it must be allowed to shrink but never to grow
 * past its ratio, which is the growth that created the slack. */
.goat-stage {
  flex: 0 1 auto;
  align-self: stretch;
  min-height: 0;
  aspect-ratio: 642 / 730;
  display: grid;
  place-items: center;
}

.goat-tube { display: block; }

/* The same treatment as .world-fees-verify under the vault globe, property for
   property: 12px, 0.04em, --text-subtle at 0.85, no rule, 11px below its
   figure. The two are deliberately identical -- one is the provenance of the
   vault's number and this is the provenance of the prize pool's, and a reader
   who learns the first should recognise the second without being told.

   Not a shared class, because the vault's copy carries position/pointer-events
   it needs only for sitting inside a click-through overlay. Kept as its own
   rule with the values written out; tests/test_kols.py asserts the pair still
   agree on size, tracking, ink and offset. */
.goat-verify {
  flex: 0 0 auto;
  margin-top: 11px;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
  text-decoration: none;
  opacity: 0.85;
  font-variation-settings: "FLAT" 0;
  transition: color var(--duration-fast) var(--ease-out),
              opacity var(--duration-fast) var(--ease-out);
}

/* A LINK INSIDE IT TAKES THE NOTE'S INK, not the browser's. `.rule-note` sets
   colour on the paragraph, and colour does not reach an <a>: the UA's own
   `a:-webkit-any-link` rule wins and the link renders in default blue on a dark
   page. Same trap the goat-verify link hit; caught here before it shipped. */
p.rule-note a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--text-subtle);
  transition: color var(--duration-fast) var(--ease-out);
}

p.rule-note a:hover { color: var(--text-primary); }
p.rule-note a:focus-visible {
  outline: 2px solid var(--text-muted);
  outline-offset: 3px;
  border-radius: 2px;
  color: var(--text-primary);
}

.goat-verify:hover { color: var(--text-primary); opacity: 1; }
.goat-verify:focus-visible {
  outline: 2px solid var(--text-muted);
  outline-offset: 3px;
  border-radius: 2px;
  color: var(--text-primary);
}

/* ---- the portrait and its blink ----------------------------------------- */

/* Sized from the column's WIDTH, not its height, and that is a correctness
 * point rather than a preference.
 *
 * The obvious way to write this is `height: 42%` -- the portrait and the
 * monitor are competing for the column's height, so state the share in that
 * axis. It does not work: the column's height is not definite (it is a
 * stretched grid row whose size is what we are trying to divide), so the
 * percentage resolves against nothing, the image falls back to its natural
 * 871px, the row grows to fit it and the copy card stretches to match. The
 * card went from 759px to 1511px on exactly that.
 *
 * The column's WIDTH is definite -- 30% of the section -- so a width-based
 * share is acyclic: portrait width is known, its height follows from the
 * aspect ratio, and .goat-stage takes what is left. */
/* flex-basis 0 and a grow factor, like the stage, so the two split the space
 * the label leaves rather than each taking its natural size first. min-height:0
 * is required with it: a flex item's default min-height is its content size,
 * and an 871px-tall image would refuse to shrink below that and overflow the
 * stack -- which it did, by 345px. */
.goat-portrait {
  position: relative;
  flex: 1.04 1 0;
  min-height: 0;
  margin-top: 16px;
  line-height: 0;
}

/* Height-driven now: the portrait's box has a definite height from the flex
 * split, and the image fills it with its width following the aspect ratio. */
.goat-portrait-art {
  display: block;
  height: 100%;
  width: auto;
  margin: 0 auto;
}

/* Positioned from the fractions the build script prints, so the tile lands on
 * the eye at any rendered size without a magic pixel offset anywhere. */
.goat-portrait-blink {
  position: absolute;
  left: 35.8824%;
  top: 20.5511%;
  width: 18.8235%;
  height: 9.5293%;
  pointer-events: none;

  /* WIPED, NOT FADED. Cross-fading this tile put a visible rectangle on the
     face every blink: the tile is opaque, so at 50% opacity every pixel in it
     was a 50/50 average of two uncorrelated hatchings, and measured local
     contrast inside the box dropped to 0.73 of its surroundings against 0.87
     at rest. That is an interior artefact, so no edge feathering reaches it.

     A wipe keeps every pixel either fully open or fully closed, so the two
     hatchings are never averaged and the moving boundary reads as the lid.

     The boundary is SOFT rather than a hard clip. Measured on real pixels, a
     hard clip at the halfway point -- where the cut crosses the iris -- gave a
     row-to-row step of 69 against the texture's own median of 44 and p95 of
     58, so for one frame there was a real edge. An 18% ramp puts it back
     inside the texture. --lid is registered below because a custom property
     cannot be transitioned unless it is. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(var(--lid) - 18%), transparent var(--lid));
          mask-image: linear-gradient(to bottom, #000 calc(var(--lid) - 18%), transparent var(--lid));
  --lid: 0%;                              /* opaque band entirely above: hidden */
  transition: --lid 130ms ease-out;       /* the OPENING */
}

.goat-portrait-blink.is-shut {
  --lid: 118%;                            /* opaque band fully across the tile */
  transition-duration: 55ms;              /* the close, faster than the open */
  transition-timing-function: ease-in;
}

@media (prefers-reduced-motion: reduce) {
  /* No blink at all rather than a slow one: the whole effect is the speed. */
  .goat-portrait-blink { display: none; }
}

/* ---- the bubbles -------------------------------------------------------- */

/* The card's inner frame. This rule and .goat-title were lost in the same
 * wholesale block replacement that took .goat-card, which is why the bubbles
 * were sitting flush on the border -- measured 0px of clearance on the left,
 * right and bottom, with the bubble borders and the card border touching.
 *
 * TWO VALUES, and the relationship between them is the point: the gap between
 * bubbles is 18, and the card's padding is 38 -- comfortably more than double.
 * If the padding is anywhere near the gap the card stops reading as a frame
 * around the group and starts reading as another cell in it. */
.goat-copy {
  /* 36px at the top, down from 88. The 88 was bought to clear the fixed topbar,
     and it cost a band of empty card above the heading that was doing nothing
     else. Worth knowing what this trades away: the heading now sits 37px from
     the card's top rather than 89, so it clears the bar's 72px only once the
     section has scrolled about 35px past the top of the viewport instead of
     immediately. scroll-margin-top still lands anchored scrolls clear. */
  padding: 36px 38px 38px;
  font-size: clamp(0.9375rem, 1.2vw, 1.0625rem);
}

.goat-title {
  position: relative;
  margin: 0 0 18px;                    /* heading to the top row */
  font-size: clamp(28px, 2.7vw, 40px);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  font-variation-settings: "FLAT" 0;
}

/* TWO MASONRY COLUMNS, not a grid.
 *
 * The copy is 2 / 3 / 3 / 2 paragraphs, which renders 185 / 274 / 328 / 185 --
 * a 143px spread. A grid aligns rows, so whichever bubble is shorter in a row
 * either stretches (dead space inside it) or leaves a hole beneath it. Neither
 * can be tuned away, because the imbalance is in the copy rather than in the
 * layout.
 *
 * Multicol has no rows at all. Each bubble takes its own height, they stack
 * down column one until it is half the content, then continue in column two --
 * and `column-fill: balance`, which is the default, is what picks that split
 * point to make the two columns finish level. The distribution is the
 * browser's, computed from the real rendered heights.
 *
 * Reading order becomes down-then-across rather than across-then-down. That
 * suits this copy: the event and how to vote are the entry, how voters win and
 * how the G.O.A.T. wins are the payout, so a reader going down one column and
 * then the other still gets them in a sensible order.
 *
 * The gap is a margin on the bubbles rather than a row-gap, because multicol
 * has no row axis -- column-gap covers the horizontal only. */
.goat-bubbles {
  position: relative;
  columns: 2;
  column-gap: 18px;
  /* Nothing to balance against here -- the columns fill to equal height on
     their own, and orphans/widows do not apply to unbreakable blocks. */
}

/* Same bubble as .think-step: same radius token, same border, same tint. */
.goat-bubble {
  /* break-inside:avoid is load-bearing, not defensive: without it multicol will
     happily cut a bubble in half at the column boundary and draw its border on
     both pieces. */
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  margin: 0 0 18px;
  padding: 18px 20px;
  border-radius: var(--radius-card);
  /* Sampled off a rendered .think-step, not copied from its CSS. Those bubbles
   * sit on the Aristotle panel and these sit on this card, so the same
   * declaration composites to two different colours -- the shared token gave
   * rgb(31,31,31) there and rgb(46,43,39) here. These are the reference's
   * COMPOSITED values stated outright, which is the only way the two are
   * actually the same colour on screen.
   *
   * Note what this costs: the reference bubble is lighter than its panel
   * (31 on 22) while this one is darker than its card (31 on 36). Matching the
   * bubble means inverting that relationship, because the two grounds differ. */
  border: 1px solid rgb(50 50 50);
  background: rgb(31 31 31);
  font-size: 1em;
  line-height: 1.6;
  color: var(--text-muted);
  font-variation-settings: "FLAT" 0;
  transition: border-color var(--duration-normal) var(--ease-out),
              background var(--duration-normal) var(--ease-out);
}

/* The reference's hover, composited the same way: rgb(38,38,38) and
 * rgb(78,78,78) on its panel. */
.goat-bubble:hover {
  border-color: rgb(78 78 78);
  background: rgb(38 38 38);
  transition-duration: var(--duration-fast);
}

/* The bold lead-in that used to open each bubble, promoted to a real heading
 * now the copy runs to two or three paragraphs. Same colour it had as a
 * <strong> -- --text-primary against the body's --text-muted -- so the
 * separation of heading from body is unchanged; only the element is. The face
 * has one weight, so size and colour carry it and font-weight would get a
 * synthesised smear. */
.goat-bubble-title {
  margin: 0 0 9px;
  font-size: 1.06em;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--text-primary);
  font-variation-settings: "FLAT" 0;
}

.goat-bubble p {
  /* Tighter than the 10px it was. These are three parts of one thought inside
   * a bordered box, not separate blocks -- the box is already doing the
   * separating, so the paragraph gap only has to be big enough to be seen. */
  margin: 0 0 7px;
  font-size: 1em;
  line-height: 1.6;
  color: var(--text-muted);
}

.goat-bubble p:last-child { margin-bottom: 0; }

/* THE VOTING-WINDOW RULE. Small print, not body copy: it is a condition on the
   instruction above it rather than another part of the explanation, and it
   reads as one because it is quieter and smaller.

   TWO USES. kol.html puts it under the deposit address -- the last thing read
   before a transfer is sent -- and the grid's "how to vote" bubble uses it for
   the link out to /docs. Both are the same kind of line: a quiet condition or
   pointer hanging off the instruction above it, not another part of it.

   (It once carried a sentence in that bubble as well, and was removed: the
   bullet above already said counts run while voting is open, so it was the
   same sentence twice in one box. The link is a different thing.)

   12px and --text-subtle: the same size and the same ink as the site's other
   small print (the two "verify" links), so this introduces no new colour and
   no new size. Given a little more air above it than a paragraph gap, because
   it is a different kind of sentence from the one it follows.

   `p.rule-note`, not `.rule-note`: a bare class is (0,1,0) and loses to any
   `<container> p` body-copy rule, which is how the first cut of this rendered
   at 15px in --text-muted -- the size and ink it exists NOT to be. Matching the
   element too keeps it from being beaten by the box it is dropped into. */
p.rule-note {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--text-subtle);
}

/* ---- the monitor -------------------------------------------------------- */

/* No card, no border, no background. Height matched to the copy card by the
 * stretched grid row; width follows the image's own square aspect.
 *
 * -3deg, and small on purpose: past about 5 the tilt starts reading as a
 * mistake in the layout rather than as an object that was put down. Leaning
 * anticlockwise tips its top edge toward the copy, so it sits against the card
 * instead of drifting away from it. */

/* ---- the dither field --------------------------------------------------- */

/* Inset a further pixel and carrying its own radius. The canvas already sat on
 * the padding box, which is inside the border -- but it is painted AFTER the
 * card's border in the stacking order, and the clip that overflow:hidden
 * applies at a rounded corner is antialiased, so the two could meet along the
 * curve. A pixel of clear space and a matching radius means they cannot. */
.goat-dither {
  position: absolute;
  inset: 1px;
  border-radius: 19px;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

@media (max-width: 900px) {
  .goat { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  /* One column, not two -- and this has to say `columns`, not
     grid-template-columns: the bubbles are a multicol block now and the old
     grid property was being ignored here. */
  .goat-bubbles { columns: 1; }
  /* The monitor stacks under the card instead of sitting beside it, so there
     is no bubble block alongside to centre against and the padding that does
     that alignment is only noise. */
  /* Back into flow when the column stacks under the card: there is no bubble
     block beside it to align to, and an absolute stack has nothing to fill. */
  /* Both sized from the WIDTH here, since a static stack has no span to divide.
     The stage needs an explicit aspect or it collapses to no height and the
     canvas fits itself to nothing -- that left the monitor at 132px against a
     362px goat. */
  /* Side by side rather than stacked: each was already only 46% wide, so the
     stack was spending a screen of height on width it was not using. A grid so
     the label can span both. */
  .goat-stack {
    position: static;
    top: auto;
    bottom: auto;
    display: grid;
    /* minmax(0,...), not 1fr: the canvas has an intrinsic width and `1fr` is
       minmax(auto,1fr), so its min-content floor took 300 of the 343 available
       and left the goat 27. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 16px;
    row-gap: 12px;
    align-items: end;
  }
  .goat-label { grid-column: 1 / -1; justify-self: center; }
  /* align-self, not just width. The base rule is `stretch`, and stretch with a
     definite width resolves to the cross-axis START -- which is why the monitor
     sat hard against the left edge while the goat and label centred. */
  /* The MONITOR is the grid item here, not the stage -- the stage is a child of
     it now. align-self: end on the wrapper bottom-aligns the pair with the goat
     beside it, which puts the link's baseline level with the goat's feet and
     lifts the tube by the link's height. That is the right way round: the link
     belongs to the monitor, so the monitor is what grew. */
  .goat-monitor { width: auto; align-self: end; }
  /* The ratio and the shrink behaviour are in the base rule now, so here the
     stage only has to stop being the grid item it used to be. Its height comes
     from the aspect ratio, which is what keeps it from collapsing to zero
     inside a flex column that has no definite height of its own. */
  .goat-stage { width: auto; height: auto; }
  .goat-portrait { width: auto; margin-top: 0; }
  .goat-portrait-art { height: auto; width: 100%; }
}

/* ==========================================================================
   Page
   ========================================================================== */

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 24px 16px 64px;
  background: var(--bg-page);
  color: var(--text-primary);
  font-family: var(--font-display);
  /* FLAT 0 is WIF Par's shipping instance and its only axis. The opsz/SOFT/WONK
   * settings that used to sit beside it were Fraunces's and are gone with it —
   * they addressed no axis in this face and described a mixed stack that no
   * longer exists. */
  font-variation-settings: "FLAT" 0;
  /* Kerning is a `kern` feature in the face and browsers apply it by default;
   * declared so a reset cannot quietly drop it. */
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
}

/* [pons] .launch-explore — the rounded panel on the near-black page. */
.panel {
  max-width: 1360px;
  /* The band above used to sit flush against this, which is what made the two
   * read as one block with a rule through it rather than as two panels. 28px is
   * the gap every other panel on the page already uses. */
  margin: 28px auto 0;
  padding: 24px;
  display: grid;
  gap: 22px;
  background: var(--bg-panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-panel);
}

.panel-head { display: grid; gap: 6px; }

.panel-title {
  margin: 0;
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -0.01em;
  font-variation-settings: "FLAT" 0;
}

.panel-subtitle {
  margin: 0;
  color: var(--text-muted);
  font-size: 13px;
  font-variation-settings: "FLAT" 0;
}

/* The rules block. Quieter than the subtitle but never behind a disclosure:
 * this is where the raw-count qualifier and the resolution rule are delivered
 * now that they are not stamped on every number. */
.panel-rules {
  margin: 4px 0 0;
  max-width: 62ch;
  color: var(--text-subtle);
  font-size: 12px;
  line-height: 1.5;
  font-variation-settings: "FLAT" 0;
}

/* ==========================================================================
   Control row
   ========================================================================== */

/* [pons] .launch-explore-toolbar */
/* After tenthousandtokens.net: a wide search field on its own row, and a row of
 * separate rounded buttons beneath it. Measured off theirs -- field 36px tall at
 * radius 6 with a 1px border on a dark fill; buttons 32px tall at radius 4,
 * ~12px apart, the active one filled light with dark text. Their palette is
 * neutral; ours is the warm ink, so the values are ours and the structure is
 * theirs. */
.controls {
  display: grid;
  gap: 12px;
}

.controls-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* [pons] .launch-explore-search — pill, flex:1, 12/14 padding */
.control-search {
  width: 100%;
  min-width: 0;
  height: 40px;
  padding: 0 14px;
  background: rgb(26 26 26);
  border: 1px solid rgb(var(--warm-ink) / 0.14);
  border-radius: 8px;
  color: var(--text-primary);
  font: inherit;
  font-size: 13px;
  font-variation-settings: "FLAT" 0;
}
.control-search::placeholder { color: var(--text-subtle); }
.control-search:focus-visible { outline: 2px solid var(--text-muted); outline-offset: 3px; }

/* [pons] .launch-explore-filter-group.tabs-pill .tabs-list */
/* Separate buttons, not a segmented pill -- each carries its own border. */
.control-sort {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/* [pons] .launch-explore-filter.tabs-tab */
.control-sort button {
  position: relative;
  z-index: 1;
  height: 34px;
  padding: 0 16px;
  border: 1px solid rgb(var(--warm-ink) / 0.20);
  background: none;
  border-radius: 6px;
  color: var(--text-muted);
  font: inherit;
  font-size: 12px;
  font-variation-settings: "FLAT" 0;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out);
}
.control-sort button:hover:not(.is-active) {
  color: var(--text-primary);
  border-color: rgb(var(--warm-ink) / 0.34);
}

/* The active one is filled and inverted, as on the reference. */
.control-sort button.is-active {
  background: #f4efe4;
  border-color: #f4efe4;
  color: #16150f;
}
.control-sort button:focus-visible { outline: 2px solid var(--text-muted); outline-offset: 2px; }

/* [pons] .tabs-indicator — the raised active segment. */

/* [pons] .launch-explore-count, widened for a full sentence. */
/* One line, not two, and heavier. */
.control-count {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.4em;
  height: 34px;
  padding: 0 14px;
  background: rgb(26 26 26);
  border: 1px solid rgb(var(--warm-ink) / 0.14);
  border-radius: 6px;
  color: #efeae0;
  font-size: 14px;
  font-variation-settings: "FLAT" 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.control-count b { color: var(--text-primary); font-weight: 500; }

/* THE SAME PILL AS THE COUNT, deliberately: same height, padding, radius,
   surface, border, type size and tabular figures. The row is one set of
   controls and a second treatment in the middle of it would read as a second
   kind of thing. Only the ink is quieter -- the count is the number people
   came for, the clock is context for it. */
.control-countdown {
  display: flex;
  align-items: center;
  gap: 0.4em;
  height: 34px;
  padding: 0 14px;
  margin: 0;
  background: rgb(26 26 26);
  border: 1px solid rgb(var(--warm-ink) / 0.14);
  border-radius: 6px;
  color: var(--text-muted);
  font-size: 14px;
  font-variation-settings: "FLAT" 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.control-countdown b { color: #efeae0; font-weight: 500; }
.control-countdown[hidden] { display: none; }

/* Four controls in two rows rather than three rows of one and a half: the sort
   pair spans the top, the clock and the count share the row below, both flush
   to the column's edges.

   The pills have to give type back to do it. At 14px they need 333px of the
   293 available at 375; the clamp lands them at 12px there and back at the
   desktop 14 by 460, so nothing steps at the breakpoint. */
@media (max-width: 574px) {
  .controls-row {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: space-between;
    align-items: center;
  }
  .control-sort { grid-column: 1 / -1; justify-self: stretch; }
  .control-sort button { flex: 1 1 0; }
  .control-countdown,
  .control-count {
    font-size: clamp(12px, 3.2vw, 14px);
    padding: 0 10px;
  }
}

/* Three across, and the middle one centred on the ROW rather than on the gap
   between its neighbours -- which is what flex would give, and it would drift
   every time the count's digits changed width.
   
   Only above the wrap breakpoint. Below it the row is a wrapping flex line and
   the timer takes its place in the stack. The `auto` middle track collapses to
   nothing while the timer is hidden, so a not-yet-open event still renders as
   the two items it has always been. */
@media (min-width: 700px) {
  .controls-row {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    gap: 10px;
  }
  .controls-row .control-sort { justify-self: start; }
  .controls-row .control-countdown { justify-self: center; }
  /* margin-left:auto is what pushed it right under flex; in grid the track
     placement does that, and leaving the margin in fights justify-self. */
  .controls-row .control-count { justify-self: end; margin-left: 0; }
}

/* On a phone the row wraps and the count lands on a line of its own, where
 * margin-left:auto strands it against the right edge with nothing to balance
 * it. Below the first breakpoint it aligns with everything above it instead. */
@media (max-width: 699px) {
  .control-count { margin-left: 0; }
}

/* ==========================================================================
   Grid  [ours: fixed column counts, not Pons's auto-fill]
   ========================================================================== */

/* Mobile first, per the spec: three across on a phone, widening up. */
.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;                   /* [pons] */
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Two across below 575. A card needs 157px for the longest name; three across
   only reaches that at 575, and at 460 it gives 118px and truncates 28 of the
   78 names -- worse than the three-across this replaces. */
@media (max-width: 574px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 700px)  { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@media (min-width: 1320px) { .grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.grid li { min-width: 0; }
.grid li[hidden] { display: none; }

/* ==========================================================================
   Card
   ========================================================================== */

/* [pons] .launch-card: radius 20, padding 10, gap 10, grid.
 * [ours] the surface: translucent tint instead of their flat --panel-blue. */
.card {
  position: relative;          /* the stretched .card-link anchors to this */
  display: grid;
  gap: 10px;
  padding: 10px;
  border-radius: var(--radius-card);
  background: rgb(var(--card-tint) / var(--card-opacity));
  border: 1px solid var(--card-border);
  color: var(--card-ink);
  text-decoration: none;
  /* [pons] .launch-card transition, extended to the border we add and to the
   * ink. The ink matters because a card crossing into or out of the top three
   * changes its whole colour scheme; see the crossover note below. */
  transition: transform var(--duration-fast) var(--ease-out),
              background var(--duration-normal) var(--ease-out),
              border-color var(--duration-normal) var(--ease-out),
              color var(--duration-normal) var(--ease-out);
}

/* [pons] .launch-card:hover — lift 2px and brighten. The border is ours; all
 * three run together on the same fast duration, as specified. */
.card:hover {
  transform: translateY(-2px);
  background: rgb(var(--card-tint) / var(--card-hover-opacity));
  border-color: var(--card-hover-border);
  transition-duration: var(--duration-fast);
}

/* THE CROSSOVER.
 *
 * A card overtaking into third changes surface, ink, measure and border all at
 * once. Two rules keep that from reading as a flash:
 *
 * 1. The class is applied by the script *when the card lands*, not when the
 *    move begins — so the change is a consequence of arriving somewhere, which
 *    is what actually happened, rather than an event at an arbitrary point
 *    mid-flight.
 * 2. Everything that changes crosses over the same duration and easing, so it
 *    is one transition and not four. That is what the shared --duration-normal
 *    above and on the measure below is for.
 *
 * The same holds in reverse for the card dropping to fourth. */

/* [pons] .launch-card-logo — square, radius 14, cover, own ground. */
.card-media {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  border-radius: var(--radius-media);
  overflow: hidden;
  /* [ours] Pons fills this with --bg-page, which is right when the card is dark
   * and wrong the moment it is not: a near-black square sits inside a paper
   * card for as long as the image takes to arrive. Tying it to the card's own
   * tint makes the empty state a quieter version of the card in both states. */
  background: rgb(var(--card-tint) / 0.35);
  display: grid;
  place-items: center;
  transition: background var(--duration-normal) var(--ease-out);
}
.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* [pons] .launch-card-body */
.card-body {
  display: grid;
  gap: 3px;
  min-width: 0;
  padding: 0 2px 2px;
}

/* [pons] .launch-card-body strong — 14px, weight 400, truncating. */
.card-name {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 400;
  font-variation-settings: "FLAT" 0;
  color: var(--card-ink);
  transition: color var(--duration-normal) var(--ease-out);
}

/* One name in the roster overruns the box the 2-across grid leaves at 375:
   brian_armstrong measures 130.77px at normal tracking. -0.03em brings it to
   124.46px against a 126.5px box -- 2.04px of margin.

   The box is 126.5px, NOT the 127 that clientWidth reports: clientWidth rounds
   to an integer, and so does scrollWidth, so at -0.02em (126.56px) the name
   overflowed by 0.063px and ellipsised while scrollWidth === clientWidth === 127
   said it fit. Measure this box with getBoundingClientRect and the text with an
   unconstrained clone -- a Range inside the element is clipped by overflow:hidden
   and just returns the box width back.

   Scoped below 430 because every width from 744px up already fits untracked.
   Placed after the base rule on purpose: an equal-specificity mobile block
   earlier in this file loses to it. */
@media (max-width: 429px) {
  .card-name { letter-spacing: -0.03em; }
}

/* [pons] .launch-card-body > small — 12px, muted. Where Pons puts the ticker.
 * Plain text, no link and no hover: their X page carries that link itself. */
.card-handle {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  font-variation-settings: "FLAT" 0;
  color: var(--card-ink-muted);
}

/* The vote count, large. [pons] .launch-card-mcap sits here in their card. */
.card-votes {
  margin-top: 6px;
  font-size: 24px;
  line-height: 1.05;
  font-variation-settings: "FLAT" 0;
  font-variant-numeric: tabular-nums;
  color: var(--card-ink);
  transition: color var(--duration-normal) var(--ease-out);
}

/* ---- the measure: a share bar, not a progress bar -------------------------
 *
 * The distinction is the whole design. A progress bar implies a finish line and
 * a thing being completed; votes have no target and nothing is being filled.
 * What this shows is a proportion of a whole, the way a results bar does — so
 * it is flat and rectangular with square ends, not a rounded capsule.
 *
 * Square ends matter more than they look. A rounded cap is what makes a bar
 * read as a token travelling toward somewhere; cut the ends square and the same
 * shape reads as a measured quantity instead.
 *
 * 5px tall: thick enough to see, which the 1px hairline rule was not, and flat
 * enough not to become the capsule it is deliberately avoiding. Full card
 * width, with five reference marks on the track.
 *
 * The scale is absolute and logarithmic now rather than share-of-total, so the
 * bar answers "how many votes does this KOL have" instead of "what fraction of
 * everything cast". See the axis note in event-grid.js for why the ticks are
 * what make a non-linear axis legible rather than flattering.
 *
 * A previous pass copied pump.fun's bar geometry here — 8px, 9999px radius,
 * rounded fill ends. That is a progress bar, correctly built for a bonding
 * curve that really is filling toward graduation. It was the wrong object for
 * this and has been removed along with its travelling gleam. */
.measure {
  display: block;
  margin-top: 9px;
}

.measure-track {
  position: relative;
  width: 100%;
  height: 5px;
  border-radius: 0;            /* square ends: a share, not a journey */
  overflow: hidden;
  background: var(--measure-track);
}

/* The five reference marks: 250, 1k, 5k, 25k, 100k. The positions come from
   --measure-ticks, which JS writes once from the same axis constants the fill
   uses -- so the marks cannot drift out of step with the bar. Full track
   height, one pixel wide. */
.measure-track::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--measure-ticks);
  pointer-events: none;
}

.measure-fill {
  position: relative;          /* over the ticks */
  height: 100%;
  border-radius: 0;
  background-color: var(--measure-fill);
  /* The sheen lives inside this box and cannot leave it. That is the whole
     containment mechanism: no mask, no clip-path, no matching the fill's width
     from script -- the highlight is a child of the fill, so "confined to the
     filled portion" is structural and stays true at every width, including
     while the bar is mid-tween. */
  overflow: hidden;
  --measure-sheen: rgb(255 255 255 / 0.92);
  /* The per-card phase. It has to travel as a CUSTOM PROPERTY, not as
     animation-delay set on this element: the animation lives on ::after, and
     animation-delay does not inherit, so setting it here silently did nothing
     and all sixty-six bars swept in lockstep. Custom properties do inherit into
     pseudo-elements, which is what makes this reach the sheen. */
  --sheen-delay: 0s;
  /* No width transition. The width is driven frame by frame by the same tween
     that drives the vote count above it, so that the two move together; a CSS
     transition here would race that tween and the pair would drift apart. */
  transition: background-color var(--duration-normal) var(--ease-out);
}

/* The travelling sheen.
 *
 * A note here used to argue against exactly this -- "a travelling highlight
 * says something is filling, and on a static share it is decoration". That was
 * written when the bar was a share of the total and genuinely static between
 * polls. It is now an absolute count on a live event, and the movement is doing
 * a job: it says the number is being watched, not that it is completing.
 *
 * 45% of the fill's width, soft at both edges, sweeping end to end and back.
 * `alternate` rather than a wrap-around, because a highlight that vanishes off
 * one end and reappears at the other reads as a loop restarting; easing at both
 * turnarounds reads as one object travelling.
 *
 * 222.22% is not arbitrary: the band is 45% of the fill, so 100/45 of its own
 * width places its left edge exactly on the fill's right edge. From -100% (its
 * right edge on the fill's left edge) that is one clean traverse with the band
 * fully out of frame at each end. */
.measure-fill::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 45%;
  background: linear-gradient(90deg,
    rgb(255 255 255 / 0), var(--measure-sheen), rgb(255 255 255 / 0));
  animation: measure-sheen 5.2s ease-in-out infinite alternate;
  /* After the shorthand, which would otherwise reset it to 0s. */
  animation-delay: var(--sheen-delay);
  pointer-events: none;
}

@keyframes measure-sheen {
  from { transform: translateX(-100%); }
  to   { transform: translateX(222.22%); }
}

@media (prefers-reduced-motion: reduce) {
  .measure-fill::after { display: none; }
}

/* The top three take the brass, and the same sheen -- but not the same sheen
 * COLOUR. The brass sits at 221,185,94, so a white highlight at the strength
 * the pale fill needs washes it to near-white at the peak and the bar stops
 * reading as brass for a third of every pass. A warm highlight at half the
 * alpha lifts it without draining the hue. */
.card.is-ranked .measure-fill {
  background-color: var(--gold);
  --measure-sheen: rgb(255 246 222 / 0.46);
}

/* ---- the rank mark ------------------------------------------------------- */

/* The ornament sits in the corner of the image, on the engraving's own paper.
 * Sized so it is cut into the corner rather than laid over it: the wreath is a
 * fixed 38px at every width. That is about a quarter of the media box on
 * desktop and 29% of the 131px box at 375, the narrowest it now gets. It
 * clears the subject at both ends. */
.card-rank {
  position: absolute;
  top: 5px;
  right: 5px;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  color: var(--gold);
  /* Cut in. One groove for numeral and laurel together so they sit at the same
   * depth: shadow above, catch below, both 1px and unblurred. On the pale
   * patches the shadow is what you actually read, so it is heavier than a
   * groove on a flat ground would need. */
  filter:
    drop-shadow(0 -1px 0 var(--incised-shadow))
    drop-shadow(0 1px 0 var(--incised-light));
  /* Fades with the crossover rather than appearing the instant the class does. */
  opacity: 0;
  transition: opacity var(--duration-normal) var(--ease-out);
}
.card.is-ranked .card-rank { opacity: 1; }


/* No corner shade either. It existed to lift brass off the highlights; ink does
 * not need lifting, and darkening the ground would work directly against it. */

.rank-numeral {
  font-size: 13px;
  font-variation-settings: "FLAT" 0;
  letter-spacing: 0.06em;
  line-height: 1;
}

/* Third place has no laurel, so its numeral is not competing with anything and
 * can sit a shade larger without crowding. */
.card-rank[data-place="3"] .rank-numeral { font-size: 15px; }

/* Wreath and numeral occupy the same cell: the numeral sits inside the ring. */
.card-rank > * { grid-area: 1 / 1; }

.rank-laurel {
  width: 100%;
  height: 100%;
  overflow: visible;
  fill: currentColor;
  stroke: none;
}

/* The stem is a hairline holding the leaves together; the leaves carry the
 * weight. non-scaling-stroke keeps it a hairline rather than scaling into a
 * band if the ornament is ever drawn larger. */
.rank-laurel .rank-stem {
  fill: none;
  stroke: currentColor;
  stroke-width: 0.9;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  opacity: 0.75;
}



/* ---- the address  [pons] .launch-card-deployer --------------------------- */

/* A row now rather than a button: the address text plus the copy icon. Same
 * position, same margins, same type -- only the element changed. */
.card-address {
  margin: 4px -4px -2px;
  padding: 2px 4px;
  display: flex;
  align-items: center;
  gap: 5px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--card-ink-subtle);
  font: inherit;
  font-size: 11px;
  font-variation-settings: "FLAT" 0;
  font-variant-numeric: tabular-nums;
  text-align: left;
  justify-self: start;
  transition: color var(--duration-normal) var(--ease-out);
}

/* Sized in em so it tracks the address text rather than needing its own scale,
 * and dimmed at rest so it reads as secondary to the address it belongs to. */
/* Covers the whole card. z-index 0 against the copy button's 1, so the button
   stays clickable inside the link's area rather than being swallowed by it. */
.card-link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}

.card-link:focus-visible {
  outline: 2px solid var(--card-ink-muted);
  outline-offset: 3px;
}

.card-copy {
  position: relative;
  z-index: 2;              /* above the stretched .card-link */
  flex: 0 0 auto;
  display: inline-flex;
  width: 1.15em;
  height: 1.15em;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  opacity: 0.55;
  cursor: pointer;
  transition: opacity var(--duration-fast) var(--ease-out);
}

.card-copy svg { width: 100%; height: 100%; display: block; }

/* A 44x44 TAP AREA BELOW 900, WITHOUT TOUCHING THE MARK OR THE LAYOUT.
   Copying the deposit address is how a vote happens, so this is the primary
   action on the primary page, and at 15x15 on a phone it misses often enough
   that people give up.

   A pseudo-element rather than padding and a negative margin. Padding would
   grow the button's box and push the address text along the row; a negative
   margin to pull it back then has to be undone on every side and is one more
   thing to get wrong. This changes no box: the ::after is positioned out of
   flow, centred on the mark, and because it belongs to the button a tap on it
   is a tap on the button.

   It sits over the stretched .card-link, which is what should happen -- the
   button is already z-index 2 against the link's 1, so the enlarged area takes
   the tap rather than navigating. That is the whole point, and it is why the
   area is centred on the mark rather than extended toward the card's middle. */
@media (max-width: 899px) {
  .card-copy::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
    /* No paint. It exists to be hit. */
  }
}

.card-copy:hover { opacity: 1; }
.card-copy:focus-visible {
  outline: 2px solid var(--card-ink-muted);
  outline-offset: 2px;
  border-radius: 3px;
  opacity: 1;
}


/* ---- the copy toast ------------------------------------------------------
 *
 * Fixed at 104px, which is the 88px topbar plus 16px of air. Centred, so it
 * would sit directly on top of the nav pill at any smaller offset -- both are
 * horizontally centred, and the pill grows to 56px inside its frame once the
 * page is scrolled. Clearing the bar entirely is the only position that holds
 * in both of the bar's states.
 *
 * z-index 200 puts it over the topbar's 100: a confirmation that can be
 * occluded by chrome is not a confirmation.
 *
 * The surface is the scrolled topbar pill's, deliberately -- same 66%-over-
 * near-black, same 12% white hairline, same blur. It is the page's one
 * established "floating over the content" treatment and the toast is the
 * second thing to use it. */
.toast {
  position: fixed;
  top: 104px;
  left: 50%;
  z-index: 950;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(92vw, 420px);
  padding: 11px 12px 11px 14px;
  border-radius: var(--radius-media);
  background: #100f0da8;
  -webkit-backdrop-filter: blur(14px);
          backdrop-filter: blur(14px);
  box-shadow: inset 0 0 0 1px #ffffff1f, 0 8px 28px #00000059;
  color: var(--text-primary);

  /* Hidden state. Not `display:none` and not `hidden`: the live region has to
   * stay in the document for its text change to be announced. */
  opacity: 0;
  transform: translateX(-50%) translateY(-10px);
  pointer-events: none;
  transition:
    opacity var(--duration-normal) var(--ease-out),
    transform var(--duration-normal) var(--ease-out);
}

.toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}

.toast-tick {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: var(--text-primary);
}

.toast-text {
  margin: 0;
  flex: 1 1 auto;
  font-size: 13px;
  line-height: 1.25;
  font-variation-settings: "FLAT" 0;
  color: var(--text-primary);
  /* Set lowercase as written, like the rest of the page's voice. */
  text-transform: none;
}

.toast-close {
  flex: 0 0 auto;
  display: inline-flex;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--text-subtle);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out),
              background var(--duration-fast) var(--ease-out);
}

.toast-close:hover { color: var(--text-primary); background: #ffffff0f; }
.toast-close:focus-visible {
  outline: 2px solid var(--text-muted);
  outline-offset: 2px;
  color: var(--text-primary);
}

.toast-tick svg, .toast-close svg { width: 100%; height: 100%; display: block; }

@media (prefers-reduced-motion: reduce) {
  /* Keep the fade, drop the travel. The opacity change is what carries the
   * "this just happened"; the slide is decoration. */
  .toast { transform: translateX(-50%); transition: opacity var(--duration-normal) linear; }
  .toast.is-visible { transform: translateX(-50%); }
}

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

.grid-empty {
  padding: 32px 4px;
  color: var(--text-muted);
  font-size: 13px;
  font-variation-settings: "FLAT" 0;
}

/* Below this width the inscription cannot stay in the stone. The plinth is a
 * fixed fraction of a building that is itself only ~160px wide on a phone, so
 * cqw sizing puts the lettering at about 5.5px -- less than half the face's
 * stated 11px floor, where the counters close and it goes to grey. Shrinking it
 * to fit would publish a line nobody can read.
 *
 * So it comes out of the plinth and sits under the building at the floor size,
 * losing the carved recess and keeping the sentence. The facade still reads top
 * to bottom; only the mounting changes. */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ==========================================================================
   The philosopher — aspect-locked section, live laptop screen
   ========================================================================== */

/* Height comes from the section's own aspect ratio, not from its content and
 * not from the viewport. Taken from contralabs.com, which holds 4:3 at 1470,
 * 1000 and 500 alike; the composition is then the same picture at every width
 * instead of reflowing into a different one. 3/2 here because our figure is
 * far taller than theirs (853x1844) and 4:3 would leave it swimming. */
.think {
  position: relative;
  /* The same container as the grid below — same max-width, inset, padding,
   * background, border and radius — so the page reads as a stack of panels
   * rather than one styled section and one loose image. The bar clearance moved
   * to the margin when the padding became the panel's own. */
  max-width: 1360px;
  margin: 96px auto 0;
  padding: 24px;
  background: var(--bg-panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-panel);
  /* Driven by the text column, not chosen. At 12/5 the panel was 599px and the
   * copy needed 728 once the type came up and the measure came in -- it
   * overflowed by 179px. 5/3 gives 863 at this width, which clears the copy
   * with room top and bottom.
   *
   * The figure scales with it, and that is a gain rather than a side effect:
   * everything in this panel is proportional, so a taller panel puts the
   * sentence on the laptop screen at a comfortable size instead of at the
   * legibility floor it was scraping before. */
  /* A FLOOR, NOT A LOCK. This was `aspect-ratio: 5 / 3`, which sizes the box
   * as if the height had been specified -- so content taller than the ratio
   * does not push the box down, it spills out of it. Between 761px (where the
   * stacked reset ends) and about 1230px the copy column needs more height
   * than the ratio gives, and the third "how it works" card rendered outside
   * the panel with the bottom border cutting through it. Measured: +183px at
   * 768, +119 at 900, +65 at 1000, +36 at 1100, +9 at 1200, fitting from ~1250.
   *
   * As a min-height the ratio still governs whenever the content fits, which
   * is every width from ~1250px up -- the engraving's proportions are held
   * exactly where they were being held before -- and below that the panel
   * grows instead of clipping.
   *
   * The width expression is the panel's own: max-width 1360 inside the page's
   * 16px side inset. Verified against the rendered box rather than assumed --
   * at a 900px viewport the panel is 868px and was 521px tall, which is
   * 868 x 0.6. */
  min-height: calc(min(1360px, 100vw - 32px) * 3 / 5);
  display: grid;
  grid-template-columns: auto 1fr;
  /* Auto, not 100%. A percentage row needs a definite container height, which
   * the aspect-ratio used to supply and the min-height deliberately does not.
   * The figure is capped in the same units instead -- see .think-art. */
  grid-template-rows: auto;
  align-content: center;
  /* Centred, NOT end-aligned. With align-items:end the figure is pinned to the
   * content box's floor, so shortening it opens a gap above his head and none
   * below him -- the clearance under the feathered cut stayed at the panel's
   * 24px padding no matter what height was set, which is not what the cut is
   * for. Centring splits the slack, and it splits it in percentages, so it
   * holds at every width instead of needing a fixed margin that overflows once
   * the aspect-locked panel gets short. */
  align-items: center;
  gap: clamp(24px, 5vw, 72px);
}

.think-figure {
  position: relative;
  /* Wraps the art, so its height is the art's. It used to be 90% of an
   * aspect-locked row; with the lock gone that percentage had nothing definite
   * to resolve against and would have fallen back to auto, rendering the
   * engraving at its natural 1844px -- the exact failure the old comment here
   * warned about. The cap moved to .think-art, in absolute units. */
  height: auto;
  min-height: 0;
}

.think-art {
  display: block;
  /* What 90% of the aspect-locked row used to come to, written directly:
   * panel height (W x 3/5) less the 24px padding and 1px border top and
   * bottom, times 0.9. At a 1438px panel that is 731px, which is what the art
   * measured before this change.
   *
   * This no longer guards the screen text. .think-line carries its own floor --
   * max(calc(11px / var(--k)), 27px) -- so the sentence renders at 11px however
   * small the figure gets. What this cap is left doing is the composition. --k
   * still tracks the art's rendered width through a ResizeObserver in
   * event-grid.js, which is the space the screen quad is POSITIONED in. */
  height: auto;
  width: auto;
  max-height: calc(min(1360px, 100vw - 32px) * 0.54 - 45px);
  max-width: 100%;
}

/* ---- the panel's own travelling highlight -------------------------------- */

/* The same mechanism as the cards, on the container. Two things had to change
 * rather than being reused as-is, and both follow from it being much bigger.
 *
 * SLOWER. The cards are ~1380px around; this panel is ~4600px, a bit over three
 * times the distance. Matching their 11s would send the light round at three
 * times the linear speed and it would whip. 32s is close to holding the same
 * speed along the edge as the cards do.
 *
 * FAINTER. Peak alpha is 0x30 against the cards' 0x5c, and the falloff is wider.
 * The same opacity on a much longer line reads as considerably louder, because
 * there is simply more lit edge in view at any moment.
 *
 * Worth knowing about the technique: a conic sweep is angularly uniform, not
 * uniform along the perimeter. On a 1408x845 box the corners sit 59 degrees off
 * VERTICAL, so the top and bottom edges take 118 degrees of sweep each and the
 * short sides take 62 -- the light climbs the short sides faster than it
 * crosses the long ones. (This note previously had those two swapped, and read
 * 31 degrees, which is the angle off HORIZONTAL.) */
.think,
.goat {
  isolation: isolate;
}

/* Shared by both panels rather than copied into each. The duration is tuned to
 * the perimeter, so if a third panel of a very different size ever takes this,
 * give it its own duration rather than assuming 32s travels at the same speed
 * on it -- see the note above about conic sweeps being uniform in angle and not
 * along the edge. */
.think::before,
.goat::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--shimmer-angle),
    #fff0 0deg,
    #fff0 212deg,
    #ffffff10 258deg,
    #ffffff30 300deg,
    #ffffff10 342deg,
    #fff0 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: -1;
  /* TWO THINGS, AND BOTH ARE ABOUT THE FIRST PAINTED FRAME.
   *
   * A CSS animation's first frame is its `from` value, and here that is
   * --shimmer-angle: 0deg. The lit arc runs 212deg to 342deg clockwise from
   * twelve o'clock, which is lower-left, through nine o'clock, to upper-left:
   * at 0deg it sits squarely on the LEFT EDGE. So the ring painted a bright
   * vertical down the left margin on load and then slid away as the sweep
   * started -- a line that flashed once and vanished, which is exactly what it
   * looked like.
   *
   * The negative delay starts the sweep 90 degrees in, so the arc begins
   * across the top rather than parked beside the copy. -8s is a quarter of
   * 32s.
   *
   * A FADE-IN WAS TRIED HERE AND REMOVED. `shimmer-arrive 1.2s both` from
   * opacity 0 looked like the tidier fix, but it makes the ring's visibility
   * depend on the animation actually ticking: anywhere frames are throttled --
   * a background tab, a low-power renderer -- `both` holds the from-value and
   * the ring stays invisible for good. The delay needs no tick to be correct,
   * because it only changes where the sweep starts.
   *
   * Measured while chasing this: the font swap is NOT involved. Layout-shift
   * observation over a full load reports CLS 0, so font-display: swap costs no
   * reflow here and is left alone. Nor is there an unstyled window to fix --
   * both stylesheets are render-blocking <link>s in the head, nothing injects
   * CSS, and no element in this section is revealed by JavaScript. */
  animation: think-shimmer 32s linear -8s infinite;
  /* OFF BY DEFAULT, exactly as .think-step::before already ships off. Set this
   * to 1 to turn the panels' travelling highlight on.
   *
   * WHY IT IS OFF. The sweep is angularly uniform, so it lights every edge in
   * turn -- that is the effect, not a fault in it. Measured on the panel at a
   * 1440 viewport: the left edge (239-301 degrees) is lit for 206 of the 360
   * degrees in a cycle, which is 18.3s of every 32s, peaking at alpha 0.188.
   * The panel's own border is 0.12, so the pass is 1.6x the border it runs
   * along, on a 1px line beside an otherwise empty margin. It does not read as
   * an edge catching light; it reads as a rule that appears and disappears,
   * which is what it was reported as four times.
   *
   * The -8s delay above still matters if it is switched on: without it the
   * first painted frame is angle 0, which parks the arc on the left edge
   * before the sweep has moved at all.
   *
   * Worth knowing before switching it on: a registration bug meant this ring
   * painted nothing at all until it was fixed, so it has never actually been
   * seen running on this page. Turning it on is a new decision, not a
   * restoration. */
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .think::before,
  .goat::before { animation: none !important; opacity: 0; }
}

/* ---- the screen --------------------------------------------------------- */

/* The four corners were measured off the asset, not estimated: the screen is
 * the largest white region enclosed by ink, found by flood-filling the paper
 * from the border and taking what was left. In the drawing's 853x1844 space it
 * is TL 584,519 / TR 821,545 / BR 764,783 / BL 536,739.
 *
 * Those edges are NOT parallel — top 238px against bottom 232, left 225 against
 * right 245 — so this is a real perspective quad and a rotate/skew cannot sit
 * on it. The matrix3d below is the homography that maps this 238x235 box onto
 * those four corners exactly, solved rather than tuned by eye.
 *
 * --k rescales the drawing's coordinate space to whatever the figure is
 * rendered at. It is set from JS because a homography's translation and
 * perspective terms are in absolute units and cannot be expressed as
 * percentages; the matrix itself never changes. */
.think-screen {
  position: absolute;
  top: 0;
  left: 0;
  width: 238px;
  height: 235px;
  overflow: hidden;                     /* clips to the quad, in perspective */
  transform-origin: 0 0;
  transform: scale(var(--k, 0)) matrix3d(
    0.722369, -0.0722652, 0, -0.000333044,
    -0.159419, 0.997987, 0, 0.0000836493,
    0, 0, 1, 0,
    584, 519, 0, 1);
}

/* Content on a small display: top-left, like a machine, and small enough to
 * read as something the screen is showing rather than a headline laid over it. */
.think-line {
  margin: 0;
  padding: 24px 12px;
  /* In the DRAWING's coordinate space, so the rendered size is this times --k.
   *
   * The screen is 238 of those units wide and the caret is part of the line.
   * At 28 the sentence measured 193 and the caret wrapped onto a second row
   * below it, which is the single most obvious way to give the trick away -- a
   * machine does not put its cursor under what it just typed.
   *
   * The figure is smaller since the crop, so this went UP rather than down: at
   * this scale the design-space size has to grow to keep the rendered size above
   * the face's floor, and the side padding came in to 12 to buy the room. */
  /* In the drawing's units, multiplied by --k on render. So `max()` here picks
     the LARGER local value, which is the SMALLER of the two rendered sizes it
     is guarding: 11px flat below a 990.6px viewport, 27 x --k above, equal at
     the crossover. 27px alone left the sentence at 8.1px between 761 and 991. */
  font-size: max(calc(11px / var(--k)), 27px);
  line-height: 1.25;
  letter-spacing: 0.01em;
  color: #f4efe4;
  font-variation-settings: "FLAT" 0;
  white-space: pre-wrap;
}

/* The caret blinks on its own clock and is never touched by the typing. This is
 * the whole trick: if the caret is re-created or its animation restarted on each
 * keystroke it blinks IN STEP with the characters, and the result reads as one
 * CSS animation instead of two independent things happening at once. Only the
 * text span's content is ever written to.
 *
 * steps(1) rather than a fade — a terminal caret is on or off, never dim.
 * 1.06s is 530ms each way, which is where terminal carets actually sit. */
.think-caret {
  display: inline-block;
  width: 0.58em;
  height: 1.02em;
  vertical-align: -0.16em;
  background: currentColor;
  animation: think-caret-blink 1.06s steps(1, end) infinite;
}

@keyframes think-caret-blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}

/* The second grid track: it holds the figure to the left half as well as
 * carrying the words. Centred with the figure rather than bottom-aligned, so
 * the two columns share an axis instead of sitting on a floor.
 *
 * THE MEASURE IS SET HERE, and the font-size on this element is what makes it
 * work: `ch` resolves against the element's OWN font-size, so putting both on
 * the same box is the only way the cap means the same thing for every child.
 *
 * Note that `ch` lies in this face. It is the width of "0", and WIF Par's
 * figures are tabular at 600/1000 em while its lowercase averages nearer 500 --
 * so 70ch measured about 78 real characters a line, not 70. 56ch lands at
 * roughly 65, which is inside the comfortable range. */
.think-copy {
  font-size: clamp(0.9375rem, 1.2vw, 1.0625rem);
  max-width: 56ch;
}

.think-title {
  margin: 0;
  font-size: clamp(32px, 3.1vw, 46px);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.01em;
  font-variation-settings: "FLAT" 0;
}

/* A step above the body, so the eye has somewhere to start. */
.think-lede {
  margin: 20px 0 0;
  font-size: clamp(17px, 1.4vw, 20px);
  line-height: 1.45;
  color: var(--text-primary);
  font-variation-settings: "FLAT" 0;
}

/* :not() rather than a bare `p`, and not for tidiness. The lede is also a <p>
 * inside .think-copy, so a plain `.think-copy p` scores 0,1,1 against the
 * lede's 0,1,0 and silently won BOTH its size and its colour -- the statement
 * rendered at body size in body grey, which looked deliberate and was not.
 * Excluding it here keeps the two rules from competing at all. */
.think-copy p:not(.think-lede) {
  margin: 16px 0 0;
  font-size: 1em;
  line-height: 1.62;
  color: var(--text-muted);
  font-variation-settings: "FLAT" 0;
}

/* A real heading now, not a bolder paragraph: clearly larger than the body and
 * with the space above it doing as much of the work as the rule. This is where
 * the section turns from what it is to what you do. */
.think-how {
  margin: 44px 0 0;
  padding-top: 28px;
  border-top: 1px solid var(--panel-border);
  font-size: clamp(20px, 1.8vw, 26px);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.005em;
  color: var(--text-primary);
  font-variation-settings: "FLAT" 0;
}

/* ---- the three steps ---------------------------------------------------- */

.think-steps {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 12px;
}

/* Same radius and border as .card, so these read as the same family. What they
 * do NOT take from .card is the lift on hover: that card is a link and the
 * translate is a promise of somewhere to go, which these have not got. */
.think-step {
  margin: 0;
  padding: 18px 20px;
  border-radius: var(--radius-card);
  border: 1px solid var(--card-border);
  background: rgb(var(--card-tint) / 0.045);
  color: var(--text-muted);
  font-size: 1em;
  line-height: 1.6;
  font-variation-settings: "FLAT" 0;
  transition: border-color var(--duration-normal) var(--ease-out),
              background var(--duration-normal) var(--ease-out);
}

.think-step:hover {
  border-color: var(--card-hover-border);
  background: rgb(var(--card-tint) / 0.075);
  transition-duration: var(--duration-fast);
}

/* ---- the travelling highlight ------------------------------------------- */

/* A soft light running around the card's outline and looping.
 *
 * The mechanism is Pump's -- a highlight travelling under a mask -- but it is
 * pointed at the OUTLINE rather than at a fill, and that difference is the
 * whole justification. On a bar, a travelling highlight says "this is filling",
 * which is why it came off the vote measures. On a static border it says
 * nothing except that the edge is catching light, so the card reads as live
 * rather than printed without claiming any progress.
 *
 * A conic gradient rotated about the card's centre, masked down to a 1px ring,
 * is what makes it a circuit of the rectangle instead of a diagonal wipe across
 * it. The angle has to be a registered property: an unregistered custom
 * property is a string to the animation engine and would jump between
 * keyframes rather than sweep. Browsers without @property simply get the
 * static border, which is the correct thing to degrade to.
 *
 * THIS COMMENT WAS BROKEN AND IT COST THE FEATURE. The --lid block below was
 * pasted into the middle of the sentence above, bringing its own comment with
 * it. CSS comments DO NOT NEST: the inner opener was ignored, the inner closer
 * ended THIS comment early, and what was left of the sentence -- "@property
 * simply get the static border, ..." -- parsed as a malformed at-rule. Error
 * recovery on an at-rule skips to the end of the next block, and the next
 * block was @property --shimmer-angle, so the registration was swallowed.
 *
 * Unregistered, --shimmer-angle has no value, conic-gradient(from var(...))
 * is invalid at computed-value time, and the ring painted nothing -- except at
 * keyframe boundaries, where an unregistered property jumps to a literal and
 * the gradient briefly became valid. A partial arc flashed onto the panel
 * edges: a faint vertical down the left margin with a short horizontal off a
 * corner, appearing and vanishing on a 32s cycle. */

/* The lid's travel. Registered so it can be transitioned -- an unregistered
   custom property jumps rather than animating. */
@property --lid {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

@property --shimmer-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.think-step {
  /* BOTH of these, and the position is the one that is easy to miss. Without it
   * the ring's inset:-1px resolves against the nearest positioned ancestor --
   * which is the section itself -- so it was being drawn at panel size over the
   * whole column instead of around the card. isolation then keeps its
   * z-index:-1 scoped to the card so it sits under the words rather than
   * disappearing behind the panel's own background. */
  position: relative;
  isolation: isolate;
}

.think-step::before,
.goat-card::before {
  content: "";
  position: absolute;
  /* -1px because an absolutely positioned child is laid out against the
   * PADDING box, and the ring has to cover the border box. */
  inset: -1px;
  border-radius: inherit;
  padding: 1px;                     /* the ring's thickness */
  background: conic-gradient(
    from var(--shimmer-angle),
    #fff0 0deg,
    #fff0 226deg,
    #ffffff1c 268deg,
    #ffffff5c 300deg,
    #ffffff1c 332deg,
    #fff0 360deg);
  /* Ring, not disc: paint the padded box and knock its own content box out. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: -1;
  /* Off unless a variant turns it on. Not tidiness: without this, a card with
   * no matching data-shimmer value still paints the conic at angle 0, i.e. a
   * stationary highlight parked on one corner -- the exact "printed" look the
   * effect exists to avoid, and the same trap the cycle variant fell into. */
  opacity: 0;
}

@keyframes think-shimmer {
  to { --shimmer-angle: 360deg; }
}

/* VARIANT A — all three running together. */
.think-steps[data-shimmer="all"] .think-step::before {
  opacity: 1;
  animation: think-shimmer 11s linear infinite;
}

/* NOT SHIPPING, kept switchable. The panel carries the light on its own now:
 * with all four running there were three of these pulsing directly under the
 * reading line at nearly twice the panel's peak brightness, and something
 * brightened beside the copy every ~3.7 seconds. The panel's is one event every
 * 32 seconds at the outer edge, in peripheral vision, which is the difference
 * between a section that is alive and a section that fidgets.
 *
 * The cards keep their hover brighten. Hover is asked for by the reader, so it
 * is not competing for attention that was pointed somewhere else.
 *
 * VARIANT C — all three running, but offset by a third of the loop each.
 *
 * Negative delays so every card is already mid-circuit on load rather than
 * queueing up. This is the one that ended up shipping: see the note at the
 * bottom of this block. */
.think-steps[data-shimmer="stagger"] .think-step::before {
  opacity: 1;
  animation: think-shimmer 11s linear infinite;
}

/* The copy card's travelling ring. One card now rather than two -- the monitor
 * lost its card, so the nth-child offset that used to stagger the pair is gone
 * with it. */
.goat-card::before {
  opacity: 1;
  animation: think-shimmer 13s linear infinite;
}
.think-steps[data-shimmer="stagger"] .think-step:nth-child(2)::before { animation-delay: -3.67s; }
.think-steps[data-shimmer="stagger"] .think-step:nth-child(3)::before { animation-delay: -7.34s; }

/* VARIANT B — one at a time, each taking a turn. One 33s cycle per card, offset
 * by a third each, with the light only alive during its own slot. */
@keyframes think-shimmer-turn {
  0%             { --shimmer-angle: 0deg;   opacity: 0; }
  3%             { opacity: 1; }
  28%            { opacity: 1; }
  33%, 100%      { --shimmer-angle: 360deg; opacity: 0; }
}

/* opacity:0 on the resting state, and it is load-bearing rather than tidy.
 * Cards 2 and 3 spend their first 11 and 22 seconds inside animation-delay,
 * during which no keyframe applies and the element falls back to its base
 * style -- so without this they sat there showing a stationary highlight at
 * angle 0, which is exactly the "printed" look the whole effect exists to
 * avoid. The keyframes bring it up for that card's own turn. */
.think-steps[data-shimmer="cycle"] .think-step::before {
  opacity: 0;
  animation: think-shimmer-turn 33s linear infinite;
}
.think-steps[data-shimmer="cycle"] .think-step:nth-child(2)::before { animation-delay: 11s; }
.think-steps[data-shimmer="cycle"] .think-step:nth-child(3)::before { animation-delay: 22s; }

@media (prefers-reduced-motion: reduce) {
  .think-step::before { animation: none !important; opacity: 0; }
}

/* Stacked, and the aspect lock comes off with it. An aspect-locked section that
 * has become one column is a very tall empty box; the lock is there to hold a
 * two-column composition together and there is no composition to hold once the
 * columns are gone. */
@media (max-width: 760px) {
  .think {
    aspect-ratio: auto;
    grid-template-columns: 1fr;
    /* Both of these have to be reset with the aspect ratio: grid-template-rows
     * was sized for one full-height row, and centring a figure vertically only
     * means something while the row has a height to centre it in.
     *
     * There was a padding-top: 88px here too, left over from when this section
     * cleared the fixed bar with its own padding. That clearance is now the
     * panel's top margin, so the padding was adding a second gap and breaking
     * the uniform 24px this panel shares with the grid below. */
    grid-template-rows: auto auto;
    align-items: start;
    justify-items: center;
  }
  /* ONE WIDTH FOR THE WHOLE STACKED RANGE. This used to be 94% here and 70%
     below 460, and the step was leftover rather than a decision: 94% existed to
     keep the figure wide enough that the laptop sentence, sized at 27px in the
     drawing's units and scaled by --k, stayed above 11px. The sentence no
     longer scales with the figure (see .think-line below), so the reason the
     step was protecting is gone.

     Removing it removes the discontinuity it caused. At 460 the figure used to
     jump 265 -> 356px in a single pixel of viewport and the sentence reflowed
     with it. Now the width is one continuous expression from the narrowest
     phone to the point the panel un-stacks.

     The 470px cap is unchanged and still binds from about 753px up, so the wide
     end of this range renders exactly as it did before. */
  .think-figure { height: auto; width: min(70%, 470px); }
  /* THE RATIO HAS TO BE STATED, and the base cap has to go with it.
     Stacked, the art is `width: 100%` of the column while `max-height` from the
     base rule still applies -- and that cap is a fraction of the VIEWPORT, so
     on a narrow screen it resolves far below the height these proportions ask
     for. Width held, height clamped, and the engraving rendered flattened:
     275x140 at 375px against a natural 1200x1632, a ratio of 1.96 where it
     should be 0.735. Two and two thirds times too wide for its height.

     `max-height: none` is the actual fix -- with the cap still in play the
     ratio below would just be overridden by it again. The ratio is then stated
     from the file's real pixels rather than left to the width/height
     attributes, which said 853x1844 and were stale: they describe neither the
     current file nor its shape, and the browser had been deriving its default
     ratio from them. */
  .think-art {
    height: auto;
    width: 100%;
    max-height: none;
    aspect-ratio: 1200 / 1632;
  }

  /* THE TYPE SIZE COMES OFF --k, FOR THE WHOLE STACKED RANGE. Everything inside
     .think-screen is scaled by `transform: scale(var(--k))`, so a font-size here
     is in the DRAWING's units and renders at that many times --k. A bare `11px`
     would render at about 2.6px on a phone. Dividing by --k cancels the scale,
     so the rendered size is 11px flat however small the figure gets.

     The POSITION stays in --k space and is untouched: the box is still the same
     238x235 quad mapped onto the screen by the homography. Only the type inside
     it stops shrinking. That is what lets the figure narrow without hiding the
     sentence, and the sentence is the point of the figure. */
  .think-line { font-size: calc(11px / var(--k)); }

  /* Was display:none while this column was empty. It carries the section's
   * only headings and all of its copy now, so it stacks under the figure
   * instead. Left-aligned, not centred with the figure: centred body text is
   * harder to read because every line starts in a different place. */
  /* The 56ch cap stays. It was overridden here back when this column was empty
   * and later full-bleed, which let the stacked copy run to 79 characters a
   * line -- the measure has to mean the same thing at every width or it is not
   * a measure. Left-aligned rather than inheriting the grid's centring, because
   * centred body text starts every line in a different place. */
  /* CONTINUOUS, not a step. These were 24/28 here and 12/12 below 460, which
     put a 28px lurch in the stack at exactly the width the figure used to jump
     at. Interpolating instead keeps both ends where they were -- about 12 at
     375, the old 24/28 by the time the panel un-stacks -- with nothing to cross. */
  .think { row-gap: clamp(12px, 3.2vw, 24px); }
  .think-copy {
    justify-self: start;
    margin-top: clamp(12px, 3.7vw, 28px);
  }
}

/* Below this the laptop is small enough that the sentence renders under WIF
 * Par's 11px floor, where its counters close and it turns to grey. Hidden
 * rather than scaled: the alternative is enlarging the figure past what the
 * column can hold, which distorts the composition to rescue three words. The
 * screen simply reads as off. */

@media (prefers-reduced-motion: reduce) {
  .think-caret { animation: none; opacity: 1; }
}

/* ==========================================================================
   Hero
   ==========================================================================
   Composition after titan-dev.webflow.io/about, measured on their page rather
   than read off their CSS: `.ab-hands` declares `width: calc(100% + 2.5em)`,
   which reads like a bleed, but the container's padding nets out to 75px of
   clear margin each side at a 1470 viewport — 5.1% a side, 89.8vw wide. Their
   hands do not move on scroll and carry no media queries.

   Ours differ in the one way that matters: the hands are split into halves and
   pushed apart, and the Vault stands in the gap. */

.hero {
  position: relative;
  /* Out of the body's 16px side padding: the arms are meant to reach the
     viewport edge, and 16px of gutter is 16px of not doing that. */
  margin: 0 -16px;
  /* 132px of this used to be the globe's room. The globe is parked, and what
   * sits under the facade now is the explanation, which brings its own margin —
   * so the padding drops to 12px. With .goat contributing 28px of top margin
   * that makes 40px of air below the band and 40px above it, which is what
   * makes the bled hands read as a band rather than as a gap.
   *
   * The hand asset also carries 81px of transparent margin below its ink (11.2%
   * of its height), so there is air under the arms before any padding is added;
   * more here would be paying for it twice. */
  padding: 40px 0 12px;
  overflow: hidden;
}


/* Hand, building, hand, edge to edge. The hero is its own band across the page
 * rather than a column with margins, so the arms bleed off both sides and the
 * only clear air is the gap either side of the Vault.
 *
 * The two hand widths are NOT equal and must not be. They are the two halves of
 * one photograph, cut at the measured gap between the fingertips (862px and
 * 938px of 1800), so equal CSS widths would render them at different scales and
 * one hand would be visibly larger than the other. The percentages below hold
 * 862:938, which is what keeps both hands at one scale. */
.hero-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin: 0 auto;
}

.hero-hand {
  display: block;
  flex: 0 0 auto;
  height: auto;
}

/* 61.2% of the stage shared in the source ratio, with 2.4% of clear air on each
 * side of the Vault. The four numbers sum to 100 and that is what puts the
 * fingertips at the edges. */
.hero-hand-left  { width: 29.31%; margin-right: 2.4%; }
.hero-hand-right { width: 31.89%; margin-left: 2.4%; }

/* ---- the Vault ---------------------------------------------------------- */

/* A container, so everything mounted on the facade is sized in cqw -- a
 * fraction of the building rather than of the viewport. The lettering and the
 * counter have to stay on their stone bands at every width, and the bands are a
 * fixed fraction of the drawing. */
.vault {
  position: relative;
  flex: 0 0 auto;
  width: 34%;
  min-width: 260px;
  container-type: inline-size;
}

/* Above the counter in the stack. This is what mounts the mechanism IN the
 * building rather than on it: the counter sits behind the facade, so it is seen
 * through the portico openings and the columns cross in front of it. The
 * interior alpha is low enough for the digits to read through the stipple. */
.vault-facade {
  display: block;
  position: relative;
  z-index: 2;
  width: 100%;
  height: auto;
  pointer-events: none;
}

/* Under the facade, in the space beneath the plinth. Centred on the section,
 * which is where the building is, and held to the same measure and body
 * treatment as the panels above so it reads as the same page. */
/* The footnote to the vault. Two elements in a row rather than stacked, which
 * is what reclaims the height: the block is now as tall as the taller of the
 * two rather than the sum of both.
 *
 * The -1.29% lives here, on the wrapper, rather than on each child. The hands
 * are 29.31% and 31.89% wide -- two halves of one photograph cut at the
 * measured gap -- so the facade's centre is at 48.71% of the stage, and the
 * whole block hangs off that line rather than the section's.
 *
 * It is wider than the facade and that is fine: the hands' ink stops well above
 * this row, so there is nothing here to collide with. */
.vault-footnote {
  position: relative;
  /* Right padding rather than a negative `left`. Both centre the content on the
   * facade's 48.71% line, but `left` moved the whole BOX 19px outside .hero,
   * which has overflow:hidden -- nothing visible was being clipped, because the
   * content sits well inside, but the box was hanging out of its clipping
   * parent waiting to catch something. Padding shifts the content and leaves
   * the box where it belongs: centring inside a box with 2.58% of right padding
   * puts the midpoint at exactly 48.71%. */
  padding-right: 2.58%;
  margin: 26px auto 0;
  /* No horizontal padding here at desktop: .world-band is sized as a percentage
   * of this box, so padding would silently shrink it -- 24px each side took the
   * band from its intended 500px to 483. The narrow-width rule adds the gutter
   * back where it is actually needed. */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(24px, 3.4vw, 56px);
}

.vault-footnote[data-order="text-first"] { flex-direction: row-reverse; }

/* LEFT, not centred, and the measure is measured rather than picked.
 *
 * Centred setting gives a block two ragged edges and no edge to align to
 * anything, which is why it read as floating beside the globe rather than set
 * against it. Left-aligned it has one hard edge at a fixed distance from the
 * globe, and that edge is what relates the two.
 *
 * The width was `56ch`, which rendered as 571px and broke the middle paragraph
 * as [58, 66, 7] -- a seven-character last line carrying "bounty." on its own.
 * `ch` is the width of "0", and in WIF Par the figures are tabular at 600 units
 * against roughly 500 for lowercase, so a ch-based measure overstates by about
 * a fifth and cannot be reasoned about. Swept 420-700px and counted the real
 * characters per line at each: 600px is the narrowest width where no paragraph
 * ends on an orphan, and it holds the longest line to 68 characters, inside the
 * 45-75 band. The three paragraphs break [57] [63, 68] [65, 39].
 *
 * flex-basis rather than max-width, so the left edge sits at 600px regardless
 * of how long the copy happens to be -- a shrink-to-fit block moves its edge
 * every time a word changes. It may still shrink on a narrow viewport. */
.vault-explain {
  flex: 0 1 600px;
  margin: 0;
  text-align: left;
  font-size: clamp(0.9375rem, 1.2vw, 1.0625rem);
  line-height: 1.62;
  color: var(--text-muted);
  font-variation-settings: "FLAT" 0;
}

.vault-explain p { margin: 0 0 12px; }
.vault-explain p:last-child { margin-bottom: 0; }



/* ---- inscriptions ------------------------------------------------------- */

/* Two of them, and they share one rule about vertical placement.
 *
 * OPTICAL, NOT MEASURED. Carved lettering sits low in its band: a mason cuts
 * with more stone above the letters than below, because a band read from below
 * with its type measured to the middle always looks high. So both inscriptions
 * are set from the bottom of their band rather than centred in it, and the
 * offset is in em so it tracks the type rather than the container. */
.vault-name,
.vault-line {
  position: absolute;
  z-index: 3;
  margin: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  line-height: 1;
  text-transform: uppercase;
  /* DELIBERATE, and not the v1 workaround the font's README says to delete.
   * That instruction exists because v1 had no lowercase; v1.1 does, and it is
   * used everywhere else on the page. These two strings are carved inscriptions
   * on a classical facade, where capitals are the convention -- the case is a
   * design decision about two elements, not a global suppression. Nothing else
   * in this stylesheet transforms case. */
  color: #efeae0;
  font-variation-settings: "FLAT" 0;
}

/* The broad blank entablature -- the wide stone band directly above the
 * capitals, measured at 10.4%-20.5% of the drawing's height. NOT the narrow
 * cornice above it, which is the projecting lip at the very top and sits
 * outside the building's face. */
.vault-name {
  top: 10.4%;
  height: 10.1%;
  left: 6%;
  right: 6%;
  /* Measured on the render, not guessed. The frieze field -- the faint hatched
   * band between the cornice rule and the mouldings below it -- runs 11.0% to
   * 19.2% of the drawing. Capitals are exactly 0.70em with no descender, so at
   * this size they leave ~11px of slack in the field; this padding puts roughly
   * 6.5 of it above the letters and 4.5 below, which is the carved proportion.
   * Measured-centre put 3 above and 8 below and read high. */
  padding-bottom: 0.03em;
  /* Floored. WIF Par's capitals are drawn for 72px and above and its own floor
   * is 11px; pure cqw takes this to ~10.7px on a 400px screen. 13px still fits
   * the band in both directions at the narrowest vault. */
  font-size: max(13px, 6.6cqw);
  letter-spacing: 0.14em;
  text-indent: 0.14em;          /* the tracking hangs off the last letter */
}

/* The stepped plinth. Unlike the entablature there is no blank band here -- the
 * base is dense hatching all the way down, and light letterspaced type laid
 * straight on it is unreadable. So the inscription gets what a real one has: a
 * panel cut into the stone. The recess is drawn rather than assumed, and its
 * ends are masked to nothing so it reads as incised rather than as a plate
 * screwed to the front. */
.vault-line {
  top: 81.8%;
  height: 6.4%;
  left: 14%;
  right: 14%;
  /* Same optical rule as the entablature above: set low in the band rather
   * than measured to its middle. */
  padding-bottom: 0.16em;
  font-size: 3.4cqw;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  color: #e8e2d6;
}

.vault-line::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: #050403cc;
  box-shadow: inset 0 2px 7px #000000, inset 0 -1px 0 #ffffff12;
  -webkit-mask-image: linear-gradient(90deg, #0000, #000 14%, #000 86%, #0000);
          mask-image: linear-gradient(90deg, #0000, #000 14%, #000 86%, #0000);
}

/* ---- the counter, set into the stone ------------------------------------ */

/* Behind the facade. Vertically it sits in the portico openings, which run from
 * below the capitals (30%) to the column bases (80%); the row is centred at
 * 52%, in the middle of that run. */
.vault-counter {
  position: absolute;
  z-index: 1;
  top: 52%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-variation-settings: "FLAT" 0;
}

/* MOUNT A -- one digit per bay.
 *
 * The building has six columns and therefore five openings, and the count is
 * five digits. That is not arranged: 10,000 is five digits and the facade has
 * five bays, so each digit gets its own window and no column crosses a numeral.
 * Cell centres are the measured bay centres (17.5 / 33.5 / 50 / 65.5 / 81.5%),
 * which are evenly spaced 16% apart; the narrowest bay is 7% wide, so that is
 * the cell width. */
.vault-counter[data-mount="bays"] .flap,
.vault-counter[data-mount="bays"] .flap-sep {
  position: absolute;
  top: 50%;
  left: var(--x);
  transform: translate(-50%, -50%);
}

/* No border and no corner radius in this mount. The portico opening is already
 * a frame, and drawing a second one around each numeral was what made the count
 * read as chips laid on the facade -- the eye saw the box and the numeral as two
 * competing rectangles even though the cells were on their bay centres to the
 * pixel. What is left is a recessed panel and the hinge seam.
 *
 * The cell is 7cqw because that is the narrowest bay: uniform across all five,
 * so no panel crowds a column, and the numeral never has to be read against
 * fluting. */
.vault-counter[data-mount="bays"] { font-size: 9.4cqw; }
.vault-counter[data-mount="bays"] .flap {
  width: 7cqw;
  height: 1.34em;
  border: 0;
  border-radius: 0;
  background: #06050480;
  box-shadow: inset 0 3px 10px #000000, inset 0 -3px 10px #00000099;
}

/* MOUNT B -- the digits span the portico and the columns overlap in front.
 *
 * Larger numerals, read through the colonnade rather than framed by it. */
.vault-counter[data-mount="span"] {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.1em;
  font-size: 11cqw;
}
.vault-counter[data-mount="span"] .flap { width: 0.78em; height: 1.18em; }
.vault-counter[data-mount="span"] .flap-sep { font-size: 0.8em; }

/* One split-flap cell. Recessed dark metal seen through the opening, rather
 * than a card laid on the front of the building -- which is what it read as
 * while the counter sat above the facade in the stack. */
.flap {
  position: relative;
  overflow: hidden;
  border: 1px solid #ffffff26;
  border-radius: 2px;
  background: #0b0a09;
  color: #faf6ec;
  box-shadow: inset 0 2px 7px #000000cc;
}

/* THE SPLIT. Each tile carries four layers, and every one of them is a half of
 * a digit clipped at the tile's midline:
 *
 *   .is-top    static, showing the INCOMING digit's top half
 *   .is-bottom static, showing the OUTGOING digit's bottom half
 *   .leaf-top  the outgoing top half, hinged at the midline, falls away
 *   .leaf-bottom the incoming bottom half, hinged at the midline, swings up
 *
 * The two static halves are what makes the halfway point a real state rather
 * than a pose: at the moment the falling leaf is edge-on, what is left on the
 * tile is the new digit's top over the old digit's bottom -- exactly what a
 * departure board shows mid-flip.
 *
 * The glyph inside each half is a FULL-HEIGHT box, positioned so its top or
 * bottom edge meets the midline. That is what makes the two halves parts of one
 * character rather than two characters cropped: the digit is centred in the
 * tile once, and each half reveals its own portion of that one centring. */
.flap-half {
  position: absolute;
  left: 0;
  right: 0;
  height: 50%;
  overflow: hidden;
  background: inherit;
  backface-visibility: hidden;
}

.flap-half.is-top    { top: 0; }
.flap-half.is-bottom { bottom: 0; }

.flap-glyph {
  position: absolute;
  left: 0;
  right: 0;
  height: 200%;                /* the whole tile, so the digit centres once */
  display: grid;
  place-items: center;
}

.flap-half.is-top .flap-glyph    { top: 0; }
.flap-half.is-bottom .flap-glyph { bottom: 0; }

/* The moving leaves sit above the static halves and are hinged ON the midline.
 * z-index 3 clears the hinge hairline at 2, so a falling leaf passes in front
 * of it rather than being cut by it. */
.flap-leaf {
  z-index: 3;
  transform-style: preserve-3d;
}

.flap-leaf.is-top    { transform-origin: 50% 100%; }
.flap-leaf.is-bottom { transform-origin: 50% 0%; }

/* Perspective on the tile, so the leaf foreshortens as it turns instead of
 * simply squashing. Small, because a departure board is read straight on. */
.flap {
  perspective: 220px;
}

/* Kept: some callers still write a plain value into a tile. */
.flap-value {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

/* The hinge line. One hairline, not a bevel. */
.flap::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: #00000099;
  box-shadow: 0 1px 0 #ffffff1a;
  z-index: 2;
}

.flap-sep {
  align-self: center;
  padding: 0 0.06em;
  color: #f4efe4b0;
}





/* PROVISIONAL — narrow-width behaviour is still the open decision. */
@media (max-width: 700px) {
  .hero { padding-top: 20px; }
  .hero-stage { width: 100%; }
  /* Widths hold the assets' 862:938 so both halves render at one scale; 26.8/29.2
     was 0.9178 against the assets' 0.9190. */
  .hero-hand-left  { width: 27.009%; margin-right: 1.6%; }
  .hero-hand-right { width: 29.391%; margin-left: 1.6%; }
  .vault { width: 40.4%; min-width: 0; }
}

/* Below this width the inscription cannot stay in the stone. The plinth is a
 * fixed fraction of a building that is itself only ~160px wide on a phone, so
 * cqw sizing puts the lettering at about 5.5px -- less than half the face's
 * stated 11px floor, where the counters close and it goes to grey. Shrinking it
 * to fit would publish a line nobody can read.
 *
 * So it comes out of the plinth and sits under the building at the floor size,
 * losing the carved recess and keeping the sentence. The facade still reads top
 * to bottom; only the mounting changes. */
@media (max-width: 760px) {
  .hero-stage { margin-bottom: 30px; }

  .vault-line {
    top: 100%;
    height: auto;
    left: -12%;
    right: -12%;
    padding: 9px 0 0;
    font-size: 11px;
    letter-spacing: 0.18em;
    text-indent: 0.18em;
    color: #efeae0a8;
  }

  .vault-line::before { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .flap-value { transition: none !important; }
}

/* ==========================================================================
   The price, and the two places digits have to hold still
   ========================================================================== */

/* Shares the instruction's line and sits hard right, directly above the votes
   pill. A fact on the page, not a feature: the instruction's own size, weight
   and muted ink, no border, no background, nothing that makes it a component. */
.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
}
.panel-head .panel-subtitle { margin: 0; flex: 1 1 22ch; }

.control-price {
  margin: 0;
  margin-left: auto;          /* hard right even when it wraps to its own line */
  text-align: right;
  white-space: nowrap;
  /* NO LONGER MATCHED TO THE INSTRUCTION BESIDE IT. It was 13px/400/muted so it
     read as part of that line; it is now its own thing on the row.

     THE BOLD IS SYNTHETIC AND THERE IS NO WAY AROUND IT. WIF Par ships one
     axis, FLAT, and one weight: usWeightClass 400, named instances Regular and
     Flat100, and the @font-face declares font-weight: 400. There is no 700 to
     select, so the browser dilates the 400 outlines itself. */
  font-size: 15px;
  font-weight: 700;
  color: var(--text-primary);
  /* THE STRING MUST NOT CHANGE WIDTH when a digit changes, or the right edge
     twitches every thirty seconds beside a votes pill that is holding still.
     WIF Par needs no help with this: every digit in the file is 600 units, so
     the face is tabular by construction and its `tnum` feature does not exist
     to turn on -- the only OpenType feature in the font is `kern`, checked.
     This declaration is for the FALLBACK. Georgia ships proportional oldstyle
     figures, so on the one load where the webfont has not arrived the number
     would jitter and sit below the baseline without it. */
  font-variant-numeric: tabular-nums;
}

/* The same, for the same reason: the count beside it changes as votes land. */
.control-count { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   The live event
   ==========================================================================

   The only filled and the only coloured element in the bar. The Vault and the
   outbound links are untouched: still outline, still muted.

   IT WAS NEVER TALLER. Measured against The Vault: both 32px high, both at
   top 28, both 0 18px padding, both 160px radius, both 14px/400. Identical
   boxes. What made it read as a different component is that .topbar-nav is
   itself a 32px pill with 0 vertical padding, so a SOLID fill spans its
   container edge to edge -- gap above 0, gap below 0 -- while its neighbour is
   just text on the faint ground. A filled segment that touches both rims of the
   thing holding it looks like it is bulging out of it.

   So the fill is inset instead. The item keeps the nav's own 32px box, and the
   lime is painted 3px in from the top and bottom, the way a selected segment
   sits inside a segmented control. Nothing about the type or the layout moves,
   and the two items still occupy identical boxes.

   THE INK IS DARK, AND THAT IS A DEPARTURE FROM THE BRIEF. The cream that was
   asked for measures 1.08:1 on this green -- not low contrast but text nobody
   can read. The page's near-black gives 16.51:1, and it is what Pons put on it
   themselves. Both measured. */
.topbar-nav a.is-live {
  position: relative;
  isolation: isolate;
  background: none;                 /* the fill is the ::before, inset */
  color: var(--bg-page);
  box-shadow: none;                 /* flat. the colour stops at the pill edge */
}

/* The fill. Flat lime, no gradient in the base, inset from the container's rims
   so it sits inside the nav rather than filling it. */
.topbar-nav a.is-live::before {
  content: "";
  position: absolute;
  inset: 3px 0;
  border-radius: 160px;
  background: var(--pons-green);
  transition: background var(--duration-fast) ease;
  z-index: -1;
}

/* THE SWEEP IS DARK, and it had to move off the border to exist at all.
   The card's ring is a light highlight travelling an outline; once the pill is
   filled lime there is no outline left to light, and a light sweep on #d4fc50
   has nowhere brighter to go. So it is a shadow passing across the fill
   instead: same device, same reason, the only thing that reads on a solid
   light ground.

   THE CARD'S RING IS UNCHANGED -- this is a second element, not a change to
   .think::before/.goat::before, which still carry the conic ring exactly as
   they did.

   Same 32s linear as think-shimmer so the two stay one device: slow enough to
   be invisible while somebody reads the rest of the bar, and there if they look
   at it.

   IT CANNOT TOUCH THE LABEL. z-index -1 puts it under the text, and the label
   stays #0d0d0d at full opacity throughout. Measured rather than guessed at:
   peak alpha 0.14 composites the lime to rgb(182,217,69), and the label reads
   12.03:1 on that against 16.51:1 on the clean fill. Nothing about the ink
   changes as the sweep passes. */
.topbar-nav a.is-live::after {
  content: "";
  position: absolute;
  inset: 3px 0;
  border-radius: 160px;
  overflow: hidden;
  background: linear-gradient(100deg,
    rgb(0 0 0 / 0) 0%,
    rgb(0 0 0 / 0.14) 50%,
    rgb(0 0 0 / 0) 100%);
  background-size: 55% 100%;
  background-repeat: no-repeat;
  animation: live-sweep 32s linear infinite;
  pointer-events: none;
  z-index: -1;
}

/* Left to right and round again. A background-position sweep rather than a
   transform, so nothing is painted outside the pill and no overflow clip is
   needed on an element that also carries the radius. */
@keyframes live-sweep {
  from { background-position: -60% 0; }
  to   { background-position: 160% 0; }
}

/* Fill brightness only. No size change, and the sweep carries on as it was. */
.topbar-nav a.is-live:hover::before,
.topbar-nav a.is-live:focus-visible::before { background: #dcff6e; }
.topbar-nav a.is-live:hover,
.topbar-nav a.is-live:focus-visible { color: var(--bg-page); }

.topbar-nav a.is-live:active { transform: translateY(1px); }

@media (hover: none) {
  .topbar-nav a.is-live:hover::before { background: var(--pons-green); }
}

/* Flat lime pill, nothing moving. The hover colour change stays. */
@media (prefers-reduced-motion: reduce) {
  .topbar-nav a.is-live::after { animation: none; display: none; }
  .topbar-nav a.is-live:active { transform: none; }
}


/* ==========================================================================
   Mobile type floor and chrome
   ========================================================================== */

/* 11px is below comfortable reading on a phone. Raised where it is read, not
   scaled uniformly: 12 for labels and small print, 14 for the copy that
   actually carries the instruction. Desktop keeps its own sizes. */
@media (max-width: 699px) {
  /* Small print: scanned, not read. A truncated hex and an inscription. */
  .card-address-text,
  .vault-line { font-size: 12px; }

  /* The line that tells someone how to vote. Body copy, so it is in rem like
     the rest of the prose -- 0.875rem is the same 14px at a default root. */
  .panel-subtitle { font-size: 0.875rem; }

  /* 24px each side is a desktop container's padding. At 375 the body's 16
     plus this was 80px of chrome, 21% of the viewport. 12 here gives 24px of
     content width back without letting anything touch the screen edge -- the
     body's 16 still does that job. */
  .panel,
  .think { padding: 12px; }
}

/* ==========================================================================
   The footer
   ========================================================================== */

/* THE LAST BLOCK ON THE PAGE. Same 1360 column as the panel and the hero, the
   same ground, and one hairline above it in the same --panel-border every
   surface on this site is drawn with. No card, no fill: it is the end of the
   page rather than another thing on it. */
/* The rail the bubble sits on. No hairline of its own any more: the bubble has
   a border, and a rule above a bordered box is two edges doing one job. */
.sitefoot {
  margin: 64px auto 0;
  /* No bottom padding of its own: body already carries 64px below everything,
     and adding to it left empty ground under the last line on the page. */
  padding: 0 16px;
}

/* ONE BUBBLE AROUND ALL OF IT -- the mark, the three columns and the link --
   rather than one per column, which would read as three notices instead of one
   disclaimer.
 *
 * THE SURFACE IS THE KOL CARDS', NOT A NEW ONE. Those cards declare a tint --
 * rgba(237 234 227 / 0.16) over the rgb(22 22 22) panel -- which PAINTS as
 * rgb(56.4 55.92 54.8). This footer sits on the bare page ground at rgb(13 13
 * 13), so re-declaring that tint here would composite to rgb(48.8 48.4 47.2)
 * and land visibly darker than the cards it is meant to match. The literals
 * below are the cards' painted values stated outright, which is the only way
 * the two are the same colour on screen. Same reasoning as the note on
 * .goat-bubble; the arithmetic is in the test.
 *
 * The blue channel is one lower than the other two on purpose: the cards' tint
 * is warm, and neutralising it here would be a new surface value.
 *
 * The border is rgb(81 80 79), not 80: --card-border is #ffffff1f, whose alpha
 * is 31/255 = 0.121568, but getComputedStyle serialises that to 0.12 and the
 * shorter number blends to 80.23 instead of 80.54. Chrome paints with the full
 * precision. Read the border off a computed style and you round the wrong
 * way. */
.sitefoot-inner {
  max-width: 1360px;
  margin: 0 auto;
  padding: 28px 32px 32px;
  border-radius: var(--radius-card);
  border: 1px solid rgb(81 80 79);
  background: rgb(56 56 55);
}

/* The topbar's treatment, which is the only wordmark this site has: same face,
   same weight, same tracking. Read off .topbar-mark rather than invented. */
/* ABOVE THE COLUMNS, not beside them. The columns then run the bubble's full
   width, which is what lets three of them hold a paragraph each without the
   middle one wrapping to eight lines. */
.sitefoot-mark {
  margin: 0 0 24px;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  font-variation-settings: "FLAT" 0;
  white-space: nowrap;
}

/* Three columns of legal copy. `minmax(0, 1fr)` rather than `1fr`: the
   paragraphs are long and a bare 1fr floors at the longest unbreakable word,
   which on "Robinhood Markets, Inc." is wide enough to push the third column
   out of the row. */
.sitefoot-legal {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}

/* --text-muted, not --text-subtle. This is the quietest thing on the page and
   it is still legal copy somebody may need to read in full; the subtle token is
   for links and captions that can afford to recede. Measured against the page
   ground rather than guessed -- see the note in the test. */
.sitefoot-legal p {
  margin: 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--text-muted);
}

.sitefoot-links {
  margin: 24px 0 0;
  font-size: 12px;
  /* Two items and a middot on one row. Flex rather than inline text so the
     space either side of the separator is one gap value and cannot drift. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 10px;
}

/* FULL INK, not the secondary colour. It was --text-subtle at 0.85 opacity,
   which composites to a lift of 78 per 255 -- quieter than the legal copy above
   it, and the only link in the footer reading as disabled text. The rule is the
   same one the docs page uses on its links: the site's ink with a quiet
   underline that brightens on hover, so it is legible as a link at rest rather
   than only once somebody happens to point at it. */
.sitefoot-links a {
  color: var(--text-primary);
  text-decoration: none;
  letter-spacing: 0.04em;
  font-variation-settings: "FLAT" 0;
  /* The address is never shortened, ellipsed or broken mid-word at any width.
     If the row runs out of room it wraps BETWEEN the two items -- see the
     stacking rule below -- rather than shrinking or truncating the type. */
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease-out);
}

.sitefoot-links a:hover { color: #fff; }
.sitefoot-links a:focus-visible {
  outline: 2px solid var(--text-muted);
  outline-offset: 3px;
  border-radius: 2px;
  color: var(--text-primary);
}

/* Below 900 the three columns become one and the mark sits above them. One
   column for the whole footer does both: the mark is simply the first row. */
/* Below 900 the three columns become one. The mark is already above them, so
   the order is unchanged -- only the columns move. */
/* The middot, DRAWN RATHER THAN TYPED. WIFPar covers no U+00B7 -- see
   WIFPAR_COVERAGE in tests/test_kols.py -- so the character would silently come
   from a fallback face, in the one row that is supposed to read as a single
   unit. A 3px dot is the same mark with no second font in it, and it cannot be
   selected or copied into a pasted address either.

   Centred on the row rather than sitting on the baseline, which is where a
   typed middot would sit and where a round dot looks dropped. */
.sitefoot-sep {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--text-subtle);
  align-self: center;
  flex: 0 0 auto;
}

/* Below 336px the two items no longer fit on one row -- measured: they need
   232.6px and the bubble's inner width is viewport minus 104. Left to wrap on
   its own the address would drop to a second line and leave the middot dangling
   at the end of the first, so the row stacks and the separator goes instead.
   The type does not shrink and the address is never shortened. 375 and up is
   one row; this only reaches 320px phones. */
@media (max-width: 336px) {
  .sitefoot-links { flex-direction: column; gap: 8px 0; }
  .sitefoot-sep { display: none; }
}

@media (max-width: 900px) {
  .sitefoot-inner { padding: 24px 20px 28px; }
  .sitefoot-legal { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .sitefoot-links { margin-top: 20px; }
}
