/* Papamon landing page.
 *
 * One theme, light. The page is paper and the product is the only dark object
 * on it: every clip sits inside a black enclosure, which is where the footage
 * was composed to live, so the paper never touches a game frame.
 *
 * One accent: the enclosure inlay orange. It fills the CTA and marks a label.
 * A darker cut of the same orange (--ember-ink) is used wherever the orange has
 * to be read as text, because the fill orange is only 3:1 on paper.
 *
 * One radius system: 6px on everything, 12px on flat media only. The enclosure
 * uses percentage radii instead, because it is drawn to millimetres.
 *
 * Video is the default state, not an option. Every clip has a real <img> under
 * it that is shown until that clip is actually playing, so the page is never
 * blank and never waiting. There is no motion switch on the page: the OS
 * setting is the only thing that turns the footage off.
 */

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
img, svg, video { display: block; max-width: 100%; }
img, video { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { list-style: none; padding: 0; }
:where(h1,h2,h3,p,dd) { overflow-wrap: break-word; }

/* ---------- tokens ---------- */
:root {
  /* paper */
  --paper:   #F4F5F8;
  --paper-2: #EAECF1;   /* alternating band */
  --paper-3: #E0E3EA;   /* the deepest tint */
  --surface: #FFFFFF;   /* cards and wells */
  --line:    #DCDFE7;
  --line-2:  #C3C8D4;

  /* the boot splash, rgba4(14,16,28) to rgba4(38,28,56). It survives in two
     places: the footer band and the CTA's text colour. */
  --ink:  #0E101C;
  --deep: #1B1630;

  /* text */
  --fg:      #131623;   /* 16.5:1 on --paper */
  --fg-dim:  #474D60;   /*  7.7:1 */
  --fg-mute: #5F6679;   /*  5.3:1, the floor. Captions and legal only. */

  /* the single accent: the enclosure inlay orange */
  --ember:     #E9620A;   /* fills only. --ink on it is 5.3:1 */
  --ember-2:   #A8460A;   /* pressed state and the solid offset shadow */
  --ember-ink: #9A4306;   /* the same orange as text, 6.1:1 on --paper */

  /* space: a restricted scale, no raw pixel values below this line */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  --r:    6px;
  --r-lg: 12px;

  --w:       1160px;
  --w-wide:  1360px;
  --w-prose: 64ch;
  --gut:     clamp(20px, 5vw, 64px);
  --nav-h:   66px;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", "DejaVu Sans Mono", monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;

  --ease:    cubic-bezier(.2,.9,.25,1);
  --ease-io: cubic-bezier(.45,0,.55,1);
}

/* ---------- base ---------- */
html {
  scrollbar-gutter: stable;
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
body {
  background: var(--paper);
  color: var(--fg);
  font-family: var(--sans);
  font-size: clamp(16px, .95rem + .25vw, 18px);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
[id] { scroll-margin-top: calc(var(--nav-h) + var(--s6)); }

/* h2 and h3 are mono uppercase: short, hard labels that read as parts stamped
   on a case. h1 is not, and deliberately. The headline is a nineteen word
   sentence, and uppercase monospace destroys word shape at that length - it
   would be the least readable text on a page whose whole job is to be
   understood in one pass. */
h2, h3 {
  font-family: var(--mono);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--fg);
}
h1 {
  font-family: var(--sans);
  font-weight: 800;
  font-size: clamp(25px, .85rem + 2.05vw, 41px);
  line-height: 1.14;
  letter-spacing: -.025em;
  color: var(--fg);
  text-wrap: balance;
}
h2 {
  font-size: clamp(26px, 1.2rem + 2.1vw, 46px);
  line-height: 1.06;
  letter-spacing: -.035em;
}
h3 {
  font-size: clamp(17px, 1rem + .5vw, 21px);
  line-height: 1.25;
  letter-spacing: -.01em;
}
p { color: var(--fg-dim); max-width: var(--w-prose); }
strong { color: var(--fg); font-weight: 600; }

a { color: var(--ember-ink); text-underline-offset: .18em; }
a:hover { color: var(--ember-2); }

:where(a, button, summary, [tabindex]):focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--paper), 0 0 0 6px var(--fg);
  border-radius: var(--r);
}
::selection { background: var(--ember); color: var(--ink); }

.skip {
  position: absolute; left: var(--s4); top: -100px; z-index: 90;
  background: var(--ember); color: var(--ink); font-family: var(--mono);
  font-size: 13px; padding: var(--s3) var(--s4); border-radius: var(--r);
  text-decoration: none; font-weight: 700;
}
.skip:focus { top: var(--s4); color: var(--ink); }

/* shared text bits ------------------------------------------------------- */
.lead {
  font-size: clamp(18px, 1rem + .55vw, 22px);
  line-height: 1.5;
  color: var(--fg-dim);
}
.note {
  font-size: 15px;
  line-height: 1.6;
  color: var(--fg-mute);
  max-width: var(--w-prose);
}
/* Section eyebrow. Rationed: three on the whole page. */
.brow {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ember-ink);
  margin-bottom: var(--s4);
}

/* ---------- nav ---------- */
.nav {
  position: sticky; top: 0; z-index: 40;
  height: var(--nav-h);
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
html[data-scrolled="1"] .nav {
  border-bottom-color: var(--line);
  background: color-mix(in srgb, var(--paper) 95%, transparent);
}
.nav__in {
  max-width: var(--w-wide); margin-inline: auto; height: 100%;
  padding-inline: var(--gut);
  display: flex; align-items: center; gap: var(--s7);
}
.mark { display: flex; align-items: center; gap: var(--s2); text-decoration: none; color: var(--fg); }
.mark__egg { width: 22px; height: 22px; }
.mark__word {
  font-family: var(--mono); font-weight: 700; font-size: 15px;
  letter-spacing: .1em;
}
/* The boot splash egg is a pale shell on a near black screen. On paper it needs
   the outline to exist at all. */
.mark__egg ellipse, .lockup__egg ellipse { stroke: var(--fg); stroke-width: 3.5; }
.nav__links { margin-right: auto; display: flex; gap: var(--s5); }
.nav__links a {
  font-family: var(--mono); font-size: 13px; letter-spacing: .04em;
  color: var(--fg-dim); text-decoration: none;
}
.nav__links a:hover { color: var(--fg); }
.nav__act { display: flex; align-items: center; gap: var(--s4); }

/* ---------- CTA: a physical button, because the device has physical buttons ---------- */
.cta {
  display: inline-block;
  font-family: var(--mono); font-weight: 700; font-size: 15px;
  letter-spacing: .02em; white-space: nowrap;
  background: var(--ember); color: var(--ink);
  padding: var(--s3) var(--s5);
  border-radius: var(--r); text-decoration: none;
  box-shadow: 0 4px 0 var(--ember-2);
  transition: transform .09s linear, box-shadow .09s linear;
}
.cta:hover { color: var(--ink); transform: translateY(-1px); box-shadow: 0 5px 0 var(--ember-2); }
.cta:active { transform: translateY(4px); box-shadow: 0 0 0 var(--ember-2); }
.cta--sm { font-size: 13px; padding: var(--s2) var(--s4); box-shadow: 0 3px 0 var(--ember-2); }
.cta--sm:active { transform: translateY(3px); }
.cta--lg { font-size: clamp(16px, 1rem + .35vw, 20px); padding: var(--s4) var(--s7); }
.cta[data-cta-unset] { outline: 2px dashed var(--fg); outline-offset: 3px; }
/* the second button in a pair. Same shape, no fill, so the pair reads as one
   choice with a default rather than as two competing calls to action. */
.cta--ghost {
  background: transparent; color: var(--ember-ink);
  box-shadow: inset 0 0 0 1.5px var(--line-2), 0 4px 0 var(--paper-3);
}
.cta--ghost:hover { color: var(--ember-2); box-shadow: inset 0 0 0 1.5px var(--fg-mute), 0 5px 0 var(--paper-3); }
.cta--ghost:active { box-shadow: inset 0 0 0 1.5px var(--fg-mute), 0 0 0 var(--paper-3); }

/* ---------- the device ---------- */
/* Every clip on this page is framed in the real enclosure rather than a plain
   crop, because the thing being sold is an object - so it is drawn as one.

   The shell is drawn to the printed case in
   hardware/enclosure/esp32_s3_touch_amoled_241: a 47.20 x 64.00 mm outline,
   14.80 mm deep, R2.22 corners, three button caps on one wall at 8.50 mm pitch,
   49.0 x 36.7 mm active area. The case is printed portrait and the game runs
   landscape, so what is drawn here is that case turned a quarter turn, which
   lands the button wall on the left with the two coloured caps at the bottom.

   It is PAINTED flat, not built out of planes in a 3D projection. The earlier
   construction kept a separate face and side wall as siblings under a shared
   perspective, needed sixteen stacked slices to fake the swept rounded corner
   a flat plane cannot turn, and put every <video> on the page inside a 3D
   rendering context - which Firefox answers by fixing the clip's surface
   rectangle at its first size and cropping it after any resize. Here the case
   is ONE rounded rectangle: the wall is its background, the FACE is a smaller
   rounded plate laid over it (::before), and the wall band is what the plate
   leaves uncovered - so the seam between face and wall turns the same corner
   arcs the shell turns, and the flat band visibly wraps the front's rounded
   corners, by construction. The back view in the hardware section (.flip) is
   drawn in this same language.

   --turn is the drawn view angle. The hero holds it, deliberately static and
   facing forward; the tilt tile's script leans it (main.js), and the lean
   lands purely in paint - the wall band widths with sin(--turn) and the drop
   shadow slides against it. Nothing above a clip is ever transformed; the
   note below the case rule says why that is absolute.

   Every number below is a percentage of the case width, so one rule scales
   from a 900 px hero to a 300 px cell in the move grid. */
.device {
  margin: 0;
  --turn: 5deg;        /* the view angle the case is drawn at: nearly frontal,
                          with just a sliver of the side to say it is a thing */
  /* The case depth, 14.80 mm, against the drawn width of about 65 mm (the
     64.00 mm face at this pose plus the wall it shows). sin(--turn) of it is
     on screen: 2% at rest. Clamped at zero because the tilt tile leans --turn
     through the frontal pose, where the wall genuinely vanishes. */
  --wall: max(0%, calc(22.6% * sin(var(--turn))));
}

.device__case {
  position: relative;
  /* Bezels drawn a shade slimmer than the printed case so the game keeps the
     pixels; the left pad starts past the wall band at its resting width. The
     pads are CONSTANT - the wall band breathes across the left bezel rather
     than pushing it - so the screen box never changes size mid-sway and the
     clip inside is never asked to re-raster. The screen keeps a true 4:3 in
     layout, so gameplay pixels are never leaned on. */
  padding: 6.4% 8.6% 6.4% 10.6%;
  border-radius: 3.5% / 4.7%;
  /* Three paint layers, and the border-radius clips them all - which is the
     entire corner story: the wall band bends round the top-left and
     bottom-left arcs exactly, with no second element to seam against. On top,
     the side wall, lit at its outer edge and falling to black where it turns
     in to meet the face; then a soft side-light the seam throws onto the
     face; under both, the face with its own top light. */
  /* The wall band is FLAT - it is the flat side of a cuboid - so it wears one
     directional gradient: lit at its outer edge, falling toward the seam,
     with the shell's rolled top and bottom edges over it. The face is not
     painted here at all; it is the ::before plate below, and the band is
     simply what that plate leaves uncovered. */
  background:
    linear-gradient(180deg, rgba(255,255,255,.10) 0, transparent 4%),
    linear-gradient(  0deg, rgba(0,0,0,.5) 0, transparent 5%),
    linear-gradient( 90deg, #3E3E49 0%, #23232B 55%, #101015 100%),
    linear-gradient(180deg, #101014 0%, #08080B 100%);
  background-size: 100% 100%, 100% 100%, calc(var(--wall) + 2%) 100%, 100% 100%;
  background-repeat: no-repeat;
  /* The middle shadow slides opposite the turn, so the tilt tile's lean
     registers on the paper as well as on the case. sin() of the rest pose is
     0.0872, which zeroes the drift for every static figure. */
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.10),
    calc((0.0872 - sin(var(--turn))) * 70px) 22px 44px -20px rgba(19,22,35,.5),
    0 5px 12px -5px rgba(19,22,35,.3);
}
/* Nothing transforms the case: it sits LEVEL, and the turn it depicts is
   painted (the wall band), not projected. A real yaw's trapezoid needs
   rotateY, and no flat transform can fake it - a shear only tilts the whole
   case, which reads as crooked, not turned. The level rule is also a hard
   constraint: 3D or CHANGING transforms above a <video> - 3D planes, a
   CSS-animated pose, will-change, the reveal's rise, even a script-driven
   per-frame 2D pose - each got the clip's compositor surface promoted and
   pinned on macOS Firefox, and cropped. So above an on-screen clip nothing
   may ever change a transform, carry will-change, enter 3D, or ask for
   object-fit.

   The tilt tile below is the one exception that proves the rule: it really
   rotates, footage and all, and it is allowed to ONLY because its footage is
   never a <video> on screen - main.js paints the playing clip onto a canvas
   and hides the video, and a canvas re-rasters correctly under any
   transform. Script leans --roll off the clip's own timeline. */
.device--turn .device__case { transform: rotate(var(--roll, 0deg)); }
/* The FACE, drawn as its own rounded plate over the wall. Its left corners
   turn with the shell's own radius, so the seam between face and wall curves
   into the top-left and bottom-left arcs and the flat band visibly wraps the
   front's corners - which no straight-edged gradient stop can do. Its edges
   roll like a printed part: lit along the seam and the top, falling away to
   the right and the bottom; and the plate casts a soft shadow onto the band
   that follows the curved corners with it. Painted under the screen, which is
   a positioned sibling later in the DOM, so none of this ever lands on the
   gameplay. */
.device__case::before {
  content: ""; position: absolute;
  inset: 0 0 0 var(--wall); pointer-events: none;
  /* the shell's R2.22, expressed against the plate's own slightly narrower box */
  border-radius: 3.6% / 4.7%;
  background:
    linear-gradient( 90deg, rgba(255,255,255,.10) 0, rgba(255,255,255,.02) 2%, transparent 6%),
    linear-gradient(270deg, rgba(0,0,0,.55) 0, rgba(0,0,0,.16) 2%, transparent 6%),
    linear-gradient(180deg, rgba(255,255,255,.13) 0, rgba(255,255,255,.03) 1.6%, transparent 4%),
    linear-gradient(  0deg, rgba(0,0,0,.55) 0, rgba(0,0,0,.18) 2%, transparent 5.5%),
    linear-gradient(180deg, #24242B 0%, #16161A 46%, #0C0C0F 100%);
  box-shadow: -7px 0 10px -5px rgba(0,0,0,.5);
}

.device__screen {
  position: relative;
  /* the screen holds its own 4:3 box, so it never collapses in the window
     between the still loading and the clip attaching */
  aspect-ratio: 4 / 3;
  border-radius: 1.4% / 1.9%;
  overflow: hidden;
  background: var(--edge, #000);
  box-shadow: 0 0 0 1px rgba(0,0,0,.85), 0 1px 0 rgba(255,255,255,.06);
}
/* glass. Kept to a corner highlight so it never sits on the gameplay. */
.device__screen::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(146deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,.03) 26%, transparent 46%);
}
/* Both the clip and the still are PINNED to all four edges rather than sized
   from `width: 100%` plus their own aspect-ratio. Two boxes deriving the same
   4:3 independently can disagree, and pinned to the containing block the frame
   has no size of its own to go stale. And no object-fit: every clip and still
   is authored at the screen's own 4:3, so plain fill is already exact - and
   fill, unlike cover, asks the compositor for no cropping at all, which is
   one less thing Firefox's video path can get wrong. */
.device__screen > video, .device__screen > img, .device__screen > canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}
/* The still is a real frame of the clip, sitting ON the video and lit from the
   first paint. It is what the reader looks at while the mp4 is still coming
   down the wire, and it fades out on the clip's own `playing` event, so a frame
   is never missing and a swap is never seen. It is also the entire picture when
   scripting is off or the OS asks for reduced motion. */
.device__screen > .still {
  opacity: 1; transition: opacity .3s var(--ease-io);
  pointer-events: none;
}
.device__screen.is-live > .still { opacity: 0; }

/* The three caps, at the same 5.0 mm length and 8.50 mm pitch as the scale
   drawing below. Drawn FLAT on purpose: solid fills, near-square corners, no
   dome and no sheen. At this nearly frontal pose a cap that stands 2.1 mm
   proud of the wall shows mostly PAST the case's silhouette, so each cap is
   anchored with its inner edge on the middle of the band and its body poking
   out to the left; when the tilt tile widens the band, the caps tuck onto it
   with it. Muted rather than the interaction orange - twenty shells is sixty
   caps, and at full --ember they out-shout the one thing on the page that is
   clickable. */
.device__btn {
  position: absolute;
  left: calc(var(--wall) / 2); width: 2.4%; height: 8%;
  transform: translateX(-100%);
  border-radius: 3px;
  background: #C97116;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.25),   /* the rolled edge catching light */
    0 4px 5px -1px rgba(0,0,0,.45);        /* thrown onto the wall below */
}
/* The cap's SIDE: the same solid extrusion the CTA button wears, which is
   what makes it protrude instead of reading as a sticker. Sized against the
   cap so it holds its proportion from the hero down to a grid tile. */
.device__btn::after {
  content: ""; position: absolute;
  left: 0; right: 0; top: 99%; height: 9%;
  border-radius: 0 0 3px 3px;
  background: #7A4409;
}
.device__btn--a { top: 28.9%; background: #45454F; }
.device__btn--a::after { background: #23232B; }
.device__btn--b { top: 47%; }   /* 8.50 mm pitch is 18% of the 47.20 mm wall */
.device__btn--c { top: 65.1%; }

.device--wide { max-width: 900px; margin-inline: auto; }
.device--sm { max-width: 420px; }

.device figcaption, .sheet figcaption, .flip figcaption, .scale figcaption {
  margin-top: var(--s5);
  font-size: 14px; line-height: 1.55; color: var(--fg-mute);
  max-width: 62ch;
}
/* the two word name a move goes by, above the sentence explaining it */
.cap__t {
  display: block;
  font-family: var(--mono); font-size: 12px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg);
  margin-bottom: var(--s2);
}

/* ---------- the one case that moves ---------- */
/* The hero is deliberately STATIC, facing forward: every way of moving it
   that was tried ended with macOS Firefox cropping its clip (the note in the
   device section). The tilt tile is the one case that moves - it genuinely
   rotates, footage and all - and it gets away with it because main.js paints
   its clip onto a canvas and hides the video (the rotation rule and the why
   live in the device section). */

/* contact sheet */
.sheet { margin: clamp(56px, 7vw, 104px) auto 0; }
.sheet img {
  width: 100%; max-width: 900px; margin-inline: auto;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface);
}
.sheet figcaption { margin-inline: auto; max-width: 900px; }

/* ---------- 1. HERO ---------- */
/* Centred, because the headline is a sentence and the device is the subject. A
   two column hero would give the sentence half the width it needs and put the
   one moving object on the page in a corner. */
.hero {
  padding: clamp(32px, 3.6vw, 56px) var(--gut) clamp(56px, 7vw, 104px);
  background:
    radial-gradient(120% 80% at 50% 0%, #FFFFFF 0%, transparent 62%),
    linear-gradient(180deg, var(--paper) 0%, var(--paper-2) 100%);
}
.hero__in { max-width: 960px; margin-inline: auto; text-align: center; }
.lockup {
  display: flex; align-items: center; gap: var(--s3);
  width: max-content; margin: 0 auto var(--s6);
}
.lockup__egg { width: 34px; height: 34px; }
.lockup__word {
  font-family: var(--mono); font-weight: 700; font-size: 20px;
  letter-spacing: .22em; color: var(--fg); line-height: 1;
}
.hero h1 { margin-bottom: var(--s5); }
.hero .lead { max-width: 48ch; margin: 0 auto var(--s6); }
.hero__micro {
  margin-top: var(--s4); font-size: 14px; color: var(--fg-mute);
  max-width: 46ch; margin-inline: auto;
}
/* Deliberately short: at 1440x900 this lands the top of the shell above the
   fold, so the thing being sold is on screen next to the price. */
.hero .device { max-width: 900px; margin: clamp(32px, 3.6vw, 56px) auto 0; }
.hero .device figcaption { margin-inline: auto; text-align: center; }

/* ---------- FACT RAIL ---------- */
.rail { border-block: 1px solid var(--line); background: var(--surface); }
.rail__in {
  max-width: var(--w-wide); margin-inline: auto; padding-inline: var(--gut);
  display: grid; grid-template-columns: repeat(4, 1fr);
}
.rail__in li {
  font-family: var(--mono); font-size: 12.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fg-dim);
  padding: var(--s5) var(--s4); text-align: center;
  border-left: 1px solid var(--line);
}
.rail__in li:first-child { border-left: 0; }

/* ---------- BAND ---------- */
.band { padding: clamp(72px, 10vw, 144px) var(--gut); }
.band__head { max-width: var(--w); margin-inline: auto; margin-bottom: var(--s8); }
.band__head h2 { margin-bottom: var(--s5); }
.band > .device, .band > .duo, .band > .note, .band > .cols, .band > .sheet, .band > .moves {
  max-width: var(--w); margin-inline: auto;
}
/* a shell at the full 1160 reads as a tablet, so a feature frame stops at 900 */
.band > .device--wide { max-width: 900px; }
.band--mp   { background: linear-gradient(180deg, var(--paper) 0%, var(--paper-3) 50%, var(--paper) 100%); }
.band--uniq { background: linear-gradient(180deg, var(--paper) 0%, var(--paper-2) 100%); }

/* ---------- FEATURE GROUP (a labelled run of frames) ---------- */
.feat { max-width: var(--w); margin-inline: auto; }
.feat + .feat, .duo--top { margin-top: clamp(64px, 8vw, 112px); }
/* ...but two groups sitting side by side in a .duo are columns, not a stack:
   the rule above would push the right one down by a whole group gap. */
.duo > .feat + .feat { margin-top: 0; }
/* A .feat inside a .duo must NOT keep its auto margins. .device is a query
   container, and inline-size containment makes an element contribute nothing to
   its parent's intrinsic width; auto margins on a grid item size it to
   fit-content, so the whole column collapsed to the width of the word ARENA. */
.duo > .feat { margin-inline: 0; max-width: none; }
.feat__title {
  font-family: var(--mono); font-size: 13px; letter-spacing: .18em;
  color: var(--ember-ink);
  padding-bottom: var(--s3); margin-bottom: var(--s6);
  border-bottom: 1px solid var(--line-2);
}
.feat__lede { margin-bottom: var(--s7); max-width: 60ch; }
.feat__in {
  display: grid; gap: clamp(32px, 4vw, 64px);
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  align-items: center;
}
.feat__copy { display: grid; gap: var(--s5); align-content: center; }
.pull {
  padding-left: var(--s5); border-left: 2px solid var(--ember);
  color: var(--fg);
}

/* The move grid is halves, not thirds. A move frame is a wide field with a
   creature somewhere in it, and at a third of the page the creature lands at
   about forty pixels tall - present, but too small to read what it is doing,
   which is the entire point of the tile. The three creature frames below are
   single-creature close-ups and hold up fine at a third. */
.moves { display: grid; gap: clamp(28px, 3.5vw, 48px); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.moves--three {
  margin-top: clamp(56px, 7vw, 96px);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* min(Xpx, 100%) rather than a bare Xpx: a fixed track floor is wider than the
   content box of a 320px viewport and drags the whole document out with it. */
.duo { display: grid; gap: var(--s7); grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); align-items: start; }

.cols {
  margin-top: clamp(56px, 7vw, 96px);
  display: grid; gap: var(--s7);
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
}
.cols h3 { margin-bottom: var(--s3); }
.cols p { font-size: 16px; }

/* ---------- 5. PARENTS ---------- */
.parents {
  padding: clamp(72px, 10vw, 144px) var(--gut);
  background: var(--paper-2); border-block: 1px solid var(--line);
}
.parents__in {
  max-width: var(--w); margin-inline: auto;
  display: grid; gap: clamp(40px, 5vw, 80px);
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  align-items: start;
}
.parents h2 { margin-bottom: var(--s7); max-width: 22ch; }
.checks { display: grid; gap: var(--s5); max-width: 58ch; }
.checks li {
  position: relative; padding-left: var(--s7);
  color: var(--fg-dim); font-size: 16px; line-height: 1.6;
}
.checks li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 20px; height: 2px; background: var(--ember);
}
.parents .note { margin-top: var(--s7); }

/* ---------- 6. PAPAMON ONLINE ---------- */
.online {
  padding: clamp(72px, 9vw, 128px) var(--gut);
  background:
    radial-gradient(90% 80% at 50% 0%, #FFFFFF 0%, transparent 70%),
    var(--paper);
}
.online__in { max-width: 720px; margin-inline: auto; text-align: center; }
/* the Papamon Online mark: the egg, broadcasting */
.online__logo { display: block; width: 78px; height: auto; margin: 0 auto var(--s5); }
.online h2 { margin-bottom: var(--s5); }
.online .lead, .online .note { margin-inline: auto; }
.online .note { margin-top: var(--s5); }

/* ---------- 7. HARDWARE ---------- */
.hw { padding: clamp(72px, 10vw, 144px) var(--gut); background: var(--paper-2); border-block: 1px solid var(--line); }
.hw__in {
  max-width: var(--w); margin-inline: auto;
  display: grid; gap: clamp(40px, 5vw, 80px);
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  align-items: center;
}
.hw h2 { margin-bottom: var(--s5); max-width: 18ch; }

/* ---------- the object, front and back ---------- */
/* The shell shown STATIC, front beside back, in the page's own flat
   construction - a revolution drawn out of flat faces never earned its keep.
   The front is a plain .device__case; the back is the same rounded plate
   over a wall band, mirrored to the right, carrying the real two-colour
   print. */
.flip { margin: 0; }
.flip__stage {
  max-width: 680px;
  /* both faces are drawn with the page's own pose */
  --turn: 5deg;
  --wall: max(0%, calc(22.6% * sin(var(--turn))));
}
.flip__pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: clamp(12px, 2vw, 20px);
}
.flip__back {
  position: relative;
  /* the front case's own proportions, so the pair sit level together */
  aspect-ratio: 1000 / 734;
  border-radius: 3.5% / 4.7%;
  /* the wordmark inside is sized in cqw */
  container-type: inline-size;
  /* seen from behind the button wall swaps sides: the band lands on the RIGHT */
  background:
    linear-gradient(180deg, rgba(255,255,255,.10) 0, transparent 4%),
    linear-gradient(  0deg, rgba(0,0,0,.5) 0, transparent 5%),
    linear-gradient(270deg, #3E3E49 0%, #23232B 55%, #101015 100%),
    linear-gradient(180deg, #101014 0%, #08080B 100%);
  background-size: 100% 100%, 100% 100%, calc(var(--wall) + 2%) 100%, 100% 100%;
  background-position: 0 0, 0 0, 100% 0, 0 0;
  background-repeat: no-repeat;
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.10),
    0 22px 44px -20px rgba(19,22,35,.5),
    0 5px 12px -5px rgba(19,22,35,.3);
}
/* the back's own plate, mirrored: its RIGHT corners curve into the band */
.flip__back::before {
  content: ""; position: absolute;
  inset: 0 var(--wall) 0 0; pointer-events: none;
  border-radius: 3.6% / 4.7%;
  background:
    linear-gradient(270deg, rgba(255,255,255,.10) 0, rgba(255,255,255,.02) 2%, transparent 6%),
    linear-gradient( 90deg, rgba(0,0,0,.55) 0, rgba(0,0,0,.16) 2%, transparent 6%),
    linear-gradient(180deg, rgba(255,255,255,.13) 0, rgba(255,255,255,.03) 1.6%, transparent 4%),
    linear-gradient(  0deg, rgba(0,0,0,.55) 0, rgba(0,0,0,.18) 2%, transparent 5.5%),
    /* the print lines a thumb would find on the back of a FDM shell */
    repeating-linear-gradient(0deg, rgba(255,255,255,.026) 0 1px, transparent 1px 5px),
    linear-gradient(158deg, #212128 0%, #16161B 54%, #0A0A0D 100%);
  box-shadow: 7px 0 10px -5px rgba(0,0,0,.5);
}
/* its caps poke out of the band on its right, mirroring the front */
.flip__back .device__btn { left: auto; right: calc(var(--wall) / 2); transform: translateX(100%); }
/* the four screw towers, on the enclosure's own 36.50 x 51.00 mm pattern */
.flip__screw {
  position: absolute; width: 3.1%; aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #3A3A44 0%, #17171C 70%);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.6), 0 1px 0 rgba(255,255,255,.05);
}
.flip__screw--tl { left: 10.2%; top: 11.3%; }
.flip__screw--tr { right: 10.2%; top: 11.3%; }
.flip__screw--bl { left: 10.2%; bottom: 11.3%; }
.flip__screw--br { right: 10.2%; bottom: 11.3%; }
/* The rear branding is the real two-colour print: a 15.50 x 18.00 mm orange egg
   inlay with its two spots left black, and a 3.50 mm PAPAMON wordmark 14.00 mm
   below it, both flush 0.80 mm inlays in #F97316 (generate_case.py). The CAD
   turns them a quarter turn so they read upright when the case is held
   landscape, which is how the page draws it. */
.flip__egg {
  position: absolute; left: 50%; top: 43.6%;
  width: 24.2%; height: auto;      /* 15.50 mm of a 64.00 mm face */
  transform: translate(-50%, -50%);
  overflow: visible;
}
.flip__word {
  position: absolute; left: 50%; top: 73.3%;
  transform: translate(-50%, -50%);
  font-family: var(--mono); font-weight: 700;
  font-size: 7.4cqw;               /* about a 3.50 mm cap height */
  letter-spacing: .14em; white-space: nowrap;
  color: #F97316;
}
/* ---------- the size drawing ---------- */
.scale {
  max-width: var(--w); margin: clamp(56px, 7vw, 104px) auto 0;
}
.scale__svg {
  width: 100%; max-width: 560px;
  height: auto;
}
.scale__pap rect { fill: var(--ink); }
.scale__pap .scale__screen { fill: #2C3550; }
.scale__pap .scale__cap { fill: var(--ember-2); }
.scale__pen rect, .scale__pen circle { fill: var(--line-2); }
.scale__pen .scale__pen-hole { fill: var(--paper-2); }
.scale__lab {
  font-family: var(--mono); font-size: 3.4px; letter-spacing: .12px;
  fill: var(--fg-mute);
}
.scale figcaption { margin-top: var(--s5); }
.specs { display: grid; margin-top: var(--s7); }
.specs > div {
  display: grid; gap: var(--s2) var(--s6);
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  padding-block: var(--s5);
  border-top: 1px solid var(--line-2);
}
.specs > div:last-child { border-bottom: 1px solid var(--line-2); }
.specs dt {
  font-family: var(--mono); font-size: 13px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ember-ink);
}
.specs dd { color: var(--fg-dim); font-size: 16px; }
.hw .note { margin-top: var(--s7); }

.services {
  max-width: var(--w); margin: var(--s8) auto 0;
  display: grid; gap: var(--s5) var(--s7);
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
}
.services li {
  color: var(--fg-dim); font-size: 16px; line-height: 1.55;
  padding-top: var(--s4); border-top: 1px solid var(--line-2);
}
.services strong { display: block; margin-bottom: var(--s1); }

/* ---------- PRICING TIERS ---------- */
.tiers {
  /* one tier now, so the card holds a readable width instead of the row */
  max-width: 560px; margin: var(--s9) auto 0;
  display: grid; gap: var(--s6);
  align-items: start;
  text-align: left;
}
.tier {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: var(--s7) var(--s6) var(--s6);
}
/* the buyable one carries the accent; the reservation is the quieter card */
.tier--now { border-top: 3px solid var(--ember); }
.tier--later { background: transparent; border-style: dashed; }
.tier__brow {
  font-family: var(--mono); font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ember-ink); margin-bottom: var(--s3);
}
.tier--later .tier__brow { color: var(--fg-mute); }
.tier__title { margin-bottom: var(--s3); }
.tier__lede { font-size: 15px; line-height: 1.55; margin-bottom: var(--s5); }
.tier__prices { display: grid; margin-bottom: var(--s6); }
.tier__prices > div {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); padding-block: var(--s4);
  border-top: 1px solid var(--line-2);
}
.tier__prices > div:last-child { border-bottom: 1px solid var(--line-2); }
.tier__prices dt { color: var(--fg-dim); font-size: 15px; }
.tier__prices dd { text-align: right; }
/* the per-unit price is the number, because that is the one a buyer compares.
   The total is under it, because that is the one they are charged. */
.tier__num {
  display: block;
  font-family: var(--mono); font-weight: 700; font-size: 22px;
  letter-spacing: -.02em; color: var(--fg); line-height: 1.15;
}
.tier__each {
  display: block; margin-top: 2px;
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .04em;
  color: var(--fg-mute); white-space: nowrap;
}
/* The buttons fill the card rather than sitting at their label width, and a
   pair that will not fit side by side stacks. A cta--lg does not fit a 340px
   tier column at all, which is why these are the regular size. */
.tier__acts { display: flex; flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s6); }
.tier__acts .cta { flex: 1 1 auto; text-align: center; }
.tier__spec { display: grid; gap: var(--s4); }
.tier__spec li { font-size: 14px; line-height: 1.55; color: var(--fg-mute); }
.tier__spec strong { color: var(--fg-dim); }

/* ---------- PREORDER ---------- */
.pre {
  padding: clamp(88px, 12vw, 176px) var(--gut);
  background:
    radial-gradient(90% 70% at 50% 0%, #FFFFFF 0%, transparent 66%),
    var(--paper);
}
.pre__in { max-width: 760px; margin-inline: auto; text-align: center; }
.pre h2 { margin-bottom: var(--s5); }
.pre .lead { margin-inline: auto; }
.pre__micro { margin-top: var(--s8); font-size: 14px; color: var(--fg-mute); margin-inline: auto; }
[data-cfg] {
  display: inline-block; min-width: 5ch;
  color: var(--fg); font-family: var(--mono); font-weight: 700;
}
.terms {
  margin-top: var(--s9); text-align: left;
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}
.terms > div { padding: var(--s5) 0; border-top: 1px solid var(--line-2); }
.terms dt {
  font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ember-ink); margin-bottom: var(--s2);
}
.terms dd { color: var(--fg-dim); font-size: 15px; line-height: 1.55; max-width: 42ch; }

/* ---------- FOOTER ---------- */
/* The one dark block on the page, and it is the boot splash: the same gradient
   and the same yellow the device paints on itself when it powers on. */
.foot {
  padding: clamp(64px, 8vw, 112px) var(--gut) var(--s9);
  background: linear-gradient(180deg, var(--ink) 0%, var(--deep) 100%);
}
.foot__in { max-width: var(--w-wide); margin-inline: auto; }
.foot__word {
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(48px, 2rem + 10vw, 152px);
  line-height: .85; letter-spacing: -.05em;
  color: #262B41; max-width: none;
}
.foot__tag { margin-top: var(--s5); font-family: var(--mono); font-size: 14px; letter-spacing: .08em; color: #FFEB8C; }
.foot__note { margin-top: var(--s2); font-size: 15px; color: #AAB1C6; }
.foot__legal { margin-top: var(--s6); font-size: 13px; line-height: 1.5; color: #848BA4; max-width: 76ch; }

/* ---------- reveal ---------- */
/* Motivated: the page is a stack of large media blocks, and a short rise on
   entry tells the reader where the next one starts. One move, no stagger. */
@media (prefers-reduced-motion: no-preference) {
  .reveal {
    opacity: 0; transform: translateY(14px);
    transition: opacity .45s var(--ease), transform .45s var(--ease);
  }
  .reveal.is-in { opacity: 1; transform: none; }
  /* Device figures fade without the rise. The rise is a transform TRANSITION
     on an ancestor of a <video>, and it is live at exactly the moment the
     hero's clip attaches and first paints; macOS Firefox can pin the clip's
     compositor surface to the rectangle it had under that transform and crop
     it for good. Nothing that contains a clip is ever transformed, even for
     half a second (see the device section). */
  .device.reveal { transform: none; transition: opacity .45s var(--ease); }
}

/* ---------- reduced motion ---------- */
/* The only thing that stops the footage. There is no switch on the page: video
   is the default and the OS setting is the exception. Hiding the <video> leaves
   the still, which is already loaded and already the right size. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .clip { display: none; }
  .device__screen.is-live > .still { opacity: 1; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---------- no JavaScript ---------- */
/* main.js attaches every clip source on approach, so with scripting off the
   <video> elements are empty. The still under each one is a real image and
   carries the page on its own. */
html:not([data-js]) .clip { display: none; }

/* ---------- responsive ---------- */
@media (max-width: 1080px) {
  .rail__in { grid-template-columns: repeat(3, 1fr); }
  .rail__in li:nth-child(4) { border-left: 0; }
  .moves--three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .feat__in,
  .parents__in,
  .hw__in { grid-template-columns: minmax(0, 1fr); }
  .feat__in .device { order: -1; }
  .device--sm { max-width: 520px; }
  .nav__links { display: none; }
  .nav__in { gap: var(--s4); justify-content: space-between; }
  .foot__word { font-size: clamp(44px, 14vw, 96px); }
  .specs > div { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 680px) {
  .rail__in { grid-template-columns: repeat(2, 1fr); }
  .rail__in li { border-left: 1px solid var(--line); text-align: left; }
  .rail__in li:nth-child(odd) { border-left: 0; }
  .moves, .moves--mp, .moves--three { grid-template-columns: minmax(0, 1fr); }
  /* A 4:3 clip in a portrait viewport is small before the bezels take their
     share, so they are halved here to hand the pixels back to the game. */
  .device__case {
    padding: 3.9% 5.4% 3.9% 7.4%;
    box-shadow:
      inset 0 0 0 1px rgba(255,255,255,.10),
      0 12px 26px -12px rgba(19,22,35,.45);
  }
  .terms { gap: 0; }
}
/* "Indicate Interest at $1" is 23 monospace characters and the button does not
   wrap, so below about 400px the generous horizontal padding is what pushes the
   document wider than the viewport, not the label. */
@media (max-width: 420px) {
  .cta--lg { font-size: 15px; padding-inline: var(--s5); }
}

/* ---------- perf ---------- */
.band, .parents, .online, .hw, .pre {
  content-visibility: auto;
  contain-intrinsic-size: auto 1200px;
}
