/* ==================================================================
   worldisflat.fun — page shell
   Structure pass. Hero card + reserved band. No content cards yet.
   ================================================================== */

/* --- Type ---------------------------------------------------------- */

@font-face {
  font-family: "WIF Par";
  src: url("../fonts/WIFPar-Flat0.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens -------------------------------------------------------- */

:root {
  /* page */
  --cream: #F2EFE7;
  /* Rebuilt on the World is Fair surface system rather than ported.
     ground 13, surfaces 22 (delta 9), hairlines white at 12% doing the
     work the fill does not, ink white and white-at-58% for secondary. */
  --ground:    rgb(13, 13, 13);
  --surface:   rgb(22, 22, 22);
  --hairline:  rgba(255, 255, 255, 0.12);
  --ink:       rgb(242, 242, 242);
  --ink-quiet: rgba(242, 242, 242, 0.58);

  /* measured off the plate — nothing here is pure white */
  --sky-deep:  #757F79;
  --sky-mean:  #949B8E;
  --cloud:     #C5C3B3;
  --haze:      #9F9E8B;

  /* Was 8px with a min-width:641px override to 16. One continuous value
     instead: 2.5vw reaches 16 at exactly 640, so the two ends are unchanged
     and the middle now moves. */
  --inset:  clamp(8px, 2.5vw, 16px);
  --radius: 28px;         /* hero card */
  --radius-card: 20px;    /* content cards, same family a step down */

  --band-pad: clamp(64px, 8vw, 120px);
  /* Was 80px with a max-width:640px override to 20. 5.556vw reaches 80 at
     exactly 1440, so the reference width is unchanged; below it the rails
     close in continuously instead of stepping. .fine and .band__rules share
     this variable, so they stay aligned to each other at every width. */
  --rail-gap: clamp(20px, 5.556vw, 80px);   /* [aside.com] measured: rail sits 80px inboard
                             of the hero card's edge at 1280 */

  /* Card geometry, structure from reference goat.png.
     Both dimensions of the block are pinned: 92% of the hero's inner
     width, and the full run from under the nav to 40px above the hero
     card's bottom edge. The cards cross the cumulus bank by design. So
     card heights are derived from the space rather than from a fixed
     aspect — what is held is the masonry, the 1.6 tall:short
     relationship, and the flat 28px gap. */
  --card-blur:     4.5px;                    /* backdrop blur radius */

  --ease:      cubic-bezier(.22, 1, .36, 1);
  --pill-fill: 0.45s;                        /* fill + label */
  --pill-disc: 0.9s;                         /* the radial disc */
  --card-gap:      clamp(16px, 2vw, 28px);   /* 28 at 1440 and above */
  /* The fixed topbar's own height, and the air an in-page jump should leave
     under it. Named --topbar-* deliberately: --nav-h already exists above at
     56/64px and feeds --block-h's hero maths, so reusing it here would have
     silently resized the hero. */
  /* [reference] type scale, read off the live page at 1280/1440/1920/2560.
     It is fluid on vw with a hard cap, and the caps land between 1416 and
     1484 — so every role is at its ceiling by 1500. Coefficients measured,
     not guessed: 3.1vw/46, 2.7vw/40, 1.8vw/26, 1.25vw/18, 1.4vw/20, 1.2vw/17.
     vw, not cqw: the reference sizes against the viewport, and our cards run
     near full width, so the two agree. */
  /* Anchored at BOTH ends, not the ceiling only. The reference's coefficients
     were pure vw, whose floors did not clear until 903-1167px, so every role
     sat frozen at its minimum from 320 all the way to 900 — a 580px span in
     which the type did not move at all.

     Each line now passes through today's floor at 320 and today's value at
     1440, so 320 and 1440 are both unchanged and everything in between
     interpolates. --t-cardhead is gone: it had no consumers, the card head
     uses --t-subhead. */
  --t-section:  clamp(28px, calc(1.48571429vw + 23.24571429px), 46px);  /* 28 @320 -> 44.64 @1440 */
  --t-subhead:  clamp(18px, calc(0.70714286vw + 15.73714286px), 26px);  /* 18 @320 -> 25.92 @1440 */
  --t-label:    clamp(15px, calc(0.27777778vw + 14.11111111px), 18px);  /* 15 @320 -> 18 @1400, held to 1440 */
  --t-lead:     clamp(16px, calc(0.36082474vw + 14.84536082px), 20px);  /* 16 @320 -> 20 @1429, held to 1440 */
  --t-body:     clamp(14px, calc(0.27358247vw + 13.12453608px), 17px);  /* 14 @320 -> 17 @1417, held to 1440 */

  --topbar-h:   88px;
  --topbar-gap: 24px;

  /* Zoomed out 11%: 120vh -> 107vh, floor 620 -> 552. Real sizes, not a
     transform — scale() would soften the type, break the travelling
     light's offset-path geometry and put the hero in its own stacking
     context. Everything downstream (--block-h, the cards, the fan's
     container query, the pills) is derived from this, so it all follows.
     Reverted below 900: there the cards stack portrait and the fourth is
     off the fold either way, so the reduction cost height and gained
     nothing. */
  /* A floor AND a ceiling. 112vh with no ceiling made every card scale
     with the window: at 2560x1440 the hero was 1613px and the two copy
     cards carried 251px and 434px of empty ground under their last line.
     940 is where the hero stops having dead space of its own: with the
     cards content-sized the block needs ~899px, and 940 leaves exactly the
     40px of --cards-bottom clearance under it. Above 940 the surplus shows
     as a black band below the last card. The floor still overfills a
     900px-tall window, so the hero is still the whole first screen. */
  /* The hero chain — --hero-h, --nav-h, --cards-top, --cards-bottom,
     --block-h, --card-short, --card-tall, --hero-min, --vw-cap, --inner —
     is gone. .hero sets no height, and every branch terminated in a variable
     with zero consumers, so none of it reached a rendered property. */
  --page-max: 1920px;

  --gutter: clamp(20px, 4vw, 44px);
  --maxw:   1240px;

  --font-display: "WIF Par", Georgia, "Times New Roman", serif;
  --font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
}



/* --- Reset --------------------------------------------------------- */

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

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

/* --- Ground ----------------------------------------------------------
   One flat colour, no texture. The reference carries no tiling image at
   all — sampled std 0.000 across its ground — and gets its depth from
   stacked surfaces held by hairlines instead. The canvas takes the same
   value so an overscroll bounce shows no band.
-------------------------------------------------------------------- */
html {
  background-color: rgb(13, 13, 13);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  position: relative;                 /* origin for the ground layer */
  /* Body must be its own stacking context. Once html carries a background
     (for the overscroll canvas), body's background stops propagating to
     the canvas and paints in place instead — which buried the z-index:-1
     noise pseudo underneath it and flattened the ground to the bare base
     colour. Isolating body puts the pseudo back on top of body's own
     background, where it belongs. */
  isolation: isolate;
  /* The hero's 16px top margin would otherwise collapse out of body and
     leave the strip above the cloud card outside the ground layer. */
  display: flow-root;
}

/* No page noise layer: the reference has none. */


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

ul { margin: 0; padding: 0; list-style: none; }

a { color: inherit; text-decoration: none; }

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

/* --- Hero card ------------------------------------------------------
   Inset on all four sides, so the corners read against the cream.
   overflow:hidden clips the plate to the radius.
-------------------------------------------------------------------- */

/* --- MASTHEAD ---------------------------------------------------------
   The page's own title card, above the G.O.A.T. section. Same rails, same
   surface and the same padding scale as the two cards below it, so it
   reads as the first of a set rather than a different kind of thing.
-------------------------------------------------------------------- */
.masthead {
  margin: var(--inset) auto;
  /* Moved verbatim off .goat, literal and not var(--topbar-h): the token
     drops to 64 below 900 and this literal does not, so taking the token
     here would have lifted every narrow width by 24px. Same number, same
     place in the flow — nothing below it moves. */
  padding-top: 88px;
  width: calc(100% - 2 * var(--inset));
  max-width: calc(var(--page-max) - 2 * var(--inset));
  box-sizing: border-box;
}

.card--masthead {
  /* The same padding .card--goat and .card--idea take. */
  padding: clamp(20px, 2.4vw, 36px);
  container-type: inline-size;
  /* Clips the dither to the card's radius. .card already carries
     position:relative (the canvas's containing block) and
     isolation:isolate — without the isolate, a z-index:-1 child escapes
     the card's stacking context and paints behind the card itself,
     where nothing of it is visible. This card has no sweep pseudo to
     cut off, which is why overflow:hidden is safe here and is not on
     .card generally. */
  overflow: hidden;
}

/* --- the dither field ------------------------------------------------
   Sized and positioned here; drawn by dither.js, which owns the backing
   store and deliberately undersamples it — see that file's header.

   z-index:-1 is the whole placement: above the card's own background,
   below every one of its children, without needing a z-index on any of
   them. inset:1px keeps the ink off the border, which along the
   antialiased curve of the radius would otherwise meet it and read as a
   dirty edge. The radius is the card's less that 1px, so the canvas
   corner sits concentric inside the border rather than cutting it.

   width/height 100% with inset:1px is as specified: the box starts 1px
   in and still measures a full padding-box, so it overhangs by 1px at
   the right and bottom and the card's overflow:hidden takes that off.
   The clip lands at the padding edge, so the ink still never reaches
   the border. */
.dither {
  position: absolute;
  inset: 1px;
  border-radius: calc(var(--radius-card) - 1px);
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}

/* The head, spanning the full card above the two columns. Same shape as
   the other two sections' heads: title, then the quieter lines under it. */
.masthead__intro { margin: 0 0 clamp(18px, 2cqw, 28px); }

.masthead__title {
  margin: 0 0 20px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-section);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* Two lines, same treatment as .goat__lead — weight 400 at full ink, the
   size and not a bold doing the work. */
.masthead__lead {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-lead);
  line-height: 1.45;
  color: var(--ink);
}

/* --- the two columns, as one grid ------------------------------------
   Three rows shared by both columns: the button, the middle block, the
   body line. Each column is a subgrid spanning all three, so the two
   buttons sit on ONE row line and the two body paragraphs on another —
   by construction, not by matching two sets of paddings and hoping they
   stay matched when the copy changes.

   The alternative was a fixed min-height on the middle block, which is
   the thing that silently breaks the first time the prize figure gains a
   digit or the kicker gains a word.
-------------------------------------------------------------------- */
.masthead__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));   /* equal, by definition */
  /* button / figures / body */
  grid-template-rows: auto auto auto;
  column-gap: clamp(24px, 3cqw, 56px);                /* .card--goat's own column gap */
  row-gap: clamp(14px, 1.6cqw, 22px);
}

.masthead__col {
  display: grid;
  grid-row: 1 / span 3;
  /* The column's rows ARE the parent's rows. */
  grid-template-rows: subgrid;
  min-width: 0;
}

/* Explicit rows, not auto-placement. Both columns now carry three
   children, so auto-placement would land on the same answer today — and
   would stop the first time either side gains or loses one, silently,
   with the two columns sliding out of step. Naming the row is what makes
   "row 2 is the claim, row 3 is the line under it" a fact about the
   layout rather than a coincidence of child order.

   Row 2 is the pair that has to be argued for: it holds the left
   column's claim and the right column's prize figure, two different
   kinds of content deliberately given one track so they share a
   baseline. Row 3 pairs the two explanatory lines the same way. */
.masthead__cta     { grid-row: 1; }
.masthead__claim,
.masthead__figures { grid-row: 2; }
.masthead__text    { grid-row: 3; }

/* --- centred on each column's own axis -------------------------------
   The columns keep the positions and widths they already had: this is
   alignment inside each one, not the two of them moving together. Both
   are treated identically, so each reads as one centred stack rather
   than three items that happen to begin at the same edge.

   Three items, three mechanisms, because they are three kinds of box:

     the pill is content-sized, so the GRID centres it (justify-self);
     the figure line fills its column, so its INLINE content is centred
       (text-align);
     the body line fills its column too but is capped at 40.8em, so it
       needs both — auto margins to centre the capped BOX, and text-align
       to centre the lines inside it. With only text-align a capped box
       would still sit at the column's start and merely look centred at
       widths where the cap does not bind, then drift off-axis at the
       widths where it does. That is the failure this note exists for.

   All three are horizontal only. align-self stays start on every item,
   so the rows still set the vertical positions and the two columns keep
   their shared baselines. */

/* A pill is content-sized; stretching it across the column would make it
   a bar. Centred on the column axis and start on the block axis, so the
   two pills still share a top edge and therefore a baseline — they are
   the same height by class. */
.masthead__cta {
  justify-self: center;
  align-self: start;
  /* --- the hero's own pill size -------------------------------------
     The bar's .tp is 36px of content / 15px type / 0 22px padding, which
     is a nav chip. These are the primary action on the page, so they go
     to 64 / 20 / 0 36 — keeping .tp's transparent 4px border-block, so
     the drawn pill is 64px tall and the hit area is 72.

     min-width equalises the pair. .tp is content-box, so this is content
     width and the 72px of padding sits outside it: 168 gives both pills
     a 240px border box. "Trade on Pons" is the longer label and measures
     158.8 of content at 20px — measured, not guessed — so 168 clears it
     by 9.2px. The type is a flat 20px at every width, so the label width
     does not move and one number holds across the whole ladder.

     240 also has to fit the narrowest column. At 320 the card's inner
     width is 262, so the pill takes 240 of it with 22 to spare; .tp is
     nowrap, so a pill wider than its column would push a horizontal
     scrollbar rather than wrap. Checked at every rung. */
  min-width: 168px;
  justify-content: center;
}

/* .tp.masthead__cta, not .masthead__cta: the base .tp sets height,
   padding and font-size and sits LATER in this file, so at equal
   specificity it wins on order and the step-up silently does nothing.
   Same trap .tp.tp--rect documents a few hundred lines down. */
.tp.masthead__cta {
  height: 64px;
  padding: 0 36px;
  font-size: 20px;
}

.masthead__claim,
.masthead__figures,
.masthead__text {
  align-self: start;
  text-align: center;
}

/* --- why the claim takes the FIGURE's size, not the body's -----------
   Row 2 pairs "Trade $WIF" with "$23,622 in growing vault". The brief
   only requires the two to share a baseline, which either size would
   satisfy — so the size is a free choice, and it decides whether the row
   reads as a row.

   At the figure's size the two columns state their case at equal
   volume: a claim on the left, a figure on the right, each with its
   quieter line beneath. At body size the left half of row 2 would
   shrink to a caption sitting level with a headline, and the row would
   read as one column having something to say and the other filling in.
   The columns are equal by construction everywhere else in this
   section; row 2 is where that would have been given away.

   It also makes the shared baseline structural rather than contrived.
   Matching sizes means both items carry the same strut and the same
   line-height, so align-self:start alone puts their baselines on the
   same pixel at every width — no baseline alignment to negotiate
   between boxes of different heights, and one clamp driving both. The
   inline label inside the figure is smaller than the figure, so the
   figure's own strut still sets that line box's baseline and the pair
   stays level. */

/* The figures line takes its type from .fee__figures and its quiet half
   from .fee__eth/.fee__sep — see the G.O.A.T. section. Shared
   declarations, not copies: "same one-line slash format" has to stay
   true when either is retuned, and two sets of numbers would not.
   Only the margin is this section's own. */
.masthead__figures { margin: 0; }
.masthead__figures[hidden] { display: none; }

.masthead__text {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--ink-quiet);
  /* The same 70-character cap .goat__body and .idea__body take. */
  max-width: 40.8em;
  /* and the cap is centred on the column, not parked at its start —
     see the alignment note above .masthead__cta. */
  margin-inline: auto;
  text-wrap: pretty;
}

/* The only inline link on the page. The base rule strips colour and
   underline from every anchor, so there was no house style to inherit —
   this builds one from what is already here: the exact lime the two live
   pills fill with, plus an underline at the 1px the card hairlines use.

   Both at rest, not on hover. Lime alone against quiet ink is a weak
   signal at body size, and an underline alone would not say that this is
   the same kind of thing as the lime buttons above it. Hover only brings
   the underline up to full strength, so nothing is REVEALED by hovering —
   it is already legible as a link when the pointer is elsewhere, and to
   someone who has no pointer at all. */
.masthead__link {
  color: #d4fc50;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: rgba(212, 252, 80, 0.55);
  transition: text-decoration-color 0.18s;
}
.masthead__link:hover,
.masthead__link:focus-visible { text-decoration-color: #d4fc50; }

/* Below 900 the columns stack, on the same breakpoint the G.O.A.T. card
   uses. Subgrid comes off with them: there is one column now, so there
   are no rows to share, and leaving both columns spanning rows 1-3 of a
   single-column grid would stack them on top of each other. */
@media (max-width: 900px) {
  .masthead__cols {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    row-gap: clamp(24px, 5cqw, 36px);
  }
  .masthead__col {
    grid-row: auto;
    grid-template-rows: none;
    row-gap: clamp(14px, 3cqw, 22px);
  }
  /* The row numbers go with the subgrid. Left in place they would put
     every column's items on three tracks of a one-column grid and
     interleave the two columns. */
  .masthead__cta,
  .masthead__claim,
  .masthead__figures,
  .masthead__text { grid-row: auto; }
}

/* --- First screen: the G.O.A.T. card ---------------------------------
   It is the hero now. One surface, full viewport, holding the heading,
   the two subtitle lines, the fan, and the two calls to action.
-------------------------------------------------------------------- */
.goat {
  margin: var(--inset) auto;
  /* The 88px that cleared the fixed topbar has moved to .masthead, which
     is what sits under the bar now. Zero here, not deleted, so the
     declaration stays where anyone looking for it expects it. */
  padding-top: 0;
  width: calc(100% - 2 * var(--inset));
  max-width: calc(var(--page-max) - 2 * var(--inset));
  box-sizing: border-box;
}

.card--goat,
.card--idea {
  display: grid;
  /* Copy left, fan right. Stacking does not fit: the copy runs ~340px
     against the ~260px a 1440x768 window leaves, and stacking wastes the
     card's 1408px of width on empty sides. */
  grid-template-columns: minmax(0, 500px) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  align-items: start;
  /* the column gap is the split between copy and fan; the row gap under
     the heading is its own, much smaller value */
  column-gap: clamp(24px, 3cqw, 56px);
  row-gap: 0;
  /* No 100dvh floor. It made the card taller than its content and put
     more space under the last element than above the first; the height is
     the content's now, and the padding is the same on both ends. */
  padding: clamp(20px, 2.4vw, 36px);
  container-type: inline-size;
}

/* The only difference between the two: what sits in the right column. */
.card--goat {
  grid-template-areas: "head head" "copy fan";
  /* Same move the idea section makes: drop the fixed 500px copy track for
     1fr and cap the visual with a percentage instead, so the measure grows
     with the card rather than staying frozen at a width chosen for 1440.
     At 500 the copy ran 50 characters against the idea column's 58.

     52, not the idea's 45. The fan is not a ring that can be any size: its
     cards are pinned at --fan-w's 197px cap, and that cap only holds while
     the column can still afford it — min(197, (100cqw - 2 * --fan-pad) /
     3.2) needs about 650px, and 45% would leave 601 and shrink every
     portrait to 182. 52% leaves 695, comfortably above that floor, so the
     cards hold their cap and the copy lands on the idea section's own 58
     characters. Measured, not derived: 54% was tried first and came up
     three short at 55. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 51%);
}
.card--idea {
  /* Copy left, ring right.

     Percent, not cqw. This declaration sits ON .card--idea, which is itself
     the container-query container, so a cqw here looks past itself for an
     ancestor container, finds none, and falls back to viewport units — the
     right region was silently 30vw, which made it 768px at 2560 while the
     card was only 1814 wide. A percentage resolves against this grid's own
     content box, which is what was meant.

     45%: measured across 30/35/40/45/50, it is the ratio where the two
     cards' stacked height and the largest square that fits on the right come
     out equal — 597.5 against 597.5 at 1440, 607.8 against 607.8 at 1920 —
     so the ring fills its region instead of sitting in a tall empty slot. */
  grid-template-areas: "head head" "copy spiral";
  grid-template-columns: minmax(0, 1fr) minmax(0, 45%);
}

/* The pill sits on the head's own line, hard right. The intro is the row:
   the head and lead stay left, the pill takes the right end. */
.goat__intro {
  position: relative;
}

/* The section head, spanning both columns. Left-aligned with the cards
   beneath it, not centred. It is the parent of the two card heads and is
   set clearly larger than them: 48px against their 31px at 1440. */
.goat__intro,
.idea__intro {
  grid-area: head;
  margin: 0 0 clamp(18px, 2cqw, 28px);
}

.goat__title,
.idea__title {
  /* 20px: the reference sets 0 on the head and 20 on the line beneath it. */
  margin: 0 0 20px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-section);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* The two cards sit in the upper portion and size to their content —
   they do not stretch to fill the column. The fan beside them runs
   lower, so the bottom of this column is deliberately empty. */
.goat__copy,
.idea__copy {
  --copy-gap: 12px;   /* [reference] gap between stacked cards */
  grid-area: copy;
  display: flex;
  flex-direction: column;
  align-self: start;
  gap: var(--copy-gap);
}

/* [reference] .think-step, their "how it works" card, read off the live
   page: rgba(237,234,227,0.043) over a rgb(22,22,22) parent, a 1px
   rgba(255,255,255,0.12) hairline and a 20px radius. The fill is
   translucent, not the flat rgb(22,22,22) — over our card (also 22) it
   composites to 31, a +9 lift. A literal 22 would be delta 0 here and
   only the hairline would show. Padding is opened up from their 18/20:
   the copy was reading as forced into the space. */
.goat__card,
.idea__card,
.fee {
  position: relative;
  display: flex;
  flex-direction: column;
  background-color: rgba(237, 234, 227, 0.043);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 20px;
  /* [reference] 18px 20px, flat — not fluid. */
  padding: 18px 20px;
}

/* :last-of-type / :first-of-type, not :last-child / :first-child. Kept from
   the removed dither work: any non-paragraph appended to a card — a canvas, a
   decorative layer — makes a :last-child trim silently stop matching, and the
   card grows by the trim. Typed selectors ignore anything that is not the
   element the trim is for.

   Optical, not box, padding. Measured cap-top to last baseline the cards sat
   35.6/38.0 and 33.8/38.0 — bottom-heavy, because the space under the last
   line is half-leading plus the descender while the space over the first is
   half-leading plus only the ascender-to-cap gap. The last line's box is
   pulled up by that difference so the ink, not the box, is centred.
   0.18em of the body's own size, so it tracks the fluid font size. Retuned
   from 0.301em when the body took the reference's 1.6 line-height: less
   half-leading under the last line means less of it to trim. */
.goat__card > .goat__body:last-of-type,
.idea__card > .idea__body:last-of-type {
  margin-bottom: -0.18em;
}

/* Card two opens on the head, not the body, and the head's 1.1 line-height
   leaves ~1.8px less half-leading above its cap than the body's 1.75 does.
   Without this the two cards' optical tops disagree by that much and only one
   of them can sit level with its own bottom. Flat px, not em: the shortfall
   is a leading difference and stays ~1.8px across the whole clamp range. */
.goat__card > .goat__head:first-of-type,
.idea__card > .idea__head:first-of-type {
  margin-top: 1.8px;
}

.goat__head,
.idea__head {
  /* 22px, not 14: the label below is an introducer, so it has to sit closer
     to the paragraph it introduces than to the heading it follows. This is
     the gap above the label. */
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-weight: 500;
  /* --t-subhead, not --t-cardhead. At the card-head size this sat within
     14% of the section head above it and the two read as equals. The
     reference's own scale has a step between them — the size it uses for
     "how it works" — and that is what this is. */
  font-size: var(--t-subhead);
  line-height: 1.15;
  letter-spacing: -0.005em;
  color: var(--ink);
}

/* above body, below head */
.goat__lead,
.idea__lead {
  margin: 0;
  font-family: var(--font-display);
  /* 400, not 700. The reference's line is weight 400 at full ink — what makes
     it read as strong is the size and the opacity, not a bold. Ours was 700
     against a font family that ships a single 400 file, so it was rendering
     as synthetic bold. */
  font-weight: 400;
  font-size: var(--t-lead);
  line-height: 1.45;
  color: var(--ink);
}

/* a label, not a line: small, tracked out, quiet, and tight to its head */
.goat__label,
.idea__label {
  /* 9px, not 18: paired with the head's 22px above, this puts the label
     against the paragraph beneath it rather than under the heading above.
     Measured cap-to-baseline the gaps were 23.6 above / 27.4 below, which
     read as the label belonging to the heading. */
  margin: 0 0 9px;
  font-family: var(--font-display);
  /* The reference's equivalent ("the event") is a sentence-case sub-head at
     18/500, not a tracked uppercase eyebrow. Taking their system means the
     tracking and the uppercase go, and the colour comes up to full ink. */
  font-size: var(--t-label);
  line-height: 1.25;
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--ink);
}

.goat__body,
.idea__body {
  margin: 0;
  font-family: var(--font-display);
  /* NOT stepped up. Measured: at 14.04px the columns run 56.5 and 58.1
     characters, so the 55-character floor is only 1.5 characters away.
     A step to 15.1px drops them to 52.5 and 54.0, under the band. The
     ceiling that stays in band is about 14.4px, a 2.6% change nobody
     would see. Left at the settled size and reported instead. */
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--ink-quiet);
  /* the figures line up: 78, 14, $10,000, $5,000, 1 $WIF */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  text-wrap: pretty;
}

/* --- the fan column ------------------------------------------------
   The fan keeps its own geometry and its 197px portrait cap. Whatever
   the column has spare stays as padding — the portraits are not
   stretched to fill it. */
.goat__fan {
  grid-area: fan;
  display: flex;
  /* A column, not a row: the fan is no longer the only thing in here —
     the creator-fee figure sits under it. .fan is width:100% and
     flex:none, so it renders exactly as it did as a row's only child;
     what changes is that a second child now lands beneath it rather
     than beside it, and align-items centres that child on the column.
     Top-aligned, not centred: the row now reads alongside the upper half
     of the copy column instead of sitting level with its middle. That
     was align-items in a row; in a column it is justify-content. */
  flex-direction: column;
  /* stretch, not center: the fee card spans the column so its edges land
     on the grid the copy cards set on the left. .fan is width:100%, so
     stretching changes nothing for it. */
  align-items: stretch;
  justify-content: flex-start;
  /* The containing block for the fee card, which is positioned against
     this column's foot above 900. The row-gap that used to sit here is
     gone with it: an absolute card is not a flex item and takes no gap,
     and stacked below 900 the card is back in flow where its own
     margin-top does the same job. */
  position: relative;
  height: 100%;
  min-width: 0;
  /* Its own container. --fan-w resolves 100cqw against the nearest
     inline-size container, which was the whole 1408px card — so at 1024
     the fan sized itself for the card and its outer lanes ran 49px past
     the viewport. Measured against this column instead, it fits the room
     it actually has. */
  container-type: inline-size;
}

/* --- section two only ------------------------------------------------
   Everything above is shared with the G.O.A.T. card by selector, so the
   surfaces, hairline, radius, head sizes, body size and padding are the
   same declarations, not copies of them. What follows is what section one
   had no equivalent for: multi-paragraph cards, and a right column that
   holds a block rather than the fan. */

/* Paragraph rhythm. Section one's inner cards are one paragraph each, so
   there was no existing value to take. 1em against the body's 1.75
   line-height is a little over half a line — enough to part the
   paragraphs without opening the column up. */
/* "THE IDEA" is set in caps where section one's head is mixed case, so its
   ink starts lower in the line box — 12.9px below the padding edge against
   section one's 5.1px, which left this card top-heavy by 7.8px against its
   own bottom. Pulled back by that difference in em of the title's own size,
   so the two section heads sit at the same optical height at every width. */
.idea__title { margin-top: -0.172em; }

/* Measure cap. On the paragraph, not the card, so the card still fills its
   column. 40.8em is 70 characters at any size: measure = width / advance, and
   this face's advance is 0.5826em, so 40.8 / 0.5826 = 70.0 regardless of the
   font size. Without it the copy ran 96 characters at 900 and 91 at 1920. */
.idea__body,
.goat__body { max-width: 40.8em; }

.idea__body + .idea__body { margin-top: 16px; }   /* [reference] block spacing */

/* The head no longer opens its card — it sits between paragraphs in card
   two — so it has to make its own space above. 1em of the head's own size
   (~29px) against the 1em-of-body (~14px) paragraph rhythm, so the break
   reads as a new section rather than another paragraph. */
.idea__body + .idea__head { margin-top: 1em; }

/* The closing line gets a full line of air above it and nothing after.
   Written as a sibling selector so it outweighs the paragraph rhythm rule
   above: as a bare .idea__close it lost on specificity and inherited the
   ordinary 1em gap. */
.idea__body + .idea__body.idea__close { margin-top: 1.75em; }

/* Right column. Top-aligned and content-sized: it is far shorter than the
   copy column and is deliberately not stretched to match it. */
/* Two cards, stacked, each as tall as its own text. No override needed —
   the shared .goat__copy/.idea__copy rule above is already a flex column
   with align-self:start, which is exactly the G.O.A.T. section's behaviour:
   nothing stretches, each card ends where its text ends. */

/* The ring's box. Above 900 it takes its height from the grid row — it is
   stretched to the copy column, so the space is the real thing the spiral
   has to fit, and no intrinsic height is needed. */
.idea__spiral {
  grid-area: spiral;
  align-self: stretch;
  min-width: 0;
}

/* The Pons card, parked in its own band below the idea section until it is
   placed. Same width rails as every other section. */
.pons-band {
  margin: 0 auto var(--inset);
  width: calc(100% - 2 * var(--inset));
  max-width: calc(var(--page-max) - 2 * var(--inset));
  box-sizing: border-box;
}
.pons-band > .pons { container-type: inline-size; }
.pons-band .pons__inner { padding: clamp(20px, 2.4vw, 36px); }

/* The Pons block keeps the inner-card surface it shares with the two copy
   cards, and supplies the container the logo's 21cqw resolves against —
   the role .card--pons used to play. */
.pons {
  container-type: inline-size;
}

.card--goat .fan {
  flex: 0 0 auto;
  width: 100%;
  /* The row's height alone. The extra --fan-clear sat at the bottom of
     the box, so centring the box left the row itself 5.22px below the
     column's midline. The cards are absolutely placed, so dropping it
     from the box crops nothing. */
  height: var(--fan-h);
  /* Raised by exactly the dead space at the top of its own box, so the
     portraits start on the column's top edge instead of hanging below
     it — the same line the first copy card starts on.

     The amount is derived, not fitted. The box is --fan-h tall
     (1.4w + 2 x --fan-pad) and the centre card's bottom is pinned one
     --fan-clear above the box's foot, so the empty strip above the card
     is (1.4w + 2 x pad) - clear - 1.4w = 2 x --fan-pad - --fan-clear.
     Measured against that expression it is 11.81px at 1440, 11.99 at
     1024 and 10.12 at 900 — the outer lanes are scaled down, so none of
     them reaches above the centre card and the strip is the whole of it.

     Nothing is resized: --fan-w and --fan-reach resolve against the
     column's inline size, which a vertical margin does not touch.

     --fan-lift is a second, deliberate step past that, taken so the fee
     card has room to reach the column's foot. It is half the separation
     the head keeps from the copy below it, and it is half exactly rather
     than approximately: the head's margin is clamp(18px, 2cqw, 28px) of
     the CARD, this column is 51% of the card by grid-template-columns, so
     one card-cqw is 1/0.51 = 1.9608 column-cqw and halving a clamp halves
     all three of its terms. Measured back: 9.41px at 1024 against a
     18.81px head margin, 13.37 at 1440 against 26.73, 14.00 at 1600 and
     up against 28.00.

     Half, not all, because the head's margin is sized to hold a
     full-width card with a hard hairline edge off the heading. What sits
     under it here is one rounded portrait with the lanes either side
     falling away and fading, so it does not read as needing the same
     air — and half leaves 9.4 to 14px of measured clearance between the
     topmost portrait and the head's box at every width, rather than
     spending the margin down to nothing. */
  --fan-lift: clamp(9px, 1.9608cqw, 14px);
  margin-top: calc(var(--fan-clear) - 2 * var(--fan-pad) - var(--fan-lift));
}

/* --- creator fees, under the fan -------------------------------------
   The card takes its surface from the .goat__card rule above by being in
   that rule's selector list — the same fill, hairline, radius and 18/20
   padding as the two copy cards, as declarations rather than as copies
   of them. Note the fill there is rgba(237,234,227,0.043), not a flat
   rgb(22,22,22): the card sits ON a 22 surface, so a literal 22 would be
   delta 0 and only the hairline would show. The translucent tint
   composites to 31 and is what makes the card read as a card.

   Spanning the column, it lands one grid gap right of the copy cards'
   right edge above 900 and flush with them when the card stacks, so the
   two columns read as one grid at every width.
-------------------------------------------------------------------- */
.fee {
  /* Out of flow, and pinned to the foot of the column.

     Absolute for a reason that is the whole point of this rule: it takes
     the card OUT of the column's height calculation. In flow the card
     was part of what sized the grid row, so the row grew to hold
     fan + gap + card, the row's foot dropped below the copy column's,
     and the card sat on that lower line — 14.39px under the second
     .goat__card at 1440, 98.26 at 1920. Out of flow the column measures
     only the fan, the row is the copy column's height again, and bottom:0
     is therefore the second .goat__card's own bottom edge.

     Sizes to its contents: no flex-grow, no auto margin, nothing that
     depends on how much room is left. What is left is where it sits, not
     how big it is. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  font-family: var(--font-display);
  /* The low digits change every block. Tabular figures keep the line
     from breathing sideways each time they do. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* The block is revealed by removing the attribute, and .fee now carries
   display:flex from the shared card rule, so the [hidden] default has to
   be restated to beat it. The two spans are inline and would obey the
   plain [hidden] default, but they are stated here beside it so the
   whole reveal story is in one place. */
.fee[hidden],
.fee__usd[hidden],
.fee__sep[hidden] { display: none; }

/* The figures line. On .fee both readings sit in ONE line box, so they
   share a baseline by construction — no vertical-align, no nudging. The
   line carries the USD's own size and weight, and the ETH and the slash
   step down from it; that way the dollar figure is set by the paragraph
   it leads and only the quieter half needs overriding.
   .masthead__figures holds the dollar figure alone, so it takes the
   first half of that and has no quieter half to step down.

   Weight 500, not 700: "WIF Par" ships a single 400 file, so anything
   heavier is synthetic. 500 is what the copy card heads already use, so
   the emphasis here is the same emphasis they carry rather than a
   different, faked one. Size and full ink do the rest of the work. */
.fee__figures,
.masthead__figures,
.masthead__claim {
  margin: 0;
  /* Stated, not inherited. .fee__figures got the display face from .fee,
     which sets font-family on the block; .masthead__figures has no such
     ancestor — .masthead__col, .masthead__cols, .card--masthead and
     .masthead all set none — so it inherited body's --font-ui and the
     prize figure alone rendered in system sans while every other figure
     on the page was in WIF Par. Nothing was missing from the font: it
     was never asked for. Declaring it here makes the shared rule carry
     the family it depends on instead of resting on one member's
     ancestry, so the pair cannot drift apart again. On .fee__figures
     this is the value it already had. */
  font-family: var(--font-display);
  font-size: var(--t-subhead);
  line-height: 1.15;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--ink);
  /* When it does not fit, it breaks at a space — either side of the
     slash on .fee, before or inside the label on .masthead__figures —
     rather than mid-figure. Nothing is scaled down to avoid it. */
  text-wrap: balance;
}

/* The dollar figure takes the line's own type — nothing to restate. */

/* The other unit, quiet: the same quantity said again, not a second
   fact. Body size and the secondary ink, both already in the scale. The
   slash matches it, so the divider belongs to the quieter half rather
   than punctuating at the headline size. */
.fee__eth,
.fee__sep {
  font-size: var(--t-body);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink-quiet);
}

/* A hair of air either side, so the slash divides rather than crowds.
   The spaces inside the element are what break the line; this only
   opens them up. */
.fee__sep { padding: 0 0.15em; }

/* The masthead's label, beside its figure rather than beneath it.

   Same relationship to its figure that .fee__label has to the G.O.A.T.
   one — 0.82em of the body size, the quiet ink — but none of its box:
   no margin and no line-height, because it shares the figure's line box
   and taking a second line-height would fight the one already set on
   .masthead__figures. Weight and letter-spacing are restated because
   .masthead__figures sets 500 and -0.005em for the number, and the
   label is neither of those things.

   The space before "in" lives in the markup, not in padding: it is the
   line's only break opportunity, and a padded label would push the
   wrap decision onto a box edge the browser cannot break at. */
.masthead__label {
  font-size: calc(var(--t-body) * 0.82);
  font-weight: 400;
  letter-spacing: 0.005em;
  color: var(--ink-quiet);
}

/* The label reads last, and is the smallest thing here.
   0.82em of the body size rather than a fourth clamp: it has to stay
   below --t-body at every width, and an em of that token guarantees it
   at every width instead of at the two the clamps were fitted on. */
.fee__label {
  margin: 5px 0 0;
  font-size: calc(var(--t-body) * 0.82);
  line-height: 1.2;
  letter-spacing: 0.005em;
  color: var(--ink-quiet);
}

/* --- where the pin has to give up ------------------------------------
   Above this width the bottom-pinned card does not fit between the fan's
   foot and the line it has to land on, and it rises INTO the fan.

   The cause is the copy column, not the card: the copy paragraph drops a
   line somewhere between 1541 and 1559, the column loses 27.2px, and the
   room below the fan goes with it. Measured either side, room below the
   fan's foot against the 89.62px the card needs:

       1540   110.07   20.45px spare      pinned, clean
       1560    82.76    6.86px short      portraits clear by 2.05, but
                                          the HELD pill crosses by 0.32
       1920    26.16   63.46px short      portraits cross by 52.35,
                                          the pill by 54.72

   At 1920 the card would have to be 37.27px tall — less than one line of
   type plus its padding — to clear the portraits. There is no version of
   this that fits, so past the boundary the card goes back in flow below
   the fan and its foot sits under .goat__card's again: 41.91px at 1600,
   98.26 at 1920. Reported rather than papered over.

   1541, not the true boundary: 1540 is the widest width measured clean,
   so gating one pixel past it cannot leave an overlapping band. To pin
   at every width instead — and accept the overlap — delete this block. */
@media (min-width: 1541px) {
  .fee {
    position: static;
    margin-top: 14px;
  }
}

/* Below 900 the goat section stacks: copy, then fan. */
@media (max-width: 900px) {
  .card--goat {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    min-height: 0;
    grid-template-areas: "head" "copy" "fan";
  }
  .goat__fan { height: auto; }
  /* Stacked, the fan follows the copy cards with a zero row gap, so
     there is nothing above it to rise into — the raise would land the
     portraits 12px inside the last copy card. It is a side-by-side move
     only. */
  .card--goat .fan { margin-top: 0; }
  /* And the card comes back into flow. There is no column to pin to
     here — .goat__fan is height:auto, so its foot IS the card's foot and
     bottom:0 would be a no-op that also took the card out of the stack's
     height. In flow it follows the fan with its own clearance.

     Flat 14px, not a clamp. What it has to clear is the HELD pill's
     overhang below the fan box, and that is 26px - 0.12 x --fan-w -
     --fan-clear — an absolute quantity driven by a fixed 26px term,
     largest where --fan-w is smallest. Measured worst case 8.93px at
     320, so 14 clears it by 5.07px there and by more everywhere else. A
     fluid value would shrink precisely where the overhang grows. */
  .fee {
    position: static;
    margin-top: 14px;
  }
}

/* The idea section stacks until 1200, not 900. Between the two the copy sat
   in a ~45% column that pulled the measure under the band — 47.9 characters
   at 1024 and 54.8 at 1180. Full width it holds 55-75. The goat fan keeps
   its own 900 split: its column carries a fan, not a paragraph, so the
   measure argument does not apply to it.

   The four cards stack full width in order; the reserved column drops below
   them. The gap is unchanged, so every gap stays equal. */
@media (max-width: 1199.98px) {
  .card--idea {
    grid-template-areas: "head" "copy" "spiral";
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    min-height: 0;
  }
  /* Stacked, the box has no content and no row to stretch to. align-self must
     come off stretch or the auto row and the empty box size each other to
     zero; the size is then driven from the width axis, which is the one that
     is actually known. 56vh caps a full-width square on a short viewport. */
  .idea__spiral {
    /* 24px of clear space below the last copy card. This was
       margin-top: var(--copy-gap), but --copy-gap is declared on
       .idea__copy, which is a SIBLING — the variable never resolved here and
       the computed margin was 0, so the spiral sat flush against the card
       above it. An explicit value, since the requirement is 24 and the
       stacked-card rhythm is 12. */
    margin-top: 24px;
    align-self: start;
    justify-self: center;
    width: min(100%, 56vh);
    aspect-ratio: 1 / 1;
  }
}

/* --- Below the fold: the three remaining cards, stacked -------------- */
/* The topbar is fixed, so an in-page jump would otherwise land the target
   underneath it. Every section that carries an id is an anchor target, so
   the offset is written once against the selector rather than per id —
   a new anchored section picks it up without a second edit. */
section[id] {
  scroll-margin-top: calc(var(--topbar-h) + var(--topbar-gap));
}

.idea {
  margin: 0 auto var(--inset);
  width: calc(100% - 2 * var(--inset));
  max-width: calc(var(--page-max) - 2 * var(--inset));
  box-sizing: border-box;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--card-gap);
  margin: 0 auto var(--inset);
  width: calc(100% - 2 * var(--inset));
  max-width: calc(var(--page-max) - 2 * var(--inset));
  box-sizing: border-box;
}

.stack > .card {
  width: 100%;
  min-width: 0;
  height: auto;
  aspect-ratio: auto;
}

/* The Pons card is a size container, so with no ratio it has no height
   source at all and collapses to its two borders — it measured 2px, its
   contents spilling out of the row. Its content is fixed, so a ratio
   suits it; the copy cards' is not, which is why they have none. */
.stack > .card--pons { min-height: 0; }

.band {
  position: relative;                 /* hosts the full-bleed dot matrix */
  background: none;                   /* the page ground shows through */
  /* Close the hero's bottom margin so the rules' first pixel row is the
     one that begins at the card's bottom edge — adjacent, no daylight.
     aside's rail starts at their y=989 against a card bottom of 988, i.e.
     the next pixel down, not a pixel of ground and then the rail. The
     earlier "+1px" here encoded that as an offset and produced exactly
     one row of visible gap. */
  margin-top: calc(-1 * var(--inset));
}



/* [aside.com] measured at 1280 from their rendered page:
   hero card left edge x=16, right edge x=1263, flat bottom edge y=988.
   Rails at x=96 and x=1183 — 80px INBOARD of the card on each side.
   They begin at y=989, the card's bottom edge + 1px, and run to y=7642,
   the bottom of the last section. 1px, #f0f0f0 on white = rgba(0,0,0,.06),
   and the same 6% ratio holds where the line crosses their seam shadow
   (204 against a 217 neighbour), so it is a proportional darkening of
   whatever is behind it, not a fixed colour.

   Their ground is #ffffff on both sides of the line and in the middle of
   the column — one continuous ground, the rail a line on it rather than
   a boundary between two. Ours is contra's #fbfaf6 + grain + matrix,
   full bleed, on the same principle. */
.band__rules {
  /* Three lines and nothing else. No background, no fill, no radius, no
     backdrop-filter — the page ground runs straight through. The top is
     open: the cloud card closes the rectangle. */
  /* Capped with the content block, not with the viewport. Left full bleed
     they sat 920px OUTSIDE the hero at 3440 — two hairlines enclosing an
     empty rectangle around a centred card. box-sizing + max-width + auto
     margins keeps the +80.0 inset from the hero edge at every width. The
     ground and its noise are on body/html and stay full bleed. */
  box-sizing: border-box;
  width: calc(100% - 2 * (var(--inset) + var(--rail-gap)));
  max-width: calc(var(--page-max) - 2 * (var(--inset) + var(--rail-gap)));
  margin-inline: auto;
  /* aside draws these at 6%, which on their #ffffff is a 15/255 drop. On
     our #fbfaf6 ground that same 6% nearly disappears on a bright screen,
     so the verticals go to 10% (~25/255) and the closing horizontal to
     12% (~30/255) — the bottom rule reads a notch heavier because it is
     what closes the section. Geometry is unchanged. */
  /* Inverted. These were a percentage of black darkening a cream ground;
     on rgb(13,13,13) that composites to a delta of 1.3 and cannot be
     seen. Derived from the same 12% white the whole surface system uses:
     delta 29.0 against the ground, against 24.0 and 28.8 before. */
  border-left: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding: var(--band-pad) var(--gutter);
  /* Halved on the bottom only. --band-pad sets both ends, and the top is
     doing real work holding the flat map heading off the cloud card, so it
     stays. Below the closing line the same value read as dead ground
     rather than breathing room: 116px at 1440. */
  padding-bottom: clamp(32px, 4vw, 60px);
}





.band__inner {
  position: relative;
  z-index: 2;                         /* content sits above the matrix */
  max-width: var(--maxw);
  margin-inline: auto;
}

.band__title {
  margin: 0 0 clamp(28px, 4vw, 56px);
  text-align: center;
  font-family: var(--font-display);
  /* Brought onto the shared section-head role: it was 64px/400 on its own
     scale, the largest type on the page and well above anything the
     reference sets. */
  font-weight: 500;
  font-size: var(--t-section);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* Five small glass squares, evenly spaced and centred. They take .card, so
   the tint, blur, hairline and travelling light all come from there; only
   the size and radius are local. Redefining --radius-card locally keeps the
   sweep ring on the same corner as the card itself. */
/* [worldisflat.fun] flat-map card row, measured from the live page's
   computed styles at 1440: grid of 5, columns 201.594px, gap 16px, row
   width 1072 inside a 1120 wrap. Card 201.594 x 151.188 = 4:3 (1.3334),
   radius 16px, padding 32px 20px, border 1px rgba(22,22,19,.16), no
   box-shadow, backdrop-filter blur(3px).

   Their fill is rgba(249,247,241,.66) over their rgb(242,240,234) ground,
   which composites to a lift of +4.62/255 on every channel. Our ground is
   nine levels darker, so the hex is not transferable — the delta is.
   rgba(249,248,244,.62) over our rendered rgb(241.57,240.57,236.57)
   reproduces it. Cards deliberately empty: geometry only. */
.socials {
  --radius-card: 16px;                /* the sweep ring follows the card */
  display: grid;
  /* Five across, not four. Ten cards over four columns is three rows with a
     half-empty last one; over five it is two full rows, which is both shorter
     and squarer. The width is unchanged, so this IS the size reduction: the
     track falls 256 -> 201.6, which is the size the max-width below was
     always written for. */
  grid-template-columns: repeat(5, 1fr);
  gap: clamp(12px, 1.2vw, 16px);
  width: 100%;
  max-width: 1072px;                  /* their row width, so cards land at 201.6 */
  margin-inline: auto;
  margin-block: 0;
  padding: 0;
  list-style: none;
}

/* .card.social, not .social: these carry .card too, and .card is defined
   further down the file, so a single-class selector loses the cascade on
   fill, border, shadow and blur. */
.card.social {
  aspect-ratio: 201.594 / 151.188;    /* 1.3334 */
  border-radius: 16px;
  /* 32/20 was set against a 256 track. At 201.6 it left the mark and label
     crowded against the edges, so it comes in with the card. */
  padding: 24px 16px;

  /* [air.inc] hairline only. Their bar cards carry a ~8/255 interior tint
     and a 1px border at +38/255 against a luminance-172 ground — the
     hairline does the work. Our ground is 241 after the noise change, so
     +38 would need level 279: the direction inverts and the hairline goes
     dark instead, -37.4/255.

     No fill at all here, not even the tint: the ground and its noise run
     through the card unbroken. No shadow, no backdrop-filter — a blur
     would smooth the speckle inside the card and break the continuity. */
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;

  /* #757F79 at 32%: a * (121.67 - 239.57) = -37.7/255.
     The settled resting hairline elsewhere is the same ink at 40% = -47.
     These sit lighter on purpose. Not to be unified. */
  /* Inverted back. The dark hairline existed only to read on cream. */
  border: 1px solid var(--hairline);
}

/* The travelling light, same mechanism as the hero cards — the conic
   gradient masked to the 1px ring, geometry shared through
   --radius-card. Only the colour inverts: white at 0.95 works over a
   plate at luminance 157 and is invisible over a ground at 241, so the
   sweep darkens instead, lifting the hairline from 32% to ~55% at peak. */
/* Flat map cards: the inverted sweep — the ground is light, so the light
   darkens instead of lightening. Same mechanism, different ink. */
.card.social {
  /* 0.55, not the conic's 0.38. The conic held its peak across a long
     span so the eye integrated it; a blob is only at full strength in its
     middle, and at 0.38 it measured a contrast of 3 against the ground —
     effectively invisible. */
  --ring-c: rgba(255, 255, 255, 0.55);
  /* ~18% of a 678px perimeter. Was a hard step from 30 below 480; the ramp
     is steep because the pill itself changes size across that span, but it
     is continuous and both ends are unchanged. */
  --blob: clamp(30px, calc(106.25vw - 310px), 200px);
}


/* --- Flat map card contents -----------------------------------------
   [worldisflat.fun] measured from the live row's computed styles: icon
   42x42 at opacity .9, 16px row-gap to the label, label 17px / 600 /
   letter-spacing .085px / line-height 27.2px, card is flex column
   centred. Those are used as measured, except the family — see below.

   The marks are their own /assets/mark-*.png, 160x160 RGBA, i.e. 3.8x
   the 42px render. They are near-monochrome but not neutral (channel
   spread up to 57), so they are painted as masks rather than drawn:
   the alpha carries the shape and the colour comes from var(--ink).
   No brand colour survives and no new hex is introduced. */

.socials > li { display: flex; }

.card.social {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;            /* optical centring inside the pad */
  row-gap: 16px;
  text-align: center;
}

.social__mark {
  width: 42px;
  height: 42px;
  flex: none;
  background-color: var(--ink);
  opacity: 0.9;                       /* theirs */
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.social__mark--dexscreener { -webkit-mask-image: url(../img/mark-dexscreener.webp); mask-image: url(../img/mark-dexscreener.webp); }
.social__mark--coingecko   { -webkit-mask-image: url(../img/mark-coingecko.webp);   mask-image: url(../img/mark-coingecko.webp); }
.social__mark--up          { -webkit-mask-image: url(../img/mark-up.webp);          mask-image: url(../img/mark-up.webp); }
.social__mark--udegen      { -webkit-mask-image: url(../img/mark-udegen.webp);      mask-image: url(../img/mark-udegen.webp); }
/* The six that joined. Same treatment throughout: an 84x84 alpha-only WebP
   whose ALPHA is the silhouette, painted in --ink by the rule above. 84 is a
   true 2x of the 42px box. The colour channels are held at a constant 0 and
   never sampled — mask-mode is left at match-source, which for a raster image
   means alpha — so only the alpha channel carries information. Backgrounds
   were removed at build time rather than covered: these are masks, so an
   opaque background would paint the whole 42px box solid. Where the source was
   light on dark (deBridge, de1, SafePal) the shape is the mark, not the field. */
.social__mark--blockscout  { -webkit-mask-image: url(../img/mark-blockscout.webp);  mask-image: url(../img/mark-blockscout.webp); }
.social__mark--kyberswap   { -webkit-mask-image: url(../img/mark-kyberswap.webp);   mask-image: url(../img/mark-kyberswap.webp); }
.social__mark--lifi        { -webkit-mask-image: url(../img/mark-lifi.webp);        mask-image: url(../img/mark-lifi.webp); }
.social__mark--debridge    { -webkit-mask-image: url(../img/mark-debridge.webp);    mask-image: url(../img/mark-debridge.webp); }
.social__mark--de1         { -webkit-mask-image: url(../img/mark-de1.webp);         mask-image: url(../img/mark-de1.webp); }
.social__mark--safepal     { -webkit-mask-image: url(../img/mark-safepal.webp);     mask-image: url(../img/mark-safepal.webp); }

/* Their label is Fraunces 17/600. We do not have Fraunces, and WIF Par
   ships a single 400 axis, so 600 here would be a browser-synthesised
   fake bold. Weight stays 400; size, tracking and leading are theirs. */
.social__name {
  font-family: var(--font-display);
  /* One continuous ramp: 11 at 320 rising to 17 by 768, replacing a base of
     17 with a max-width:600 override that switched into a clamp. */
  font-size: clamp(12px, calc(1.5625vw + 7px), 17px);
  font-weight: 400;
  letter-spacing: 0.085px;
  line-height: 27.2px;
  color: var(--ink);
}

/* The anchor must not bring any default link styling with it. The card
   is transparent with a hairline and a sweep; nothing here may add an
   underline, a colour shift or a ring. */
.card.social,
.card.social:link,
.card.social:visited,
.card.social:hover,
.card.social:active,
.card.social:focus,
.card.social:focus-visible {
  color: var(--ink);
  text-decoration: none;
  background: transparent;
  box-shadow: none;
  outline: none;
}

/* Hover: the pills' mechanism, unchanged in structure and timing — a
   clipper span, a fill plate on ::after at --pill-fill, a skewed sheen
   disc on ::before at --pill-disc that rotates 180deg and scales to 3.
   Only the colours invert.

   The pills bloom cream (#F2EFE7, luminance 238). These cards sit on a
   ground at 239.6, so cream would land at -1.6/255 — invisible. The
   bloom darkens here instead: #757F79 at 10% = 0.10 * (121.67 - 239.6)
   = -11.8/255. The disc still has to read lighter than the fill it sits
   on, and the hovered fill is 227.8, so white at 22% gives
   0.22 * (255 - 227.8) = +6.0/255 over it.

   The fill is translucent, not an opaque panel: the ground's noise
   carries straight through it, hovered or not. */
.social__fx {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
}

.social__fx::after {
  content: "";
  position: absolute;
  left: -10px;
  top: -10px;
  width: calc(100% + 20px);
  height: calc(100% + 20px);
  z-index: 1;
  /* A light lift, not a dark one. At 10% white the delta is 23.2 against
     the ground; the old dark 10% would have been 10.9 and would not have
     read at all. */
  background-color: rgba(255, 255, 255, 0.10);
  opacity: 0;
  transition: opacity var(--pill-fill) var(--ease);
}

.social__fx::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: 120%;
  aspect-ratio: 1 / 1;
  z-index: 2;
  background: radial-gradient(circle at 32% 64%,
                              rgba(255, 255, 255, 0.28) 0,
                              rgba(255, 255, 255, 0) 50%);
  transform: skewX(45deg);
  rotate: 0deg;
  scale: 1;
  /* Unlike the pills, the disc is not visible at rest — the card has to
     measure identical to the ground when untouched. It fades in on the
     disc timing, so the bloom still reads as one movement. */
  opacity: 0;
  will-change: transform;
  transition: rotate var(--pill-disc) var(--ease),
              scale  var(--pill-disc) var(--ease),
              opacity var(--pill-disc) var(--ease);
}

/* contents above the fx layer; the sweep ring is z-index 3, above both */
.social__mark,
.social__name { position: relative; z-index: 2; }

.card.social:hover .social__fx::after,
.card.social:active .social__fx::after { opacity: 1; }

.card.social:hover .social__fx::before,
.card.social:active .social__fx::before { rotate: 180deg; scale: 3; opacity: 1; }

/* A tap has no hover to lead it, so :active arrives with no delay. */
.card.social:active .social__fx::after,
.card.social:active .social__fx::before { transition-delay: 0s; }

/* Hairline 32% -> 45%, easing with the fill. No shadow, no lift, no
   transform, no scale on the card itself. */
.card.social {
  transition: border-color var(--pill-fill) var(--ease);
}

.card.social:hover,
.card.social:active,
.card.social:focus-visible {
  border-color: rgba(255, 255, 255, 0.45);
}

/* The stagger keys off the <li>, not the anchor. It used to read
   .social:nth-child(n), but each anchor is the only child of its li,
   so :nth-child(1) matched all five and 2..5 matched nothing — every
   card ran in phase. */
/* Respaced for ten: the travel is a 6s cycle, so the stagger is 0.6s. Four
   delays for ten cards left six of them sharing 0s and running in phase —
   the same fault the note above describes, reintroduced by the row growing. */
.socials li:nth-child(1)  .ring::before { animation-delay:  0s; }
.socials li:nth-child(2)  .ring::before { animation-delay: -0.6s; }
.socials li:nth-child(3)  .ring::before { animation-delay: -1.2s; }
.socials li:nth-child(4)  .ring::before { animation-delay: -1.8s; }
.socials li:nth-child(5)  .ring::before { animation-delay: -2.4s; }
.socials li:nth-child(6)  .ring::before { animation-delay: -3s; }
.socials li:nth-child(7)  .ring::before { animation-delay: -3.6s; }
.socials li:nth-child(8)  .ring::before { animation-delay: -4.2s; }
.socials li:nth-child(9)  .ring::before { animation-delay: -4.8s; }
.socials li:nth-child(10) .ring::before { animation-delay: -5.4s; }


/* [worldisflat.fun] at and below 900 they go two across: gap 12, a fixed
   124.8px height rather than a ratio, padding 26px 12px. Measured at 900,
   768 and 500 — identical at all three. */
@media (max-width: 900px) {
  .socials {
    grid-template-columns: repeat(2, 1fr);
    max-width: 852px;
  }

  .card.social {
    aspect-ratio: auto;
    /* Two-up is unchanged; ten cards are five rows there, so the height is
       what shortens the run. 112 is the floor that still clears its own
       contents: 42 mark + 27.2 label = 69.2, against 112 - 36 = 76 of inner
       height. Far above the 44px hit minimum either way. */
    height: 112px;
    padding: 18px 12px;
  }
}

/* --- Cards ----------------------------------------------------------
   Two-column masonry, after reference goat.png. Both columns start level;
   left runs short-then-tall, right tall-then-short, so the row boundaries
   land at different heights and no card shares a row with another. That
   mismatch is the whole diagonal — there is no column offset.

   Glass follows our own Pons panel: a cream-tinted vertical gradient over
   a backdrop blur, defined by a hairline. Written here, not lifted.
-------------------------------------------------------------------- */





.card {
  --r: var(--radius-card);
  position: relative;
  isolation: isolate;
  border-radius: var(--r);
  /* The reference's surface: an opaque rgb(22,22,22) on rgb(13,13,13),
     nine levels apart, held by a 12% white hairline. The hairline does
     the work; the fill barely separates on its own. The translucent cream
     that used to produce this lift is gone with the plate it composited
     against. */
  background: var(--surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid var(--hairline);
  box-shadow: none;
  color: var(--ink);

  --ring-c: rgba(255, 255, 255, 0.325);
  --blob: 545px;
  --ring-dur: 6s;
}

/* --- Hero card copy --------------------------------------------------
   The two left cards. Body stays at the settled hero size — 16px at 1440,
   floored at 12 — and the type does the work, not a scrollbar: both cards
   were measured to fit before this was written.

   Top-aligned on purpose. The tall card's slack belongs at the bottom, so
   the two cards' first lines sit level with each other.
-------------------------------------------------------------------- */
.card__body {
  /* Was clamp(24px, 2.6vw, 40px) = 37.4px at 1440. It was already
     symmetric — top and bottom identical, and the first line box sat
     flush on the inner edge with 0.0px of slack — so there was no
     asymmetry to remove. The band is the padding itself: it is derived
     from viewport WIDTH while the card's height comes from 120vh, so on
     a short window the card shrinks and the padding does not, reaching
     27% of the short card's height at 1440x740. Cut so the inset stays
     proportionate; still equal on all four sides. */
  padding: clamp(20px, 1.9vw, 28px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Fill the card and centre the block in it. The body used to be
     content-height and top-aligned, which parked every pixel of the
     card's slack at the bottom: measured from the render at 1440, the
     short card sat 34.5px from the top border and 69.0px from the
     bottom, the tall card 32.0 and 98.0. Centring splits that evenly. */
  height: 100%;
  justify-content: center;
  /* every numeral in these cards — "three decades" has none, but $WIF and
     anything added later should not shift the measure */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Centring the BOX is not centring the INK. The first and last paragraphs
   have different font sizes and leading, so each line box carries a
   different amount of empty space above its cap and below its descender —
   measured from the render, that left the short card 53.5/50.5 and the
   tall card 90.0/94.0. Trimming the half-leading off the first and last
   children makes the block's edges sit on the type rather than on the
   leading, so the centring lands optically. */
.card__body > :first-child {
  text-box-trim: trim-start;
  text-box-edge: cap alphabetic;
}

.card__body > :last-child {
  text-box-trim: trim-end;
  text-box-edge: cap alphabetic;
}

/* The short card's last line ends "...and real-world assets." but carries a
   descender in "ownership"; the tall card's "World Is Flat." has none. The
   alphabetic edge trims to the baseline, so on the short card the ink runs
   a descender below the block's edge and the measured bottom gap comes up
   about 5px short. Its last child takes the font's descent edge instead. */
.card--short .card__body > :last-child { text-box-edge: cap text; }

/* A subtitle, not a headline: above the hero body, below the flat map
   heading's clamp(28px, 5vw, 64px). */
.card__lede {
  margin: 0 0 0;
  font-family: var(--font-display);
  font-weight: 400;
  /* 20px at 1440, down from 21. The ladder is forced: body is fixed at 16
     and the eyebrow now has to sit clearly above it, so the eyebrow takes
     18 and the lede has to clear that while staying under the 21 that ate
     the card. 16 / 18 / 20 is the only integer ladder left. */
  font-size: clamp(14.7px, 1.29vw, 18.4px);   /* was 20px, -8% */
  line-height: 1.34;
  color: rgb(242, 242, 242);
  /* balance, not pretty: on a two-line subtitle pretty only protects the
     last line, which still left "follow." alone. balance evens the two. */
  text-wrap: balance;
}

/* A label. More space above than below, so it attaches to what follows
   rather than floating between the two blocks. */
.card__eyebrow {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-weight: 700;
  /* Stepped down from 18. Body is 16 and the lede is 20, and the eyebrow
     has to sit above the one and clearly below the other, so 17 is the
     only value left. See the note in .card__lede — the ladder is full. */
  font-size: clamp(12px, 1.09vw, 15.6px);   /* was 17px, -8% */
  line-height: 1.35;
  letter-spacing: 0.14em;
  /* Taken from .fair__title, the "Who is the G.O.A.T." heading: computed
     colour rgb(35, 38, 31) at opacity 1, i.e. --ink at full strength with
     no ancestor opacity anywhere in its chain. Read off the page, not
     matched by eye, and no new hex introduced. */
  color: rgb(242, 242, 242);
}

.card__lede + .card__eyebrow { margin-top: 14px; }

.card__copy {
  margin: 0 0 18px;   /* was 12: the beats separate more clearly */
  /* The measure is capped here, on the text, not on the page. The advance
     is a constant 7.270px once the size clamp maxes at 14.7px, so 537px is
     73.9 characters — inside the 65-75 band. Uncapped, the paragraph ran
     to 160 characters at 3440; capped on the PAGE instead, the whole
     layout shrank to a strip to hold the paragraph in.

     537 rather than a rounder number because that is the column width a
     1440 viewport already produces (536.5px): the cap is set exactly where
     the tuned layout already sat, so nothing at or below 1440 moves. */
  max-width: 537px;
  font-family: var(--font-display);
  font-size: clamp(11px, 1.06vw, 14.7px);   /* was 16px, -8% */
  line-height: 1.5;
  color: rgb(242, 242, 242);
  text-wrap: pretty;         /* no single-word last lines */
}

.card__copy:last-child { margin-bottom: 0; }

/* The short card's one long paragraph strands "intermediaries." on its own
   line at 900 and 375 under `pretty`, which only protects the last line
   within a small budget. `balance` evens all four lines and clears it. The
   tall card is the other way round: its paragraphs are long enough that
   `balance` strands "in." at 375, so it keeps `pretty`. Measured both. */
.card--short .card__copy { text-wrap: balance; }

/* The two "Not ..." lines are a pair: tighter between them than around
   them, so they read as one beat rather than two stray sentences. */
.card__copy--pair          { margin-bottom: 9px; }
.card__copy--pair-end      { margin-bottom: 28px; }
.card__copy--pair:first-of-type { margin-top: 8px; }

/* Closes the piece: space above, nothing after. */
.card__close { margin-bottom: 0; }


/* Below 768: one column, four stacked, equal heights, no stagger. The
   hero grows to hold them and the stack does cross the bank — four
   portrait cards in a single column cannot avoid it. */


/* --- The World is Fair preview -----------------------------------------
   Heading and one line at the top, the fan anchored to the bottom and
   allowed to crop against the card's edge so the row rises out of it.

   Fan geometry ported from fwa.fun's pixel form, expressed as fractions of
   the fan card's own width so it scales with our card:
     centre  x 0       scale 1     rotate 0     z 60
     +-1     x +-.347w scale 0.88  rotate +-2.5 z 40
     +-2     x +-.625w scale 0.76  rotate +-5   z 20
   A +-3 lane at +-.90w sits at opacity 0, so cards fade at the edges.
   2D only — translate, scale, rotate.

   The fan cards carry the World is Fair card's own values, read from that
   site's stylesheet rather than approximated. See the note on .fan__card.
-------------------------------------------------------------------- */

/* The card's own display, columns and height live in the layout block
   further up. This one only carries what is left of the old single-column
   version: the ink, and inline-size containment so the fan can size off
   the card's width. */
.card--goat {
  color: var(--ink);
  text-decoration: none;
  container-type: inline-size;
}

/* Title left, button right on the same baseline, both on the card's own
   margin — the nav bar's relationship. The copy spans beneath. */
.fair__head {
  flex: none;
  padding: clamp(18px, 2.2cqw, 30px) clamp(18px, 2.2cqw, 30px) 0;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "title cta"
    "copy  copy";
  align-items: baseline;
  column-gap: 14px;
  row-gap: 8px;
}

.fair__title { grid-area: title; }
.fair__copy  { grid-area: copy; }

.fair__cta {
  grid-area: cta;
  justify-self: end;
}

/* Measured against WIFPar: below about 380px of card the title and the
   button cannot share a line with any separation left. Stack rather than
   shrink either — and on the card's own width, not the viewport, since
   that is what actually constrains them. */
@container (max-width: 380px) {
  .fair__head {
    grid-template-columns: 1fr;
    grid-template-areas:
      "title"
      "copy"
      "cta";
    justify-items: start;
  }
  .fair__cta { justify-self: start; }
}

.fair__title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(17px, 4.6cqw, 30px);
  line-height: 1.12;
  color: rgb(242, 242, 242);
}

.fair__line {
  margin: 0 0 2px;
  font-family: var(--font-display);
  font-size: clamp(12px, 2.5cqw, 16px);
  line-height: 1.35;
  color: rgba(242, 242, 242, 0.58);   /* [reference] secondary ink */
}

/* The fan takes the rest of the card and sits at the bottom of it. */
/* --- the held state -------------------------------------------------
   Small and quiet, deliberately not a play icon over the artwork: a
   hairline rule under the fan with the word HELD beside it, in the same
   ink and tracking the eyebrows use. It has to read as "this is stopped
   on purpose", not as a control or an error. Cursor turns to a pointer
   over the whole card so a mouse user can see the surface is live. */
.card--goat { cursor: pointer; }

.card--goat[data-fan-held] .fan::after {
  content: "HELD";
  position: absolute;
  left: 50%;
  /* The overlap into the card's foot has to SCALE, because the thing it
     has to stay clear of does. The card's empty strip below its handle is
     about 0.21 x --fan-w, so it is 41px at the 197px cap and only 14px at
     320 — while the pill's own height is a fixed 26px. At bottom:0 the
     overlap is a constant ~18px, which fits under the handle at 1440 and
     runs straight through it at 375 and below. That never showed before
     only because the pill was behind the card.

     Solving overlap = 0.12 x --fan-w for `bottom` gives the line below:
     ~24px of overlap at the cap, ~9px at 320, clear of the handle at both
     ends and still tucked into the card rather than floating under it. */
  bottom: calc(0.12 * var(--fan-w) + var(--fan-clear) - 26px);
  translate: -50% 0;
  /* Above the whole row, not above two lanes of it. The fan's slots carry
     z 2/5/10/20/40/60, so a 4 here put the pill under every visible card
     and under the centre one in particular: 75% of it was hidden and the
     ~9px sliver that cleared the card's bottom edge read as a detached
     tab floating below the row. 70 clears 60 with room for another lane.

     The overlap itself is wanted and is not what was wrong — the card's
     handle ends 41px above its bottom edge, so the pill tucks into empty
     card, which is what makes it read as belonging to the card rather
     than to the page. It reaches neither the label nor the portrait. */
  z-index: 70;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(10, 10, 10, 0.55);
  backdrop-filter: blur(6px);
  font-family: var(--font-display);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.14em;
  color: rgba(242, 242, 242, 0.72);
  pointer-events: none;
  /* fades in rather than snapping, so a tap does not flash */
  animation: fan-held 0.22s cubic-bezier(.22, 1, .36, 1) both;
}

@keyframes fan-held {
  from { opacity: 0; translate: -50% 4px; }
  to   { opacity: 1; translate: -50% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .card--goat[data-fan-held] .fan::after { animation: none; }
}

.fan {
  --fan-pad: clamp(10px, 1.2cqw, 16px);
  /* Width bound: the outermost visible lane sits at 0.90w and its rotated,
     scaled half-extent is 1.30w; a card mid-fade between the +-4 and +-3
     lanes reaches about 1.40w. 2.9w covers both, so the fan card shrinks
     to fit rather than the offsets compressing. */
  /* Width-bound only. The height term was the other half of the circular
     sizing above: the fan asked the card how tall it was, and the card had
     been told how tall to be. It is the width that actually constrains the
     row, so that is the only input now, and the fan area is exactly as
     tall as the row it holds. */
  /* Capped at the width a 1440 viewport already produces. Past that the
     card keeps widening but the fan does not, so the surplus goes to the
     card's padding rather than to softer portraits: the 384px sources
     stay at the same 1.95x they have always been at 1440, instead of
     dropping further as the screen grows. */
  /* 3.2, not 2.9: the outermost visible lane moved from 0.90w to 1.12w
     when the row went from seven cards to nine, and its rotated, scaled
     half-extent carries it to about 1.50w either side. The 197px cap is
     unchanged and still binds at 1440, so the portraits do not shrink. */
  --fan-w: min(197px, calc((100cqw - 2 * var(--fan-pad)) / 3.2));
  /* How far the outermost lane's centre sits from the fan's centre.
     Split out from --fan-w so the arc can widen to fill the column while
     the portraits stay at their 197px cap: the fan was rendering 587px
     inside a 794px column, leaving 103px dead on each side, and sliding it
     sideways only moved the gap.
     Second term = the room actually available: half the column, less the
     outermost card's own rotated half-extent. That extent is 0.371 x
     --fan-w (a 0.62-scaled 5:7 card turned 8.5deg), so the card's outer
     edge lands exactly on the column's inner edge.
     First term caps the spread on very wide pages, where the column grows
     but the 197px cards do not. 2.6 is the widest that still keeps adjacent
     lanes overlapping: neighbours sit 0.1964 x --fan-reach apart, and at 2.6
     that is 101px against the 128px of card that has to bridge it. Past this
     the lanes separate and it stops reading as a fan. */
  --fan-reach: min(
    calc(2.6 * var(--fan-w)),
    calc((100cqw - 2 * var(--fan-pad)) / 2 - 0.371 * var(--fan-w))
  );
  --fan-h: calc(var(--fan-w) * 1.4 + 2 * var(--fan-pad));
  --fan-clear: clamp(8px, 1.2cqw, 16px);   /* under the handle line */
  position: relative;
  flex: none;
  height: calc(var(--fan-h) + var(--fan-clear));
  min-height: 0;
}

/* --- Active-card bloom ------------------------------------------------
   APPROVED EXCEPTION to the no-glow rule, scoped to these two elements and
   nothing else. It does not extend to the CTAs, the Pons card, the map
   cards or any other surface.

   The fill is a blurred copy of the active card's own portrait. See fan.js
   for why (it carries real colour, needs no accent, and covers all 78 cards
   including the 10 monochrome ones). The image is positioned here at the
   same size and offset it occupies on the card — a 0.91w square inset 0.045w
   — rather than stretched over the whole box, so the card's own margins are
   part of what gets blurred. That dark surround is what keeps a white
   portrait from reading as a lit slab.

   TWO layers crossfading on OPACITY. A transition on the fill itself would
   repaint a blurred image layer every frame; opacity on a promoted layer is
   compositor-only. Only one carries .is-on at a time. */

.fan__bloom {
  position: absolute;
  left: 50%;
  /* .fan__card's `top` is its CENTRE (it carries translateY(-50%)), so this
     shares that centre line and then takes its downward bias: 0.03 of card
     height, which is 0.042 of card width at the card's 5:7 ratio. */
  top: calc(100% - var(--fan-clear) - 0.7 * var(--fan-w) + 0.042 * var(--fan-w));
  width: var(--fan-w);
  height: calc(var(--fan-w) * 1.4);
  translate: -50% -50%;
  border-radius: var(--radius-card);

  /* The portrait, where the card puts it. Same URL the card already loaded,
     so this is a cache hit and never a second request. */
  background-repeat: no-repeat;
  /* cover, not the portrait's own square at the card's offset. Placed where
     the card puts it, the image sits in the top 0.91w of a 1.4w box and the
     bottom half is empty — the blur then cannot reach the card's lower edge
     and the bottom perimeter measured 0.0 at every width. Filling the box
     puts light along all four edges. Nothing is lost to the "slab" risk: the
     card is opaque and exactly covers this element, so the only part that is
     ever seen is the blurred fringe outside its perimeter. */
  background-size: cover;
  background-position: center;

  /* Behind every card. The lowest z any card takes is 2 (the parked/fade
     lane), so 1 puts this under all of them and neighbours occlude it. */
  z-index: 1;
  pointer-events: none;

  /* url(#fan-bloom-floor) adds a flat neutral floor to the fill BEFORE the
     blur, so no portrait can put a fill darker than the ground under the
     card. Uniform across every card — no per-card value, and no
     brightness() multiply, which could not create light on a black edge
     however far it was pushed. The filter is built in fan.js. */
  filter: url(#fan-bloom-floor) blur(calc(var(--fan-w) * 0.117)) saturate(1.25);

  opacity: 0;
  transition: opacity 260ms linear;
  will-change: opacity;
}

/* --- the motion gate ---------------------------------------------------
   No bloom on a moving card. While the row travels, both layers are pinned
   to zero; when it settles, the current one rises from zero to whatever
   paintBloom last wrote.

   !important is deliberate and is the whole mechanism: it outranks the
   inline opacity the crossfade writes, so the two never argue. The
   crossfade goes on swapping layers underneath with nothing visible, and
   the card that emerges at the end is always the one on stage — a stale
   fill cannot be revealed mid-settle, because nothing is revealed until
   the row has stopped.

   Out fast, in slow. 120ms out, so the light is gone before the cards have
   travelled far enough for it to smear; 260ms back in (the inherited
   duration), so it arrives after the row has visibly stopped rather than
   racing it. Opacity is the only property on either edge — the filter and
   its blur are never recomputed. */
[data-fan-moving] .fan__bloom {
  opacity: 0 !important;
  transition-duration: 120ms;
}

@media (prefers-reduced-motion: reduce) {
  /* The bloom stays — it is not motion. Only the crossfade goes. */
  .fan__bloom { transition: none; }
}

/* Anchored low but not cropped: the centre card is the lowest thing on
   stage, so its bottom edge — half its height below the row's centre —
   is placed one clearance above the bottom of the fan area. */
.fan__card {
  position: absolute;
  left: 50%;
  top: calc(100% - var(--fan-clear) - 0.7 * var(--fan-w));
  width: var(--fan-w);
  aspect-ratio: 5 / 7;
  display: flex;
  flex-direction: column;
  gap: 2px;

  /* [World is Fair] .card — values read from that site's stylesheet:
     background rgb(237 234 227 / 0.16), border 1px #ffffff1f,
     radius --radius-card 20px, ink #f7f5f1. Ours sits on a light plate
     rather than their #0d0d0d page, so the tint is composited against
     their page colour to keep the card the same dark it is there. */
  padding: calc(var(--fan-w) * 0.045);
  --r: var(--radius-card);
  border-radius: var(--r);             /* 20px, same as the voting site */
  background: #31302F;
  color: #f7f5f1;

  /* Per-card edge colour. Set --kol-accent on a card to colour its border;
     unset, it falls back to the neutral border and nothing changes. The
     accents are derived from the original full-size portraits.
       <article class="fan__card" style="--kol-accent:#b3884f"> */
  border: 1px solid var(--kol-accent, rgba(255, 255, 255, 0.122));

  opacity: 0;
  will-change: transform, opacity;
  /* --fan-k is a multiplier set by the script; the width it multiplies is
     resolved here, so the row never depends on a measured value. */
  /* --fan-k is now a fraction of --fan-reach (outermost lane = 1), not a
     multiple of --fan-w, so the spread follows the column. */
  transform: translateX(calc(-50% + var(--fan-k, 0) * var(--fan-reach))) translateY(-50%)
             scale(var(--fan-s, 1)) rotate(var(--fan-r, 0deg));
  transition: transform 0.34s var(--ease), opacity 0.34s var(--ease);
}

/* Parked off-stage: snapped, never animated across the fan. */
.fan__card[data-parked] { transition: none; }

/* Square media box. A portrait <img> dropped in here fills it.
   [World is Fair] .card-media — radius --radius-media 14px. */
.fan__media {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
  overflow: hidden;
}

.fan__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.fan__name,
.fan__handle {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-display);
  line-height: 1.25;
}

/* [World is Fair] .card-name 14px / --card-ink, .card-handle 12px /
   --card-ink-muted. Sized off the fan card so they hold at every width. */
.fan__name   { margin-top: calc(var(--fan-w) * 0.05); font-size: calc(var(--fan-w) * 0.075); color: #f7f5f1; }
.fan__handle { font-size: calc(var(--fan-w) * 0.062); color: rgba(247, 245, 241, 0.659); }

/* --- Travelling light, centre card only -----------------------------
   A conic gradient masked to a 1px ring over the card's border: brightest
   at one point in the card's own accent, falling a long way off to nothing
   so the neutral border shows through the rest of the perimeter. The angle
   is animated through a registered custom property, which is the only way
   a conic gradient's start angle can be transitioned.

   The script sets data-centre on the middle card, whatever that card is.
   The colour is chosen here, not there: --kol-accent when the portrait
   extracted one, white at 0.9 when it did not. Null accent means the
   extractor found no usable colour, which is what dark and monochrome
   portraits give, and those are exactly the ones a white ring suits. The
   chrome is rgb(49,48,47) at luminance 48, so white has +207/255 of
   headroom here — the one place on this site where it does.
-------------------------------------------------------------------- */


.fan__card[data-centre] {
  /* the accent arrives via the sweep, so the static edge stays neutral */
  border-color: rgba(255, 255, 255, 0.122);
}

/* Centre fan card only: the ring exists on every card, but only the one
   at centre is lit, and only in that card's own accent — a portrait that
   extracted null sweeps white instead. */
.fan__card .ring::before { animation: none; opacity: 0; }

.fan__card[data-centre] {
  --ring-c: var(--kol-accent, rgba(255, 255, 255, 0.9));
  --blob: 130px;      /* ~18% of a 911px perimeter */
}

.fan__card[data-centre] .ring::before { animation: ring-travel 6s linear infinite; opacity: 1; }



@media (prefers-reduced-motion: reduce) {
  .fan__card { transition: none; }
  /* static bright point, no rotation */
  .fan__card[data-centre] .ring::before { animation: none; }
}

/* Stacked, this card carries a heading and a fan rather than nothing, so
   3:2 leaves the fan too small to read. Taller here only. */

/* --- Travelling light on the hero cards -------------------------------
   Same mechanism as the fan's, inverted. The fan cards are dark, so their
   point brightens; these are glass on a pale plate, where a brighter point
   has almost no headroom — the same wall the white dot matrix hit on the
   cream band. So the point DARKENS: it goes to the plate's deepest sky at
   high opacity and falls back to the resting cream hairline around the
   rest of the perimeter.

   The ring shares the card's exact geometry — border box, same radius,
   1px — so it reads as the border itself lighting rather than a second
   ring beside it.
-------------------------------------------------------------------- */


/* --- Travelling light -------------------------------------------------
   Parameterised by POSITION ALONG THE PERIMETER, not by angle.

   The old conic swept by angle from the element's centre, so its isolines
   were straight radial lines. On a near-square card that reads fine; on a
   124x38 lozenge those lines cross the long edges at a shallow angle and
   the highlight became a wedge. Measured on a nav pill, frozen at four
   angles: the highlight ran 51.1 / 46.9 / 8.9 / 51.5px, and the peak
   advanced 274.8 / 90.0 / 90.2px per equal 45deg step against 36.5px for
   constant travel. It lurched and it changed shape. No radius correction
   touches that — it is geometric.

   .ring is a static element carrying the padding-box mask, so the 1px
   band it clips to never moves. Its pseudo is a soft blob riding
   offset-path: border-box, which follows the host's real border shape at
   any aspect ratio, at constant speed, without spinning.
-------------------------------------------------------------------- */
.ring {
  position: absolute;
  inset: -1px;
  border-radius: calc(var(--r) + 1px);
  pointer-events: none;
  z-index: 3;

  /* the mask from last turn, unchanged: a transparent 1px border makes the
     padding box the inner edge, and a padding box's corners are rounded,
     so the band stays a constant 1px all the way round */
  border: 1px solid transparent;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;

  /* The blob is far wider than its host (200-545px) and, although the mask
     hides everything outside the 1px band, an unclipped pseudo still
     CONTRIBUTES scrollable overflow. That was a fixed 100px of horizontal
     scroll at every viewport below 900. Clipping it costs nothing visually
     because the mask already discards what falls outside.

     overflow-clip-margin is load-bearing: the default clip edge is the
     PADDING box, and the visible band lives in the border area just
     outside it, so a bare `overflow: clip` would clip away the whole
     sweep. 1px reaches the border box in theory, but Chrome's inflation
     of a ROUNDED rect loses a sliver at each corner — measured as up to
     47/255 of missing highlight across ~80 device px on the copy button.
     2px clears the corners and diffs pixel-identical to the unclipped
     ring at every phase, while still discarding the 200-545px blob. */
  overflow: clip;
  overflow-clip-margin: 2px;
}

.ring::before {
  content: "";
  position: absolute;
  width: var(--blob);
  height: var(--blob);
  /* A plateau, not a straight falloff. The conic held near its peak over
     a long span; a bare closest-side gradient drops from full to nothing
     across the radius, which read as almost nothing on the flat map cards
     where the ink is only 0.38 alpha. */
  background: radial-gradient(circle closest-side,
      var(--ring-c) 0%,
      var(--ring-c) 22%,
      transparent 100%);
  offset-path: border-box;      /* the host's own border shape, radii included */
  offset-rotate: 0deg;          /* travels, never spins */
  offset-anchor: 50% 50%;
  offset-distance: 0%;
  animation: ring-travel var(--ring-dur, 6s) linear infinite;
}

@keyframes ring-travel { to { offset-distance: 100%; } }

/* Hero cards: white, as the conic was. --blob is roughly 18% of this
   card's perimeter; CSS cannot compute a perimeter, so each family
   carries its own tuned length and the measured percentage is reported. */
.card {
  /* On a plate at luminance 157 white at 0.95 lifted +85. On a plate at
     12.5 the same white lifts +223 and reads as a hard flare. 0.40 of
     white over a card at 21.6 gives 0.40 * (255 - 21.6) = +93. */
  --ring-c: rgba(255, 255, 255, 0.325);
  /* ~18% of a 1847px perimeter at 1440. Was 545 with steps to 447 at 900 and
     391 at 767; one ramp instead, unchanged at 1440. It is a glow length on a
     clipped pseudo-element, so nothing here is load-bearing. */
  --blob: clamp(288px, calc(23.7962963vw + 211.8518519px), 545px);
  --ring-dur: 6s;
}



/* Out of phase with each other: a quarter turn apart.

   The child combinator is load-bearing. As a descendant selector these sit
   at (0,3,1) and also matched the ring inside a pill nested in the card,
   beating the pill's own (0,2,1) rule — so the fair card's CTA swept in
   lockstep with the card at -3s instead of its -6s. .ring is a direct
   child of .card, so `> .ring` reaches the card's own ring and nothing
   deeper. */

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

/* --- Card four: the Pons card -----------------------------------------
   No second panel: this IS the panel, with the same geometry, radius,
   tint, border and travelling light as the other three. The difference is
   what sits under the glass.

   The other three get their softness from backdrop-filter blurring the
   plate behind them. This card carries its own image, which backdrop-filter
   cannot reach — an element never blurs its own background — so the ground
   layer blurs itself at the same radius and wears the same cream tint on
   top. Blurring a smooth gradient changes nothing, so the tint survives the
   filter intact.

   The layer is oversized and clipped rather than inset, because blur alone
   would leave a soft transparent fringe at the card's edge; clip-path runs
   after the filter, so the edge comes back hard. Clipping here rather than
   overflow:hidden on the card, which would cut off the sweep ring.
-------------------------------------------------------------------- */

.card--pons {
  /* inline-size: everything inside sizes off the card's WIDTH (the logo
     is 21cqw), so the card no longer needs to be given a height and can
     take one from its contents. Under size containment it had none at all
     and collapsed to its two borders in the stack. */
  container-type: inline-size;
}


/* Button top right on the card's own margin — the G.O.A.T. card's
   position. Logo and text sit below it, side by side and centred against
   each other, in whatever height is left.

   The heading matches the G.O.A.T. card's, clamp(17px, 4.6cqw, 30px), so
   the two read as a pair. It is held to one line: at 1280 that is 24px of
   type running 335px wide, against a 360px text column once the logo and
   its gap are taken out.
-------------------------------------------------------------------- */

/* In flow, not absolute. It was inset:0 over a card that had been given a
   height; with the card content-sized there was nothing in flow to give
   it one and it collapsed to its two borders. */
.pons__inner {
  position: relative;
  z-index: 1;
  /* A row: logo, then the text block, then the CTA. Everything centred on
     everything else rather than the CTA floating in a corner. */
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: clamp(20px, 3cqw, 44px);
}

/* The Pons block's own inset, inside whatever card holds it. */
.pons__inner {
  padding: 0;
}

/* The head row: title left, CTA right, tops aligned. */
/* .pons__head is gone: the CTA now sits below the lead-in as its own row in
   the .pons__text column, so there is no title/CTA row left to lay out. */

.pons__cta {
  flex: 0 0 auto;
}

.pons__row {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  /* The text block starts at the top so its heading — and the CTA aligned to
     it — sit in the card's top corner. The logo keeps its own centring. */
  align-items: flex-start;
  gap: clamp(16px, 2.8cqw, 36px);
}

.pons__logo {
  flex: 0 0 auto;                    /* fixed: never shrinks */
  align-self: center;                /* centred against the row, as before */
  /* Bigger: it was 120px against a full-width card and read as an icon
     rather than the mark. */
  width: clamp(96px, 18cqw, 200px);
  height: auto;
  transform-style: preserve-3d;
  /* perspective() lives in the logo's own transform rather than on an
     ancestor. The perspective property only reaches an element's DIRECT
     children, and this markup has been restructured twice — the logo went
     from being a child of the card to a grandchild, which silently
     flattened the tilt. Keeping it here means no future reshuffle can
     break it again. */
  transform:
    perspective(800px)
    rotateX(var(--pons-rx, 0deg))
    rotateY(var(--pons-ry, 0deg));
}

/* Slow vertical float. Uses the independent `translate` property rather
   than a translateY inside `transform`: the transform above is driven by
   pons-tilt.js through --pons-rx/--pons-ry, and putting the float in the
   same property would have the animation and the pointer fighting over
   it. `translate` composes with `transform` and is still a pure vertical
   move — no scale, no rotation, no shadow, nothing that reads as hover.
   It is a transform, so the layout box does not move and nothing around
   it reflows. Scoped to this logo; deliberately not generalised. */
@media (prefers-reduced-motion: no-preference) {
  .pons__logo { animation: pons-float 6s ease-in-out infinite; }
}

@keyframes pons-float {
  from, to { translate: 0 0; }
  50%      { translate: 0 -10px; }   /* 10px peak to trough */
}

.pons__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* A block, not three floating lines. */
  align-items: flex-start;
  gap: 10px;
}

.pons__title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(17px, 4.6cqw, 30px);   /* same as .fair__title */
  line-height: 1.12;
  color: rgb(242, 242, 242);   /* [reference] primary ink */
  white-space: nowrap;               /* one line, never wraps */
}


/* The address, on the same lime as the CTAs and in the same rectangle. */
/* Two classes: .ca sets background: var(--surface) further down and would
   otherwise win on order. */
.ca.ca--lime {
  /* Clear of the line above. The text block's own rhythm is 10px; at 14 the
     total was 24 and the address still read as part of the paragraph, so it
     is 30 here for a 40px gap. */
  margin-top: 30px;
  /* The address is 42 characters and does not shrink, so on a phone it has
     to be allowed to wrap rather than push the page wide. Wrapping keeps all
     42 visible; truncating would not. */
  min-width: 0;
  max-width: 100%;
  /* One row, never two. The address wraps inside its own box; the icon stays
     beside it and is centred on the whole address block rather than dropping
     underneath it, which is what flex-wrap used to do at narrow widths and
     what made this row 103px tall at 393. */
  flex-wrap: nowrap;
  align-items: center;
  /* The height is the address plus this padding and nothing else — it is no
     longer set by a 44px button box. */
  padding: 10px 14px;
  position: relative;
  isolation: isolate;
  /* No overflow:hidden. The sweep pseudo is inset:0 with its own 14px radius,
     so it never painted outside this box anyway — but the clip was cropping
     the copy control's 44px tap pad down to the row's height (40px at 1440). */
  border: 0;
  border-radius: 14px;
  background: #d4fc50;
}

/* Dark ink on lime, not white — the whole point of the lime treatment. */
.ca.ca--lime .ca__addr {
  color: rgb(13, 13, 13);
  min-width: 0;
  flex: 1 1 auto;
  /* .ca__addr is nowrap by default; below the width where 42 characters fit
     on one line it breaks instead of overflowing. */
  overflow-wrap: anywhere;
  word-break: break-all;
  white-space: normal;
}
/* The copy control has no box of its own on lime: the icon sits directly on
   the fill. Its 44px tap area is a centred pseudo-element, so the drawn
   control stays icon-sized and contributes nothing to the row's height. */
.ca.ca--lime .ca__copy {
  color: rgb(13, 13, 13);
  position: relative;
  align-self: center;
  width: auto;
  height: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}
.ca.ca--lime .ca__copy::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: max(100%, 44px);
  height: max(100%, 44px);
}
.ca.ca--lime .ca__copy:hover { background: none; color: rgba(13, 13, 13, 0.62); }

/* The same sweep the lime pills run, on the same clock. */
.ca.ca--lime::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 14px;
  background-image: linear-gradient(100deg,
      rgba(0, 0, 0, 0) 0%,
      rgba(0, 0, 0, 0.14) 50%,
      rgba(0, 0, 0, 0) 100%);
  background-repeat: no-repeat;
  background-size: 55% 100%;
  animation: live-sweep 32s linear infinite;
}

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

.pons__line {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(12px, 2.5cqw, 16px);   /* same as .fair__line */
  line-height: 1.35;
  color: rgba(242, 242, 242, 0.58);   /* [reference] secondary ink */
}

/* Below a 380px card the button keeps its corner; the logo and text stack
   underneath it rather than sitting side by side. */
@container (max-width: 760px) {
  .pons__inner { flex-direction: column; align-items: stretch; }
  .pons__cta { align-self: flex-start; }
}

@container (max-width: 380px) {
  .pons__row {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: clamp(10px, 3cqw, 16px);
  }
  .pons__text { width: 100%; }
  .pons__title { white-space: normal; }
}

/* [ponsfamily.com/analytics] .analytics-chart-bars rect / rect.is-active
   fill  var(--chart-line)   — #6f9f18 light theme, #d4fc50 dark
   glow  drop-shadow(0 0 5px color-mix(in srgb, var(--chart-line) 48%, transparent))
   The bar rests at opacity .72 and goes to 1 with the glow when active;
   it is a .18s transition, not a loop — nothing pulses.
   Their green as the fill, their glow around it, and the travelling
   border light every other button here carries. */
.pill--lime {
  /* the Pons lime, their --chart-line in dark theme */
  background: #d4fc50;
  border-color: rgba(13, 13, 13, 0.28);
  /* ink on the lime measures 13.04:1; cream would be 1.08:1 */
  color: var(--ink);
  /* their glow, retargeted to the lime. Measured at 1.06-1.09:1 against
     this pale ground — it is not doing anything visible here. */
  filter: drop-shadow(0 0 5px rgba(212, 252, 80, 0.48));
}

/* The resting sheen disc is tuned for a transparent pill; at its usual
   strength it lifted this fill from rgb(111,159,24) to rgb(171,198,119)
   and the green stopped being their green. Softened so the fill survives. */
.pill--lime .pill__fx::before {
  background: radial-gradient(circle at 32% 64%,
      rgba(255, 255, 255, 0.16) 0,
      rgba(255, 255, 255, 0) 55%);
}

/* the hover bloom stays, in the family of the fill rather than cream */
.pill--lime .pill__fx::after { background-color: #e2ff8a; }
/* --blob is a length in px, but a perimeter is not something CSS can
   compute, so each family carries a tuned value — and it has to follow the
   host as the host resizes. At 375 the nav pills collapse to a 33x32
   near-circle whose perimeter is 103px; the desktop 100px blob wrapped the
   entire ring. These keep every family near 18% of its own path. */

/* --- Page ending -----------------------------------------------------
   The tail, not a section. No surface, no card, no container, no border,
   no fill — the ground and its noise run straight through both.
-------------------------------------------------------------------- */

/* Inside the rails, under the flat map row, above the closing rule.
   Between body (16px) and the flat map heading (clamp(28px,5vw,64px)). */
.close-line {
  margin: clamp(28px, 2.8vw, 40px) 0 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 2.1vw, 28px);
  line-height: 1.2;
  color: var(--ink);
}

/* Outside the rails, on the ground. Three short columns read as fine
   print; one long stack reads as another passage. Aligned to the flat
   map row's own bounds: the rails' inset plus their gutter. */
.fine {
  /* Aligned to the flat map row, not to the rails: that row is capped at
     1072 and centred, so the columns share its exact left and right. */
  box-sizing: border-box;
  width: 100%;
  /* The row's own geometry, rebuilt: the rails' inset and gap, plus their
     gutter, with the 1072 cap the row carries. Matches its left and right
     at every width rather than only at 1440. */
  max-width: calc(1072px + 2 * (var(--inset) + var(--rail-gap) + var(--gutter)));
  padding-inline: calc(var(--inset) + var(--rail-gap) + var(--gutter));
  margin: clamp(20px, 1.8vw, 26px) auto clamp(24px, 2.2vw, 32px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(18px, 2vw, 30px);
  align-items: start;
}

.fine p {
  margin: 0;
  font-family: var(--font-display);
  font-size: 12px;
  line-height: 1.35;                  /* tighter than the body's 1.5 */
  color: var(--ink-quiet);
  text-wrap: pretty;
}

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

/* Below 900 the address bar cannot share a row with the wordmark and the
   buttons: it is 445px wide and was overflowing, with .hero clipping the
   buttons out of reach. It drops to its own full-width row inside the same
   sticky container. The address stays complete — all 42 characters, EIP-55
   casing intact. */
/* Raised from 900 to 1100 when the two lime CTAs arrived. With four full
   pills the group needs 587px, and while the address bar still shares the
   row there is nowhere near that: at 1024 the pills wrapped one-per-line
   into a 176px nav. Dropping the bar to its own row through 1100 gives the
   group the full width and it sits on one line again. Above 1100 the bar
   fits alongside and .nav__end's own wrap handles the rest. */
/* Page beneath does not move while the panel is open.

   overflow:hidden on html/body is the pattern iOS Safari has never honoured:
   the page behind keeps scrolling under the panel. Taking the body out of
   flow is the one that holds there — fixed at a negative offset equal to the
   scroll position, so there is no scrollable document left to move. The
   offset itself is written inline by nav-menu.js, which puts the exact
   position back on close.

   .topbar is position:fixed, so it is not carried by this: the bar stays put
   while the body is displaced beneath it. */
html.nav-open body {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
  /* top: -<scrollY>px is set inline by nav-menu.js */
}

/* Below 900 the hero keeps its original height: the cards stack portrait
   there, so shrinking the hero buys no extra card above the fold. */

/* .hero must not be the nav's sticky containing block. overflow:hidden made
   it a scroll container, so `position: sticky` bound to the hero and the nav
   scrolled away with it (top -781 at scrollY 1400). overflow:clip clips
   exactly the same — the fan overflows its card by 176px and that clipping
   is load-bearing — but does not create a scroll container, so sticky binds
   to the viewport instead. */
.hero { overflow: clip; }

/* Grid items default to min-width:auto. The item here is the <li>, not the
   anchor — putting min-width:0 only on .card.social left the <li> binding
   and the tracks still came out 147.98/121.30 instead of two equal 1fr.
   So a .card.social could not shrink
   below its own min-content and the row measured 381 against a 277 track at
   375. That pushed document.scrollWidth to 430 and gave the page horizontal
   scroll on the most common mobile width. */
.socials > li,
.card.social { min-width: 0; }

/* --- Left cards size to their content -------------------------------
   They were `height: var(--card-short/tall)`, fixed, while the copy is
   not — so below about 890px of window height the text ran past the
   bottom edge. min-height keeps the masonry proportions at the sizes
   where they fit and lets the card grow when the window is short.
   Scoped to the first column: the fair card's fan is absolutely
   positioned and the pons card is a size container, so neither can be
   content-sized without collapsing. */
/* Content-sized, with a floor that is a constant rather than a slice of
   the viewport. These two are text: they should end shortly after their
   last line at any window size, and the floor exists only so a short card
   cannot collapse. */




/* --- Flat map below 600 ----------------------------------------------
   Five cards in a row never fitted 375: the labels' min-content held the
   tracks open and the document scrolled sideways. Three across, then two,
   centred. Aspect ratio, hairline, sweep and hover are untouched. */

/* --- Copy toast ------------------------------------------------------
   [reference] Values read off their rendered DOM and stylesheet:
   fixed at top 104px, centred; radius 14px (--radius-media); fill
   #100f0da8; backdrop-filter blur(14px); hairline and shadow together in
   one box-shadow, inset 0 0 0 1px #ffffff1f plus 0 8px 28px #00000059;
   ink #f2f2f2 with the close at #f2f2f266; padding 11px 12px 11px 14px;
   gap 10px; text 13px/1.25; tick 15px at stroke 1.7, cross at 1.5;
   close 20x20 at radius 5px.

   Their transition is declared as opacity + transform over 0.28s
   cubic-bezier(.22,1,.36,1) and then overridden later in the same sheet
   to opacity only, 0.28s linear, with the -10px entry drop removed. The
   override is what actually runs, so that is what is reproduced here.

   Never display:none — the live region has to stay in the document for a
   text change to be announced. */
.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: 14px;
  background: rgba(16, 15, 13, 0.659);          /* #100f0da8 */
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.122),
              0 8px 28px rgba(0, 0, 0, 0.349);
  color: #f2f2f2;

  opacity: 0;
  transform: translateX(-50%);
  pointer-events: none;
  transition: opacity 0.28s linear;
}



.toast.is-visible { opacity: 1; pointer-events: auto; }



.toast__tick {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  color: #f2f2f2;
}



.toast__text {
  margin: 0;
  flex: 1 1 auto;
  font-family: var(--font-display);
  font-size: 13px;
  line-height: 1.25;
  color: #f2f2f2;
  text-transform: none;
}



.toast__close {
  flex: 0 0 auto;
  display: inline-flex;
  /* Stays 20x20 on screen — the 44px tap area is a pseudo-element so the
     toast's own layout is untouched. */
  position: relative;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: rgba(242, 242, 242, 0.4);              /* #f2f2f266 */
  cursor: pointer;
  transition: color 0.18s cubic-bezier(.22, 1, .36, 1),
              background 0.18s cubic-bezier(.22, 1, .36, 1);
}



.toast__close:hover { color: #f2f2f2; background: rgba(255, 255, 255, 0.059); }



/* --- 44px tap areas -------------------------------------------------
   Both targets keep their drawn size; only the hit region grows. The
   pseudo is centred on the element and sized to the shortfall, so it
   never extends further than it has to. */
.toast__close::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: max(100%, 44px);
  height: max(100%, 44px);
  /* invisible, and it must not eat the pointer for anything underneath
     other than its own control */
  background: none;
}



.toast__close:focus-visible {
  outline: 2px solid rgba(242, 242, 242, 0.58);
  outline-offset: 2px;
  color: #f2f2f2;
}



.toast__tick svg,
.toast__close svg { width: 100%; height: 100%; display: block; }

/* --- Topbar ---------------------------------------------------------
   The World is Fair structure, read off the live page: a fixed 88px band
   holding a 40px lozenge. Transparent at rest; on scroll it fills and an
   inset ring appears, over 0.3s cubic-bezier(0.83, 0, 0.17, 1).
-------------------------------------------------------------------- */
.topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--topbar-h);
  padding: 0 56px;
  pointer-events: none;          /* the lozenge takes the pointer, not the band */
  /* [reference] the band. Full width, fixed, transparent at rest and solid
     rgb(13,13,13) once scrolled, with a 1px bottom hairline appearing with
     it. At the top of the page the ground is the same 13, so it reads as
     nothing; its job is to occlude what scrolls under it. */
  background-color: rgba(13, 13, 13, 0);
  transition: background-color 0.3s cubic-bezier(0.83, 0, 0.17, 1),
              box-shadow 0.3s cubic-bezier(0.83, 0, 0.17, 1);
}

.topbar.is-scrolled {
  background-color: rgb(13, 13, 13);
  box-shadow: rgba(255, 255, 255, 0.06) 0 1px 0 0;
}

.topbar-pill {
  position: relative;         /* the centre pair is positioned against it */
  pointer-events: auto;
  box-sizing: border-box;
  width: 100%;
  max-width: calc(var(--page-max) - 160px);
  /* Height from the content, not a fixed 40: the pills are 44 tall (36 drawn
     plus the transparent 4px block borders that make the tap target), so a
     40px lozenge had them overflowing it and touching both edges. */
  height: auto;
  padding: 8px 16px;
  border-radius: 160px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  color: var(--ink);
  /* Filled at rest, not only when scrolled. These are the values this file
     already carried on .is-scrolled — see the report: the reference's own bar
     is transparent in BOTH states (sampled at 13,13,13 either way), so it
     cannot supply a resting fill and none has been invented here. The two
     transparent declarations that used to sit here are gone; they came later
     in this same block and were quietly winning. */
  background-color: rgb(26, 25, 23);
  box-shadow: rgba(255, 255, 255, 0.12) 0 0 0 1px inset;
  /* Only the geometry moves on scroll now, so only the geometry transitions. */
  transition: height 0.3s cubic-bezier(0.83, 0, 0.17, 1),
              padding 0.3s cubic-bezier(0.83, 0, 0.17, 1);
}

/* Scrolled, only the geometry changes — the fill and ring are already on at
   rest, so this is the padding step alone and the height follows it. */
.topbar.is-scrolled .topbar-pill {
  padding: 10px 20px;
}

/* Two groups, not one even run. Both are pushed right by the auto margin
   so the last pill ends flush with the bar; the wordmark keeps the left.
   8px inside a group, 40px between them — five times the inner gap, which
   is what makes them read as two things rather than six. */
.topbar-groups {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.8vw, 40px);
  min-width: 0;
}

.topbar-nav {
  display: flex;
  align-items: center;
  /* 12, not 8: at 8 the pills' 44px boxes sat one gap apart and read as
     touching. */
  gap: 12px;
  min-width: 0;
}

.topbar-mark {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: 17px;
  letter-spacing: 0.01em;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
}



/* The cluster: a tinted trough the pills sit in. The 4% is what makes the
   quiet pills read as pills without giving them a fill of their own. */
/* No trough. Each pill carries its own shape now, so the cluster fill
   the reference uses would only muddy them. */

.topbar-nav-plain {
  height: auto;
  padding: 0;
  background: none;
  gap: 22px;
}

.topbar-nav-plain a {
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--ink-quiet);
  text-decoration: none;
  transition: color 0.18s;
}
.topbar-nav-plain a:hover { color: var(--ink); }

/* --- The pill ------------------------------------------------------
   One mechanism, the reference's: the host is transparent and the fill
   and the sweep are its two pseudo-elements. No conic ring here.
-------------------------------------------------------------------- */
.tp {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  /* The reference draws these 32px tall, which is under the 44px tap
     minimum this project holds. Both are satisfied without changing what
     is drawn: content-box plus a transparent 6px block border makes the
     border box 44 while the padding box — which the fill and sweep
     pseudos are positioned against — stays 32. */
  box-sizing: content-box;
  /* The reference's floor is 32px tall, 0 18px, 14px/400, ls 0.28px.
     Ours goes up from there: 36 tall, 0 22px, 15px. content-box plus a
     transparent 4px block border makes the border box 44 for the tap
     target while the padding box — which the fill and sweep pseudos are
     positioned against — stays 36. */
  height: 36px;
  border-block: 4px solid transparent;
  padding: 0 22px;
  border-inline: 0;
  border-radius: 160px;
  background: none;
  background-clip: padding-box;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.18s, background 0.18s;
}

/* The reference's Vault treatment, with the one thing it can leave out
   put back. Theirs is transparent with no border because a cluster fill
   gave it shape; ours has no cluster, so it keeps a fill and a hairline
   and nothing else — no pseudo-elements, no animation. The light band
   read as a smear across the label on a dark pill, so the lime pair is
   the only thing moving in the bar. */
.tp--dark {
  color: var(--ink-quiet);
  background-color: var(--surface);
  /* One hairline, not two. A border-inline plus an outer shadow ring put
     two of them on the left and right edges and the sampled value came
     out at 55/255 instead of 29. An INSET ring draws inside the padding
     box, so it traces the 36px pill and not the 44px tap border. */
  border-block: 4px solid transparent;
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: color 0.18s, background-color 0.18s;
}

.tp--dark:hover,
.tp--dark:active {
  color: var(--ink);
  background-color: rgb(32, 32, 32);
}

.tp--live { color: rgb(13, 13, 13); }

.tp--live::before {
  content: "";
  position: absolute;
  inset: 3px 0;
  z-index: -2;
  border-radius: 160px;
  background-color: #d4fc50;
  transition: background-color 0.18s;
}

/* The pair had a transition to nothing, so hovering did nothing. This
   lifts the fill by the same amount the dark pills move: they go 22.0 to
   32.0, so the lime goes 181.3 to about 191.3 — the same 10/255, scaled
   toward white from its own colour rather than a different hue. */
.tp--live:hover::before,
.tp--live:active::before { background-color: rgb(218, 252, 104); }

/* A rectangle rather than a lozenge, for the lime CTAs that sit on cards.
   14px is the established next step down in this file's radius family —
   28 hero, 20 content card, 14 the controls on one (.ca already uses it),
   10 the button inside those. The pseudos carry the fill and the sweep, so
   they have to lose the 160px too or the corners stay round.
   .tp.tp--rect, not .tp--rect: .tp--live::after re-sets 160px further down
   this file and would otherwise win on order at equal specificity. */
.tp.tp--rect,
.tp.tp--rect::before,
.tp.tp--rect::after { border-radius: 14px; }

/* The sweep is the lime pair's alone. */
.tp--live::after {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 160px;
  background-image: linear-gradient(100deg,
      rgba(0, 0, 0, 0) 0%,
      rgba(0, 0, 0, 0.14) 50%,
      rgba(0, 0, 0, 0) 100%);
  background-repeat: no-repeat;
  /* 55%, not 300%: background-position percentages resolve against
     (container - image), so a band wider than its box travels backwards
     and barely moves. 55% is what the reference computes, and it is what
     makes -60%..160% read as one pass across the pill. */
  background-size: 55% 100%;
  animation: live-sweep 32s linear infinite;
}

.tp--live::after { inset: 3px 0; }

@keyframes live-sweep {
  from { background-position: -60% 0; }
  to   { background-position: 160% 0; }
}

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

.tp:focus-visible,
.topbar-mark:focus-visible,
.topbar-nav-plain a:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* --- The address bar, parked ----------------------------------------
   Styled on the new surface system so it needs no work when it lands in
   its section. Nothing renders it: it lives in <template id="parked-ca">.
-------------------------------------------------------------------- */
.ca {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 5px 5px 14px;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--hairline);
}

.ca__addr {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.01em;
  color: var(--ink);
  white-space: nowrap;
}

.ca__copy {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 10px;
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.ca__icon {
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Topbar below 1148 -----------------------------------------------
   Re-measured after the pills were grouped: the 40px gap between the two
   groups costs 32px over the flat 8px run, so the bar now needs 1036px
   and the narrowest viewport that holds it is 1148 rather than 1116. At
   1140 the run still ran 4.80px past the bar's right edge.

   Below it the four dark pills move into the panel and the bar keeps the
   wordmark, the two lime CTAs and the button.
-------------------------------------------------------------------- */
.topbar-menu-btn,
.topbar-menu { display: none; }

.topbar-mark-short { display: none; }

@media (max-width: 1147px) {
  :root { --topbar-h: 64px; }
  .topbar { height: var(--topbar-h); padding: 0 var(--inset); }

  /* flex, not the three-column grid: the centre and right slots are gone
     here, and as a grid the button landed in the second column on top of
     the pills. */
  .topbar-pill {
    max-width: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    height: 48px;
  }

  /* The wordmark stays on the bar with the two lime CTAs; the four dark
     section pills go to the panel. */
  .topbar-mark { flex: 0 0 auto; }

  .topbar-groups { gap: 0; }
  .topbar-nav {
    flex: 0 0 auto;
    justify-content: flex-end;
    gap: 8px;
  }
  .topbar-nav .tp--dark { display: none; }

  .tp--live {
    padding: 0 clamp(7px, 2vw, 14px);
    font-size: clamp(10px, 2.9vw, 13px);
  }

  /* The rules sit directly on the bar, like the wordmark — no border, no
     fill, no rounded container. The 44px hit area comes from padding around
     the 18px rules instead of from a drawn box: with border-box the padded
     total is exactly the 44x44 the bordered version occupied, so the button
     keeps its width and nothing else on the bar moves. */
  .topbar-menu-btn {
    display: inline-flex;
    position: relative;
    flex: 0 0 auto;
    min-width: 44px;
    min-height: 44px;
    padding: 21px 13px;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 0;
    background: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  /* three rules drawn in CSS: the middle is the element, the outer two
     are its pseudos. No icon font, no SVG. */
  .topbar-menu-mark,
  .topbar-menu-mark::before,
  .topbar-menu-mark::after {
    content: "";
    display: block;
    width: 18px;
    height: 1.5px;
    border-radius: 1px;
    background: var(--ink);
    transition: transform 0.22s cubic-bezier(.22, 1, .36, 1), opacity 0.14s linear;
  }
  .topbar-menu-mark { position: relative; }
  .topbar-menu-mark::before { position: absolute; top: -6px; left: 0; }
  .topbar-menu-mark::after  { position: absolute; top:  6px; left: 0; }

  .topbar-menu-btn[aria-expanded="true"] .topbar-menu-mark { background: transparent; }
  .topbar-menu-btn[aria-expanded="true"] .topbar-menu-mark::before { transform: translateY(6px) rotate(45deg); }
  .topbar-menu-btn[aria-expanded="true"] .topbar-menu-mark::after  { transform: translateY(-6px) rotate(-45deg); }

  /* No pressed fill either — without a radius it would read as a square
     patch on the bar, and the wordmark it now matches has none. */
  .topbar-menu-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

  /* --- the panel --------------------------------------------------- */
  .topbar-menu {
    display: flex;
    pointer-events: auto;
    position: absolute;
    left: var(--inset);
    right: var(--inset);
    /* 16px of clear space below the bar's outer bottom edge. The bar's pill
       ends 8px above the topbar box (64 tall, 48 pill, centred), so top:100%
       alone left only 8. Side insets are untouched — both the bar and this
       panel take var(--inset), so they already match by construction. */
    top: calc(100% + 8px);
    z-index: 5;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    border: 1px solid var(--hairline);
    border-radius: 20px;
    background-color: var(--surface);
    animation: nav-panel-in 0.2s cubic-bezier(.22, 1, .36, 1) both;
    /* No max-height and no scroll port: the content is three rows, and a
       scroll container here forced overflow-x to auto alongside it. The
       panel sizes to its content. */
    overscroll-behavior: contain;
  }

  .topbar-menu[hidden] { display: none; }

  .topbar-menu-row {
    /* No width:100%. .tp is content-box, so width:100% resolved to the
       panel's 347px content width and then added 32px of padding, pushing
       every row 32px past the panel and producing the horizontal scrollbar.
       Stretch (the flex default) sizes the border box to the panel. */
    height: 48px;
    justify-content: flex-start;
    padding: 0 16px;
    border-radius: 12px;
    font-size: 0.9375rem;
    background: rgba(255, 255, 255, 0.04);
  }

  @keyframes nav-panel-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
  }
}

/* Below 424 the mark becomes the ticker rather than shrinking the type.
   Re-measured for this bar: at 420 the full wordmark still pushed the run
   1.52px past the bar's right edge and at 424 it clears. */
@media (max-width: 423px) {
  .topbar-mark-full  { display: none; }
  .topbar-mark-short { display: inline; }
}

/* 44px tap areas for the two text-only controls. Drawn size unchanged;
   the pad is a pseudo so neither element's box moves. */
.topbar-mark,
.topbar-nav-plain a { position: relative; }

.topbar-mark::after,
.topbar-nav-plain a::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: max(100%, 44px);
  height: max(100%, 44px);
}
