/*
 * DWELL — night outside, fire inside.
 *
 * The page is one journey rather than a stack of sections: it opens on the
 * dwelling seen from outside under a real night sky, and the ground under the
 * content warms as you scroll until you are sitting by the hearth. That is why
 * there are two half-palettes here — cold unsaturated violet for the night,
 * and one warm accent that belongs to the fire and to nothing else.
 *
 * There is no light theme on purpose: a room lit by a fire stops meaning
 * anything in daylight. Every colour is stated outright so the page never
 * borrows the viewer's.
 */

/* ------------------------------------------------------------------- base */

/*
 * The user-agent rule for [hidden] is display:none at specificity (0,1,0),
 * which any class that sets a display of its own then beats. Two elements
 * here are toggled from the script by the hidden attribute — the mined
 * readout and the claim button — and both carry a class with a display, so
 * without this they stayed on screen while the script believed them hidden.
 * A visitor with no wallet read someone else is mining and was offered a
 * claim button that could do nothing.
 *
 * !important is the exception this rule exists for: hiding must not be
 * something a later declaration can quietly win back.
 */
[hidden] {
  display: none !important;
}

/* ------------------------------------------------------------------ faces */

/*
 * Served by our own process from web/fonts, not by a font CDN, and preloaded
 * from the page head so the swap happens early.
 *
 * The three overrides below are the important part. Browsers disagree about
 * which of a font's several vertical metrics to believe — Windows reads one
 * pair, macOS another — and this face states them 200 units apart, so the
 * baseline would land in a different place on each platform. Stating them
 * here settles it: the cap height is exactly one em, the descender exactly
 * one design pixel below the baseline, no line gap. Every line box is then a
 * whole number of pixels tall on every machine, which is the whole point of
 * using a pixel face.
 */
@font-face {
  font-family: "Akedopikuseru";
  src: url("/fonts/akedopikuseru.otf") format("opentype");
  font-weight: 400;
  font-display: swap;
  ascent-override: 100%;
  descent-override: 14.2857%;
  line-gap-override: 0%;
}

/*
 * The file declares an ascent of 1.1em (hhea and OS/2's win metrics agree,
 * and USE_TYPO_METRICS isn't set, so browsers use that figure) while the
 * font's own bbox shows real ink reaching only 0.35em above the baseline —
 * the same cap height as its descent is real (0.1em, and that one the file
 * gets right). With a tight line-height the browser reserves the full,
 * bogus 1.1em of ascent space and anchors the line box to it, which pushes
 * the actual glyphs into the bottom third of that space — inside a button
 * this reads as the label sitting low instead of centred. The overrides
 * restate the ascent as what the glyphs actually use, so a line box built
 * from it centres on the ink instead of on empty reserved space above it.
 */
@font-face {
  font-family: "PB Pixel";
  src: url("/fonts/pb-pixel.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
  ascent-override: 35%;
  descent-override: 10%;
  line-gap-override: 0%;
}

:root {
  /* night, outside */
  --night-deep: #0a0a14;
  --night-mid: #16142a;
  --night-soft: #23203d;
  --dusk: #4a4266;
  --haze: #9c94ba;

  /* the room, inside */
  --soot: #14131a;
  --stone: #3a3849;
  --ember: #ff7a34;
  --flame: #ffc24a;
  --linen: #e8e0d2;
  --bark: #2e2733;
  --glass: #1c1b33;

  /*
   * Three faces, three jobs.
   *
   * The split is not decorative: a bitmap face is drawn for one pixel size,
   * so it holds a heading and falls apart in a footnote. Display carries the
   * project's own name and its biggest numbers; pixel labels everything
   * smaller — nav, buttons, unit labels; Karla is the only one that ever
   * sets running prose, because a pixel face comes apart in a paragraph.
   *
   * Display is also the only monospaced one of the two pixel faces, which is
   * why the terminal headings (.term) stay on it rather than moving to the
   * smaller label face: a shell prompt whose columns do not line up is not a
   * shell prompt.
   */
  --display: "Akedopikuseru", ui-monospace, monospace;
  --pixel: "PB Pixel", "Silkscreen", ui-monospace, monospace;
  --body: "Karla", system-ui, -apple-system, sans-serif;

  /*
   * The seven-pixel grid.
   *
   * Akedopikuseru is an arcade face drawn in an eight-by-eight cell: the
   * letters fill seven pixels and the eighth is the gap that separates them.
   * Its em is those seven pixels, so one design pixel is exactly 1/7 em and
   * the face is crisp only at font sizes that are whole multiples of 7 —
   * 14, 21, 28, 35, 42, 63, 84. Every size on this page is one of those.
   * Do not make the type fluid: a clamp() lands between steps at nearly
   * every viewport width, and every letter goes soft.
   *
   * --px-gap is that one design pixel expressed as tracking. Letter-spacing
   * is either 0 or a multiple of it; anything else pushes letters off grid.
   * The line heights are 8/7 and 10/7 of the size, which land on whole
   * pixels precisely because the size is a multiple of 7.
   */
  --px-gap: 0.142857em;
  --lh-pixel: 1.142857;
  --lh-label: 1.428571;

  /* Everything spaces off four pixels, so nothing lands between art pixels. */
  --u: 4px;
  --page: 1200px;
  --side: 40px;

  /*
   * .bar's real rendered height (padding + tallest child), so .hero can size
   * itself against what's actually left of the screen below a sticky header
   * that sits in normal flow. Measured at the desktop breakpoint (80px);
   * the header is shorter below 640px (62px), so this slightly under-uses
   * the available height there rather than risk it the other way — a few
   * idle pixels beats the section running past the fold again.
   */
  --header-h: 80px;
}

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100%;
  background: var(--night-deep);
  color: var(--linen);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  /*
   * The pixel face ships one weight. Without this a stray bold would be
   * faked by smearing the glyph sideways, which on hard pixel edges reads
   * as a rendering fault rather than as emphasis.
   */
  font-synthesis: none;
}

/*
 * The travelling ground. Absolute rather than fixed: it has to scroll with
 * the page for the sky to actually become a room, which a fixed attachment
 * would flatten into a single static wash.
 */
body::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -3;
  background: linear-gradient(
    to bottom,
    var(--night-deep) 0%,
    var(--night-mid) 18%,
    var(--night-soft) 38%,
    #1c1a2c 52%,
    var(--soot) 68%,
    var(--soot) 88%,
    #0b0a10 100%
  );
}

img {
  max-width: 100%;
  image-rendering: pixelated;
}

a {
  color: inherit;
}

::selection {
  background: var(--ember);
  color: var(--soot);
}

/* ---------------------------------------------------------------- layers */

/*
 * The optional backdrop.
 *
 * It sits above the page's own gradient rather than replacing it, so the
 * ground is already painted before the first frame decodes and stays painted
 * if the file is missing or the browser refuses it. When it is playing, the
 * drawn sky underneath is switched off — two skies at once is one too many.
 */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  display: none;
}

[data-backdrop="video"] .backdrop {
  display: block;
}

[data-backdrop="video"] .clouds,
[data-backdrop="video"] .sky {
  display: none;
}

.sky,
.clouds,
.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

.sky {
  z-index: -2;
  width: 100%;
  height: 100%;
}

.clouds {
  z-index: -1;
  overflow: hidden;
}

.band {
  position: absolute;
  left: 0;
  width: 300%;
  height: 128px;
  background-repeat: repeat-x;
  background-size: auto 128px;
  image-rendering: pixelated;
  will-change: transform;
}

.band-far {
  top: 22vh;
  background-image: url("/art/clouds-far.svg");
  opacity: 0.55;
}

.band-mid {
  top: 40vh;
  background-image: url("/art/clouds-mid.svg");
  opacity: 0.45;
}

.band-near {
  top: 58vh;
  background-image: url("/art/clouds-near.svg");
  opacity: 0.7;
}

/*
 * A vignette rather than scanlines. Scanlines say "terminal"; this page is a
 * room, and what a room does at night is fall off into the dark at the edges.
 */
.vignette {
  z-index: 3;
  background: radial-gradient(120% 90% at 50% 45%, transparent 55%, rgba(4, 4, 9, 0.75) 100%);
}

/* ------------------------------------------------------------------- bar */

/*
 * A grid, not a flex row with space-between: with three children of
 * unequal width, space-between only equalises the two gaps, not the two
 * outer widths — so the nav drifted toward whichever side (brand or
 * bar-end) happened to be narrower. A 1fr/auto/1fr grid gives the nav its
 * own column and centres it in the row regardless of what its neighbours
 * weigh.
 */
.bar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: calc(var(--u) * 4);
  max-width: var(--page);
  margin: 0 auto;
  padding: calc(var(--u) * 4) var(--side);
  backdrop-filter: blur(6px);
}

.brand {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 3);
  /* Left, not justify-self:start: that keeps the item at its own max-content
     size and ignores the column, which is harmless here but inconsistent
     with .bar-end below — see the note there for why it matters on a narrow
     viewport. */
  min-width: 0;
  text-decoration: none;
}

.brand-mark {
  display: block;
  width: auto;
  height: 32px;
}

.wordmark {
  font-family: var(--display);
  font-size: 21px;
  /* Never above 400: the face ships one Regular, and anything more is faked. */
  font-weight: 400;
  line-height: 1;
  letter-spacing: var(--px-gap);
  color: var(--linen);
}

.nav {
  display: flex;
  justify-self: center;
  gap: calc(var(--u) * 9);
}

.nav a {
  font-family: var(--pixel);
  font-size: 26px;
  /* Without this it fell through to body's 1.65 leading, which at this size
     pushed the link's box far taller than its underline sits, throwing off
     the row's vertical centring against the brand mark and the button. */
  line-height: 1;
  letter-spacing: 0.08em;
  text-decoration: none;
  color: var(--haze);
  padding-bottom: var(--u);
  border-bottom: 2px dotted transparent;
  /* No easing on purpose — an instant switch reads more pixel than a fade. */
  transition: none;
}

.nav a:hover,
.nav a:focus-visible {
  color: var(--flame);
  border-bottom-color: var(--ember);
}

.bar-end {
  display: flex;
  align-items: center;
  /*
   * flex-end, not justify-self:end on the grid item: justify-self:end sizes
   * a grid item to its own max-content width and lets it ignore the column
   * entirely, so on a narrow viewport the connect button — the widest thing
   * in the header since the label sizes went up — overflowed past the edge
   * instead of shrinking. Left at its default (stretch), the item fills the
   * 1fr column, and flex-end here pushes its contents to that column's
   * right edge the same as before; now the column width is a real
   * constraint the button's own text can wrap inside instead of overflow.
   */
  justify-content: flex-end;
  gap: calc(var(--u) * 4);
  min-width: 0;
}

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

.cta {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--u) * 2);
  font-family: var(--pixel);
  font-size: 28px;
  /* Without this it fell through to body's 1.65 leading: at this font-size
     that inflated the button to roughly 66px tall while border-radius
     stayed at the 24px tuned for the old, smaller label size, so the pill
     rendered as a squashed, uneven rounded box instead of a clean stadium. */
  line-height: 1;
  letter-spacing: 0.06em;
  color: var(--linen);
  background: rgba(58, 56, 73, 0.35);
  border: 2px solid var(--dusk);
  /* Always a full pill regardless of how tall the button ends up: a radius
     this large is clamped by the browser to exactly half the shorter side,
     so it can't go stale again the next time the label size changes. */
  border-radius: 999px;
  padding: calc(var(--u) * 2) calc(var(--u) * 5);
  cursor: pointer;
  transition:
    transform 100ms ease,
    border-color 100ms ease;
}

.cta:hover:not(:disabled) {
  border-color: var(--ember);
  color: var(--flame);
  transform: translateY(-2px);
}

/* Pressing it moves down: a physical button, not a hover card. */
.cta:active:not(:disabled) {
  transform: translateY(2px);
}

.cta:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.cta-mark {
  width: 12px;
  height: 12px;
  background: var(--ember);
}

.cta-warm {
  border-color: var(--ember);
  color: var(--flame);
  background: rgba(255, 122, 52, 0.12);
}

:focus-visible {
  outline: 2px solid var(--flame);
  outline-offset: 3px;
}

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

main {
  position: relative;
  z-index: 2;
}

.hero {
  position: relative;
  /* Scopes .stage's negative z-index to this section, so it can't sink
     behind the page's own fixed sky/vignette layers. */
  isolation: isolate;
  min-height: calc(100vh - var(--header-h));
  display: flex;
  flex-direction: column;
  justify-content: center;
  max-width: 900px;
  margin: 0 auto;
  padding: calc(var(--u) * 8) var(--side);
  text-align: center;
}

/*
 * A scrim between the photograph and the copy: without it the blurb and
 * the readout sit directly on roof shingles and window detail and are
 * nearly unreadable. Peaks around the blurb, then eases back to nothing
 * by the very bottom — .stage's own fade already dissolves the art into
 * the page's plain gradient there, and if this stayed dark right up to
 * the edge, removing it would itself be the hard cut we're avoiding: the
 * section would end noticeably darker than the next one begins. Same
 * stack level as .stage, painted after it, so both stay behind the text
 * regardless of source order.
 */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    rgba(10, 10, 20, 0.2) 0%,
    rgba(10, 10, 20, 0.55) 50%,
    rgba(10, 10, 20, 0.6) 75%,
    rgba(10, 10, 20, 0) 100%
  );
  pointer-events: none;
}

.title {
  font-family: var(--display);
  /* Stepped, not fluid: see the note on the seven-pixel grid above. */
  font-size: 84px;
  font-weight: 400;
  line-height: var(--lh-pixel);
  /* The cell already carries its pixel of gap; tracking only loosens it. */
  letter-spacing: 0;
  margin: 0;
  text-shadow: 0 0 56px rgba(255, 122, 52, 0.3);
}

/* One letter carries the accent, the way the reference reddens its numeral. */
.title-last {
  color: var(--ember);
  /*
   * Cancels the gap trailing the last glyph. Every cell in this face carries
   * one design pixel of space on its right, so a centred line is always half
   * that far left of true centre — six pixels at the size above, which is
   * plainly visible. Removing the last gap makes the advance box equal the
   * ink, and the word centres on what you can actually see.
   */
  margin-right: calc(var(--px-gap) * -1);
}

.tagline {
  font-family: var(--pixel);
  font-size: 32px;
  /* See .cta for why the --pixel labels need this stated explicitly rather
     than falling through to body's 1.65. */
  line-height: 1.2;
  letter-spacing: 0.16em;
  color: var(--haze);
  margin: var(--u) 0 calc(var(--u) * 3);
}

.stage {
  /*
   * Breaks out of .hero's centered 900px column to the full viewport width.
   * left:50% + margin-left:-50vw lands the left edge at the screen edge
   * regardless of the column's own width, because .hero is itself centred
   * on the page — the trick only works against a centred ancestor.
   */
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  margin-left: -50vw;
  z-index: -1;
  /* Clips the zoomed scenes below back to the section's own box. */
  overflow: hidden;
  /*
   * Dissolves the art into the page's own travelling-ground gradient
   * (body::before) instead of ending on a hard rectangle where the next
   * section starts — the two are close in tone already, so this reads as
   * one continuous night rather than a seam.
   */
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}

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

/*
 * The two scenes share a frame, so they overlay to the pixel. Scaled up
 * from the bottom edge: the house was reading small and distant against a
 * mostly-empty sky, and growing from a fixed bottom anchor enlarges it
 * without the ground line drifting away from the section's own bottom.
 */
.scene-exterior,
.scene-interior {
  position: absolute;
  inset: 0;
  transform-origin: 50% 100%;
  transform: scale(1.3);
  transition:
    opacity 700ms ease,
    transform 700ms ease;
}

.hearth-art {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Connecting a wallet carries you over the threshold, camera and all. Each
 * chains onto the base scale(1.3) above rather than replacing it, so the
 * crossfade still zooms in/out relative to the enlarged composition.
 */
[data-scene="outside"] .scene-interior {
  opacity: 0;
  transform: scale(1.3) scale(1.08);
}

[data-scene="inside"] .scene-exterior {
  opacity: 0;
  transform: scale(1.3) scale(0.92);
}

/* ------------------------------------------------- the fire is the status */

/*
 * The hearth is one photograph now, not layered SVG groups the page could
 * fade independently. State reads through a scrim over the whole room
 * instead: thick when the fire is barely alive, gone once it's burning.
 */
.scene-interior::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--soot);
  opacity: 0;
  transition: opacity 700ms ease;
  pointer-events: none;
}

/* Cold: coals barely alive, the room almost unlit. */
[data-state="cold"] .scene-interior::after,
[data-state="short"] .scene-interior::after {
  opacity: 0.6;
}

/* Banked: someone lit it and left. Dimmer than burning, brighter than cold. */
[data-state="idle"] .scene-interior::after {
  opacity: 0.3;
}

/* Burning: the scrim lifts and the fire itself breathes, subtly. */
[data-state="burning"] .hearth-art {
  animation: breathe 2400ms ease-in-out infinite;
}

@keyframes breathe {
  0%,
  100% {
    filter: brightness(1);
  }
  50% {
    filter: brightness(1.045);
  }
}

/* ---------------------------------------------------- hero readout & copy */

.readout {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: calc(var(--u) * 2);
  margin: calc(var(--u) * 2) 0 0;
}

.readout-label {
  font-family: var(--pixel);
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--dusk);
}

.readout-value {
  font-family: var(--display);
  font-size: 35px;
  font-weight: 400;
  line-height: var(--lh-pixel);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.readout-unit {
  font-family: var(--pixel);
  font-size: 28px;
  line-height: 1.2;
  letter-spacing: 0.14em;
  color: var(--ember);
}

.status {
  font-family: var(--body);
  font-size: 21px;
  letter-spacing: 0.03em;
  color: var(--haze);
  margin: calc(var(--u) * 2) auto 0;
  max-width: 46ch;
  min-height: 2.4em;
}

.stage[data-state="burning"] ~ .status {
  color: var(--flame);
}

.actions {
  margin-top: calc(var(--u) * 2);
  min-height: 48px;
}

.blurb {
  max-width: 640px;
  margin: calc(var(--u) * 8) auto 0;
  font-size: 15px;
  line-height: 1.9;
  color: var(--haze);
  /*
   * The tail dissolves, as in the reference. Nothing load-bearing is put down
   * there — the sentence that matters is the first one.
   */
  -webkit-mask-image: linear-gradient(to bottom, #000 38%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 38%, transparent 100%);
}

.scroll-hint {
  display: inline-block;
  margin-top: calc(var(--u) * 6);
  padding: var(--u);
}

.chev-down,
.chev {
  display: block;
  width: 20px;
  height: 12px;
  background: var(--haze);
  /* Stepped, not a smooth triangle: the arrow admits the grid like everything else. */
  clip-path: polygon(
    0 0,
    25% 0,
    25% 33%,
    50% 33%,
    50% 66%,
    75% 66%,
    75% 33%,
    100% 33%,
    100% 0,
    75% 0,
    75% 33%,
    50% 33%,
    50% 66%,
    25% 66%,
    25% 33%,
    0 33%
  );
}

.chev-down {
  clip-path: polygon(0 0, 20% 0, 20% 40%, 40% 40%, 40% 70%, 60% 70%, 60% 40%, 80% 40%, 80% 0, 100% 0, 100% 40%, 80% 40%, 80% 70%, 60% 70%, 60% 100%, 40% 100%, 40% 70%, 20% 70%, 20% 40%, 0 40%);
  animation: nudge 2.4s ease-in-out infinite;
}

@keyframes nudge {
  0%,
  100% {
    transform: translateY(0);
    opacity: 0.5;
  }
  50% {
    transform: translateY(5px);
    opacity: 1;
  }
}

/* ------------------------------------------------------------- countdown */

.countdown {
  text-align: center;
  padding: calc(var(--u) * 10) var(--side);
}

.countdown-lead {
  font-family: var(--pixel);
  font-size: 30px;
  line-height: 1.2;
  letter-spacing: 0.1em;
  color: var(--haze);
  margin: 0;
}

.clock {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: calc(var(--u) * 4);
  margin-top: calc(var(--u) * 3);
}

.unit {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 76px;
}

.unit b {
  font-family: var(--display);
  font-size: 35px;
  font-weight: 400;
  line-height: var(--lh-pixel);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.unit i {
  font-family: var(--body);
  font-size: 14px;
  font-style: normal;
  letter-spacing: 0.2em;
  color: var(--dusk);
  margin-top: var(--u);
}

.sep {
  font-family: var(--display);
  /* Same size as the digits: the colon sits centred in its cell, so matching
     the size is what puts it at the right height between them. */
  font-size: 35px;
  color: var(--stone);
  line-height: var(--lh-pixel);
}

.countdown-note {
  font-family: var(--body);
  font-size: 18px;
  color: var(--dusk);
  margin: calc(var(--u) * 4) 0 0;
}

/* The fire being out is a fact, not an error: it is stated, not alarmed about. */
.countdown.is-out .unit b {
  color: var(--stone);
}

.countdown.is-out .countdown-note {
  color: var(--ember);
}

/* ---------------------------------------------------- terminal headings */

.section {
  max-width: var(--page);
  margin: 0 auto;
  padding: calc(var(--u) * 24) var(--side);
}

.term {
  display: flex;
  align-items: baseline;
  font-family: var(--display);
  font-size: 21px;
  font-weight: 400;
  /* A shell prompt whose columns drift is not a shell prompt; --display is
     the monospaced one of the two pixel faces for exactly this reason. */
  letter-spacing: 0;
  line-height: var(--lh-pixel);
  margin: 0;
  color: var(--linen);
}

.term-text {
  white-space: nowrap;
  overflow: hidden;
}

/* Set only once JS is alive, so a page without it shows the heading outright. */
/* Measured by JS from the rendered text: ch only holds for a monospaced face. */
.term {
  --type-width: max-content;
}

.term.will-type .term-text {
  width: 0;
}

.term.will-type.is-visible .term-text {
  animation: type 800ms steps(var(--chars)) both;
}

@keyframes type {
  from {
    width: 0;
  }
  to {
    width: var(--type-width);
  }
}

.term::after {
  content: "";
  /* Measured off the type rather than in pixels, so it stays a block of whole
     pixels at every step of the scale and needs no breakpoint of its own. */
  width: calc(var(--px-gap) * 4);
  height: 1em;
  margin-left: var(--px-gap);
  background: var(--ember);
  animation: blink 1s step-end infinite;
}

@keyframes blink {
  0%,
  50% {
    opacity: 1;
  }
  51%,
  100% {
    opacity: 0;
  }
}

.sigil {
  color: var(--dusk);
}

.cmd {
  color: var(--ember);
}

.section-lede {
  font-size: 17px;
  color: var(--haze);
  margin: calc(var(--u) * 3) 0 calc(var(--u) * 12);
  max-width: 60ch;
}

/* -------------------------------------------------------------- timeline */

.timeline {
  display: flex;
  align-items: center;
  gap: calc(var(--u) * 3);
}

.chev {
  border: 0;
  padding: 0;
  background: var(--dusk);
  cursor: pointer;
  flex: none;
}

.chev-left {
  transform: rotate(90deg);
}

.chev-right {
  transform: rotate(-90deg);
}

.chev:hover {
  background: var(--ember);
}

.track {
  display: flex;
  justify-content: space-between;
  gap: calc(var(--u) * 4);
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  overflow-x: auto;
  scrollbar-width: none;
}

.track::-webkit-scrollbar {
  display: none;
}

.node {
  display: grid;
  justify-items: center;
  gap: var(--u);
  flex: 1;
  min-width: 128px;
  position: relative;
  scroll-snap-align: center;
}

/* The dotted rule between nodes: the reference's line, drawn as dots. */
.node:not(:last-child)::before {
  content: "";
  position: absolute;
  top: 62px;
  left: 50%;
  width: calc(100% + var(--u) * 4);
  height: 4px;
  background-image: radial-gradient(circle, var(--stone) 1px, transparent 1px);
  background-size: 12px 4px;
}

.node-at {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 400;
  line-height: var(--lh-pixel);
  color: var(--ember);
}

.node-when {
  font-family: var(--body);
  font-size: 15px;
  letter-spacing: 0.08em;
  color: var(--flame);
}

.node-dot {
  width: 12px;
  height: 12px;
  background: var(--ember);
  transform: rotate(45deg);
  margin: calc(var(--u) * 2) 0;
  box-shadow: 0 0 12px rgba(255, 122, 52, 0.7);
}

.node-what {
  font-size: 15px;
  color: var(--linen);
  text-align: center;
}

/* ------------------------------------------------------------------ flow */

/*
 * The path the reward actually travels, as four bordered stages.
 *
 * This section used to be a podium of live figures — vault balance, released,
 * claimed, miners present. Before launch every one of them reads zero or
 * close to it, which says "nothing is happening here" to a first-time
 * reader, and inventing friendlier numbers was never on the table. Stating
 * the mechanism instead is true at any point in the protocol's life, and it
 * is the part that earns trust anyway. The figures are still served by
 * /v1/stats and can come back once they mean something.
 *
 * An <ol> because the order is load-bearing: fees cannot be swapped before
 * they are collected. Boxes wired by diamonds, rather than the dotted line
 * used by the timeline above, so two horizontal sequences on one page do not
 * read as the same device twice.
 */
.flow {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: calc(var(--u) * 6);
  list-style: none;
  margin: 0;
  padding: 0;
}

.flow-step {
  position: relative;
  border: 1px solid var(--stone);
  padding: calc(var(--u) * 5);
}

/* The diamond sitting midway across the gap to the next stage. */
.flow-step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  width: 12px;
  height: 12px;
  background: var(--ember);
  transform: translate(calc(50% + var(--u) * 3), -50%) rotate(45deg);
}

.flow-tag {
  display: block;
  font-family: var(--pixel);
  font-size: 24px;
  /* See .cta for why the --pixel labels state this rather than inheriting. */
  line-height: 1.2;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ember);
}

.flow-title {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 400;
  line-height: var(--lh-pixel);
  letter-spacing: 0;
  color: var(--linen);
  margin: calc(var(--u) * 4) 0 calc(var(--u) * 3);
}

.flow-body {
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.75;
  color: var(--haze);
  margin: 0;
}

.footnote {
  font-family: var(--body);
  font-size: 16px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--dusk);
  margin: calc(var(--u) * 12) auto 0;
  max-width: 70ch;
}

/* ------------------------------------------------------ operating conditions */

/*
 * The four conditions for mining, as one filled band.
 *
 * This section used to be a carousel of HOLD / TEND / BURN / CLAIM. Its
 * content survives elsewhere — the timeline above states the schedule and
 * the flow states where the reward comes from — and a gallery you have to
 * click through hid three quarters of the answer to "can I do this?" behind
 * an interaction most readers never start.
 *
 * Filled band with internal dividers, deliberately unlike the outlined,
 * diamond-linked boxes of .flow directly above: three sequences of cards in
 * a row would otherwise read as one device repeated.
 */
.conds {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  margin: 0;
  background: var(--soot);
  border: 1px solid var(--stone);
}

.cond {
  padding: calc(var(--u) * 5);
  border-right: 1px solid var(--stone);
}

.cond:last-child {
  border-right: 0;
}

.cond-label {
  font-family: var(--pixel);
  font-size: 24px;
  /* See .cta for why the --pixel labels state this rather than inheriting. */
  line-height: 1.2;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--haze);
}

.cond-body {
  display: grid;
  gap: calc(var(--u) * 3);
  margin: calc(var(--u) * 4) 0 0;
}

.cond-value {
  font-family: var(--display);
  font-size: 35px;
  font-weight: 400;
  line-height: var(--lh-pixel);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--flame);
}

.cond-note {
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.75;
  color: var(--haze);
}

/* ----------------------------------------------------------- token address */

/*
 * The address is the one string on this page a reader will copy into a
 * wallet, so it gets its own bar rather than a line of prose: a mistyped
 * character here buys an impostor token, and this chain carries several.
 */
.addr {
  display: flex;
  align-items: stretch;
  margin-top: calc(var(--u) * 6);
  border: 1px solid var(--stone);
}

.addr-tag {
  display: flex;
  align-items: center;
  padding: 0 calc(var(--u) * 4);
  background: var(--soot);
  border-right: 1px solid var(--stone);
  font-family: var(--pixel);
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: 0.12em;
  color: var(--flame);
  white-space: nowrap;
}

/*
 * A system mono, not --display: forty-two hex characters in a face whose
 * every glyph is eight design pixels wide would run past any viewport.
 */
.addr-value {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  padding: calc(var(--u) * 4);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 15px;
  letter-spacing: 0.02em;
  color: var(--linen);
  white-space: nowrap;
  scrollbar-width: none;
}

.addr-value::-webkit-scrollbar {
  display: none;
}

.addr-actions {
  display: flex;
  flex: none;
}

.addr-btn {
  display: flex;
  align-items: center;
  padding: 0 calc(var(--u) * 4);
  border: 0;
  border-left: 1px solid var(--stone);
  background: transparent;
  font-family: var(--pixel);
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--haze);
  cursor: pointer;
}

.addr-btn:hover:not(:disabled) {
  background: var(--soot);
  color: var(--flame);
}

.addr-btn:disabled,
.addr-btn[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

/* aria-disabled is advisory on a link; this is what actually stops the click. */
.addr-btn[aria-disabled="true"] {
  pointer-events: none;
}

/* Not an address but a status, so it does not read as a value to copy. */
.addr-value.is-pending {
  font-family: var(--body);
  letter-spacing: 0.04em;
  color: var(--dusk);
}

/* ------------------------------------------------------------------- faq */

.qa {
  margin: 0;
  max-width: 74ch;
}

.qa dt {
  margin-top: calc(var(--u) * 4);
}

.qa button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--body);
  /* Questions carry weight where the pixel face used to carry shape. */
  font-weight: 500;
  font-size: 21px;
  letter-spacing: 0.02em;
  color: var(--linen);
}

.qa button:hover {
  color: var(--flame);
}

.brk {
  color: var(--dusk);
}

.sign {
  color: var(--ember);
  display: inline-block;
  width: 1ch;
}

.qa dd {
  margin: calc(var(--u) * 2) 0 0 4ch;
  font-size: 16px;
  color: var(--haze);
  max-width: 62ch;
}

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

.foot {
  position: relative;
  z-index: 2;
  overflow: hidden;
  text-align: center;
  padding: calc(var(--u) * 20) var(--side) calc(var(--u) * 8);
}

/*
 * The oversized mark, cropped by the bottom of the page. Low contrast on
 * purpose: it is the room's last wall, not a second logo.
 */
.watermark {
  position: absolute;
  left: 50%;
  /*
   * Twelve design pixels below the fold, which crops the letters at about a
   * third. The overrides on the @font-face are what make that reliable —
   * without them the baseline moves between platforms and so does the crop.
   */
  bottom: -0.428571em;
  transform: translateX(-50%);
  font-family: var(--display);
  font-size: 84px;
  font-weight: 400;
  letter-spacing: 0;
  line-height: var(--lh-pixel);
  color: #221d33;
  white-space: nowrap;
  user-select: none;
}

.watermark b {
  color: #4a2a1c;
  /* Same trailing-space cancellation as the headline; see .title-last. */
  margin-right: calc(var(--px-gap) * -1);
}

.foot-line {
  position: relative;
  font-family: var(--pixel);
  font-size: 36px;
  line-height: 1.2;
  color: var(--haze);
  margin: 0;
}

.foot-line a {
  color: var(--flame);
  text-decoration-color: var(--ember);
  text-underline-offset: 6px;
}

.foot-fine {
  position: relative;
  font-family: var(--body);
  font-size: 15px;
  letter-spacing: 0.04em;
  color: var(--dusk);
  margin: calc(var(--u) * 10) 0 0;
}

#miners {
  color: var(--ember);
}

/* --------------------------------------------------------------- reveals */

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 500ms ease-out,
    transform 500ms ease-out;
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ------------------------------------------------------------ responsive */

@media (max-width: 1024px) {
  :root {
    --side: 28px;
  }

  .nav {
    gap: calc(var(--u) * 5);
  }

  /* Four conditions across is a ribbon of text by this width; they pair up. */
  .conds {
    grid-template-columns: repeat(2, 1fr);
  }

  /* Every cell now ends a row or sits above one, so the seams change axis. */
  .cond:nth-child(2n) {
    border-right: 0;
  }

  .cond:nth-child(-n + 2) {
    border-bottom: 1px solid var(--stone);
  }

  /*
   * Four stages across is already a ribbon of text by this width — the
   * display face is wide enough that the titles start breaking into three
   * lines. They pair up instead.
   */
  .flow {
    grid-template-columns: repeat(2, 1fr);
  }

  /* The diamond after stage two would point at the end of the row. */
  .flow-step:nth-child(2n)::after {
    display: none;
  }
}

@media (max-width: 768px) {
  /* Down the seven-pixel scale, never between its steps. */
  .title {
    font-size: 63px;
  }

  .cond-value {
    font-size: 28px;
  }

  .watermark {
    font-size: 63px;
  }

  .unit b,
  .sep {
    font-size: 21px;
  }

  .nav {
    display: none;
  }

  .section {
    padding-block: calc(var(--u) * 16);
  }

  .track {
    scroll-snap-type: x mandatory;
    justify-content: flex-start;
  }

  .node {
    min-width: 150px;
  }

  /*
   * The address bar stacks: forty-two hex characters plus three actions
   * cannot share a row this narrow without the address collapsing to a
   * sliver. The tag rejoins the actions as one strip beneath it.
   */
  .addr {
    flex-wrap: wrap;
  }

  .addr-value {
    order: -1;
    flex: 1 0 100%;
    border-bottom: 1px solid var(--stone);
  }

  .addr-tag {
    border-right: 0;
  }

  .addr-actions {
    margin-left: auto;
  }
}

/*
 * Below 768px nav is already gone, so the header is down to just the brand
 * mark and the connect button — but the button grew wide enough at its
 * desktop size, from making the small text bigger earlier, that the two
 * started crowding each other well before the 480px step where the rest of
 * the type scale shrinks. Caught by rendering the page at 500-600px CSS
 * width, not by guessing.
 */
@media (max-width: 640px) {
  .brand {
    gap: calc(var(--u) * 2);
  }

  .brand-mark {
    height: 22px;
  }

  /* One step down the 7px grid, same as the hero title takes at 480px. */
  .wordmark {
    font-size: 14px;
  }

  .cta {
    font-size: 18px;
    padding: calc(var(--u) * 1) calc(var(--u) * 3);
  }
}

@media (max-width: 480px) {
  :root {
    --side: 20px;
  }

  /* One stage at a time; every diamond would now sit off the row's end. */
  .flow {
    grid-template-columns: 1fr;
  }

  .flow-step::after {
    display: none;
  }

  .title {
    font-size: 42px;
  }

  .watermark {
    font-size: 42px;
  }

  /* The cursor needs no rule here: it is sized in em off the heading. */
  .term {
    font-size: 14px;
  }

  /* Four across does not fit; two rows of two do. */
  .clock {
    display: grid;
    grid-template-columns: repeat(2, auto);
    justify-content: center;
    gap: calc(var(--u) * 4) calc(var(--u) * 8);
  }

  .sep {
    display: none;
  }

  /* One condition at a time; the seams run horizontally all the way down. */
  .conds {
    grid-template-columns: 1fr;
  }

  .cond {
    border-right: 0;
    border-bottom: 1px solid var(--stone);
  }

  .cond:last-child {
    border-bottom: 0;
  }

  .qa button {
    font-size: 19px;
  }
}

/* ------------------------------------------------------ respect the reader */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  /* The fire holds still instead of breathing. */
  [data-state="burning"] .hearth-art,
  .chev-down {
    animation: none;
  }

  .term.will-type .term-text {
    width: auto;
  }

  .term.will-type.is-visible .term-text {
    animation: none;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  *,
  *::before,
  *::after {
    transition-duration: 1ms !important;
  }
}
