@font-face {
  font-family: Ioskeley;
  src: url("assets/IoskeleyMono-Regular.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

:root {
  color-scheme: dark;
  --background: #0a0a0a;
  --surface: #161616;
  --red: #ff2d2d;
  --amber-deep: #d88900;
  --text: var(--amber-deep);
  --amber: #ffb300;
  --line: #492020;
  /* Pixel-field inks, read by hero-pixels.js (Omarchy's --t-field-* tokens). */
  --field-dim: #391719;
  --field-mid: #882126;
  --field-lit: #d42b2b;
  --field-crest: #ff6a4d;
  font-family: Ioskeley, "SFMono-Regular", Consolas, monospace;
  color: var(--text);
  background: var(--background);
  font-synthesis: none;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 104px; }
body { margin: 0; font-size: 1rem; line-height: 1.65; }
::selection { color: var(--background); background: var(--amber); }
a { color: inherit; text-decoration: none; text-underline-offset: .3em; }
a:hover { text-decoration: underline; }
a:focus-visible { outline: 2px solid var(--amber); outline-offset: 6px; }
img { display: block; max-width: 100%; }
h1, h2, h3, p { margin: 0; }
h1, h2, h3 { color: var(--red); font-weight: 400; }
.wrap { width: min(1120px, calc(100% - 64px)); margin-inline: auto; }
.skip-link { position: absolute; top: -100px; left: 20px; padding: 12px 18px; background: var(--surface); z-index: 5; }
.skip-link:focus { top: 12px; }
/* Sticky, like omarchy.org. Unlike theirs, a translucent wash keeps the links
   legible where body copy scrolls under the bar; over the hero it reads as
   transparent, since the hero shares this background. */
.site-header { position: sticky; top: 0; z-index: 3; background: #0a0a0abf; backdrop-filter: blur(10px); }
.navigation { min-height: 84px; display: flex; align-items: center; gap: 28px; }
.logo-menu { position: relative; z-index: 4; }
.logo-menu summary { list-style: none; cursor: pointer; }
.logo-menu summary::-webkit-details-marker { display: none; }
.home { position: relative; display: flex; align-items: center; gap: 12px; font-size: 1.125rem; color: var(--red); user-select: none; }
/* The mark is a double rule around hollow double-line strokes, so it needs
   room: at 40px the box reads but the glyph inside is cramped, and below ~34px
   the whole thing closes into a red smudge. 48px is where both work. */
.logo-mark {
  position: relative;
  display: block;
  width: 48px;
  height: 48px;
  flex: 0 0 48px;
}
.logo-mark-svg {
  display: block;
  width: 48px;
  height: 48px;
}
.logo-amber-hum,
.logo-amber-shape {
  opacity: 0;
  transition: opacity .12s linear;
}
.logo-amber-shape {
  filter: drop-shadow(0 0 10px #ffb300aa);
}
.logo-menu:hover .logo-amber-hum,
.logo-menu[open] .logo-amber-hum,
.home:hover .logo-amber-hum {
  opacity: .28;
}
.logo-menu:hover .logo-amber-shape,
.logo-menu[open] .logo-amber-shape,
.home:hover .logo-amber-shape {
  opacity: 1;
}
.menu-panel {
  position: absolute;
  top: calc(100% + 14px);
  left: 0;
  width: min(330px, calc(100vw - 36px));
  border: 1px solid var(--line);
  border-top: 2px solid var(--red);
  background: #0a0a0af2;
  box-shadow: 12px 12px 0 #2a0c0f80;
  padding: 10px;
}
.menu-panel a {
  display: flex;
  align-items: baseline;
  gap: 15px;
  min-height: 36px;
  padding: 6px 10px;
  color: var(--amber-deep);
  border-bottom: 1px solid #2a1c1c;
}
.menu-panel a:last-child { border-bottom: 0; }
.menu-panel a span { color: var(--red); font-size: .75rem; min-width: 22px; }
.menu-panel a:hover { text-decoration: none; color: var(--red); background: #251010; }
.nav-status { display: inline-flex; align-items: center; gap: 10px; font-size: .875rem; }
.nav-download { min-height: 38px; padding: 6px 16px; font-size: .875rem; gap: 12px; }
/* Still in use by 404.html, which shares this stylesheet: the site header no
   longer carries these, but the not-found page's does. */
.nav-actions { display: flex; align-items: center; gap: 14px; margin-left: auto; }
.button-chip { padding: 7px 14px; border: 1px solid var(--line); background: var(--background); transition: border-color .15s, color .15s; }
.button-chip:hover { text-decoration: none; border-color: var(--red); color: var(--red); }
.status-dot { display: inline-block; width: 6px; height: 6px; flex-shrink: 0; background: var(--amber); box-shadow: 0 0 12px #ffb30033; }
.hero { min-height: 740px; position: relative; isolation: isolate; overflow: hidden; display: flex; flex-direction: column; justify-content: center; border-bottom: 1px solid var(--line); }
.pixel-field { position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%; pointer-events: none; }
.hero-content { text-align: center; padding-block: 54px 72px; }
.hero h1 { position: relative; margin: 35px auto 27px; max-width: 1000px; padding-block: 100px 50px; }
.wordmark { position: relative; width: 100%; height: auto; z-index: 1; }
/* Once the canvas has rasterised the mark, the SVG stands down but stays in
   the layout and the accessibility tree. Without JS — or if the raster fails
   — this class is never set and the real wordmark shows. */
.wordmark.drawn-by-field { opacity: 0; }
/* Overscan gives the gathering pixels room without changing document flow. */
.character { pointer-events: none; image-rendering: pixelated; }
.character > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.character > canvas { position: absolute; width: 166.6667%; height: 125%; left: -33.3333%; top: -12.5%; max-width: none; opacity: 0; }
.character.particle-ready > img { opacity: 0; }
.character.particle-ready > canvas { opacity: 1; }
.swordsman { position: absolute; height: 108%; aspect-ratio: 2 / 3; max-width: 72%; left: 50%; top: -4%; transform: translateX(-50%); opacity: .9; z-index: -2; }
.ingredients-intro { display: grid; grid-template-columns: 1.2fr 1fr; align-items: center; min-height: 360px; gap: 40px; }
.ingredients-intro h2 { max-width: 600px; }
.skater-scene { position: relative; height: 370px; isolation: isolate; }
.skater-scene::before { content: ""; position: absolute; inset: 8% -3% 0; z-index: -1; background-image: radial-gradient(#64101a 1px, transparent 1px); background-size: 8px 8px; mask-image: radial-gradient(ellipse, #0009, transparent 68%); }
.skater { position: absolute; height: 100%; aspect-ratio: 2 / 3; left: 50%; transform: translateX(-50%); }
.about, .ingredients, .redclaw-section { overflow: clip; }
.about-portrait { min-width: 0; }
.juanita-scene { position: relative; height: 330px; margin-top: 90px; }
.juanita { position: absolute; height: 100%; aspect-ratio: 2 / 3; left: 50%; transform: translateX(-50%); }
.redclaw-grid { display: grid; grid-template-columns: 1fr 1.2fr; align-items: center; gap: 72px; }
.redclaw-scene { position: relative; height: 350px; }
.redclaw { position: absolute; width: min(100%, 340px); aspect-ratio: 1; left: 50%; top: 50%; transform: translate(-50%, -50%); }
.redclaw-eyebrow { color: var(--red); font-size: .75rem; letter-spacing: .1em; margin-bottom: 18px; }
.redclaw-copy > p + p { margin-top: 16px; }
.redclaw-copy { max-width: 530px; }
.section-punch { color: var(--red); }

/* The Librarian mirrors the Redclaw band with the figure on the right, so two
   character sections in a row do not read as the same layout twice. The art is
   a 2:3 portrait rather than Redclaw's square, and the figure sits well inside
   its own frame -- its opaque pixels span only ~60% of the image width -- so
   the old fixed 420px scene with a 250px box spent most of that height on
   nothing and still drew the figure small. Sizing the scene by the portrait
   ratio instead gives the figure as much room as the column allows at every
   viewport, with no dead band above or below it. */
.librarian-section { overflow: clip; }
.librarian-grid { display: grid; grid-template-columns: 1.2fr 1fr; align-items: center; gap: 72px; }
.librarian-scene { position: relative; width: min(100%, 430px); aspect-ratio: 2 / 3; margin-inline: auto; }
.librarian { position: absolute; inset: 0; }
.librarian-copy { max-width: 530px; }
.librarian-copy > p + p { margin-top: 16px; }

/* Fido is the one landscape asset -- a low quadruped, 3:2 rather than the 2:3
   the standing figures use. He sits under the release panel spanning the
   section, which reads as a guard in front of the thing that is not out yet. */
.fido-scene { position: relative; height: 400px; margin-top: 40px; }
.fido { position: absolute; width: min(100%, 580px); aspect-ratio: 3 / 2; left: 50%; top: 50%; transform: translate(-50%, -50%); }
.fido-caption { margin-top: 4px; text-align: center; color: var(--text-dim, #8a7f7c); font-size: .8rem; line-height: 1.7; }

/* The Arrangement: two figures in opposition. Side by side rather than one
   hero, because the section's argument is the contrast -- the man who owns you
   against the man whose word holds. Neither is the subject on their own. */
.arrangement { overflow: clip; }
.arrangement-head { max-width: 640px; margin-bottom: 56px; }
.arrangement-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: start; }
.figure-card { margin: 0; }
.arrangement-scene { position: relative; height: 420px; }
.suit { position: absolute; height: 100%; aspect-ratio: 2 / 3; left: 50%; transform: translateX(-50%); }
.figure-card figcaption { margin-top: 20px; border-top: 1px solid var(--hairline, #2a1c1c); padding-top: 16px; }
.figure-name { color: var(--red); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 10px; }
.arrangement-close { max-width: 640px; margin-top: 64px; }
.arrangement-close > p + p { margin-top: 28px; }
.arrangement-claim { color: var(--text-primary, #e8ded9); font-size: 1.25rem; line-height: 1.5; }

/* Text-first sections for forthcoming character art. They use the same hairline
   and two-column rhythm as the rest of the page, but leave a clean insertion
   point for generated cutouts later. */
.manifesto-section { overflow: clip; }
.manifesto-grid { display: grid; grid-template-columns: .95fr 1.05fr; gap: 72px; align-items: start; }
.manifesto-grid.reverse { grid-template-columns: 1.05fr .95fr; }
.manifesto-copy { max-width: 620px; border-left: 1px solid var(--line); padding-left: 28px; }
.manifesto-grid.reverse .manifesto-copy { border-left: 0; border-right: 1px solid var(--line); padding-left: 0; padding-right: 28px; }
.manifesto-copy > p + p { margin-top: 16px; }

/* Sections 04-06 were text-first while their cutouts were still being
   generated. Each now carries its figure under the heading, the way Juanita
   sits beneath the h2 in section 01: the art states the claim, the copy beside
   it argues it. Raven is the page's second landscape asset (3:2, like Fido);
   Ng and the Nam-shub tablet are 2:3 portraits like the standing figures.
   Every `.character` keeps its source ratio so `object-fit: contain` fills the
   box exactly and the particle lattice samples the same shape the still does. */
.raven-scene, .ng-scene, .namshub-scene { position: relative; margin-top: 44px; }
.raven-scene { height: 380px; }
.ng-scene, .namshub-scene { height: 470px; }
.raven { position: absolute; width: min(100%, 520px); aspect-ratio: 3 / 2; left: 50%; top: 50%; transform: translate(-50%, -50%); }
.ng, .namshub { position: absolute; width: min(100%, 300px); aspect-ratio: 2 / 3; left: 50%; top: 50%; transform: translate(-50%, -50%); }

/* Character lead-in. Every borrowed figure gets one: who they are in the book,
   then the turn into what this section argues. The characters are the argument
   rather than decoration, so the note sits above the claim it sets up and is
   deliberately quieter than the body copy -- it is setup, not the point. */
.character-note {
  border-left: 2px solid var(--hairline, #2a1c1c);
  padding-left: 16px;
  margin-bottom: 24px;
  color: var(--text-dim, #8a7f7c);
  font-size: .8rem;
  line-height: 1.8;
  max-width: 46ch;
}
.character-who { color: var(--red); }

/* The note is a <p>, so parent rules like `.prose > p + p` and
   `.redclaw-copy > p + p` also match it and stack their gap on top of its own
   margin. That produced 45px under the note in the prose column and 20px
   elsewhere. Pin both sides per container, and zero the margin on whatever
   follows, so a note always sits the same distance from the copy it sets up. */
.prose > .character-note,
.redclaw-copy > .character-note,
.librarian-copy > .character-note {
  margin-top: 0;
  margin-bottom: 26px;
}
.redclaw-copy > .character-note,
.librarian-copy > .character-note { margin-top: 18px; }
.prose > .character-note + p,
.prose > .character-note + *,
.redclaw-copy > .character-note + *,
.librarian-copy > .character-note + * { margin-top: 0; }
/* Y.T.'s note follows .section-intro, which sets no bottom margin, so without
   this it sits flush against the copy above it. */
.ingredients-intro .character-note { margin-top: 18px; margin-bottom: 0; }
.fido-note { margin: 20px auto 0; max-width: 58ch; text-align: left; }

@media (max-width: 600px) {
  .ingredients-intro { grid-template-columns: 1fr; gap: 12px; }
  .skater-scene { height: 320px; }
  .juanita-scene { height: 310px; margin-top: 48px; }
  .redclaw-grid { grid-template-columns: 1fr; gap: 24px; }
  .redclaw-scene { height: 290px; }
  .redclaw { width: 280px; }
  .librarian-grid { grid-template-columns: 1fr; gap: 24px; }
  .librarian-scene { width: min(100%, 300px); }
  .fido-scene { height: 230px; margin-top: 28px; }
  .fido { width: 100%; }
  .arrangement-grid { grid-template-columns: 1fr 1fr; gap: 20px; }
  .arrangement-scene { height: 260px; }
  .arrangement-head { margin-bottom: 32px; }
  .arrangement-close { margin-top: 40px; }
  .arrangement-claim { font-size: 1.05rem; }
  .manifesto-grid,
  .manifesto-grid.reverse { grid-template-columns: 1fr; gap: 28px; }
  .manifesto-copy,
  .manifesto-grid.reverse .manifesto-copy { border-left: 1px solid var(--line); border-right: 0; padding-left: 18px; padding-right: 0; }
  /* The heading column is now full width here, so the art needs its own cap:
     a landscape scene at 100% would be taller than its box and overflow it. */
  .raven-scene { height: 300px; margin-top: 28px; }
  .raven { width: min(100%, 450px); }
  .ng-scene, .namshub-scene { height: 400px; margin-top: 28px; }
  .ng, .namshub { width: min(100%, 260px); }
}
.hero-heading { color: var(--red); font-size: clamp(1.45rem, 2.65vw, 2.125rem); line-height: 1.35; letter-spacing: -.045em; }
.hero-description { margin-top: 19px; font-size: 1rem; line-height: 1.85; }
.hero-actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 16px; margin-top: 31px; }
.button { display: inline-flex; align-items: center; justify-content: center; gap: 25px; min-height: 48px; padding: 9px 20px; border: 1px solid var(--red); color: var(--red); background: var(--background); transition: background .15s, box-shadow .15s; }
.button:hover { text-decoration: none; background: #251010; box-shadow: 3px 3px 0 var(--amber); }
.button.primary { background: var(--red); color: var(--background); }
.button.primary:hover { background: var(--amber-deep); }
.section { padding-block: 67px 75px; border-bottom: 1px solid var(--line); }
.section-label { display: flex; align-items: center; justify-content: space-between; gap: 20px; font-size: .75rem; letter-spacing: .08em; margin-bottom: 35px; }
.tiny-mark { color: var(--red); font-size: 1.5rem; line-height: 1; }
/* Every section is named for the character it is about. The older, generic name
   that used to be the whole label stays beside it as an alias, so the section is
   still findable under either. */
.section-alias { font-style: normal; color: #6f6660; }
/* A section answers to the name of the character it is about. The older, generic
   name survives as an empty marker inside it, so links written before the rename
   still arrive at the same slide. Out of flow and zero-sized: it is an address,
   not content. */
.anchor-alias { position: absolute; top: 0; left: 0; width: 0; height: 0; }
h2 { font-size: clamp(1.7rem, 3vw, 2.5rem); line-height: 1.25; letter-spacing: -.055em; }
.about-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 80px; }
.underlined { text-decoration: underline; text-decoration-color: var(--amber); text-decoration-thickness: 2px; text-underline-offset: 9px; }
.prose p + p { margin-top: 21px; }
.prose .note { border-left: 2px solid var(--amber); padding-left: 17px; color: var(--red); }
.section-intro { margin-top: 20px; }

/* Form carrying the argument. Each section states one thing, and a section is
   stronger when the shape of its text performs that thing rather than only
   saying it. Three primitives do that work; all of them reuse the page's own
   hairline, red and amber tokens so the sections still read as one document
   rather than three experiments. */
/* A forced sequence. Used where the copy is already a list in disguise --
   what has been designed, what the countermeasure is, what Raven does not
   have -- so the line stops pretending to be a sentence. */
.section-set { color: var(--red); letter-spacing: .06em; }

/* Two things held against each other, in text. .arrangement-grid already does
   this with two portraits; this does it where a second image would be noise.
   Mirrors .figure-name's label treatment so the page keeps one label voice. */
.micro-label { color: var(--red); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; }
.section-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  max-width: 620px;
  margin: 24px auto 0;
  text-align: left;
}
.section-pair > div { border-top: 1px solid var(--line); padding-top: 14px; }
.section-pair p + p { margin-top: 8px; }
/* .prose matches descendants, so its paragraph rhythm reaches inside the pair
   and would space the label off its own body. Pin it per container. */
.prose .section-pair p + p { margin-top: 8px; }
/* A pair or a receipt breaks paragraph adjacency, so the paragraph that
   follows one gets no margin from the usual `p + p` rules. */
.section-pair + *, .section-receipt + * { margin-top: 24px; }

/* A ledger. Used where the copy is literally about a record, so the section
   shows the record instead of describing it. */
.section-receipt {
  border: 1px solid var(--line);
  border-left: 2px solid var(--amber);
  padding: 18px 20px;
  margin-top: 24px;
  max-width: 520px;
}
.section-receipt dl { margin: 12px 0 0; }
.section-receipt dl > div {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--line);
}
.section-receipt dl > div:last-child { border-bottom: 0; }
.section-receipt dt { color: var(--amber-deep); }
.section-receipt dd { margin: 0; color: var(--red); text-align: right; }
.section-receipt .receipt-foot {
  margin-top: 14px;
  font-size: .8rem;
  color: var(--text-dim, #8a7f7c);
}
/* The sister band centres everything it holds, so a pair dropped into it has to
   be centred as a block and inside its columns or it reads as a mistake. */
.sister-pair { margin-inline: auto; }
.sister-pair > div { text-align: center; }

/* Every figure here is on loan from a novel, and the reason to borrow it is the
   software underneath. This panel is where the loan is repaid: the fiction on
   the left, the thing it actually maps to in Rednode on the right. Deliberately
   the same device in every character section -- the repetition is the point,
   because the mapping is the argument, not the decoration. The left column is
   quiet and the right column is red, so the eye lands on the software. */
.section-map {
  display: grid;
  grid-template-columns: 1fr 1fr;
  max-width: 700px;
  margin-top: 30px;
  border: 1px solid var(--line);
  border-top: 2px solid var(--red);
}
/* Each half gets a label band with its own rule under it, so the two sides read
   as the two columns of a table rather than one block of text that happens to
   change colour halfway. The band is a real row, not a margin, which is what
   keeps the label visually attached to the text it belongs to. */
.section-map > div { display: flex; flex-direction: column; }
.section-map > div + div { border-left: 1px solid var(--line); }
.section-map dt {
  color: var(--amber-deep);
  font-size: .68rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 7px 15px 8px;
  border-bottom: 1px solid var(--line);
  background: #ffffff09;
}
.section-map dd { margin: 0; padding: 12px 15px 14px; font-size: .875rem; line-height: 1.65; }
.section-map .map-book dd { color: var(--text-dim, #8a7f7c); }
.section-map .map-rednode dd { color: var(--red); }
/* Top-hairline columns with an inline mark, matching omarchy.org's card grids. */
.ingredient-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 44px; margin-top: 44px; }
.ingredient-grid article { display: flex; flex-direction: column; border-top: 1px solid var(--line); padding-top: 21px; }
.ingredient-symbol { color: var(--red); letter-spacing: -.1em; margin-right: 11px; }
h3 { display: flex; align-items: baseline; font-size: 1.25rem; margin-bottom: 12px; letter-spacing: -.035em; }
.ingredient-grid p { flex: 1; }
.text-link { display: inline-flex; align-items: center; gap: 14px; width: fit-content; margin-top: 24px; color: var(--red); border-bottom: 1px solid var(--red); padding-bottom: 2px; }
.text-link:hover { text-decoration: none; border-color: var(--amber); }
.status-label { display: inline-flex; align-items: center; gap: 10px; }
.release-panel { border: 1px solid var(--line); border-top: 2px solid var(--amber); background: var(--surface); padding: 40px; display: flex; align-items: flex-start; gap: 25px; }
.release-cursor { color: var(--red); font-size: 2.3rem; line-height: 1.2; }
.release-panel p { margin-top: 18px; }
.release-note { font-size: .875rem; max-width: 690px; }
.release-list { margin: 12px 0 0; padding: 0; list-style: none; max-width: 760px; }
.release-list li { position: relative; padding-left: 20px; margin-top: 10px; }
.release-list li::before { content: ""; position: absolute; left: 0; top: .82em; width: 7px; height: 7px; background: var(--red); box-shadow: 0 0 12px #ff2d2d33; }
.release-list-muted li::before { background: var(--amber-deep); box-shadow: 0 0 12px #d8890033; }
/* The sister band mirrors the hero's lattice, with the Redstar horizon carrying
   the family relationship instead of another character portrait. */
.sister { position: relative; isolation: isolate; overflow: hidden; min-height: 620px; display: flex; flex-direction: column; justify-content: center; }
.sister .pixel-field { position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%; pointer-events: none; }
.sister-content { text-align: center; padding-block: 64px 0; }
.sister-content .section-label { text-align: left; }
/* The star spans the whole band like the reference art, sitting under the
   pixel field so the lattice reads over the sky. Dim on purpose — it should
   back the wordmark, not compete with it. */
/* Anchored to the bottom, not centred. On a wide viewport cover scales to
   match width and crops the height from both edges, which lifts the star
   off the ridgeline and loses the sunset. Pinning the bottom keeps the
   horizon where the art puts it at every width. */
.star-backdrop { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: cover; object-position: center bottom; opacity: .42; z-index: -3; pointer-events: none; }
/* The backdrop is a full-bleed image whose top edge is a flat dark tone. Even
   at .42 opacity that edge composited to a faint band where it met the slide
   above, which was the last of the seam. Fading its opening slice into the
   page leaves the two running together. */
.star-backdrop {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 110px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 110px);
}
.sister-heading { color: var(--red); font-size: clamp(1.7rem, 3vw, 2.5rem); line-height: 1.25; letter-spacing: -.055em; margin-top: 40px; }
.sister-description { margin-top: 19px; font-size: 1rem; line-height: 1.85; }
.sister-by { margin-top: 27px; font-size: .875rem; }
.sister-by a { display: inline-flex; align-items: center; gap: 9px; color: var(--red); }
.sister-bottom { position: relative; display: flex; justify-content: space-between; padding-block: 76px 26px; font-size: .75rem; letter-spacing: .08em; }
/* One block, no internal rules: brand, what it is built on, who made it. */
.site-footer { display: flex; flex-direction: column; gap: 20px; padding-block: 40px 46px; font-size: .75rem; }
.footer-row { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.footer-brand { color: var(--red); font-size: 1.125rem; }
.footer-links { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 26px; }
.footer-label { color: var(--red); letter-spacing: .08em; }
.footer-links a:hover { color: var(--red); }
.footer-credit { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 24px; }
.redstar { display: inline-flex; align-items: center; gap: 9px; color: var(--red); }
.footer-type { opacity: .62; }

.notfound { min-height: 62vh; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; padding-block: 80px; }
.notfound-code { color: var(--red); font-size: 4rem; line-height: 1; letter-spacing: -.06em; }
.notfound h1 { font-size: clamp(1.7rem, 3vw, 2.5rem); letter-spacing: -.055em; margin-block: 22px 14px; }
.notfound .button { margin-top: 34px; }

@media (min-width: 1600px) { .hero { min-height: 800px; } }
/* The status controls make the bar too wide to hold logo + links + both
   controls; the chip goes first, since the release section repeats it. */
@media (max-width: 980px) {
  .nav-status { display: none; }
}
@media (max-width: 800px) {
  /* Two columns inside an already-narrow column collapse to ~140px each, which
     is under 20 monospace characters. Stack them before that happens. */
  .section-pair { grid-template-columns: 1fr; gap: 18px; }
  .navigation { gap: 25px; }
  .nav-status { font-size: .75rem; }
  .hero { min-height: 660px; }
  .about-grid { gap: 35px; }
  .ingredient-grid { gap: 26px; }
}
@media (max-width: 600px) {
  .wrap { width: calc(100% - 36px); }
  .navigation { flex-wrap: wrap; gap: 10px 20px; padding-block: 16px; }
  .home { font-size: 1rem; }
  .nav-actions { margin-left: auto; gap: 10px; }
  .nav-download { min-height: 34px; padding: 5px 12px; font-size: .8125rem; }
  .hero { min-height: 620px; }
  .hero-content { padding-block: 45px 48px; }
  .hero h1 { margin-block: 42px 32px; }
  .hero-heading { font-size: 1.65rem; }
  .hero-description { font-size: .875rem; }
  .hero-actions { gap: 12px; }
  .button { gap: 14px; padding-inline: 13px; font-size: .875rem; }
  /* Three items do not fit one 375px row; drop the toggle onto its own line. */
  .section { padding-block: 44px 50px; }
  .section-label { font-size: .75rem; gap: 16px; margin-bottom: 27px; }
  .section-label > span:last-child { text-align: right; }
  .about-grid, .ingredient-grid, .arrangement-grid { grid-template-columns: 1fr; }
  .about-grid { gap: 30px; }
  .arrangement-grid { gap: 36px; }
  .arrangement-scene { height: 300px; }
  .section-intro br, .sister-description br { display: none; }
  .section-pair { grid-template-columns: 1fr; gap: 18px; }
  .sister-pair > div { text-align: left; }
  .section-map { grid-template-columns: 1fr; }
  .section-map > div + div { border-left: 0; border-top: 1px solid var(--line); }
  .sister-content { padding-top: 44px; }
  .sister { min-height: 520px; }
  .sister-heading { margin-top: 24px; }
  .sister-heading { font-size: 1.65rem; }
  .sister-description { font-size: .875rem; }
  .sister-bottom { flex-wrap: wrap; gap: 12px 20px; padding-top: 30px; }
  .sister-bottom span:last-child { text-align: right; }
  .ingredient-grid { gap: 30px; margin-top: 30px; }
  .ingredient-grid article { padding-top: 18px; }
  .release-panel { padding: 25px 20px; gap: 13px; }
  .release-cursor { font-size: 1.6rem; }
  .release-panel h2 { font-size: 1.5rem; }
  .release-panel br { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .button { transition: none; }
  /* Sliding from section to section is exactly the travel this setting asks us
     not to animate. The snap still lands on a slide; only the easing goes. */
  .deck { scroll-behavior: auto; }
  .deck-rail-fill { transition: none; }
}

/* ==========================================================================
   Horizontal deck
   The page is a slide deck: every section is one viewport-sized slide and the
   document scrolls sideways, so the next slide enters from the right and
   travels left. Scoped under .deck-page / .deck because 404.html shares this
   stylesheet and still scrolls vertically.
   ========================================================================== */
/* The page itself still scrolls -- vertically, one screen, down to the closing
   band below the deck. Only the horizontal axis is owned by the deck. */
.deck-page { overflow-x: hidden; }
/* The header leaves the flow and floats over every slide, so it survives the
   horizontal scroll the way a sticky bar survives a vertical one. */
.deck-page .site-header { position: fixed; top: 0; left: 0; right: 0; z-index: 6; }
/* The header's edge lines up with the slides, not with the narrower .wrap the
   rest of the site uses. Slides centre at 1200px with a 40px floor; the header
   takes the same inset so the logo starts on the same line as the copy below. */
.deck-page .navigation {
  width: 100%;
  max-width: none;
  margin-inline: 0;
  padding-inline: max(40px, calc((100% - 1200px) / 2));
}

.deck {
  display: flex;
  height: 100vh;
  height: 100dvh;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.deck::-webkit-scrollbar { display: none; }

/* One slide per section. flex-basis overrides the .wrap width the sections
   already carry, so that gutter is re-expressed as padding below. overflow-y
   is a safety valve: the content is tuned to fit one viewport, but on a very
   short screen nothing becomes unreachable. */
.deck > * {
  position: relative;
  flex: 0 0 100%;
  min-width: 0;
  min-height: 0;
  height: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-width: none;
}
.deck > *::-webkit-scrollbar { display: none; }

/* When the deck jumps several slides at once, the slides it passes over dim so
   their copy reads as motion rather than noise. The slide you leave and the one
   you land on stay sharp; the particle figures and images are not text, so they
   are never dimmed. */
.deck :is(h1, h2, h3, h4, p, li, dt, dd, figcaption, .section-label, .micro-label, .text-link) { transition: opacity .24s ease; }
.deck > section.is-passing :is(h1, h2, h3, h4, p, li, dt, dd, figcaption, .section-label, .micro-label, .text-link) { opacity: .16; transition-duration: .07s; }

/* Slides centre their content vertically when it fits, and fall back to the
   top when it does not. The "safe" keyword is the whole trick: a slide taller
   than the screen keeps its top line at the padding edge and scrolls, instead
   of clipping both ends the way a plain "center" would. On a slide with room,
   the block -- label, copy, comparison -- sits mid-screen rather than hugging
   the header with dead air underneath. Browsers that predate "safe" keep the
   flex-start above it, which is the previous behaviour, not a breakage. */
.deck > section {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  justify-content: safe center;
}
.deck > section > * { flex: none; }

/* Slide composition: readable copy, a shared art scale and generous comparisons.
   Short viewports scroll within the slide instead of shrinking body text. */
html.deck-page { font-size: 15px; }
.deck-page .deck { line-height: 1.6; }
.deck-page {
  --fig-h: clamp(260px, 30dvh, 380px);
  --fig-h-wide: calc(var(--fig-h) * .72);
  --fig-h-square: calc(var(--fig-h) * .86);
  --art-gap: 28px;
}
.deck-page .deck > section.wrap {
  /* Slides sit 48px clear of the fixed header and hold everything else below
     them. The shift from the old 104/72 is taken out of the bottom, so the
     total is unchanged and no slide gained or lost room to fit. */
  --slide-pad-b: 44px;
  padding-inline: max(40px, calc((100% - 1200px) / 2));
  padding-block: 132px var(--slide-pad-b);
}
.deck-page .deck .section-label { margin-bottom: 26px; color: #a77b42; }
.deck-page h2 { font-size: clamp(1.7rem, 2.5vw, 2.5rem); text-wrap: balance; }
.deck-page .underlined { text-decoration: none; }
.deck-page .about-grid,
.deck-page .manifesto-grid,
.deck-page .manifesto-grid.reverse,
.deck-page .librarian-grid,
.deck-page .redclaw-grid { gap: clamp(36px, 5vw, 80px); align-items: center; }
.deck-page .manifesto-copy,
.deck-page .manifesto-grid.reverse .manifesto-copy { border: 0; padding: 0; }
.deck-page :is(.prose, .manifesto-copy, .librarian-copy, .redclaw-copy) { min-width: 0; max-width: 58ch; }
.deck-page :is(.prose, .manifesto-copy, .librarian-copy, .redclaw-copy) > p + p { margin-top: 14px; }
.deck-page .character-note { border: 0; padding: 0; color: #a3978b; line-height: 1.75; max-width: 55ch; }
.deck-page .character-who { display: block; margin-bottom: 8px; letter-spacing: .08em; text-transform: uppercase; font-size: .75rem; }
.deck-page :is(.prose, .redclaw-copy, .librarian-copy) > .character-note { margin: 0 0 26px; }
.deck-page .prose .note { border: 0; padding: 0; font-size: .85rem; }
.deck-page .redclaw-eyebrow { color: var(--amber-deep); margin-bottom: 16px; }
.deck-page .section-punch { font-size: 1.05rem; line-height: 1.6; margin-top: 28px; text-wrap: pretty; }
.deck-page .section-set { letter-spacing: 0; color: #b98e56; }
.deck-page .section-intro { margin-top: 18px; }
.deck-page .section-pair { gap: 24px; margin-block: 26px; }
.deck-page .section-pair > div { border: 0; padding: 0; }
.deck-page .section-pair { margin-top: 18px; }
.deck-page .section-pair .micro-label { color: var(--amber-deep); }
.deck-page .section-pair + * { margin-top: 14px; }

/* Labels and prose share one padded surface, without nested table rules. */
.deck-page .section-map {
  grid-template-columns: 1fr 1fr;
  border: 0;
  background: #141110;
  margin: 16px 0 0;
  padding: 20px;
  gap: 24px;
}
.deck-page .about > .section-map { grid-template-columns: 1fr 1fr; max-width: none; }
.deck-page .character-dossier { min-width: 0; }
.deck-page .character-dossier .character-note { margin: 22px auto 0; max-width: 38ch; }
.deck-page .redclaw-figure > .character-note { margin: 18px auto 0; }
.deck-page .section-map > div { border: 0; padding: 0; min-width: 0; }
.deck-page .section-map dt { border: 0; background: none; padding: 0; margin-bottom: 14px; color: var(--amber); font-size: .68rem; }
.deck-page .section-map dd { padding: 0; font-size: .875rem; line-height: 1.75; text-wrap: pretty; }
.deck-page .section-map .map-book dd { color: #a3978b; }
.deck-page .section-map .map-rednode dd { color: #daa15d; }
.deck-page .section-receipt { padding: 24px 28px; margin-top: 26px; border: 0; background: #141110; }
.deck-page .section-receipt dl > div { border: 0; padding-block: 5px; }
.deck-page .section-receipt + * { margin-top: 24px; }
.deck-page .ingredient-grid { gap: 36px; margin-top: 30px; }
.deck-page .ingredient-grid h3 { font-size: 1.1rem; }
.deck-page .ingredient-grid article { border: 0; padding-top: 0; }
.deck-page .text-link { margin-top: 20px; }

/* Explicit scene widths keep absolutely positioned art from collapsing a
   grid track. Source ratios are preserved for particle/still alignment. */
.deck-page :is(.juanita-scene, .skater-scene, .arrangement-scene, .ng-scene, .namshub-scene, .librarian-scene) {
  position: relative; width: 100%; aspect-ratio: auto;
  height: calc(var(--fig-h) * 1.25); margin: var(--art-gap) auto 0;
}
.deck-page :is(.raven-scene, .fido-scene) {
  position: relative; width: 100%; height: calc(var(--fig-h-wide) * 1.25); margin: var(--art-gap) auto 0;
}
.deck-page .redclaw-scene { position: relative; width: 100%; height: calc(var(--fig-h-square) * 1.25); margin: var(--art-gap) auto 0; }
/* Hiro's plate is an illustration rather than a silhouette, so the black it was
   authored against is keyed out of the asset itself -- that is why it needs no
   frame, card or panel behind it. It is otherwise an ordinary character: the
   same 2:3 ratio, the same scene-over-figure ratio of 1.25, assembled from
   particles like every other figure on the page. */
.deck-page .hiro-scene { position: relative; width: 100%; height: calc(var(--fig-h) * 1.7); margin: var(--art-gap) auto 0; }
/* THE IDEA carries the framing note as well as its own copy, so its art column
   now runs the full height of the slide. The two gaps inside it tighten to keep
   the slide on one screen without losing a line of anyone's copy. */
.deck-page .about-portrait :is(.juanita-scene, .section-map) { margin-top: 18px; }
.deck-page .about-portrait .section-map { padding: 18px; }
.deck-page :is(.juanita, .skater, .suit, .ng, .namshub, .librarian) { --fig: var(--fig-h); --figure-ratio: .6666667; aspect-ratio: 2 / 3; }
.deck-page :is(.raven, .fido) { --fig: var(--fig-h-wide); --figure-ratio: 1.5; aspect-ratio: 3 / 2; }
/* The Hiro slide is the one section that is a character study, so its figure
   runs a third larger than the rest. Scene over figure is still exactly 1.25 --
   the ratio every other scene uses -- so only the scale differs. */
.deck-page .hiro { --fig: calc(var(--fig-h) * 1.36); --figure-ratio: .6666667; aspect-ratio: 2 / 3; image-rendering: auto; }
.deck-page .redclaw { --fig: var(--fig-h-square); --figure-ratio: 1; aspect-ratio: 1; }
/* One stationary pixel bed for the whole deck, pinned to the foot of the
   viewport. Being fixed, it holds still while slides move sideways and while a
   slide's own copy scrolls, and it is identical on every slide -- a band per
   section meant a different height, a different amount of movement, and none at
   all on the slides that filled the screen. It sits behind everything, so copy
   reads over it. */
.pixel-stage {
  position: fixed;
  inset: 0;
  height: auto;
  /* The hero and the closing band are opaque, so they cover the bed rather than
     the bed hiding itself for them. Nothing toggles, so nothing fades: slide to
     a content slide and the bed is simply already there. */
  /* Stacked explicitly below the deck rather than with a negative index: a
     negative z-index paints behind any ancestor background, which is a quiet
     way to end up invisible. */
  z-index: 0;
  pointer-events: none;
}
.deck, .closing { position: relative; z-index: 1; }
/* The closing band is opaque, which is what takes the stage off screen for the
   last slide. The hero is deliberately transparent, so the stage is what shows
   behind it -- slide one is no longer where the field lives, it is simply the
   first slide standing on it. */
.closing { background: var(--background); }

.pixel-stage .pixel-field {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
}

/* The hero's Hiro is placed by the hero's own rules. Every other figure --
   including the Hiro slide's plate, which shares the character name so that it
   assembles the same way the hero's does -- is centred in its scene here. */
.deck-page .deck > section:not(.hero) [data-character] {
  position: absolute; inset: auto; top: 50%; left: 50%;
  height: auto; width: min(100%, calc(var(--fig) * var(--figure-ratio)));
  transform: translate(-50%, -50%);
}
.deck-page .redclaw-figure { min-width: 0; }
.deck-page .release-body { display: grid; grid-template-columns: 1.3fr 1fr; gap: 48px; align-items: center; }
.deck-page .release-panel { padding: 28px; gap: 18px; border: 0; background: #141110; }
.deck-page .fido-note { margin: 24px auto 0; max-width: 46ch; }

/* Two portraits on a shared baseline, the argument in its own column. */
.deck-page .arrangement-head { max-width: none; margin-bottom: 28px; }
.deck-page .arrangement-body { display: grid; grid-template-columns: 1.15fr 1fr; gap: 48px; align-items: center; }
.deck-page .arrangement-grid { grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
.deck-page .figure-card { display: block; min-width: 0; margin: 0; }
.deck-page .figure-card .arrangement-scene { margin-top: 0; }
.deck-page .figure-card figcaption { border: 0; padding: 0; margin-top: 20px; font-size: .85rem; line-height: 1.75; color: #a3978b; }
.deck-page .figure-name { color: var(--red); margin-bottom: 12px; }
.deck-page .arrangement-close { display: block; max-width: none; margin: 0; }
.deck-page .arrangement-close > p + p { margin-top: 26px; }
.deck-page .arrangement-claim { color: var(--red); font-size: 1.15rem; }
/* The hero was the one slide with its own centred composition. Its first line
   now starts on the same line as every section label, and the bottom strip
   falls to the foot of the slide instead of trailing the content. */
.deck-page .hero { min-height: 0; padding-block: 132px 16px; justify-content: flex-start; }
/* The announcements pill used to occupy 24px at the head of this column. That
   padding is what it left behind: without it the whole hero -- art included --
   creeps 24px back up towards the header, which is the direction it was moved
   away from. */
.deck-page .hero-content { flex: 1 1 auto; padding-block: 24px 0; }
.deck-page .hero h1 { max-width: min(1000px, 112vh); margin: 18px auto 16px; padding-block: 54px 26px; }
.deck-page .hero-actions { margin-top: 24px; }
.deck-page .sister { min-height: 0; }

/* --- Deck chrome --- */
.deck-hud {
  position: fixed;
  right: 24px;
  bottom: 26px;
  z-index: 7;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  background: #0a0a0ae6;
  backdrop-filter: blur(10px);
  font-size: .75rem;
  letter-spacing: .08em;
}
.deck-step {
  font: inherit;
  color: var(--red);
  background: none;
  border: 0;
  padding: 5px 9px;
  line-height: 1;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.deck-step:hover { background: #251010; }
.deck-step:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.deck-step[disabled] { color: var(--line); cursor: default; }
.deck-step[disabled]:hover { background: none; }
.deck-pos { display: flex; align-items: baseline; gap: 5px; color: var(--amber-deep); }
/* Space is a two-speed key, so the counter says so while it waits: a hold that
   gives no feedback for half a second reads as a dropped keypress. */
.deck-hud[data-holding] .deck-pos,
.deck-hud[data-holding] .deck-sep { color: var(--red); }
.deck-sep { opacity: .5; }
.deck-rail { position: fixed; left: 0; right: 0; bottom: 0; height: 2px; background: #2a1c1c; z-index: 7; }
.deck-rail-fill {
  display: block;
  height: 100%;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .4s ease;
}
/* A section rule along the bottom of a slide means nothing in a deck. The
   slides sit in a row, so every one of them shares the same bottom edge: the
   bottom edge of the screen. That edge is also the seam with the closing band
   below, which is why the rule read as a hard line between the two -- and why
   it has to go on every slide rather than just the last one, since whichever
   slide you happen to be on is the one drawing the line you see.
   .hero carries its own border-bottom, so this covers that too. */
.deck > section { border-bottom: 0; }

/* The closing band sits BELOW the deck rather than inside it. It is not a
   slide: it is one screen tall, so a single scroll down from any slide lands
   on it whole -- the star, the family statement and the colophon together. */
.closing {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
.closing .sister { flex: 1; min-height: 0; }
/* The single closing line belongs on the star, not in the vertical middle of the
   band. The backdrop is bottom-anchored and fills the band, so the star's centre
   sits ~68% down; the line is pushed to that height instead of being centred. */
.deck-page .sister { justify-content: flex-start; }
.deck-page .sister-content { padding-block: 0; margin-top: 68vh; }
.deck-page .sister-by { margin-top: 0; }

/* Preserve reading size on compact screens; the slide retains vertical scroll. */
@media (min-width: 701px) and (max-height: 820px) {
  .deck-page { --fig-h: 260px; }
.deck-page .deck > section.wrap { --slide-pad-b: 48px; padding-block: 128px var(--slide-pad-b); }
  /* The hero has to take the same top edge, or it sits 4px off every label. */
.deck-page .hero { padding-block: 128px 16px; }
  .deck-page .deck .section-label { margin-bottom: 24px; }
  .deck-page .hero h1 { padding-block: 28px 18px; }
}
@media (min-width: 701px) and (max-width: 1050px) {
  .deck-page .arrangement-body { grid-template-columns: 1fr 1fr; gap: 32px; }
  .deck-page .arrangement-grid { gap: 20px; }
  .deck-page .section-map { grid-template-columns: 1fr; gap: 24px; }
  .deck-page .arrangement-scene { height: calc(var(--fig-h) * .95); }
  .deck-page .suit { --fig: calc(var(--fig-h) * .85); }
}
@media (max-width: 700px) {
  html.deck-page { font-size: 16px; }
  .deck { display: block; height: auto; overflow: visible; scroll-snap-type: none; }
  .deck > * { height: auto; overflow-x: clip; overflow-y: visible; scroll-snap-align: none; }
  .deck-page { --fig-h: 330px; }
  .deck-page .deck > section.wrap { padding: 112px 24px 56px; }
  .deck-page .hero { min-height: 620px; }
  .deck-page :is(.about-grid, .manifesto-grid, .manifesto-grid.reverse, .librarian-grid, .redclaw-grid, .ingredients-intro, .release-body, .arrangement-body) { grid-template-columns: 1fr; gap: 32px; }
  .deck-page .arrangement-grid { grid-template-columns: 1fr 1fr; gap: 20px; }
  .deck-page .arrangement-scene { height: 280px; }
  .deck-page .suit { --fig: 250px; }
  .deck-page .figure-card figcaption { font-size: .8rem; }
  .deck-page .section-map, .deck-page .about > .section-map { grid-template-columns: 1fr; padding: 26px; gap: 26px; }
  .deck-page .manifesto-grid.reverse > :last-child { grid-row: 1; }
  .deck-page .release-panel { padding: 24px 20px; }
  .deck-page .release-cursor { display: none; }
  .deck-hud, .deck-rail { display: none; }
}

/* ==========================================================================
   Download window
   The page has one call to action and this is where it leads. It is a native
   <dialog> in the top layer, so the deck behind it is inert while it is open
   and Escape closes it without any script of ours.
   ========================================================================== */
.download-modal { padding: 0; border: 0; background: none; color: var(--text); }
.download-modal::backdrop { background: #050404d9; backdrop-filter: blur(4px); }
/* The panel carries the padding so that a click on the dialog element itself
   is always a click on the backdrop, and never on an invisible margin. */
.download-panel {
  position: relative;
  width: min(560px, calc(100vw - 32px));
  max-height: min(86vh, 760px);
  overflow-y: auto;
  padding: 38px 38px 32px;
  border: 1px solid var(--line);
  background: #141110;
}
.download-close {
  position: absolute; top: 10px; right: 10px;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; color: #6f6660;
  font: inherit; font-size: 1rem; line-height: 1; cursor: pointer;
  transition: color .15s;
}
.download-close:hover { color: var(--red); }
.download-modal h2 { margin: 12px 0 0; font-size: clamp(1.5rem, 3.4vw, 2rem); }
.download-lede { margin-top: 16px; color: #a3978b; line-height: 1.75; }
.download-list { list-style: none; margin: 28px 0 0; padding: 0; border-top: 1px solid var(--line); }
.download-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: 15px 0; border-bottom: 1px solid var(--line);
}
.download-what { display: flex; flex-direction: column; gap: 5px; }
.download-what em { font-style: normal; font-size: .8125rem; color: #6f6660; }
.download-state { flex-shrink: 0; color: #6f6660; font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; }
.download-foot { margin-top: 24px; font-size: .875rem; color: #a3978b; }
.download-foot a { color: var(--amber); }
@media (max-width: 520px) {
  .download-panel { padding: 30px 22px 26px; }
  .download-list li { flex-direction: column; gap: 8px; }
}

/* The hypercard occupies the spare space below the primary action. */
.hypercard-trigger {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  width: fit-content; margin: 22px auto 0; padding: 10px 22px;
  color: var(--amber-deep); background: none; border: 0;
  font: inherit; cursor: pointer; perspective: 700px;
}
.hypercard-trigger img {
  width: auto; height: clamp(88px, 15dvh, 170px); max-height: 170px;
  object-fit: contain; transform-origin: center;
  animation: hypercard-hover 7s ease-in-out infinite;
  filter: drop-shadow(0 6px 12px #7d0d1933);
}
.hypercard-trigger:hover img { filter: drop-shadow(0 0 14px #a5162955); }
.hypercard-trigger:focus-visible { outline: 1px solid var(--amber); outline-offset: 5px; }
.hypercard-label { font-size: .6rem; letter-spacing: .16em; }
.hypercard-trigger[aria-disabled="true"] { cursor: progress; }
.hypercard-announcement { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@keyframes hypercard-hover {
  0%, 100% { transform: translateY(3px) rotate(-3deg) rotateY(-8deg); }
  50% { transform: translateY(-7px) rotate(2deg) rotateY(7deg); }
}
/* Filter the rendered page so sampled canvas inks and embedded SVG colors
   change together. The original pixels and all controls keep running. */
html[data-snow-crash] { filter: grayscale(1) brightness(1.8); transition: filter 500ms ease; }
html[data-snow-crash="recover"] { filter: grayscale(0) brightness(1); transition: filter 1400ms ease; }
html[data-snow-crash] body::after {
  content: ''; position: fixed; inset: 0; z-index: 20; pointer-events: none;
  background: repeating-linear-gradient(0deg, transparent 0 5px, #ffffff09 5px 6px);
  opacity: .65;
}
html[data-snow-crash="breach"] main { animation: snow-tear 700ms steps(1, end); }
html[data-snow-crash="hold"] main { animation: snow-echo 2400ms steps(1, end) infinite; }
html[data-snow-crash="recover"] body::after { opacity: 0; transition: opacity 1s ease; }
@keyframes snow-tear {
  0%, 40%, 100% { transform: none; }
  15% { transform: translateX(5px) skewX(.2deg); }
  28% { transform: translateX(-3px); }
  62% { transform: translateX(2px); }
  75% { transform: none; }
}
@keyframes snow-echo {
  0%, 8%, 100% { transform: none; }
  3% { transform: translateX(-2px); }
  5% { transform: translateX(3px); }
}
@media (max-height: 820px) and (min-width: 701px) {
  .deck-page .hero h1 { max-width: min(760px, 82dvh); padding-block: 12px 8px; }
  .hypercard-trigger { margin-top: 12px; gap: 8px; }
  .hypercard-trigger img { height: 88px; }
}
@media (max-width: 700px) {
  .hypercard-trigger img { height: 120px; }
  .deck-page .hero { min-height: 780px; }
}
@media (prefers-reduced-motion: reduce) {
  .hypercard-trigger img { animation: none; }
  html[data-snow-crash] main { animation: none; }
  html[data-snow-crash] body::after { display: none; }
}

/* ==========================================================================
   Hidden object pages (#pizza, #hypercard)
   Fixed, full-screen, reachable only by hash. They overlay the deck entirely.
   ========================================================================== */
.object-page {
  position: fixed; inset: 0; z-index: 40;
  display: grid; grid-template-rows: auto 1fr;
  background: var(--background);
  overflow-y: auto;
}
.object-page[hidden] { display: none; }
html.object-open .deck-hud, html.object-open .deck-rail { display: none; }
.object-header {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 20px max(40px, calc((100% - 1200px) / 2));
  border-bottom: 1px solid var(--line);
}
.object-back { font: inherit; cursor: pointer; color: var(--amber-deep); background: none; border: 0; padding: 0; font-size: .875rem; }
.object-back:hover { color: var(--amber); }
.object-eyebrow { color: var(--amber-deep); font-size: .7rem; letter-spacing: .16em; }
.object-body {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px;
  gap: 40px; align-items: center;
  width: 100%; max-width: 1400px; margin-inline: auto;
  padding: 24px max(40px, calc((100% - 1200px) / 2)) 48px;
}
.object-stage { height: 68vh; min-height: 380px; position: relative; background: radial-gradient(ellipse at center, #210c1044, transparent 65%); }
/* Pages where the object is the whole point. The shared layout caps the stage
   at 68vh and reserves a 340px column for the controls, which leaves the
   two-card chain in a short narrow box with half the width empty. Here the
   stage takes the page and the controls float over it. */
.object-page.full-stage .object-body { display: block; position: relative; max-width: none; padding: 0; gap: 0; }
.object-page.full-stage .object-stage { height: 100%; min-height: 0; }
.object-page.full-stage .object-controls { position: absolute; top: 22px; right: max(22px, calc((100% - 1200px) / 2)); width: min(320px, 32vw); padding: 16px 18px 18px; background: rgba(10, 10, 10, .84); border: 1px solid var(--line); backdrop-filter: blur(7px); }
.object-page.full-stage .object-controls h2 { font-size: clamp(1.2rem, 1.9vw, 1.6rem); margin-bottom: 10px; }
.object-page.full-stage .object-hint { margin-bottom: 12px; }
.object-fine { margin-top: 16px; font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; color: #7c5730; }
@media (max-width: 820px) {
  .object-page.full-stage .object-body { display: grid; grid-template-rows: 1fr auto; }
  .object-page.full-stage .object-stage { height: 56vh; }
  .object-page.full-stage .object-controls { position: static; width: auto; background: transparent; border: 0; backdrop-filter: none; padding: 14px 18px 24px; }
}
.object-stage canvas { display: block; width: 100%; height: 100%; touch-action: none; }
.object-controls h2 { color: var(--red); font-size: clamp(1.5rem, 3vw, 2.25rem); line-height: 1.2; letter-spacing: -.05em; margin-bottom: 18px; }
.object-hint { color: #a3978b; line-height: 1.7; margin-bottom: 24px; }
.object-controls button { font: inherit; cursor: pointer; color: #c28d5b; background: transparent; border: 1px solid var(--line); padding: 10px 16px; }
.object-controls button:hover { border-color: var(--amber-deep); color: var(--amber); }
.object-controls button:disabled { opacity: .35; cursor: default; }
#pizza-lid { background: #b82029; color: #fff0de; border-color: #b82029; margin: 0 0 20px; }
.object-micro { font-size: .7rem; letter-spacing: .12em; color: var(--amber-deep); margin: 0 0 10px; }
.slice-buttons { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 20px; }
.slice-buttons button { padding: 9px; }
.slice-buttons button[aria-pressed="true"] { background: #64101a; color: var(--amber); }
#pizza-reset, #hypercard-reset { width: 100%; margin-bottom: 16px; }
.object-status { min-height: 20px; font-size: .8rem; color: var(--amber-deep); margin-bottom: 20px; }
.early-access { border-top: 1px solid var(--line); padding-top: 20px; }
.early-access label { display: block; font-size: .7rem; letter-spacing: .12em; color: var(--amber-deep); margin-bottom: 8px; }
.early-row { display: flex; gap: 8px; }
.early-row input { flex: 1; min-width: 0; font: inherit; background: #141110; color: var(--text); border: 1px solid var(--line); padding: 9px 12px; }
.early-row button { background: var(--red); color: var(--background); border-color: var(--red); white-space: nowrap; }
.early-note { font-size: .8rem; color: #a3978b; margin-top: 10px; }
@media (max-width: 760px) {
  .object-body { grid-template-columns: 1fr; gap: 20px; }
  .object-stage { height: 52vh; min-height: 320px; }
}
.object-char { font-size: .7rem; letter-spacing: .14em; color: var(--amber-deep); text-transform: uppercase; margin-bottom: 8px; }
.char-picker { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; }
.char-picker button { padding: 7px 10px; font-size: .72rem; letter-spacing: .04em; }
.char-picker button[aria-pressed="true"] { background: #64101a; color: var(--amber); border-color: var(--amber); }

/* The SlimJacks panel: a plain vertical list and a readout, nothing more.
   The status is set in the same amber-on-smoked-glass language as the card
   displays, so the page reads as one instrument rather than a web form. */
.slimjack-panel { display: flex; flex-direction: column; gap: 14px; align-items: stretch; }
.slimjack-panel .object-char { margin: 0; color: var(--amber); letter-spacing: .16em; font-size: .74rem; }
.slimjack-list { flex-direction: column; flex-wrap: nowrap; gap: 3px; margin-bottom: 0; max-height: 40vh; overflow-y: auto; scrollbar-width: thin; }
.slimjack-list button { width: 100%; text-align: left; padding: 7px 12px; font-size: .76rem; letter-spacing: .07em; }
.slimjack-list button[aria-pressed="true"] { background: #64101a; color: var(--amber); border-color: var(--amber); }
.slimjack-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.slimjack-readout {
  margin: 0; padding: 11px 13px; min-height: 2.9em;
  background: #070707; border: 1px solid var(--line);
  color: var(--amber); font-size: .68rem; line-height: 1.5;
  letter-spacing: .14em; text-transform: uppercase;
  box-shadow: inset 0 0 22px rgba(255, 179, 0, .1);
}
.slimjack-readout::before { content: '\25B8 '; color: var(--amber-deep); }

/* The SlimJacks slide, in the deck like every other slide. The object takes the
   whole slide and the panel floats over it, so the two cards get every pixel -
   the shared object-page layout (68vh stage, 340px column) would crush them.
   touch-action none is what lets a drag rotate the cards instead of scrolling
   the deck sideways. */
.slimjack-slide { position: relative; display: grid; grid-template-rows: auto 1fr; }
.slimjack-slide .object-header { padding: 18px 24px 0; }
.slimjack-slide .object-body { display: block; position: relative; max-width: none; padding: 0; gap: 0; }
.slimjack-slide .object-stage { height: 100%; min-height: 0; touch-action: none; }
.slimjack-slide .object-stage canvas { touch-action: none; }
.slimjack-slide .object-controls { position: absolute; top: 22px; max-height: calc(100% - 44px); overflow-y: auto;  right: max(22px, calc((100% - 1200px) / 2)); width: min(320px, 32vw); padding: 16px 18px 18px; background: rgba(10, 10, 10, .84); border: 1px solid var(--line); backdrop-filter: blur(7px); }
@media (max-width: 820px) {
  .slimjack-slide .object-body { display: grid; grid-template-rows: 1fr auto; }
  .slimjack-slide .object-stage { height: 54vh; }
  .slimjack-slide .object-controls { position: static; width: auto; background: transparent; border: 0; backdrop-filter: none; padding: 14px 18px 24px; }
}

/* Snow Crash only exists once a link is made: it infects the receiving card. */
#download-crash { border-color: var(--amber-deep); color: var(--amber); }
#download-crash[hidden] { display: none; }
#download-crash:hover { border-color: var(--amber); background: rgba(255, 179, 0, .08); }

/* SlimJacks slide: fill the slide. The object gets everything between the fixed
   site header and the bottom edge - the header is the only thing that may clip
   it. The earlier rules left an empty grid row (the object-page header is gone)
   and the deck's own slide padding, which together cost ~180px of height. */
.slimjack-slide { position: relative; display: block; padding: 0; }
.slimjack-slide .object-body {
  display: block; position: relative; max-width: none; gap: 0;
  height: 100%; padding: 88px 0 0; box-sizing: border-box;
}
.slimjack-slide .object-stage { height: 100%; min-height: 0; }
.slimjack-slide .object-controls {
  top: 100px; max-height: calc(100% - 124px);
}
@media (max-width: 820px) {
  .slimjack-slide .object-body { display: grid; grid-template-rows: 1fr auto; height: auto; padding: 76px 0 0; }
  .slimjack-slide .object-stage { height: 54vh; }
  .slimjack-slide .object-controls { position: static; max-height: none; }
}

/* Da5id's slide: the last thing before the closing band. Centred, silent, and
   deliberately short - it is the warning, not a section. */
.warning { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: clamp(18px, 3vh, 34px); }
.warning-figure { display: flex; justify-content: center; }
.warning-figure img { max-height: 48vh; width: auto; max-width: 88vw; opacity: .92; }
.warning-slogan { color: var(--red); font-size: clamp(1.9rem, 4.4vw, 3.2rem); line-height: 1.05; letter-spacing: -.045em; margin: calc(10px - 6vh) 0 0; }
.warning-sub { color: #c28d5b; max-width: 44ch; margin: 0; }

/* The figure is a live character like the others: silhouette plus the particle
   canvas that assembles and glitches it. */
.warning-figure .character { position: relative; display: inline-block; }
.warning-figure .character img { display: block; }
.warning-figure .character canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* The particle system hides the <img> and paints the character onto the canvas,
   so the wrapper has to be a real box or the canvas is 0x0 and nothing shows.
   Da5id's art is portrait (1024x1536, 2:3) where fido's is landscape, so the
   size comes from an explicit aspect ratio rather than copying fido's rules. */
.warning-figure .character { position: relative; height: min(48vh, 420px); aspect-ratio: 2 / 3; margin-inline: auto; }
.warning-figure .character img { display: block; height: 100%; width: 100%; object-fit: contain; }
.warning-figure .character canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Da5id is a character like the others. These are fido's sizing rules
   cloned, rather than a new layout invented for this slide - which is what
   left the particle canvas at 0x0. Aspect differs (portrait, not landscape),
   so the ratio is overridden at the end. */


/* Fido is the one landscape asset -- a low quadruped, 3:2 rather than the 2:3
   the standing figures use. He sits under the release panel spanning the
   section, which reads as a guard in front of the thing that is not out yet. */
.da5id-scene { position: relative; height: 66vh; width: auto; aspect-ratio: 2 / 3; flex: none; margin: 0 auto; }

.da5id { position: absolute; width: min(100%, 580px); aspect-ratio: 3 / 2; left: 50%; top: 50%; transform: translate(-50%, -50%); }

.da5id-caption { margin-top: 4px; text-align: center; color: var(--text-dim, #8a7f7c); font-size: .8rem; line-height: 1.7; }

.da5id-note { margin: 20px auto 0; max-width: 58ch; text-align: left; }

  .da5id-scene { height: 230px; margin-top: 28px; }

  .da5id { width: 100%; }

.deck-page :is(.raven-scene, .da5id-scene) {
  position: relative; width: 100%; height: calc(var(--fig-h-wide) * 1.25); margin: var(--art-gap) auto 0;
}

.deck-page :is(.raven, .da5id) { --fig: var(--fig-h-wide); --figure-ratio: 1.5; aspect-ratio: 3 / 2; }

.deck-page .da5id-note { margin: 24px auto 0; max-width: 46ch; }
.da5id-scene .da5id { aspect-ratio: 2 / 3; }

/* Da5id's art is portrait (1024x1536), so the ratio is the inverse of fido's.
   The -scene wrapper supplies the height; without it the character box has no
   dimensions and the particle canvas stays 0x0. */
.da5id-scene { position: relative; height: min(58vh, 460px); aspect-ratio: 2 / 3; margin: 0;
  flex: 0 0 auto; }   /* flex items shrink by default; the column was crushing it */
/* The scene needs its own width: it is a flex item whose only child is
   absolutely positioned, so shrink-to-fit resolves to zero and the whole
   figure collapses. The aspect ratio gives it a definite width. */
.da5id { position: absolute; height: 100%; width: auto; aspect-ratio: 2 / 3; left: 50%; top: 50%; transform: translate(-50%, -50%); }

/* Da5id fills the slide: there is nothing else on it, so the figure is sized
   from a width with the height derived from the 2:3 portrait ratio. Sizing it
   from a height let the flex column crush it - the same crush that leaves fido
   at 234px instead of his intended 400. */
.warning .da5id-scene { position: relative; width: clamp(300px, 40vw, 560px); height: auto; aspect-ratio: 2 / 3; flex: none; margin: 0 auto; }
.warning .da5id-scene .da5id { position: absolute; inset: 0; width: 100%; height: 100%; transform: none; }

/* Da5id's slide, sized to fit the viewport. The section carries the deck's
   .section padding, which pushed the pair past the fold; this slide is just a
   figure and a line, so the padding comes off and the scene is capped so both
   always fit. The figure fills the scene exactly (the canvas overscan is
   matched in CSS and JS), and the slogan is pulled up over that overscan. */
.warning { padding: 0; gap: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.warning .da5id-scene { position: relative; height: min(52vh, 400px); width: auto; aspect-ratio: 2 / 3; flex: none; margin: 0 auto; }
.warning .da5id-scene .da5id { position: absolute; inset: 0; width: 100%; height: 100%; transform: none; }
.warning-slogan { color: var(--red); font-size: clamp(1.6rem, 3.4vw, 2.6rem); line-height: 1.05; letter-spacing: -.045em; margin: -6.5vh 0 0; }

/* The logo menu has thirteen entries and opens downward with no cap, so on a
   short window the last entries fell off the bottom with no way to reach them.
   This rule was lost once when this file was restored from a backup during the
   Da5id slide work. */
.menu-panel { max-height: calc(100vh - 110px); overflow-y: auto; scrollbar-width: thin; }

/* The katana slide follows the SlimJacks one: the object takes the whole slide
   and the controls float over it, below the fixed site header. */
.katana-slide { position: relative; display: block; padding: 0; }
.katana-slide .object-body { display: block; position: relative; max-width: none; gap: 0; height: 100%; padding: 88px 0 0; box-sizing: border-box; }
.katana-slide .object-stage { height: 100%; min-height: 0; touch-action: none; }
.katana-slide .object-controls { position: absolute; top: 100px; right: max(22px, calc((100% - 1200px) / 2)); width: min(320px, 32vw); padding: 16px 18px 18px; background: rgba(10, 10, 10, .84); border: 1px solid var(--line); backdrop-filter: blur(7px); max-height: calc(100% - 124px); overflow-y: auto; }
.katana-parts { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 10px; }
.katana-parts button[aria-pressed="true"] { background: #64101a; color: var(--amber); border-color: var(--amber); }
.katana-slide .object-controls button { margin-right: 6px; }
/* Transparency shows the object has no background of its own: a checker, as on
   the standalone katana page. */
.katana-slide .object-stage.transparent { background: conic-gradient(#23262a 25%, #15181b 0 50%, #23262a 0 75%, #15181b 0) 0 0/28px 28px; }
@media (max-width: 820px) {
  .katana-slide .object-body { display: grid; grid-template-rows: 1fr auto; height: auto; padding: 76px 0 0; }
  .katana-slide .object-stage { height: 54vh; }
  .katana-slide .object-controls { position: static; width: auto; max-height: none; background: transparent; border: 0; backdrop-filter: none; padding: 14px 18px 24px; }
}

/* Artifact panels, as an overlay rather than a column: wider and quieter, with
   a smaller face and a denser ground so the art behind reads as the subject.
   The heading is a bar that folds the rest away.

   Scoped through :is() with the slide classes so these outrank the per-slide
   rules above - a bare .object-controls loses to .katana-slide .object-controls. */
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .object-controls { width: min(430px, 34vw); padding: 0; background: rgba(7, 7, 8, .93); font-size: .8rem; line-height: 1.55; border: 1px solid var(--line); }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-panel > summary { display: flex; align-items: center; gap: 10px; padding: 13px 18px; cursor: pointer; list-style: none; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-panel > summary::-webkit-details-marker { display: none; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-panel > summary h2 { font-size: .95rem; letter-spacing: -.01em; margin: 0; flex: 1; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-caret { width: 8px; height: 8px; border-right: 1px solid var(--amber-deep); border-bottom: 1px solid var(--amber-deep); transform: rotate(45deg) translateY(-2px); transition: transform .18s ease; flex: none; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-panel:not([open]) > summary .ui-caret { transform: rotate(-45deg) translateX(-2px); }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-panel-body { padding: 0 18px 16px; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .ui-panel-body > :first-child { margin-top: 0; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .object-hint { font-size: .76rem; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) button { font-size: .72rem; padding: 7px 12px; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .object-status { font-size: .72rem; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .char-picker button { font-size: .68rem; padding: 5px 9px; }
@media (max-width: 820px) { :is(.katana-slide, .slimjack-slide, .object-page.full-stage) .object-controls { width: auto; font-size: .78rem; } }

/* The legal notice. Centred, alone, and the last thing on the page. */
.legal { min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: 12vh 6vw; border-top: 1px solid var(--line); }
.legal p { max-width: 70ch; margin: 0; text-align: center; color: #8b807b; font-size: .82rem; line-height: 1.95; letter-spacing: .015em; }

/* Screencast mode: the katana panel can be hidden outright, and every control on
   it stays reachable from the keyboard (see bootKatana in objects.js). Scoped to
   the wrapper so nothing is left on screen but the object itself. */
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .object-controls.is-hidden { display: none; }
:is(.katana-slide, .slimjack-slide, .object-page.full-stage) .object-keys { font-size: .66rem; letter-spacing: .05em; color: #6d635c; margin: 14px 0 0; }
