/* ==========================================================================
   $3310 — styles

   The banner's world: a near-black navy lit by a blue glow from above, white
   pixel type, and one bright blue (#2a9bfc, sampled from "BUY $3310. GET
   $NOKc."). Built on the $BLUE template's bones — square corners, cards
   lifted by a stepped block shadow, cubes drifting in the gutters — which is
   already pixel art, and only had to be turned dark.

   Two faces: Silkscreen, the pixel face, for the ticker's voice — headings,
   labels, figures, keys; Inter for anything you read as a sentence.
   ========================================================================== */

:root {
  /* Surfaces & ink — --page is the banner's own ground (#000e25 at its
     edges), so the hero sits on the page with no seam */
  --page:        #000e27;
  --surface:     #071a43;
  --ink:         #ffffff;
  --ink-2:       #b9c8e8;
  --muted:       #7d92bf;
  --line:        #143068;
  --line-strong: #1d3f84;

  /* The banner's blue, its lighter tint for text on dark, and the LCD's green */
  --accent:      #2a9bfc;
  --accent-deep: #7cc2ff;
  --accent-soft: rgba(42, 155, 252, .14);
  --lcd:         #8bd06b;
  --lcd-soft:    rgba(139, 208, 107, .16);

  --base-blue:   #0052ff;

  /* Shape. NO radius anywhere: right angles are the whole of the pixel-art
     idea — a rounded corner is an anti-aliased curve, which is the one thing
     an 84×48 LCD never draws.

     Notching the corners was the first attempt and it was worse: at card size
     it read as a cinema ticket, and on a 52px icon the four cuts met in the
     middle and turned the tile into a plus sign.

     Depth instead comes from --stack, a shadow drawn as two offset blocks
     with no blur — a pixel staircase — over
     one soft shadow that does the actual lifting. */
  --face:        linear-gradient(176deg, #0c2457 0%, #091d4a 50%, #06163b 100%);
  /* On the dark page a card separates by being a shade lighter, but mostly — there is nowhere lighter to go. It separates by EDGE
     and by DEPTH instead: a definite 2px outline, then two solid blocks
     stepping away from it. Both are pitched to stay quiet on white; a darker
     stack turns the dashboard into a comic panel. */
  --face-edge:   #1b3f88;
  --stack:       4px 4px 0 #0b2459, 8px 8px 0 #061840, 0 16px 30px rgba(0, 0, 0, .45);
  --stack-lift:  5px 5px 0 #12367e, 10px 10px 0 #0b2459, 0 22px 40px rgba(42, 155, 252, .28);

  --r-btn:       0px;
  --r-card:      0px;

  --shadow-card: 0 1px 2px rgba(0,0,0,.3), 0 6px 18px rgba(0,0,0,.35);

  --wrap:        1000px;

  --font: "Inter", system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --pixel: "Silkscreen", "Courier New", ui-monospace, monospace;

  color-scheme: dark;
}

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

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

body {
  margin: 0;
  /* The containing block for .cubes, which spans the document rather than
     the viewport. */
  position: relative;
  font-family: var(--font);
  color: var(--ink);
  background-color: var(--page);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;

  /* The faint isometric cubes behind everything — drawn rather than shipped,
     so crisp at any zoom and free. No glow of its own: the header clip
     brings its own, and a page glow under the clip's faded edges shows them. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='104' viewBox='0 0 120 104'%3E%3Cg fill='none' stroke='%232a9bfc' stroke-opacity='.07' stroke-width='1'%3E%3Cpath d='M30 0 60 17v34L30 68 0 51V17Z'/%3E%3Cpath d='M0 17l30 17 30-17M30 34v34'/%3E%3Cpath d='M90 52l30 17v34l-30 17-30-17V69Z'/%3E%3Cpath d='M60 69l30 17 30-17M90 86v34'/%3E%3C/g%3E%3C/svg%3E");
  background-position: center top;
}

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

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

:where(a, button):focus-visible {
  outline: 2.5px solid var(--accent-deep);
  outline-offset: 3px;
  border-radius: var(--r-btn);
}

/* Present to screen readers, absent on screen. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.wrap {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 20px;
}

/* ==========================================================================
   Falling cubes — the artwork's own confetti, behind everything

   The page already carries a static isometric tile; these are the same
   shapes let loose, in the coin's blue and the screen's green. Decorative in the strict sense: aria-hidden, pointer-events: none,
   and nothing on the page depends on them.

   ABSOLUTE, not fixed. Fixed was the first version and it was wrong: the
   cubes hung in the viewport like dust on the lens, going nowhere while the
   page moved under them. Anchored to the document they belong to the page —
   they scroll with it, and each one then lags by its own --depth, so the
   field has some thickness instead of being one flat sheet.

   z-index: -1 puts the layer above the body's own background and behind all
   in-flow content, so the opaque cards hide the cubes and they read in the
   gutters rather than under the figures.

   Two transforms, composed by nesting rather than fighting over one property:
   the <i> carries the scroll parallax, its ::before carries the drift. A
   single element cannot do both, because an animation owns `transform`
   outright for as long as it runs.
   ========================================================================== */

:root {
  --cube-blue:  url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 28'><path d='M12 1 23 7.5 12 14 1 7.5Z' fill='%235b80d6'/><path d='M1 7.5 12 14v13L1 20.5Z' fill='%230b3fb3'/><path d='M23 7.5 12 14v13l11-6.5Z' fill='%23002d82'/></svg>");
  --cube-green: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 28'><path d='M12 1 23 7.5 12 14 1 7.5Z' fill='%23b5d6a6'/><path d='M1 7.5 12 14v13L1 20.5Z' fill='%237bab70'/><path d='M23 7.5 12 14v13l11-6.5Z' fill='%234f7f47'/></svg>");
  /* Set by the scroll handler in index.html; 0 until then, and forever if
     that script never runs, which just means no parallax. */
  --scroll: 0px;
}

.cubes {
  position: absolute;
  inset: 0;              /* the body's full height, however tall the page is */
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.cubes i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: block;
  width: var(--s);
  height: calc(var(--s) * 1.1667);   /* the cube SVG's own 24:28 */
  /* The parallax. A cube shifted DOWN by a fraction of the scroll position
     appears to scroll slower than the text beside it — near cubes keep pace,
     far ones drag. */
  transform: translate3d(0, calc(var(--scroll) * var(--depth)), 0);
}

.cubes i::before {
  content: '';
  position: absolute;
  inset: 0;
  background: center / contain no-repeat var(--cube-blue);
  animation: cube-drift var(--d) linear var(--delay) infinite;
}

.cubes i.is-green::before { background-image: var(--cube-green); }

/* Falls a short way and fades out at both ends, so the loop has no seam: a
   cube that simply jumped back to its start would flicker once per cycle,
   and at these durations that is the only thing the eye would catch. */
@keyframes cube-drift {
  0%   { transform: translate3d(0, -70px, 0) rotate(0deg); opacity: 0; }
  14%  { opacity: var(--o); }
  86%  { opacity: var(--o); }
  100% { transform: translate3d(var(--drift), 210px, 0) rotate(var(--spin)); opacity: 0; }
}

/* Decoration is the first thing to go when someone asks for less motion. */
@media (prefers-reduced-motion: reduce) {
  .cubes { display: none; }
}

/* Thinner on a phone: the same count in a third of the width reads as weather
   rather than as a few cubes drifting past. */
@media (max-width: 620px) {
  .cubes i:nth-child(3n) { display: none; }
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  padding: 8px 20px 0;
  overflow: hidden;
}

/* The header clip IS the hero, as on $BLUE: it carries the wordmark and the
   line, so the page's <h1> is screen-reader only. Its ground is the page's
   navy, so it needs no frame: the glow at its top and the reflection at its
   bottom fade into the page, and the sides only barely, because "$3310"
   starts 3% in from the left edge.

   The clip is served at 1080×720 and the hero stops at 960px, so it is
   never scaled up. */
.hero__inner {
  /* 960px at most, and never so tall that the dashboard falls below the
     fold: the clip is 3:2, so its height is 2/3 of this width, and the
     dashboard with its legend needs about 230px under it. svh, so a phone's
     collapsing toolbar cannot push it off; vh where svh is unsupported. */
  max-width: min(960px, calc((100vh - 230px) * 1.5));
  max-width: min(960px, calc((100svh - 230px) * 1.5));
  margin: 0 auto;
}

.hero__media {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;    /* 1080×720 — the clip's own ratio, and its poster's.
                             Encoded on the element as well: change both. */
  object-fit: cover;
  background: var(--page);
}

/* The stage is exactly the clip, and a size container, so the overlays can
   be placed in cqw — fractions of the clip's own width — and stay pinned to
   the artwork at any size. The clip is 3:2, so its height is 66.67cqw. */
.hero__stage {
  position: relative;
  container-type: inline-size;
}

/* The clip's edges are faded into the page's own navy in the video file itself
   (images/src/README.md), so it needs no mask, no band and no halo here —
   any of those would draw a second edge of its own. */
.hero { position: relative; }

/* "Play Snake", low on the clip's reflection: below the phone and clear of
   every line of type in the clip. */
.hero__cta {
  position: absolute;
  left: 50%;
  bottom: 3.2%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 22px 12px;
  font: 700 17px/1 var(--pixel);
  letter-spacing: .02em;
  color: #fff;
  white-space: nowrap;
  background: linear-gradient(174deg, #5cb6ff 0%, var(--accent) 52%, #1468d6 100%);
  border: 2px solid #9fd2ff;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .45),
              4px 4px 0 #0b2459,
              0 0 26px rgba(42, 155, 252, .55);
  transition: transform .14s ease, box-shadow .14s ease;
}

/* X, chart and CA, top right of the clip, centred on the line of its BASE
   mark (6.9% down the clip = 4.6cqw) and inset as far from the right edge as
   BASE is from the left. */
.hero__actions {
  --btn: clamp(30px, 4.6cqw, 44px);
  position: absolute;
  top: calc(4.6cqw - var(--btn) / 2);
  right: 3.2cqw;
  padding: 0;
  z-index: 2;
}

.hero__actions .actions__inner { gap: clamp(6px, 1cqw, 10px); }

.hero__actions .btn {
  width: var(--btn);
  min-height: var(--btn);
  height: var(--btn);
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}

/* No frame: just the glyphs, white like the BASE mark they sit level with,
   lifting into the accent blue with a glow on hover. */
.hero__actions .btn::before { content: none; }
.hero__actions .btn__icon {
  width: calc(var(--btn) * .5);
  height: calc(var(--btn) * .5);
  color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, .5));
  transition: color .14s ease, filter .14s ease;
}
.hero__actions .btn:hover,
.hero__actions .btn:active {
  transform: none;
  border: 0;
  box-shadow: none;
}
.hero__actions .btn:hover .btn__icon,
.hero__actions .btn:focus-visible .btn__icon {
  color: var(--accent-deep);
  filter: drop-shadow(0 0 8px rgba(42, 155, 252, .8));
}
.hero__actions .btn:active .btn__icon { transform: translateY(1px); }

/* The template's phone rules size .actions and .btn for the old row under
   the hero; these outrank them at every width. */
.hero .hero__actions { padding: 0; }
.hero .hero__actions .btn { width: var(--btn); min-height: var(--btn); padding: 0; }

/* In the corner the toast would spill off the right edge if centred under
   the copy button, so it hangs from the button's right side instead. */
.hero__actions .toast { left: auto; right: 0; transform: translateY(-6px) scale(.96); }
.hero__actions .toast.is-on { transform: none; }

/* The button row used to space the hero from the dashboard; now the hero
   does it itself. */
.hero { padding-bottom: 28px; }

@media (max-width: 620px) {
  .hero { padding-bottom: 14px; }
  .hero__actions .toast { padding: 7px 11px; font-size: 11px; gap: 6px; }
}

.hero__cta-icon { width: 14px; height: 14px; flex: none; }

.hero__cta:hover {
  transform: translate(calc(-50% - 2px), -2px);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .45),
              6px 6px 0 #0b2459,
              0 0 34px rgba(42, 155, 252, .75);
}

/* Travels into its own shadow, like the other buttons. */
.hero__cta:active {
  transform: translate(calc(-50% + 4px), 4px);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .45), 0 0 0 #0b2459, 0 0 20px rgba(42, 155, 252, .45);
}

/* The jump to the game glides, unless the reader asked for less motion; and
   the section stops a little below the top edge rather than flush with it. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
#play { scroll-margin-top: 18px; }

/* ==========================================================================
   Actions — the three buttons under the hero
   ========================================================================== */

.actions { padding: 20px 20px 34px; }

.actions__inner {
  max-width: var(--wrap);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 14px;
}

/* Square, because there is no label to set the width any more: the padding
   that framed "X (Twitter)" would leave a lone 19px glyph adrift in a lozenge
   three times its width. The .sr-only span inside contributes no box.

   Cut and lit like the cards, and sitting on a hard 4px block of its own —
   which is what makes the press below feel like a real key travelling into
   its own shadow rather than a rectangle changing colour. */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 52px;
  width: 52px;
  padding: 0;
  border: 2px solid var(--face-edge);
  background: var(--face);
  box-shadow: 4px 4px 0 #0b2459, 0 8px 14px rgba(0, 0, 0, .35);
  font: inherit;
  font-size: 15.5px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  cursor: pointer;
  transition: transform .14s ease, filter .14s ease;
}

/* The lit top edge, as on the cards. */
.btn::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: rgba(124, 194, 255, .22);
}

.btn > * { position: relative; z-index: 1; }

.btn:hover {
  transform: translate(-2px, -2px);
  border-color: var(--accent);
  box-shadow: 6px 6px 0 #0f2e6c, 0 14px 24px rgba(42, 155, 252, .3);
}
.btn:hover .btn__icon { color: var(--accent-deep); }

/* Travels INTO its own shadow: the button moves exactly as far as the block
   beneath it shrinks, so the two read as one object being pressed rather than
   as a rectangle changing colour. */
.btn:active {
  transform: translate(4px, 4px);
  box-shadow: 0 0 0 #0b2459, 0 2px 4px rgba(0, 0, 0, .4);
}

.btn__icon { width: 19px; height: 19px; flex: none; color: var(--ink); }

.btn__icon--stroke {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Copied toast */
.ca-wrap { position: relative; }

.toast {
  position: absolute;
  top: calc(100% + 10px);
  left: 50%;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: var(--r-btn);
  background: #eaf3ff;
  color: var(--page);
  font-family: var(--pixel);
  font-size: 13.5px;
  white-space: nowrap;
  box-shadow: 0 8px 22px rgba(0,0,0,.5);
  opacity: 0;
  transform: translate(-50%, -6px) scale(.96);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
  z-index: 4;
}

.toast.is-on { opacity: 1; transform: translate(-50%, 0) scale(1); }

.toast__check { width: 15px; height: 15px; color: #2f9e44; flex: none; }
.toast.is-error .toast__check { color: #d6453a; }

/* ==========================================================================
   Dashboard cards
   ========================================================================== */

.stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}

/* Label and figure left, icon right — the tile is the card's only colour. The
   card sits on its own staircase of shadow blocks and steps further out of
   the page on hover, moving up AND left so it travels along the diagonal the
   stack is offset on rather than sliding across it. */
.stat {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 24px 26px;
  border: 2px solid var(--face-edge);
  background: var(--face);
  box-shadow: var(--stack);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}

/* The lit top edge. Every voxel in the artwork has one, and it is the whole
   difference between a flat rectangle and a moulded surface. */
.stat::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: rgba(124, 194, 255, .22);
}

.stat:hover {
  transform: translate(-2px, -3px);
  border-color: var(--accent);
  box-shadow: var(--stack-lift);
}

.stat__body { min-width: 0; }

/* A ticker keeps its own case — "$NOKc", not "$NOKC" — inside an uppercased label. */
.ticker { text-transform: none; }

.stat__label {
  margin: 0 0 8px;
  font-family: var(--pixel);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--accent);
}

.stat__value {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  font-family: var(--pixel);
  font-size: clamp(23px, 2.9vw, 30px);
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1.15;
  color: var(--ink);
  word-break: break-word;
  /* Same-width digits, so a figure that ticks up does not jostle the label
     beside it — and so the six cards line up as a set rather than as six
     independent numbers. */
  font-variant-numeric: tabular-nums;
}

.stat__unit {
  font-size: .56em;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-2);
}

/* No source for this metric yet — don't dress a dash up as a figure */
.stat__value .is-empty { color: var(--muted); font-weight: 500; }

.stat__sub {
  margin: 6px 0 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--ink-2);
  white-space: nowrap;
}

.stat__sub b { font-weight: 500; }

/* On the payout card the dollar figure is the one a reader can size: a bare
   reward-token amount means nothing without knowing what a unit is worth — so
   it runs nearly as large as the token amount above it. */
.stat__sub--lead {
  margin-top: 4px;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -.01em;
}

/* Where the fee actually goes. Static text, not a figure. */
.stat__note {
  margin: 7px 0 0;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--accent);
  letter-spacing: .01em;
}

/* Values are numeric strings that swap in — keep them from reflowing */
.stat__value, .stat__sub b { font-variant-numeric: tabular-nums; }

/* One icon spec for the whole set: a tan disc with a drawn glyph inside */
/* Not discs any more: square blue blocks, lit the way the voxels are — a
   bright top face, a deeper bottom, a hard block of shadow beneath. The icon
   inside goes white, which is what turns a pale badge into the card's one
   piece of colour. */
.stat__icon,
.fact__icon {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  background: linear-gradient(174deg, #5cb6ff 0%, var(--accent) 52%, #1468d6 100%);
  /* A voxel, read straight off the artwork: lit top face, shadowed bottom,
     and a hard block of its own colour underneath so it stands off the card
     rather than lying on it. */
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .5),
              inset 0 -2px 0 rgba(6, 34, 96, .38),
              3px 3px 0 #061840;
}

.stat__icon svg,
.fact__icon svg {
  width: 26px;
  height: 26px;
  fill: none;
  stroke: #fff;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Base keeps its own blue, being a network mark rather than one of the set —
   so its block stays pale and its stroke stays blue. */
.fact__icon--base {
  background: rgba(255, 255, 255, .06);
  box-shadow: inset 0 0 0 2px var(--face-edge);
}
.fact__icon--base svg { stroke: var(--base-blue); }

/* Blinking "…" placeholder, shown until the first data load resolves */
.dots {
  display: inline-flex;
  align-items: flex-end;
  gap: .18em;
  padding-bottom: .12em;
}

.dots i {
  width: .2em;
  height: .2em;
  border-radius: 50%;
  background: currentColor;
  opacity: .25;
  animation: dot-blink 1.25s infinite ease-in-out;
}

.dots i:nth-child(2) { animation-delay: .18s; }
.dots i:nth-child(3) { animation-delay: .36s; }

@keyframes dot-blink {
  0%, 65%, 100% { opacity: .22; }
  32%           { opacity: 1; }
}

/* Live-data legend. Discreet by design: it should be findable when someone
   wonders how current a figure is, and invisible the rest of the time. */
.legend {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 16px 0 0;
  font-size: 12.5px;
  letter-spacing: .01em;
  color: var(--muted);
}

.legend__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--muted);
  flex: none;
}

/* Reading the chain right now */
.legend.is-live .legend__dot {
  background: #46a24b;
  box-shadow: 0 0 0 0 rgba(70,162,75,.45);
  animation: legend-pulse 2.4s infinite ease-out;
}

/* Showing what was last read, because something did not answer */
.legend.is-stale .legend__dot { background: var(--accent); }

@keyframes legend-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(70,162,75,.45); }
  70%  { box-shadow: 0 0 0 6px rgba(70,162,75,0); }
  100% { box-shadow: 0 0 0 0 rgba(70,162,75,0); }
}

/* ==========================================================================
   Game — Snake on the phone, under the dashboard

   One card in the house style: the words on the left, the phone on the
   right, stacking on a phone screen. The phone is a photo with the game
   drawn over its own LCD (assets/js/snake/phone.js); its hit areas are
   percentages of the photo, so it can be any width and the keys stay put.
   ========================================================================== */

.game {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 250px);
  align-items: center;
  gap: 36px;
  margin-top: 30px;
  padding: 30px 34px;
  border: 2px solid var(--face-edge);
  background: var(--face);
  box-shadow: var(--stack);
}

/* The LCD's green, as the same 2px rule the other cards wear in blue. */
.game::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--lcd);
}

.game__title {
  margin: 0;
  font-family: var(--pixel);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--ink);
}

.game__lede {
  margin: 10px 0 0;
  max-width: 34ch;
  font-size: 16px;
  color: var(--ink-2);
}

.game__keys {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 9px;
  font-size: 14px;
  color: var(--muted);
}

.game__keys li { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.game__keys span { margin-left: 6px; }

.game__keys kbd {
  min-width: 26px;
  padding: 3px 7px;
  font: 400 12px/1.2 var(--pixel);
  text-align: center;
  color: var(--accent-deep);
  background: var(--accent-soft);
  border: 2px solid var(--face-edge);
}

.game__sound {
  margin-top: 22px;
  min-height: 40px;
  padding: 0 16px;
  font: 400 13px var(--pixel);
  color: var(--accent-deep);
  background: var(--surface);
  border: 2px solid var(--face-edge);
  box-shadow: 3px 3px 0 var(--face-edge);
  cursor: pointer;
}

.game__sound:hover { border-color: var(--accent); }

.game__phone { justify-self: center; width: 100%; max-width: 250px; }

/* ---- the phone itself ---- */

.phone {
  position: relative;
  width: 100%;
  aspect-ratio: 480 / 1168;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  filter: drop-shadow(0 22px 30px rgba(0, 45, 130, .28));
  outline: none;
}

/* Focus is what lets the arrow keys reach the game, so it has to be seen —
   but as a glow on the screen rather than a box around a phone-shaped photo. */
.phone:focus-visible .phone__lcd {
  box-shadow: 0 0 0 3px var(--lcd), 0 0 18px 4px rgba(123, 171, 112, .7);
  border-radius: 4px;
}

.phone__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Game pixels drawn over the photo's own LCD glass. */
.phone__lcd {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.phone__lcd canvas { width: 92%; aspect-ratio: 84 / 48; }

.phone__key {
  position: absolute;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  touch-action: manipulation;
}

.phone__key.is-held,
.phone__key:active {
  background: radial-gradient(closest-side, rgba(0, 0, 0, .28), rgba(0, 0, 0, .12) 70%, transparent);
}

@media (max-width: 760px) {
  .game { grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 24px 20px 28px; text-align: center; }
  .game__lede { margin-left: auto; margin-right: auto; }
  .game__keys { justify-items: center; }
  .game__phone { max-width: 240px; }
}

/* ==========================================================================
   Token facts — one card, three fixed properties
   ========================================================================== */

.facts {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 28px;
  margin-top: 26px;
  padding: 22px 26px;
  border: 2px solid var(--face-edge);
  background: var(--face);
  box-shadow: var(--stack);
}

.facts::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: rgba(124, 194, 255, .22);
}

.fact {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
}

/* Base keeps its own blue — it is a network mark, not part of the set */
.fact__icon--base { background: transparent; }
.fact__icon--base svg { stroke: var(--base-blue); stroke-width: 1.8; width: 30px; height: 30px; }

.fact__body { min-width: 0; }

.fact__label {
  margin: 0 0 5px;
  font-family: var(--pixel);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--accent);
}

.fact__value {
  margin: 0;
  font-family: var(--pixel);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1.25;
  color: var(--ink);
}

.fact__accent { color: var(--accent); }

.fact__sub {
  margin: 3px 0 0;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.3;
}

/* ==========================================================================
   Ecosystem
   ========================================================================== */

.eco {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 20px;
  margin: 44px auto 0;
  max-width: 660px;
  padding: 22px 24px 26px;
  border: 2px solid var(--face-edge);
  background: var(--face);
  box-shadow: var(--stack);
}

.eco__half {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 4px 8px;
  text-align: center;
  transition: transform .18s ease;
}

.eco__half:hover { transform: translateY(-2px); }

.eco__kicker {
  font-family: var(--pixel);
  font-size: 13px;
  letter-spacing: .03em;
  color: var(--ink-2);
  line-height: 1.3;
}

/* Both lockups have the resolution to run large — 550×112 and 800×261 — so
   they are sized by height and left to keep their own ratios. The
   Stonks.Exchange bar is much the wider of the two, which makes it read
   smaller at a matched height, so it gets a little more. */
.eco__logo { height: 44px; width: auto; }
.eco__logo--stonks { height: 52px; }

/* ==========================================================================
   Footer
   ========================================================================== */

.foot {
  text-align: center;
  color: var(--muted);
  font-size: 13px;
  padding: 34px 20px 40px;
}

.foot p { margin: 0; }


/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 900px) {
  .stats { gap: 14px; }
  .stat { padding: 18px 20px; gap: 14px; }
  .stat__icon, .fact__icon { width: 46px; height: 46px; }
  .stat__icon svg, .fact__icon svg { width: 23px; height: 23px; }
  .facts { gap: 20px; padding: 20px; }
}

@media (max-width: 760px) {
  .btn { min-height: 46px; padding: 0 18px; font-size: 14px; gap: 8px; }
  .btn__icon { width: 17px; height: 17px; }

  /* One fact per row, so the value never wraps under its own icon */
  .facts { grid-template-columns: minmax(0, 1fr); gap: 18px; }
}

@media (max-width: 620px) {
  /* Edge to edge on a phone, never overscanned: the clip has type within
     3% of both sides. Only the reflection at the bottom is feathered. */
  .hero { padding: 4px 0 0; }
  /* At phone width the reflection is too short to hold it without covering
     the phone, so it steps out of the clip and sits just beneath it. */
  .hero__cta {
    position: relative;
    left: auto;
    bottom: auto;
    transform: none;
    display: flex;
    width: max-content;
    margin: 2px auto 26px;
    padding: 8px 12px 7px;
    font-size: 12px;
    gap: 6px;
  }
  .hero__cta:hover { transform: translate(-2px, -2px); }
  .hero__cta:active { transform: translate(4px, 4px); }

  .hero__cta-icon { width: 9px; height: 9px; }

  .actions { padding: 4px 14px 22px; gap: 10px; }
  .actions__inner { gap: 10px; }
  .btn { min-height: 46px; width: 46px; }

  .wrap { padding: 0 14px; }

  /* Two cards per row. At this width the label, the figure and a 42px disc
     cannot share a line, so the card turns: the disc goes above the text and
     the figure gets the card's full width instead of what the disc leaves. */
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }

  .stat {
    flex-direction: column-reverse;   /* markup keeps the disc last, for reading order */
    align-items: flex-start;
    justify-content: flex-start;
    padding: 14px 13px;
    gap: 10px;
  }

  .stat__body { width: 100%; }

  .stat__label { font-size: 10.5px; letter-spacing: .05em; margin-bottom: 5px; }

  /* A figure is one word. The global break-word would hyphenate a market cap
     across two lines mid-thousands ("$4,567,890." / "12"), so it is turned off
     here and the type scales with the card instead. */
  .stat__value {
    font-size: clamp(15px, 4.6vw, 19px);
    gap: 5px;
    letter-spacing: -.01em;
    white-space: nowrap;
    word-break: normal;
  }
  .stat__unit { font-size: .66em; }
  .stat__sub { font-size: 13px; }
  .stat__sub--lead { font-size: 16px; margin-top: 3px; }
  .stat__note { font-size: 11px; margin-top: 5px; }

  .stat__icon { width: 34px; height: 34px; }
  .stat__icon svg { width: 17px; height: 17px; }
  .fact__icon { width: 42px; height: 42px; }
  .fact__icon svg { width: 21px; height: 21px; }

  /* Stack the two ecosystem blocks */
  .eco { grid-template-columns: minmax(0, 1fr); gap: 18px; margin-top: 32px; padding: 18px 16px 20px; }
  .eco__kicker { font-size: 12.5px; }
  .eco__logo { height: 34px; }
  .eco__logo--stonks { height: 30px; }

  .foot { padding: 26px 16px 32px; }
}

@media (max-width: 420px) {
  .actions__inner { gap: 8px; }
  .btn { min-height: 40px; padding: 0 11px; font-size: 12.5px; gap: 6px; }
  .btn__icon { width: 15px; height: 15px; }

  /* Two-up at 320px leaves ~140px a card. The turned card spends the disc's
     space vertically rather than beside the figure, so it can stay — only the
     type and the padding give. */
  .stats { gap: 8px; }
  .stat { padding: 12px 11px; gap: 8px; }
  .stat__icon { width: 30px; height: 30px; }
  .stat__icon svg { width: 15px; height: 15px; }
  .stat__label { font-size: 10px; letter-spacing: .04em; }
  .stat__sub, .stat__sub--lead { white-space: nowrap; }
  .legend { font-size: 11.5px; gap: 6px; }
  .stat__sub--lead { font-size: 15px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
  /* …but the loading placeholder still needs to be visible */
  .dots i { opacity: .55; }
}

/* ==========================================================================
   Dashboard, compact and unframed

   The six figures and the three facts read as one quiet block under the
   header rather than as nine boxes: no card, edge or shadow — just the icon
   block beside its label and figure, three to a row (two on a phone). Last
   in the file so it outranks the template's card rules at every width.
   ========================================================================== */

.stats {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px 28px;
}

.stat,
.stat:hover {
  flex-direction: row-reverse;      /* icon first on screen, still last in the markup */
  justify-content: flex-end;
  align-items: center;
  gap: 14px;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  transform: none;
}

.stat::before,
.facts::before { content: none; }

.stat__body { width: auto; }

.stat__icon,
.fact__icon {
  width: 38px;
  height: 38px;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .45),
              inset 0 -2px 0 rgba(6, 34, 96, .38),
              2px 2px 0 #061840;
}

.stat__icon svg,
.fact__icon svg { width: 19px; height: 19px; }

.fact__icon--base { box-shadow: inset 0 0 0 2px var(--face-edge); }
.fact__icon--base svg { width: 22px; height: 22px; }

.stat__label { margin-bottom: 4px; font-size: 11px; }

.stat__value {
  gap: 6px;
  font-size: clamp(17px, 1.9vw, 21px);
  white-space: nowrap;
  word-break: normal;
}

.stat__sub,
.stat__sub--lead { margin-top: 2px; font-size: 13px; }
.stat__note { margin-top: 3px; font-size: 11px; }

.legend { margin-top: 22px; }

.facts {
  gap: 20px 28px;
  margin-top: 34px;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}

.fact { gap: 14px; }
.fact__label { font-size: 11px; }
.fact__value { font-size: 15px; }
.fact__sub { font-size: 12.5px; }

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

@media (max-width: 620px) {
  .stats { gap: 18px 12px; }
  .stat,
  .stat:hover { flex-direction: row-reverse; align-items: center; padding: 0; gap: 10px; }
  .stat__icon { width: 30px; height: 30px; }
  .stat__icon svg { width: 15px; height: 15px; }
  .stat__label { font-size: 9.5px; letter-spacing: .03em; }
  .stat__value { font-size: clamp(14px, 4.2vw, 17px); }
  .stat__sub, .stat__sub--lead { font-size: 12px; }
  .facts { margin-top: 28px; gap: 16px; }
}

/* With the cards gone nothing hides the cubes behind the figures any more,
   so they keep to the gutters: masked out across the 1000px content column
   (plus a little air), and so absent altogether on screens without gutters. */
.cubes {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(50% - 540px), transparent calc(50% - 500px),
                                      transparent calc(50% + 500px), #000 calc(50% + 540px));
  mask-image: linear-gradient(90deg, #000 calc(50% - 540px), transparent calc(50% - 500px),
                              transparent calc(50% + 500px), #000 calc(50% + 540px));
}

/* ==========================================================================
   The first screen — header and dashboard, and nothing else

   .fold is at least one viewport tall, so whatever follows it (the game)
   starts below the fold. svh is the viewport with a phone's toolbars
   showing, so the game cannot peek in under them; vh where svh is not
   supported. The dashboard takes the room left under the header and centres
   itself in it, so the spare height is shared rather than pooled at the end.
   ========================================================================== */

.fold {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

.fold__dash {
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: 24px;
}

.fold + .wrap .game { margin-top: 0; }
