/* ---- MaMills game page -------------------------------------------------
   The sheet for mamills/index.html, and nothing else loads it. It lived in an
   inline <style> in the draft under workspace/private/per_mamills/, which the site's
   CSP (style-src 'self', no 'unsafe-inline') drops on the floor: the page
   rendered unstyled in a browser while looking finished over file://. Moving
   it to a file is what makes the page shippable.
   Paths here are relative to assets/, which is where this file sits.
   The name carries a version (mamills.css until 2026-09-19): the asset cache
   is a day, so a page that needs new rules would otherwise arrive with
   yesterday's sheet. That is how the screenshot labels stayed misaligned on
   the developer's screen after they were fixed. Bump the number, and the
   <link> in mamills/index.html, whenever markup and CSS must change together. */
/* ---------- fonts (self-hosted, zero external requests) ---------- */
@font-face{font-family:'Inter';font-weight:100 900;font-display:swap;src:url('fonts/Inter-var.woff2') format('woff2');}
@font-face{font-family:'Libre Baskerville';font-weight:400;font-display:swap;src:url('fonts/LibreBaskerville-400.woff2') format('woff2');}
@font-face{font-family:'Libre Baskerville';font-weight:700;font-display:swap;src:url('fonts/LibreBaskerville-700.woff2') format('woff2');}
@font-face{font-family:'Fraunces';font-weight:500;font-display:swap;unicode-range:U+004D,U+0061,U+0069,U+006C,U+0073;src:url('fonts/Fraunces-500.woff2') format('woff2');}

/* ---------- tokens (scripts/settings.gd is the truth) ---------- */
:root{
  --paper:        #F0EBDB;
  --paper-light:  #F7F2E0;
  --paper-hi:     #FCEBBD;
  --ink:          #1A1A1F;
  --ink-faint:    #6B6861;
  --mustard:      #F7D44D;
  --mustard-deep: #E8BD33;
  --red:          #C8463B;
  --blue:         #4A7CB5;
  --green:        #5CB06E;
  /* piece fills only */
  --violet:       #8C68D6;  --leaf:      #65B860;
  --cb-blue:      #0072B2;  --cb-orange: #E69F00;
  --gold:         #D4AF37;  --silver:    #ABAEB4;
  /* The Halloween theme's own colours, used by one control: the pill in the
     hero that points at the theme's band. They are the game's Halloween
     menu buttons, from the dark palette in the game repo's
     scripts/ui_theme.gd: its cream text (INK) and its purple line (LINE).
     The game's button fill #231637 read too dark on the cream page (the
     developer's call, 2026-09-25), and so did each step lighter up to the
     theme's grid purple #4A3568; the fill now sits between that and the
     line, with a hover a step lighter. Cream on the fill measures 7.1:1, and the line on the
     page's cream 5.95:1. */
  --night:        #5A4278;
  --night-hi:     #654D84;
  --night-ink:    #FFE8C8;
  --night-line:   #6A4A8A;

  --serif: 'Libre Baskerville', Georgia, 'Times New Roman', serif;
  --sans:  'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --gutter:  clamp(1rem, 4vw, 2rem);
  --section: clamp(3.5rem, 9vw, 6.5rem);
}

/* ---------- base ---------- */
*,*::before,*::after{box-sizing:border-box;}
html{overflow-x:clip;-webkit-text-size-adjust:100%;}
body{margin:0;background:var(--paper);color:var(--ink);font:400 clamp(1rem, .95rem + .25vw, 1.0625rem)/1.55 var(--sans);}
img,svg,video{max-width:100%;}
img{height:auto;}
p{margin:0 0 1rem;}
main p{max-width:60ch;}
.wrap{width:min(100% - 2*var(--gutter), 1040px);margin-inline:auto;}
a{color:var(--ink);text-decoration:underline;text-decoration-color:var(--mustard);text-decoration-thickness:2px;text-underline-offset:3px;}
a:hover{text-decoration-color:var(--mustard-deep);}
:focus-visible{outline:3px solid var(--blue);outline-offset:3px;}
p a:focus-visible,.link:focus-visible,.links a:focus-visible{border-radius:4px;}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}
.defs{position:absolute;width:0;height:0;overflow:hidden;}

/* ---------- type ---------- */
h1,h2,h3{margin:0;font-family:var(--serif);font-weight:700;}
h1{font:500 clamp(3.5rem, 2rem + 7vw, 6.5rem)/0.9 'Fraunces', var(--serif);letter-spacing:-.01em;color:var(--ink);white-space:nowrap;}
h2{font-size:clamp(1.625rem, 1.25rem + 1.6vw, 2.375rem);line-height:1.15;letter-spacing:-.005em;margin:0 0 1rem;}
h3{font-size:clamp(1.125rem, 1.05rem + .35vw, 1.3125rem);line-height:1.25;}
.pitch{font:400 clamp(1.125rem, 1rem + .6vw, 1.5rem)/1.4 var(--serif);max-width:32ch;margin:1.25rem auto 1.75rem;}
/* The plain line under the pitch (2026-09-14): what the game is and where it
   runs, in words a search can match - "3D", "board game", the four platforms -
   which the page otherwise only said in its metadata. */
.platforms{font-size:.9375rem;line-height:1.5;color:var(--ink-faint);max-width:44ch;margin:-1rem auto 1.5rem;}
.eyebrow{display:flex;align-items:center;gap:.5rem;margin:0 0 .6rem;font:700 .75rem/1 var(--sans);text-transform:uppercase;letter-spacing:.12em;color:var(--ink-faint);}
.eyebrow .mark{width:15px;height:21px;flex:none;}
.caption,.sig,.contact,.copyright{font:400 .8125rem/1.4 var(--sans);color:var(--ink-faint);}

/* ---------- skip link ---------- */
.skip{position:absolute;left:50%;top:.5rem;z-index:10;transform:translate(-50%,-200%);padding:.6rem 1rem;border:2px solid var(--ink);border-radius:999px;background:var(--paper-light);box-shadow:3px 3px 0 var(--ink);font:700 .9375rem/1 var(--sans);color:var(--ink);text-decoration:underline;text-decoration-color:var(--mustard);text-decoration-thickness:2px;text-underline-offset:3px;}
.skip:focus{transform:translate(-50%,0);}

/* ---------- the two players in the screenshots band (2026-09-24) ----------
   The trailer and the Halloween teaser play where they are, the way the
   landing page's ident does, rather than behind a link and a dialog. They
   borrow the band's own grid and frame by carrying `class="shots players"`,
   so what is new here is only the poster, the knob and the description.
   assets/site.js drives them: the same .js-player wiring as the landing page,
   so there is one player behaviour on the site rather than two.

   This is also why the sheet keeps taking new names (v3 on 2026-09-24,
   v4 on 2026-09-25): the markup below needs these
   rules on the day it ships, and the stylesheets keep their names under a
   day-long asset cache, so new markup either ships `hidden` or arrives with a
   sheet nobody can have cached. The 2026-09-19 stamp taught that once. */
.players video{display:block;width:100%;height:auto;aspect-ratio:16/9;border-radius:8px;background:var(--ink);}
.players .vposter{position:absolute;inset:0;z-index:1;border-radius:8px;overflow:hidden;}
/* The band's chip sits at z-index 1 like the stills', which puts it under the
   poster that covers a player's frame. It rides above both layers here. */
/* Each player is named above its picture rather than on it: the stills' yellow
   label sits where a video's own controls appear once it is playing, and the
   name belongs to the video, not to a corner of the frame. */
/* Each video is titled above its own picture, centred on it, with the page's
   mustard drawn under the words as a thin marker line - the same yellow as the
   chips and the CTA tab, a band across the descenders only, so it lifts the
   title without becoming a block of colour behind it. The inner <span> is what carries it: a
   block-level background would run the width of the column. */
.players .vlabel{margin:0 0 .65rem;text-align:center;color:var(--ink);}
.players .vlabel span{background-image:linear-gradient(transparent 74%, var(--mustard) 74%, var(--mustard) 90%, transparent 90%);
  padding:0 .3em;box-decoration-break:clone;-webkit-box-decoration-break:clone;}

/* The band sits right under the hero, so it takes a shorter run-up than a
   section that follows a full band of copy: the hero's own bottom padding is
   already between them. */
#players-band{padding-top:clamp(1rem, 2.5vw, 2rem);}
/* A band with one player in it: the two-column grid would leave the video at
   half width beside nothing. It keeps the same frame and title, centred and
   held to a comfortable reading width. Used by the trailer under the hero and
   by the Halloween teaser in its own band (2026-09-25). */
.shots.players.solo{grid-template-columns:1fr;max-width:820px;margin-inline:auto;}
.players .vposter img{height:100%;}
.players .vposter[hidden]{display:none;}
.players .vplay{position:absolute;left:50%;top:50%;z-index:2;display:grid;place-items:center;
  width:76px;height:76px;padding:0;transform:translate(-50%,-50%);
  border:2px solid var(--ink);border-radius:999px;background:var(--mustard);
  box-shadow:4px 4px 0 var(--ink);cursor:pointer;transition:transform .15s ease;}
.players .vplay[hidden]{display:none;}
.players .vplay:hover{transform:translate(-50%,-50%) scale(1.06);}
/* The triangle is a border trick, like the landing page's knob, so forced
   colours need it named below. */
.players .vplay .vknob{width:0;height:0;margin-left:7px;
  border-left:24px solid var(--ink);border-top:15px solid transparent;border-bottom:15px solid transparent;}
.players .vdesc{margin-top:.45rem;font-size:.8125rem;}
.players .vdesc summary{cursor:pointer;font-weight:700;width:max-content;padding:.25rem 0;}
.players .vdesctext{max-height:min(14rem,30vh);overflow:auto;margin-top:.4rem;padding:.25rem .5rem .25rem 0;}
.players .vdesctext p{margin:0 0 .5rem;line-height:1.45;}

/* ---------- the hero's Halloween pill (2026-09-25) ----------
   The theme is the loudest thing in the game and the quietest thing on the
   page: it lives in band 05, three screens down, because it only means
   anything once a visitor knows what MaMills is. So the hero carries a small
   pointer to it, in the theme's own night purple rather than the page's
   mustard, and it is a jump link - it explains nothing itself. It sits on its
   own line under the two CTAs so it crowds neither, and the band it lands on
   takes focus (tabindex="-1") so a keyboard visitor arrives inside it. */
.themerow{margin:1.6rem 0 0;}
.themepill{position:relative;display:inline-flex;align-items:center;gap:.5rem;
  padding:.5rem 1rem;border:2px solid var(--night-line);border-radius:999px;
  background:var(--night);color:var(--night-ink);
  font:700 .8125rem/1.1 var(--sans);letter-spacing:.01em;text-decoration:none;
  box-shadow:3px 3px 0 var(--ink);transition:transform .12s ease, box-shadow .12s ease;}
.themepill:hover{transform:translate(1px,1px);box-shadow:2px 2px 0 var(--ink);background:var(--night-hi);}
/* It is a link inside a <p>, so the rule that squares off focused text links
   (p a:focus-visible, near the top) would turn the pill into a rounded box. */
.themepill:focus-visible{border-radius:999px;}
.themepill .spark{flex:none;}
/* On the narrowest phones the sparks, gaps and text shrink so the words stay
   on one line and the walker, resting on the tip, stays inside the screen
   with room to spare: Linux renders the words a few pixels wider than a Mac
   The walker comes down to 34 px here too, since resting on the tip it
   reaches past the pill by its own height.
   (CI caught 324 px at 320 on 2026-09-25 with no margin here, then the
   walker 3 px from the edge with 11 px on a Mac: Linux runs about 8 px
   wider, so keep 16 px or more on a Mac). */
@media (max-width: 380px){
  .themepill{gap:.2rem;padding:.5rem .5rem;white-space:nowrap;font-size:.75rem;}
  .themepill .spark.four,.themepill .spark.three{width:14px;height:11px;}
  .themepill .walker{width:34px;height:21px;margin-left:-17px;}
}
/* The drawings are all decoration: aria-hidden, no pointer events, and
   neither spider pushes the band below down or grows the hero, because
   both live out of the pill's box (position:absolute).
   - Still sparks either side of the words, in the text flow: a cluster of
     four before and three after, in different sizes.
   - The hanger is the game's spider on its silk, dropped from the pill's
     bottom edge at the start of the pill, swinging as the title spider does
     in the game (6 degrees each way, one slow 4.6 s swing).
   - The walker crosses the round end. .walkway is a square as tall as the
     pill, laid over the end cap, so its centre is the centre of the cap's
     semicircle; the spider stands on the square's top edge, feet on the
     border, and turning the square half a turn walks it round the curve to
     the bottom edge, upside down, the way a spider takes a corner. The
     translate before the turn walks it along the straight edge first, from
     the end of the word. It walks like a spider rather than gliding: short
     eased scurries with stops between, the body level with a small lift per
     stroke, and its legs working in two alternating sets of four (front and
     third on one side with second and back on the other), each leg turning
     at its own hip. At the far end it looks round before it heads back, as
     the game's title spider does. The four animations share one 14 s clock,
     so the strokes land only inside the scurries; tools/spider_walk.py writes
     them from one timeline, between the markers below. */
.themepill .hanger{position:absolute;top:calc(100% - 1px);left:.7rem;
  pointer-events:none;transform-origin:50% 0;animation:mm-swing 4.6s ease-in-out infinite;}
.themepill .walkway{position:absolute;top:-2px;bottom:-2px;right:-2px;aspect-ratio:1;
  pointer-events:none;transform:translateX(-.6rem);animation:mm-walk 14s linear infinite;}
.themepill .walker{position:absolute;bottom:100%;left:50%;margin-left:-21px;display:block;
  transform-origin:50% 100%;animation:mm-step 14s linear infinite;}
/* Each leg turns at its hip, in the drawing's own units (r = 100). */
.walker .leg{animation:mm-legs-a 14s ease-in-out infinite;}
.walker .leg.b{animation-name:mm-legs-b;}
.walker .l1{transform-origin:-62px -10px;} .walker .r1{transform-origin:62px -10px;}
.walker .l2{transform-origin:-60px 12px;}  .walker .r2{transform-origin:60px 12px;}
.walker .l3{transform-origin:-55px 32px;}  .walker .r3{transform-origin:55px 32px;}
.walker .l4{transform-origin:-44px 48px;}  .walker .r4{transform-origin:44px 48px;}
/* One slow swing each way, hanging straight at the start and the middle of
   every cycle, so it can come to rest at either without a jump. */
@keyframes mm-swing{0%,50%,100%{transform:none;}25%{transform:rotate(6deg);}75%{transform:rotate(-6deg);}}
/* Now and then each spider glances the other way and back: the pupils rest
   a little to the right in the game's drawing, so the glance is to the left,
   with a peek down on the way. Different lengths, so the two never glance
   together. */
.themepill .eyes{animation:mm-glance 7s ease-in-out infinite;}
.themepill .hanger .eyes{animation-delay:-1.5s;}
.walker .eyes{animation-duration:9.5s;animation-delay:-3.3s;}
@keyframes mm-glance{
  0%,52%,82%,100%{transform:none;}
  56%,66%{transform:translate(-9px,1px);}
  70%,78%{transform:translate(-3px,3px);}}
/* spider-walk:begin - written by tools/spider_walk.py, do not edit by hand */
.themepill .walkway{--mm-still:0.0002 0.0712,0.1027 0.1237,0.1852 0.2169,0.2785 0.2958,0.3274 0.3626,0.4452 0.5412,0.5727 0.5973,0.6588 0.6726,0.7342 0.7623,0.7938 0.9998;}
@keyframes mm-walk{
  0%{transform:translateX(-0.6rem) rotate(0deg);}
  7.1429%{transform:translateX(-0.6rem) rotate(0deg);animation-timing-function:ease-in-out;}
  10.25%{transform:translateX(0rem) rotate(0deg);}
  12.3929%{transform:translateX(0rem) rotate(0deg);animation-timing-function:ease-in-out;}
  18.5%{transform:translateX(0rem) rotate(90deg);}
  21.7143%{transform:translateX(0rem) rotate(90deg);animation-timing-function:ease-in-out;}
  27.8214%{transform:translateX(0rem) rotate(180deg);}
  29.6071%{transform:translateX(0rem) rotate(180deg);animation-timing-function:ease-in-out;}
  32.7143%{transform:translateX(-0.6rem) rotate(180deg);}
  54.1429%{transform:translateX(-0.6rem) rotate(180deg);animation-timing-function:ease-in-out;}
  57.25%{transform:translateX(0rem) rotate(180deg);}
  59.75%{transform:translateX(0rem) rotate(180deg);animation-timing-function:ease-in-out;}
  65.8571%{transform:translateX(0rem) rotate(90deg);}
  67.2857%{transform:translateX(0rem) rotate(90deg);animation-timing-function:ease-in-out;}
  73.3929%{transform:translateX(0rem) rotate(0deg);}
  76.25%{transform:translateX(0rem) rotate(0deg);animation-timing-function:ease-in-out;}
  79.3571%{transform:translateX(-0.6rem) rotate(0deg);}
  100%{transform:translateX(-0.6rem) rotate(0deg);}}
@keyframes mm-legs-a{
  0%,7.1429%,10.25%,12.3929%,18.5%,21.7143%,27.8214%,29.6071%,32.7143%,54.1429%,57.25%,59.75%,65.8571%,67.2857%,73.3929%,76.25%,79.3571%,100%{transform:none;}
  8.1786%,13.6143%,16.0571%,22.9357%,25.3786%,30.6429%,55.1786%,60.9714%,63.4143%,68.5071%,70.95%,77.2857%{transform:rotate(14deg);}
  9.2143%,14.8357%,17.2786%,24.1571%,26.6%,31.6786%,56.2143%,62.1929%,64.6357%,69.7286%,72.1714%,78.3214%{transform:rotate(-14deg);}}
@keyframes mm-legs-b{
  0%,7.1429%,10.25%,12.3929%,18.5%,21.7143%,27.8214%,29.6071%,32.7143%,54.1429%,57.25%,59.75%,65.8571%,67.2857%,73.3929%,76.25%,79.3571%,100%{transform:none;}
  8.1786%,13.6143%,16.0571%,22.9357%,25.3786%,30.6429%,55.1786%,60.9714%,63.4143%,68.5071%,70.95%,77.2857%{transform:rotate(-14deg);}
  9.2143%,14.8357%,17.2786%,24.1571%,26.6%,31.6786%,56.2143%,62.1929%,64.6357%,69.7286%,72.1714%,78.3214%{transform:rotate(14deg);}}
@keyframes mm-step{
  0%,7.1429%,8.1786%,9.2143%,10.25%,12.3929%,13.6143%,14.8357%,16.0571%,17.2786%,18.5%,21.7143%,22.9357%,24.1571%,25.3786%,26.6%,27.8214%,29.6071%,30.6429%,31.6786%,32.7143%,36.2857%,44.5%,54.1429%,55.1786%,56.2143%,57.25%,59.75%,60.9714%,62.1929%,63.4143%,64.6357%,65.8571%,67.2857%,68.5071%,69.7286%,70.95%,72.1714%,73.3929%,76.25%,77.2857%,78.3214%,79.3571%,100%{transform:none;}
  7.6607%,8.6964%,9.7321%,13.0036%,14.225%,15.4464%,16.6679%,17.8893%,22.325%,23.5464%,24.7679%,25.9893%,27.2107%,30.125%,31.1607%,32.1964%,54.6607%,55.6964%,56.7321%,60.3607%,61.5821%,62.8036%,64.025%,65.2464%,67.8964%,69.1179%,70.3393%,71.5607%,72.7821%,76.7679%,77.8036%,78.8393%{transform:translateY(-1.5px);}
  38.7857%{transform:rotate(-8deg);}
  42%{transform:rotate(7deg);}}
/* spider-walk:end */
/* Motion off (the rule at the foot of this sheet stops them all): the hanger
   straight down, the walker resting on the tip of the cap, both looking
   ahead. */
@media (prefers-reduced-motion: reduce){
  .themepill .walkway{transform:rotate(90deg);}
}
/* WCAG 2.2.2: nothing on the page may move for more than five seconds on its
   own. So every spider animation starts paused and runs only while the pill
   is pointed at or focused. site.js takes over from these two rules where it
   runs: once the hero board's own animation has finished, it plays one
   4.6 s opening (one swing, the walker's trip round the cap, a glance each). When the pointer or focus leaves, they carry on for
   2 s and then stop only in a pose they could hold: the walker standing
   between scurries (the --mm-still stretches below), the hanger straight,
   a glance finished. */
.themepill .hanger,.themepill .walkway,.themepill .walker,.walker .leg,.themepill .eyes{animation-play-state:paused;}
.themepill:hover :is(.hanger,.walkway,.walker,.leg,.eyes),
.themepill:focus-visible :is(.hanger,.walkway,.walker,.leg,.eyes){animation-play-state:running;}
/* On a phone the hero stacks the board right under the pill, and the
   hanger's full thread lowered it onto the board's backdrop. Below 600px
   the thread is shortened by 120 of its 220 units (about 20 px) and the
   spider lifted with it; both are drawing units, the viewBox of .hanger. */
@media (max-width: 600px){
  .hanger .thread{transform-origin:0 -290px;transform:scaleY(.45);}
  .hanger .dangle{transform:translateY(-120px);}
}
#halloween:focus{outline:none;}
#halloween:focus-visible{outline:3px solid var(--blue);outline-offset:6px;}

/* ---------- CTA: the composite pill ---------- */
/* silhouette = the in-game composite pill (ui_theme.gd make_button_with_glyph): mirrored-asymmetric
   corners, top-left + bottom-right soft (0.34 x height, 19px at 56px), top-right + bottom-left sharp (3px);
   the glyph cell repeats the same corners so its 2px ink seam arcs at the bottom.
   Shadow and press motion are the page's own hard-offset design, kept as is. */
.cta{--cta-soft:19px;--cta-sharp:3px;
     /* padding:0 and font:inherit date from the day this was a <button>
        (2026-09-11 to 2026-09-12): a button carries a user-agent padding of
        1px 6px, and that inset left a band of paper between the mustard cell
        and the ink border. Harmless on the link, and kept so the two stay
        the same if it ever changes back. */
     padding:0;font:inherit;
     display:inline-flex;align-items:stretch;min-height:56px;border:2px solid var(--ink);
     border-radius:var(--cta-soft) var(--cta-sharp) var(--cta-soft) var(--cta-sharp);
     background:var(--paper-light);color:var(--ink);text-decoration:none;overflow:hidden;
     box-shadow:3px 3px 0 var(--ink);transition:transform .12s ease, box-shadow .12s ease, background-color .12s;}
/* No radius of its own: .cta is overflow:hidden, so the tab is clipped to the
   button's corners already. Rounding it a second time - by two pixels less -
   left a sliver of paper between the mustard and the ink border down the left
   side, which is what made the cell look unfilled. */
.cta__tab{display:grid;place-items:center;width:56px;flex:none;background:var(--mustard);
     border-right:2px solid var(--ink);border-radius:0;}
/* The Steam mark sits in a filled ink disc, the mark itself knocked out in the
   tab's own mustard. Drawn straight onto the <svg> - background and radius on
   it, content-box padding for the ring - so it needs no extra element. */
.cta__tab svg{width:24px;height:24px;display:block;box-sizing:content-box;padding:3px;
  border-radius:50%;background:var(--ink);color:var(--mustard);}
.cta:hover .cta__tab svg{color:var(--mustard-deep);}
/* Everywhere else the mark is ink, as it was when the symbol carried the fill. */
.chip--steam svg, .card svg use[href="#steam"]{color:var(--ink);}
.cta__body{display:flex;flex-direction:column;justify-content:center;gap:2px;padding:.55rem 1.4rem .55rem 1rem;}
.cta__body b{font:700 1.0625rem/1.1 var(--sans);white-space:nowrap;}
.cta__body small{font:500 .75rem/1.2 var(--sans);color:var(--ink-faint);}
/* The "Coming Soon" stamp that hung off both Wishlist buttons from 2026-09-11
   to 2026-09-12, and the disabled state under it, are gone: the store opened.
   DESIGN.md keeps the record. */
.cta:hover{transform:translate(-1px,-1px);box-shadow:4px 4px 0 var(--ink);background:var(--paper-hi);}
.cta:hover .cta__tab{background:var(--mustard-deep);}
.cta:active{transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink);}
.cta:focus-visible{outline:3px solid var(--blue);outline-offset:3px;}

/* ---------- card ---------- */
.card{background:var(--paper-light);border:2px solid var(--ink);border-radius:14px;
      box-shadow:4px 4px 0 var(--ink);padding:clamp(1rem, 2.2vw, 1.5rem);position:relative;}
.card h3{margin:.9rem 0 .35rem;}
.card p{margin:0;color:var(--ink);}
.card .ill{height:clamp(170px, 18vw, 220px);}
.card .ill svg{width:100%;height:100%;display:block;}
.card--palette{padding:1rem;text-align:center;}
.card--palette svg{width:100%;max-width:140px;height:auto;margin-inline:auto;display:block;}
.card--palette h3{font-size:1rem;margin:.5rem 0 .2rem;}
.card--palette p{font-size:.8125rem;color:var(--ink-faint);}

/* ---------- stamp (used exactly three times) ---------- */
.stamp{position:absolute;display:inline-block;padding:.42em .7em;border:2.5px solid currentColor;border-radius:6px;
       box-shadow:inset 0 0 0 2px var(--paper), inset 0 0 0 3.5px currentColor;
       color:#9E2D24;/* 4.7:1 on paper at the stamp's .85 opacity (WCAG 1.4.3); the game red #C8463B only reached 3.3:1 */background:transparent;font:800 .6875rem/1 var(--sans);
       letter-spacing:.14em;text-transform:uppercase;white-space:nowrap;text-align:center;
       transform:rotate(var(--rot,-8deg));opacity:.85;mix-blend-mode:multiply;pointer-events:none;}
.stamp small{display:block;font-size:.5625rem;letter-spacing:.1em;margin-top:.3em;}

/* ---------- hero ---------- */
.hero{padding-top:clamp(1.25rem, 4vw, 2.5rem);}
.hero .wrap{display:grid;grid-template-columns:minmax(0,1fr);text-align:center;}
.presenter{display:flex;align-items:center;justify-content:center;gap:.6rem;margin:0 0 1.25rem;font:700 .8125rem/1 var(--sans);text-transform:uppercase;letter-spacing:.1em;color:var(--ink-faint);}
.presenter b{font-weight:800;color:var(--ink);}
.presenter img{width:22px;height:21px;display:block;}
.presenter .flip{transform:scaleX(-1);}
.cta-row{display:flex;flex-direction:column;align-items:center;gap:1rem;}
.board-wrap{position:relative;isolation:isolate;width:min(100%, 420px);margin:2rem auto 0;}
.board-wrap::before{content:"";position:absolute;left:50%;top:50%;width:min(112%, 100% + 2*var(--gutter) - 4px);aspect-ratio:1.18;transform:translate(-50%,-50%);border-radius:50%;background:var(--paper-hi);z-index:-1;}
.board{display:block;width:100%;height:auto;max-width:520px;}
.board .drop{transform-box:fill-box;animation:drop .7s cubic-bezier(.3,0,.2,1) .35s both;}
.board .flash{opacity:0;animation:flash .5s ease 1.05s both;}
/* the spotlight: once the winning cylinder lands, everything that did not win steps back and the four get their white rim (same as in the game) */
.board .lose{animation:dim .45s ease 1.9s both;}
@keyframes dim{to{opacity:.45}}
.stamp--hero{left:2%;bottom:6%;animation:thump .25s ease-out 1.15s both;}
@keyframes drop{0%{transform:translateY(-70px);opacity:0} 15%{opacity:1} 80%{transform:translateY(3px)} 100%{transform:translateY(0)}}
@keyframes flash{from{opacity:0} to{opacity:1}}
@keyframes thump{from{transform:rotate(var(--rot)) scale(1.25);opacity:0} to{transform:rotate(var(--rot)) scale(1);opacity:.85}}

/* The release stamp (2026-10-27, the developer's call): a big rubber stamp
   pressed across the board, stamp red on paper with a double rule, and a
   link to the store page (mamills-stamp). */
.releasestamp{
  position:absolute; z-index:5; display:block; text-align:center; text-decoration:none; white-space:nowrap;
  padding:.5em .9em .55em; border:4px solid currentColor; border-radius:10px;
  box-shadow:inset 0 0 0 3px var(--paper-light), inset 0 0 0 5px currentColor, 0 3px 10px rgba(26,26,31,.18);
  background:var(--paper-light); color:#9E2D24; transform:rotate(-9deg);
  font-family:var(--sans); text-transform:uppercase;
}
.releasestamp{right:2%; top:8%;}
@media (max-width:599px){.releasestamp{font-size:.8rem;}}
@media (max-width:399px){.releasestamp{font-size:.68rem;right:8px;}}
.releasestamp b{display:block; font-size:2.1em; line-height:1; font-weight:900; letter-spacing:.08em;}
.releasestamp small{display:block; margin-top:.3em; font-size:.8em; font-weight:800; letter-spacing:.06em; text-decoration:underline; text-underline-offset:2px;}
.releasestamp:focus-visible{outline:3px solid var(--ink); outline-offset:4px;}
/* Pressed on once, a beat after the page settles; it leans in under the
   pointer. Neither for a visitor who asks for reduced motion. */
.releasestamp{animation:stamp-thump .3s ease-out .6s both; transition:transform .15s ease;}
@keyframes stamp-thump{from{transform:rotate(-9deg) scale(1.15); opacity:0} 70%{transform:rotate(-9deg) scale(.97); opacity:1} to{transform:rotate(-9deg) scale(1); opacity:1}}
@media (hover: hover){ .releasestamp:hover{transform:rotate(-6deg) scale(1.04);} }
@media (prefers-reduced-motion: reduce){ .releasestamp{animation:none; transition:none;} }

/* ---------- sections ---------- */
section{padding-top:var(--section);}
.grid-3{display:grid;grid-template-columns:1fr;gap:1.25rem;}
.grid-4{display:grid;grid-template-columns:repeat(2,1fr);gap:1rem;}

/* screenshots */
/* phones: the four screenshots stack full-width so nothing hides off-screen; from 640px a 2x2 grid */
.shots{display:grid;grid-template-columns:1fr;gap:1.25rem;}
.shots figure{margin:0;min-width:0;}
.shots .frame{border:2px solid var(--ink);border-radius:10px;box-shadow:4px 4px 0 var(--ink);background:var(--paper);padding:0;}
.shots img{display:block;width:100%;height:auto;aspect-ratio:16/9;object-fit:cover;border-radius:8px;}
.shots figcaption{margin-top:.6rem;}

/* the four accessibility features that matter most: highlighted tiles on the paper-hi ground */
.keys{display:grid;grid-template-columns:1fr;gap:.9rem;list-style:none;margin:1.75rem 0 2rem;padding:0;}
.key{display:grid;grid-template-columns:44px 1fr;grid-template-rows:auto auto;column-gap:.85rem;row-gap:.2rem;align-items:start;
     padding:.95rem 1rem 1rem;border:2px solid var(--ink);border-radius:12px;background:var(--paper-hi);box-shadow:3px 3px 0 var(--ink);}
.key__icon{grid-row:1 / span 2;width:44px;height:44px;display:block;}
.key b{font:700 1.0625rem/1.2 var(--serif);color:var(--ink);}
.key span{font-size:.875rem;line-height:1.45;color:var(--ink);}
/* palettes + facts */
.card--palette .stamp{top:-.55rem;right:-.6rem;}
.access{display:grid;grid-template-columns:1fr;gap:1.25rem 2rem;margin:2rem 0 0;}
.access__group h3{margin:0 0 .5rem;font:700 1.0625rem/1.2 var(--serif);}
.access__group ul{list-style:none;margin:0;padding:0;}
/* list marks: a mini cylinder per line — the page's own #cyl symbol (to the game's proportions) in the group's piece colour */
.access__group li{position:relative;padding-left:1.75rem;margin:0 0 .55rem;font-size:.9375rem;line-height:1.45;}
.li-mark{position:absolute;left:0;top:-.08em;width:17px;height:24px;display:block;}
/* 02: the four modes are cards like the how-it-plays steps */
.modes{display:grid;grid-template-columns:1fr;gap:1rem;}
.mode{padding:1.1rem 1.15rem 1.25rem;display:flex;flex-direction:column;}
/* the mode picture: a framed little stage on the warm paper so the platform reads at a glance */
.mode > .mode__pic,.puzzles .mode__pic{display:block;width:100%;max-width:200px;height:auto;aspect-ratio:160/96;margin:0 0 .8rem;padding:.35rem .4rem 0;background:var(--paper-hi);border:1.5px solid var(--ink);border-radius:10px;}
.mode .plat{margin:0 0 .35rem;}
.chip--plat{display:inline-flex;align-items:center;gap:.35em;padding:.22rem .6rem;border:1.5px solid var(--ink);border-radius:999px;background:var(--mustard);font:800 .6875rem/1.2 var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--ink);}
.chip--plat svg{width:15px;height:15px;display:block;}
.mode .tag{margin:0 0 .3rem;font:800 .6875rem/1.2 var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint);}
.mode h3{margin:0 0 .55rem;font-size:1.125rem;}
.mode li{font-size:.9rem;}
.mode li:last-child{margin-bottom:0;}
/* The extra after the four ways to play (2026-09-29): Puzzles, one wide card
   in the mode cards' style, the words beside the real puzzle frame. Stacked
   on a phone; side by side from 900px, where the four cards are a row. */
.puzzles{margin-top:clamp(1.25rem,3vw,2rem);display:grid;gap:1.25rem;align-items:center;}
.puzzles__text{min-width:0;}
.puzzles__shot{margin:0;min-width:0;}
.puzzles__shot img{display:block;width:100%;height:auto;aspect-ratio:16/9;border:1.5px solid var(--ink);border-radius:10px;}
.puzzles__shot figcaption{margin-top:.5rem;font-size:.875rem;color:var(--ink-faint);}
/* 03: a chip names each screenshot; captions lead with a bold beat */
.shots figure{position:relative;}
/* the screenshot labels sit in the still's empty bottom-left corner (the board is centred, the HUD hugs the top corners),
   on mustard with the page's hard offset shadow so they read at a glance. Each chip lives inside its .frame and is
   measured from the picture's own bottom edge. Until 2026-09-19 it sat in the figure and was lifted by a guess at
   one caption line (bottom: .75rem + 2.4em), so a caption that wrapped to two lines (Halloween, Hint on) pushed its
   chip higher up the picture than the others. */
.shots .frame{position:relative;}
.shots .chip{position:absolute;left:1rem;bottom:.75rem;z-index:1;background:var(--mustard);color:var(--ink);border:2px solid var(--ink);border-radius:8px;
     padding:.38rem .8rem;font:800 .8125rem/1.2 var(--sans);letter-spacing:.1em;text-transform:uppercase;box-shadow:3px 3px 0 var(--ink);}
.shots figcaption b{color:var(--ink);font-weight:700;}
/* 04: the feature groups sit on one outlined sheet under the palette cards */
.sheet{background:var(--paper-light);border:2px solid var(--ink);border-radius:14px;box-shadow:4px 4px 0 var(--ink);padding:1.15rem 1.3rem 1.25rem;margin-top:1.25rem;}
.sheet__eyebrow{margin:0 0 .9rem;font:800 .6875rem/1.2 var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint);}
.sheet .access{margin:0;gap:1.1rem 0;}
.sheet .access__note{border-top:1.5px solid rgba(26,26,31,.14);padding-top:.9rem;margin-top:1.1rem;max-width:none;}
.access__note{margin:1.25rem 0 0;max-width:62ch;font-size:.9375rem;line-height:1.5;color:var(--ink-faint);}

/* stats */
.stats{display:grid;grid-template-columns:repeat(2,1fr);gap:1.5rem 1rem;margin:0;padding-block:.5rem;}
/* column-reverse packs content at the BOTTOM of a stretched grid cell, so a one-line label pushed its
   number lower than its neighbours; flex-end is the top here and keeps every number and rule on one line */
.stat{display:flex;flex-direction:column-reverse;justify-content:flex-end;margin:0;}
.stat dd{margin:0;font:700 clamp(2rem, 1.5rem + 2vw, 3rem)/1 var(--serif);font-variant-numeric:tabular-nums;}
.stat dd::after{content:"";display:block;width:2.5rem;height:4px;margin:.55rem 0 .6rem;background:var(--rule,var(--ink));}
.stat dt{font:500 .875rem/1.3 var(--sans);color:var(--ink-faint);}

/* 07 Along the way (2026-10-26): the Steam Next Fest share image, kept as
   history. It wears the frame it had in the hero during the fest (the
   developer's call, the same day): the ink border and the hard ink shadow,
   black and sleek, but not the lift and press, because it is not a link any
   more. A cream mat, tried first, melted into the image's own cream half and
   into the page. Side by side with its words from 900px, stacked and
   centred below. */
.history{display:grid;gap:1.5rem;justify-items:center;text-align:center;}
.history__shot{margin:0;width:min(100% - 8px, 340px);border:2px solid var(--ink);border-radius:8px;
  overflow:hidden;box-shadow:6px 6px 0 var(--ink);}
.history__shot picture, .history__shot img{display:block;width:100%;height:auto;}
.history__text h2{text-wrap:balance;}
/* The dedication's heart at the end of the thank-you, emoji-sized: no
   taller than the letters beside it. */
.history__text .heart{width:.85em;height:.78em;vertical-align:-.06em;margin-left:.05em;}
.history__text p:last-child{max-width:46ch;margin:0 auto;}
.history__text .eyebrow{justify-content:center;}
@media (min-width:900px){
  .history{grid-template-columns:340px minmax(0, 1fr);align-items:center;justify-items:start;text-align:left;gap:2.5rem;}
  .history__text p:last-child{margin:0;}
  .history__text .eyebrow{justify-content:flex-start;}
}

/* dedication */
#mami .wrap{max-width:52ch;text-align:center;padding-inline:var(--gutter);}
.empty-board{display:block;width:clamp(150px, 22vw, 210px);height:auto;margin:0 auto 1.5rem;}
.ded-title{font-size:clamp(1.5rem, 1.2rem + 1.2vw, 2rem);margin:0 0 .6rem;}
.dedication{font:400 clamp(1.0625rem, 1rem + .4vw, 1.25rem)/1.6 var(--serif);max-width:52ch;margin:0 auto .75rem;}
.dedication .heart{width:1.05em;height:1em;vertical-align:-.12em;margin-left:.1em;}
.sig{margin:0;}

/* ---------- footer ---------- */
footer{padding-top:var(--section);padding-bottom:2rem;}
.signoff{position:relative;display:grid;grid-template-columns:1fr;gap:1.25rem;justify-items:center;text-align:center;}
.signoff .crest{width:96px;height:auto;display:block;}
.signoff .eyebrow{justify-content:center;}
.signoff h2{margin-bottom:.75rem;}
.signoff p{max-width:60ch;}
/* 2.5rem, not 1.25: set on 2026-09-11 to give the "Coming Soon" stamp its own
   row above the button. The stamp went on 2026-09-12; the air stays, since
   the mail line and the button read better apart. */
.signoff .contact{margin:0 0 2.5rem;}
/* inline mail link: padding grows the tap target to the WCAG 2.5.8 24px minimum without moving the line */
.signoff .contact a{padding:.3rem 0;}
.signoff .cta-row{align-items:center;}
.stamp--filed{top:-.5rem;right:0;}
.foot{margin-top:2.5rem;padding-top:1rem;border-top:1px solid rgba(26,26,31,.18);border-top-color:color-mix(in srgb, var(--ink) 18%, transparent);
      display:flex;flex-direction:column-reverse;align-items:center;gap:.75rem 1.5rem;}
.links{display:flex;flex-wrap:wrap;justify-content:center;gap:0 .5rem;margin:0;padding:0;list-style:none;}
.links a{display:inline-block;padding:.5rem .25rem;font:400 .8125rem/1.4 var(--sans);color:var(--ink);text-decoration:none;}
.links a:hover{text-decoration:underline;text-decoration-color:var(--mustard-deep);text-decoration-thickness:2px;text-underline-offset:3px;}
.copyright{margin:0;padding:.5rem 0;}

/* The trailer dialog lived here from 2026-09-11 to 2026-09-24: the videos were
   behind links that opened a modal. They play in the page now (see the players
   above), so the dialog, its backdrop and its .dialog__* rules are gone, and so
   is assets/mamills.js - the page's own script - because assets/site.js already
   carries the player behaviour. DESIGN.md keeps the record. */
.contact a,.dedication a{text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;}
/* Windows High Contrast / forced colours: keep the piece colours on the drawn marks and cylinders,
   let everything else take the system palette (borders and outlines survive on their own) */
@media (forced-colors: active){
  .li-mark,.key__icon,.cta__tab,.chip,.stamp{forced-color-adjust:none;}
  .themepill{border:1px solid ButtonBorder;background:ButtonFace;color:ButtonText;box-shadow:none;}
  .players .vplay{border:1px solid ButtonBorder;background:ButtonFace;}
  .players .vlabel span{background-image:none;}
  /* The triangle is a border trick: forced colours paint its transparent top
     and bottom borders too, which turns the play mark into a square (a stop
     icon). Opting the element out keeps them transparent. The landing page's
     knob carries the same fix. */
  .players .vplay .vknob{forced-color-adjust:none;border-top-color:transparent;border-bottom-color:transparent;border-left-color:ButtonText;}
  .cta,.card,.sheet,.shots .frame,.btn,.history__shot{box-shadow:none;}
}
/* The way back to the studio page. This page is reached from the hero of the
   landing page, so a reader arriving here from a search result has nowhere to
   go without it. Its own row above the presenter line: centred with the rest
   of the hero on a phone, left-aligned with the text column above 900px. */
.backrow{display:flex;justify-content:center;margin:0 0 1.1rem;}
.btn--back{padding:.5rem 1.1rem;font-size:.875rem;}
.btn{display:inline-block;min-height:44px;padding:.6rem 1.2rem;border:2px solid var(--ink);border-radius:999px;background:var(--paper-light);color:var(--ink);box-shadow:3px 3px 0 var(--ink);font:700 .9375rem/1 var(--sans);cursor:pointer;}
.btn:hover{background:var(--paper-hi);}
.btn:active{transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink);}

/* ---------- breakpoints: sm = 640, md = 900 ---------- */
@media (max-width:300px){
  h1{font-size:3rem;}
}
@media (min-width:640px){
  .grid-3{grid-template-columns:repeat(3,1fr);gap:clamp(1rem,2.5vw,1.75rem);}
  .shots{grid-template-columns:repeat(2,1fr);gap:clamp(1rem,2.5vw,1.75rem);}
  .access{grid-template-columns:repeat(3,1fr);}
  .keys{grid-template-columns:repeat(2,1fr);}
  .modes{grid-template-columns:repeat(2,1fr);}
  .stats{grid-template-columns:repeat(3,1fr);}
  .signoff{grid-template-columns:auto 1fr;gap:2rem;align-items:start;justify-items:start;text-align:left;}
  .signoff .crest{width:128px;}
  .signoff .eyebrow{justify-content:flex-start;}
  .signoff .cta-row{align-items:flex-start;}
  .foot{flex-direction:row;justify-content:space-between;flex-wrap:wrap;}
  .links{justify-content:flex-end;}
  .copyright{padding:0;}
}
@media (min-width:900px){
  .modes{grid-template-columns:repeat(4,1fr);}
  .puzzles{grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:clamp(1.5rem,3vw,2.5rem);}
  .sheet .access{gap:0 2rem;}
  .sheet .access__group + .access__group{border-left:1.5px solid rgba(26,26,31,.14);padding-left:2rem;}
  .hero .wrap{grid-template-columns:minmax(0,11fr) minmax(0,9fr);gap:clamp(2rem,5vw,4rem);align-items:center;text-align:left;}
  .backrow{justify-content:flex-start;}
  .presenter{justify-content:flex-start;}
  .pitch{margin-inline:0;}
  .platforms{margin-inline:0;}
  .cta-row{align-items:flex-start;}
  .board-wrap{width:100%;margin:0;}
  .grid-4{grid-template-columns:repeat(4,1fr);}
  .keys{grid-template-columns:repeat(4,1fr);}
  .stats{grid-template-columns:repeat(6,1fr);}
}

/* ---------- piece colours: exact cap / edge / shade from the game's UiTheme.THEME_PALETTES ----------
   One class per colour, each carrying all four values. The draft wrote --c in a
   style="" attribute on all 64 cylinders and keyed the other three off it with
   a use[style*="#C8463B"] selector; a style attribute is inline style, which
   this site's CSP drops, so the colour has to come from the sheet. */
.c-red{--c:#C8463B;--cap:#E27265;--edge:#5A1812;--shade:#9E2914;}
.c-blue{--c:#4A7CB5;--cap:#7BA2D0;--edge:#152848;--shade:#1A4D75;}
.c-violet{--c:#8C68D6;--cap:#A893E0;--edge:#3A1E78;--shade:#5C298C;}
.c-leaf{--c:#65B860;--cap:#8FCF8B;--edge:#1F5A1C;--shade:#386B3D;}
.c-cb-blue{--c:#0072B2;--cap:#3499D4;--edge:#003D63;--shade:#004C78;}
.c-cb-orange{--c:#E69F00;--cap:#F2BD3D;--edge:#7A5400;--shade:#9A6900;}
.c-gold{--c:#D4AF37;--cap:#E8C765;--edge:#7A5C1B;--shade:#9A791B;}
.c-mustard{--c:#F7D44D;}
.c-silver{--c:#ABAEB4;--cap:#D8D8DC;--edge:#5C5C66;--shade:#6B7079;}

/* ---------- motion off ---------- */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{animation:none !important;transition:none !important;}
  .board .drop, .stamp{opacity:.85;transform:rotate(var(--rot,0deg));}
  .board .drop{opacity:1;transform:none;}
  .board .flash{opacity:1;}
  .board .lose{opacity:.45;}
}
@media print{
  .stamp,.cta,.skip,.players .vplay,.themepill,.releasestamp{display:none !important;}
}

/* ---- the values that used to ride in a style="" attribute ---------------
   `style-src 'self'` carries no 'unsafe-inline', so a style attribute is
   dropped by the browser exactly like an inline <style> block is: the board
   arrived with no piece colours at all. Each attribute wrote one custom
   property from a closed set, so each set is a class here instead. The piece
   classes above carry all four values at once, which is what the attribute
   selector behind them was doing by hand. */
.mk-red{--mark:var(--red);}          .mk-blue{--mark:var(--blue);}
.mk-violet{--mark:var(--violet);}    .mk-leaf{--mark:var(--leaf);}
.mk-gold{--mark:var(--gold);}        .mk-cb-blue{--mark:var(--cb-blue);}
.mk-cb-orange{--mark:var(--cb-orange);}
.rl-red{--rule:var(--red);}          .rl-blue{--rule:var(--blue);}
.rl-violet{--rule:var(--violet);}    .rl-leaf{--rule:var(--leaf);}
.rl-gold{--rule:var(--gold);}        .rl-cb-orange{--rule:var(--cb-orange);}
/* The three stamps, each pressed on at its own angle. */
.rot-back{--rot:-12deg;}  .rot-fwd{--rot:8deg;}  .rot-tilt{--rot:-6deg;}
