/* ============================================================================
   give.css — the donate page's midway.

   ⛔ GENERATED. This file is `python tools/build_donate.py` run over the
   partials in parts/donate/. Editing it directly works exactly until the next
   build, which is the worst kind of works. Edit the partial.
   ============================================================================ */


/* ==== part_moneyhead.css ==== */
/* ── THE FACE ─────────────────────────────────────────────────────────────
   The owner released this header from the logo's own face ("The font of BOTH
   doesn't need to be the same as Isometricland - or either"), and that is what
   finally lets the joke land. Rye is an INLINE type: every glyph is several
   nested contours, so the brass edge stroke draws the ornament as well as the
   silhouette and floods the letter with gold — the cash inside was fighting the
   typeface for room. Alfa Slab One is a single-contour fat slab: enormous
   counters, no inline, nothing competing with the fill. More money, less
   outline, exactly as asked.

   231.5px is not a taste: "DONATE" measures 4.837 em in this face, so 231.5
   makes its natural width exactly the 1120 the layout draws it at — the
   textLength correction is therefore zero and no letter is stretched. */
@font-face{
  font-family:"AlfaSlabOne";
  src:url("../assets/fonts/AlfaSlabOne.ttf") format("truetype");
  font-weight:400; font-style:normal; font-display:swap;
}

/* ============================================================================
   COMPONENT 1 — THE MONEY HEADER  ("mh-")
   The word DONATE, set enormous in the park's own 1880s wood-type face, with a
   heavy gold keyline and the inside of every letter packed with cash. The joke
   is the whole point: the word is literally built out of money.

   WHY THERE IS ALMOST NO TYPE STYLING IN HERE
   The word is drawn SEVEN times in one <svg> — a cast shadow, an emboss, the
   dark keyline, the clipPath that cuts the money to the letters, an inner
   shadow, the brass edge, and the travelling glint — and every one of those
   copies must resolve to the SAME glyph outlines or the money slides out from
   under the outline. One of them lives inside <clipPath>, and a stylesheet is
   not reliably applied to clipPath content across engines. So the type (family,
   size, textLength, lengthAdjust) is set with presentation ATTRIBUTES in the
   markup, where every copy is guaranteed to see the same values. To resize the
   word, edit the attributes in part_moneyhead.html — not this file. Everything
   below is layout, the glint, and the optional kicker.
   ============================================================================ */

/* The header hangs above the hero like a painted board over a booth.
   ⚠ IT CARRIES NO PADDING OF ITS OWN. Its paste point is inside
   <section class="pay__hero pay__hero--money">, and pay.css already gives that
   54px above and 30px below. The board previously added another 38/6 on top,
   which is 92px of dead grass over the fold at desktop and the reason this rule
   is now empty of spacing. If this board is ever pasted somewhere that is NOT
   .pay__hero, give the wrapper its own room there — do not put it back here. */
.mh-head{
  /* ⚠ POSITIONED, BECAUSE THE TURNING LETTERS HANG OFF IT. .mh-flips is
     `position:absolute; inset:0` and needs this box to be its containing
     block; without it the tiles would be laid out against the viewport and
     would sit over whatever happened to be scrolled into view. */
  position:relative;
  margin:0 auto;
  padding:0;
  text-align:center;
}

/* ⭐⭐⭐ EACH LETTER IS ITS OWN COMPOSITED LAYER, AND THIS ONE LINE IS THE
   DIFFERENCE BETWEEN A HOVER COSTING 150ms AND COSTING NOTHING.

   Hiding a letter used to mean re-rasterising the whole word: seven <text>
   layers, 235 <use>s of banknotes and coins, two Gaussian blurs. Measured with
   tools/paint_cost.py — the median duration of the frame that CONTAINS the
   mutation, ten trials, five idle frames between so no trial inherits its
   neighbour's paint:

       a no-op                                          16.7 ms
       one hover, as it shipped 2026-08-21             150.1 ms
       opacity flip, letter-sized group, plain          50.0 ms  (p75 200)
       opacity flip, letter-sized group, will-change    33.3 ms  (p95 33.4)

   Promoted, the flip is one frame and the spread collapses. Unpromoted it is
   NOT enough on its own — Chrome still repaints past the group's own bounds —
   which is why the promotion is declared here rather than left to be inferred.

   ⚠ TWELVE LAYERS, AND THAT IS THE PRICE. Six letters × two passes, each about
   a quarter-megabyte at 1x. It is paid for a decorative header, deliberately,
   because the alternative is a 150ms hitch every time the cursor crosses a
   letter — and the cursor crosses six of them on the way anywhere. */
.mh-lw{ will-change:opacity; }
/* The hidden half of a turn. `opacity` and not `visibility` or `display`:
   only opacity is animatable on the compositor, and only opacity leaves the
   layer rasterised and ready for the letter to come back. */
.mh-lw.is-away{ opacity:0; }

.mh-head{
  /* ⚠ POSITIONED, BECAUSE THE TURNING LETTERS HANG OFF IT. .mh-flips is
     `position:absolute; inset:0` and needs this box to be its containing
     block; without it the tiles would be laid out against the viewport and
     would sit over whatever happened to be scrolled into view. */
  position:relative;
  margin:0 auto;
  padding:0;
  text-align:center;
}




/* The word is the page's <h1> — donate.html has no other rank-1 heading, and
   the board IS the page's name. That means skeuo.css's `h1,h2,h3` type rule
   lands on it (font-size:clamp(34px,5.4vw,60px), margin:.2em 0 .4em,
   line-height:1.12, a text-shadow and --title-gold), all of which would put a
   phantom text box and a stray margin around an element whose only child is an
   svg. Reset it to a bare box. font-size:0 is safe because nothing inside sizes
   in em — the svg is width:100%/height:auto and everything in it is user
   units. */
.mh-h1{
  margin:0;
  padding:0;
  font-size:0;
  line-height:0;
  text-shadow:none;
}

/* width:100% + height:auto is the whole reason this is one inline <svg> with a
   viewBox: at 1120px the word is 40px of brass keyline, at 375px it is 12px of
   the same keyline, and neither is a separate asset. */
.mh-svg{
  display:block;
  width:100%;
  height:auto;
  /* the sign's own drop shadow is drawn INSIDE the svg (a blurred copy of the
     word), not with a CSS filter here — a CSS filter on this element would
     wrap the animating glint mask in a filtered layer and re-run the blur on
     every frame of the sweep. */
  overflow:visible;
}

/* ══ THE TURNING LETTERS ════════════════════════════════════════════════════
   Owner, 2026-08-20: "curser over a glyf causes it to spin around into a gold
   dollar sign, curse away spins back to normal." — and 2026-08-21, after the
   first fix: "The 'donate' flips cause lag."

   ⛔⛔⛔ THE TURN IS A COMPOSITED HTML TILE, AND IT HAS TO BE. A CSS 3-D
   `rotateY` on an HTML box is composited: the face rasterises once and the
   compositor turns the texture. An SVG group's transform is NOT — measured on a
   letter-sized group, writing `style.transform` costs a full extra frame WITH
   `will-change: transform` and without it alike. Turning the letter in place
   inside the board therefore re-rasterised about a thousand shapes of banknote
   on every frame, which is the lag he reported.

   ⭐⭐⭐ AND THE DECK IS A CHILD OF THE <h1> ON PURPOSE. A copy of a letter in a
   second <svg> landed about three pixels to the right — reproduced with a
   <use>, with a deep clone, and with every referenced filter, clip, gradient
   and symbol cloned in under rewritten ids, while every DOM check said the two
   agreed (same viewBox, rendered rects equal to a hundredth of a pixel, screen
   matrices equal to full float precision, getComputedTextLength equal to four
   decimals). The board's <svg> is inside an <h1>; an <h1> is `font-weight:
   bold`; that inherits into SVG text; AlfaSlabOne has ONE weight, so Chrome
   SYNTHESISES the bold and smears every glyph slightly wider. A copy anywhere
   else on the page inherits 400 and is drawn unsmeared.

   One inherited property, invisible to every geometry API, worth 20 960
   changed pixels. Putting the deck inside the heading makes it inherit whatever
   the board inherits — for this property and for whatever somebody adds next —
   and `--probe=headflip` compares the two computed weights so it can never
   drift back. */
.mh-flips{
  position:absolute; inset:0;
  pointer-events:none;
  /* ⭐ THE PERSPECTIVE IS ON THE DECK, NOT ON EACH TILE. With it on the tile,
     every letter turns about its own vanishing point and a hand sweeping the
     word produces six independent little worlds. On the deck they share one
     eye, so a letter near the edge foreshortens the way an edge letter should. */
  perspective:1100px;
  /* A letter reaches outside its own tile — the A's ink runs 26.9 units past
     its column — and its cast shadow reaches further still. Nothing may clip. */
  overflow:visible;
}

.mh-flip{
  position:absolute;
  transform-style:preserve-3d;
  /* ⚑ THE EASE IS ASYMMETRIC ON PURPOSE. A coin flicked with a thumb leaves
     fast and settles slow; a linear turn reads as a mechanism. */
  transition:transform .46s cubic-bezier(.22,.72,.24,1);
  will-change:transform;
}
.mh-flip.is-turned{ transform:rotateY(180deg); }

/* ⛔⛔⛔ A TILE AT REST IS HIDDEN, NOT ABSENT — AND THAT IS A PERFORMANCE
   DECISION, NOT A TIDINESS ONE. The six tiles are built once, up front, and
   parked here; hovering only shows one. Building them per hover meant a
   forced synchronous layout of the whole document (`void el.offsetWidth`,
   the only way to give a just-inserted element a value to transition FROM)
   on every crossing — 11–29ms each, the entire cost of the feature.

   `visibility`, not `opacity` and not `display`:
     · `display:none` skips style resolution for the subtree, so the tile
       would be brand-new again on the very frame it is shown and the reflow
       would be right back;
     · `opacity:0` keeps it PAINTING — 55 clipped banknotes and two blurs per
       letter, six times over, forever, for a picture nobody sees. */
.mh-flip--idle{ visibility:hidden; }

.mh-flip__face{
  position:absolute; inset:0;
  /* ⚠ overflow stays VISIBLE, and the tile holds ONE letter, so what spills
     past the box is that letter and nothing else. Any other value would also
     FLATTEN the preserve-3d above and turn the turn into a squeeze. */
  overflow:visible;
  /* Without this both faces paint at every angle and the letter reads as a
     double exposure through its whole turn — the commonest way this effect is
     got wrong, and invisible to every geometry assertion. */
  backface-visibility:hidden;
  -webkit-backface-visibility:hidden;
}
.mh-flip__face--back{ transform:rotateY(180deg); }

/* The face's own svg is the BOARD, at the board's size, shifted so that this
   tile's letter lands inside it. js/moneyhead.js writes left/top/width/height;
   nothing here may set them. */
.mh-flip__face > svg{ display:block; position:absolute; overflow:visible; }

/* ⛔⛔⛔ THE FAST FACE — A WINDOW ONTO ONE BAKED IMAGE OF THE WHOLE BOARD.
   js/moneyhead.js writes background-image/size/position from the SAME two
   offsets the <svg> twin above is placed by; nothing here may set them.

   The picture is identical because it IS the board, rastered once at the
   device's pixel ratio. What changes is what the compositor has to do with it:
   the live construction is a clip-path over 55 banknotes, two feGaussianBlurs
   and a mask, and a 3-D turn re-scales the layer continuously, so Chrome redraws
   that entire stack every frame — measured at 160 rendered frames across three
   sweeps of the word against 452 with no turning at all, and 417 with the
   filters, masks and clips stripped out. A background image is one texture.

   `background-repeat:no-repeat` is not cosmetic: the sheet is two board-heights
   tall and the back face samples the LOWER half, so a repeat would tile the
   word into the dollar sign's row. */
/* ⚠ THE CLIP GOES HERE, ON A CHILD — never on the face and never on the tile.
   `overflow` other than `visible` FLATTENS a preserve-3d context, so a crop one
   level up would turn the turn into a squeeze. The letter plus its cast shadow
   fits inside the tile by construction (letterBox adds SHADOW_PAD, which is
   wider than the shadow's offset plus its blur), so nothing of the letter is
   cropped — only the rest of the board. */
.mh-flip__win{ position:absolute; inset:0; overflow:hidden; }

.mh-flip__sheet{
  position:absolute; left:0; top:0;
  /* The sheet is never magnified — a rotateY only ever squashes it — so this is
     a downsample at worst, and the browser's default filtering is right for it. */
  transform-origin:0 0;
  /* An <img> is inline by default and would sit on a text baseline, which is a
     couple of pixels of offset that nothing in js/moneyhead.js asked for. */
  display:block;
  max-width:none;
}

@media (prefers-reduced-motion: reduce){
  /* Somebody who asked for less motion has not asked for a header that does
     nothing — they have asked for it not to SPIN. The letter still becomes a
     dollar under the cursor and still comes back; it simply arrives. */
  .mh-flip{ transition:none; }
}

/* ---- The glint ------------------------------------------------------------
   A band of light that crosses the brass, once, then waits. It is a masking
   rect inside the svg, so nothing here knows about letterforms — it just walks
   the mask from off-left to off-right in user units (transform-box defaults to
   view-box for SVG elements, so 1px == 1 user unit here).

   The long park at the start is not laziness: a continuous sweep on a sign this
   size is a repaint every frame forever, and a midway bulb catching the gold
   once every nine seconds is both cheaper and more like the real thing. */
@keyframes mh-glint{
  0%, 74%  { transform:translateX(0); }
  100%     { transform:translateX(1700px); }
}
.mh-shine{
  /* stated rather than inherited: transform-box only recently defaulted to
     view-box for SVG children, and on an engine that still assumes border-box
     a 1700px travel would be measured against the rect's own 290-unit width and
     the glint would fly off in the first frame. */
  transform-box:view-box;
  transform-origin:0 0;
  animation:mh-glint 9s linear infinite;
  /* deliberately NO will-change: promoting a <mask> child to its own layer has
     historically made masks drop out entirely, and the animation is short and
     infrequent enough that it buys nothing worth that risk. */
}

/* ---- The optional kicker --------------------------------------------------
   Small brass caps under the board, the same voice as .pay__eyebrow. Present so
   the integrator has it if a line is wanted; the markup ships commented out
   because the owner writes every word on this site.
   font-size is restated because .mh-kicker's parent .mh-head is fine, but if it
   is ever nested inside .mh-h1 (it should not be) the 0 would inherit. */
.mh-kicker{
  margin:10px 0 0;
  font-family:var(--font-display);
  font-weight:700;
  font-size:clamp(11px, 1.5vw, 14px);
  line-height:1.3;
  letter-spacing:.22em;
  text-transform:uppercase;
  color:var(--brass-light);
  text-shadow:0 1px 0 rgba(0,0,0,.8), 0 2px 6px rgba(0,0,0,.5);
}

/* ---- Phone ----------------------------------------------------------------
   Nothing shrinks and nothing is repositioned: the word already fills the
   column at any width because the svg scales, and there is not one fixed pixel
   width, absolute position or hard-coded canvas size in this component. At
   375px the column is 335px and the board renders 335 x 77 — the note frames
   and the portrait ovals were weighted for exactly that downscale (see the note
   over #mh-bill in the markup). All that changes here is the kicker's tracking,
   which at .22em pushes a short line to two on a narrow screen. */
@media (max-width:600px){
  .mh-kicker{ letter-spacing:.16em; }
}

/* ---- Reduced motion -------------------------------------------------------
   The glint does not disappear — a dead sign is worse than a still one. It
   parks with its brightest point over the middle of the word so the gold still
   has a catch of light, and never moves again.
   THE NUMBER IS DERIVED, NOT PICKED. The band is a rect at x=-380, 290 wide, so
   its bright core sits at its centre, x=-235. The word's ink runs 49 -> 1155
   (measured off the real Rye outlines), so its middle is 602, and the offset
   that lands the core there is 602-(-235) = 837. The previous 560 put the
   highlight at x=325 — over the O, not the middle, which is the sort of claim a
   comment can make and nothing checks. */
@media (prefers-reduced-motion: reduce){
  .mh-shine{
    animation:none;
    transform:translateX(837px);
  }
}


/* ==== part_mtmoney.css ==== */
/* ============================================================================
   MT. MONEY — the cannon and the hoard.  (component partial, prefix "mm-")

   A carnival midway game bolted onto the donate page: a vault full of gold, a
   brass cannon that follows your cursor, and a plunger marked FIRE. The whole
   thing is ringed in chasing bulbs, because that is what tells you at a glance
   that a thing on a midway is a GAME and not an advertisement.

   Everything the player actually looks at is painted in <canvas> by
   js/mtmoney.js. What lives here is the CABINET the canvas sits in — the frame,
   the bulbs, the plunger and the brass counter plate — because those are chrome
   and text, and chrome and text belong in the DOM where they can be read,
   focused, and enlarged.

   ⚑ THE BULBS ARE CSS-ANIMATED ON PURPOSE. They could have been drawn into the
   canvas and advanced in the rAF loop, and that would have cost the loop ~50
   more draws every frame for a thing that never changes shape. As opacity
   keyframes they run on the compositor, they keep chasing while the canvas loop
   is parked, and prefers-reduced-motion switches them off without the game
   knowing anything happened.
   ============================================================================ */

.mm-game{
  /* The bulbs are white, not brass — the owner asked for a white hue, and white
     is also the only bulb colour that stays a BULB against this much gold. */
  --mm-bulb-hot:#FFFFFF;
  --mm-bulb-warm:#FFF4DE;
  --mm-bulb-off:#8B8070;
  --mm-cycle:2.6s;      /* one lap of the chase */
  --mm-lag:-0.12s;      /* per-bulb offset; js/mtmoney.js overwrites this */
  margin-top:22px;
}

/* ── THE FRAME ────────────────────────────────────────────────────────────
   A painted board with a brass rim, the same object the game hangs a ride sign
   on. The bulbs are mounted just inside its edge, so the board reads as the
   thing they are screwed to rather than a box drawn around them — and since
   2026-08-18 the board carries a CARVED GILT CREST above the picture and a
   matching APRON below it (see the SVG in part_mtmoney.html for how the relief
   is built; this file only makes room for them).

   ⚑ THE ORNAMENT IS IN THE FLOW, NOT ABSOLUTELY POSITIONED. A crest pinned to
   the frame's top edge would have to be given a height, and its height is a
   function of the frame's WIDTH (one viewBox, uniformly scaled) — so the number
   would be right at one cabinet size and wrong at every other, and the picture
   would either be overlapped or float below a gap. In the flow the frame's own
   height is crest + stage + apron by construction, at every width, forever. */
.mm-frame{
  position:relative;
  padding:22px 24px 20px;
  border-radius:16px;
  /* A midway board is a PAINTED PLANK, and the grain is what says so — without
     it this is a rounded rectangle with a gradient and a ring of lights on it,
     which is a description of a div and not of an object. Four irregular
     stripes at 3% and 6% alpha, repeating on a 9px pitch: at that strength it
     never reads as stripes, it reads as wood under a lamp. */
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255,233,168,.10) 0%, rgba(255,233,168,0) 62%),
    repeating-linear-gradient(90deg,
      rgba(255,233,168,.030) 0 1px, rgba(0,0,0,0) 1px 3px,
      rgba(0,0,0,.060)       3px 4px, rgba(0,0,0,0) 4px 9px),
    linear-gradient(180deg,#3E2812 0%,#2A1A0B 46%,#1B1108 100%);
  box-shadow:
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-mid),
    0 0 0 4px var(--wood-line),
    0 0 34px rgba(255,244,222,.14),          /* the bulbs' spill onto the page */
    0 16px 34px rgba(0,0,0,.55),
    inset 0 1px 0 rgba(255,233,168,.16);
}

/* ── THE BULBS ────────────────────────────────────────────────────────────
   Positioned in px by the script, walking the perimeter clockwise from the
   top-left corner, so --i is a position along the run and the chase is just a
   delay proportional to it. Uniform pitch on all four sides is the reason this
   is measured rather than laid out with flex: a rail of `space-between` bulbs
   puts them 30px apart across the top and 52px apart down the side, and the eye
   catches that instantly on a border it is meant to read as one string. */
.mm-bulbs{
  position:absolute; inset:11px;
  pointer-events:none;
}
.mm-bulb{
  position:absolute;
  width:15px; height:15px; margin:-7.5px 0 0 -7.5px;   /* left/top are centres */
  border-radius:50%;
  /* the socket: a brass collar with the dull glass of an unlit bulb inside it */
  background:
    radial-gradient(circle at 42% 34%, #EFE7D6 0%, #B4A894 34%, var(--mm-bulb-off) 52%, rgba(0,0,0,0) 53%),
    linear-gradient(180deg,#F5C25A 0%,#C99230 44%,#5C4210 100%);
  box-shadow:0 1px 2px rgba(0,0,0,.7), inset 0 1px 0 rgba(255,247,221,.55);
}
/* The lit filament + its bloom, faded up and down by the chase. Only `opacity`
   is animated: a keyframed box-shadow re-rasterises the glow every frame on
   fifty elements at once, and this border is on screen the whole time. */
.mm-bulb::after{
  content:"";
  position:absolute; left:50%; top:50%;
  width:11px; height:11px; margin:-5.5px 0 0 -5.5px;
  border-radius:50%;
  background:radial-gradient(circle at 45% 38%, var(--mm-bulb-hot) 0%, var(--mm-bulb-warm) 46%, #FFE7BE 78%, rgba(255,231,190,0) 100%);
  box-shadow:
    0 0 9px 2px rgba(255,252,244,.90),
    0 0 22px 7px rgba(255,246,226,.50),
    0 0 44px 16px rgba(255,242,214,.22);
  opacity:.13;                                  /* the always-warm filament */
  animation:mm-chase var(--mm-cycle) linear infinite;
  animation-delay:calc(var(--i) * var(--mm-lag));
}
/* A short, hard crest with a soft tail — a chase reads as MOVEMENT only if the
   bright band is narrow. A slow sine over the whole cycle is a wave, not a
   chase, and a midway board does not do waves. */
@keyframes mm-chase{
  0%   { opacity:1; }
  8%   { opacity:.62; }
  19%  { opacity:.13; }
  100% { opacity:.13; }
}

/* ── THE CREST AND THE APRON ──────────────────────────────────────────────
   The two carved bands. `height:auto` off the intrinsic ratio is the whole
   sizing rule: the SVG has a viewBox and NO width/height attribute, so the
   browser gives it the box's width and the ratio gives it the height. Nothing
   here can transfer a minimum into the other axis — which is exactly the trap
   .mm-stage documents below, and the reason neither of these has a min-height.

   The small negative margins pull the carving down onto the picture's edge so
   the moulding OVERLAPS the frame line it caps. A cornice that stops one pixel
   short of what it is capping reads as a sticker sitting near a box. */
.mm-crest{
  display:block; width:100%; height:auto;
  margin:0 0 -6px;
  /* the carving throws its own shadow inside the SVG; this is the board
     catching a little of the bulbs' light under it */
  filter:drop-shadow(0 3px 4px rgba(0,0,0,.45));
}
.mm-apron{
  display:block; width:100%; height:auto;
  margin:-5px 0 0;
  filter:drop-shadow(0 3px 4px rgba(0,0,0,.45));
}

/* ── THE STAGE ────────────────────────────────────────────────────────────
   The canvas and everything overlaid on it.

   ⛔ IT IS A LETTERBOX NOW, NOT A WINDOW (owner, 2026-08-18: "the whole mt money
   minigame doesn't have to be so tall, it can be much shorter"). 16:9 gave a
   537px picture inside a 585px cabinet on a 1002px page; 3.6:1 gives 265px, and
   the crest and apron spend about a third of what that saves on ornament. The
   change is not only a size: a SHORT room is what makes the peel legible.
   js/mtmoney.js pins the summit to the ceiling at $10,000 and turns every dollar
   after that into sideways slump, so the room has to be short enough that a big
   total visibly runs out of it. In a tall stage the same code just draws a
   taller triangle and the joke never arrives.

   ⛔⛔⛔ THERE IS NO `min-height` HERE ANY MORE, AND THAT IS A LOAD-BEARING
   ABSENCE. `aspect-ratio` + a definite `min-height` does not merely make a box
   taller — the minimum is TRANSFERRED THROUGH THE RATIO INTO A MINIMUM WIDTH,
   and that minimum beats the containing block. `min-height:250px` at 16/9 is a
   444px floor under this element's width; the phone rule below it,
   `min-height:300px` at 4/3, is a 400px floor. Measured on a 375px viewport:
   .mm-frame 305px wide, .mm-stage 400px wide INSIDE it, documentElement
   scrollWidth 453 against clientWidth 375 — so the browser widened the layout
   viewport to 453 and every phone silently got a tablet. It is invisible on a
   desk, it throws no error, and `overflow:hidden` on the stage cannot help
   because the overflow is the stage itself.

   The min-heights were also DEAD in the range where they applied: above 700px
   the ratio already yields 340px of height at the narrowest width this rule
   ever sees, so 250 never bound. A constraint that never binds and silently
   sets a floor under a different axis is pure liability. Gone; the phone rule
   sizes with an explicit `height`, which transfers nothing. */
.mm-stage{
  position:relative;
  aspect-ratio:3.6/1;
  border-radius:11px;
  overflow:hidden;
  background:#150C04;
  cursor:crosshair;
  touch-action:manipulation;    /* a tap fires; no 300ms wait for a double-tap */
  box-shadow:
    inset 0 0 0 1px var(--wood-line),
    inset 0 0 0 3px var(--brass-dark),
    inset 0 6px 22px rgba(0,0,0,.8);
}
.mm-canvas{ display:block; width:100%; height:100%; }

/* ── THE COUNTER ──────────────────────────────────────────────────────────
   Bottom right, where the owner asked for it: a brass plate screwed to the
   inside of the vault. pointer-events:none so it never eats an aim or a shot —
   it is a readout, and the whole stage is a trigger. */
.mm-counter{
  position:absolute; right:14px; bottom:14px;
  margin:0;
  padding:9px 15px 8px;
  text-align:right;
  line-height:1.2;
  border-radius:10px;
  pointer-events:none;
  /* Four brass screws, one per corner — the plate is SCREWED to the inside of
     the vault, and a bevelled box with no fixings is a floating readout. The
     screw heads read from the same --brass-* pair as the rim rather than a
     hand-picked pair of hexes, so the plate cannot drift out of the cabinet's
     palette when the cabinet is retuned. */
  background:
    radial-gradient(circle at 7px 7px,
      var(--brass-light) 0 1.5px, var(--brass-dark) 1.5px 2.5px, rgba(0,0,0,0) 2.9px),
    radial-gradient(circle at calc(100% - 7px) 7px,
      var(--brass-light) 0 1.5px, var(--brass-dark) 1.5px 2.5px, rgba(0,0,0,0) 2.9px),
    radial-gradient(circle at 7px calc(100% - 7px),
      var(--brass-light) 0 1.5px, var(--brass-dark) 1.5px 2.5px, rgba(0,0,0,0) 2.9px),
    radial-gradient(circle at calc(100% - 7px) calc(100% - 7px),
      var(--brass-light) 0 1.5px, var(--brass-dark) 1.5px 2.5px, rgba(0,0,0,0) 2.9px),
    linear-gradient(180deg,#3E2812 0%,#1A1006 100%);
  box-shadow:
    0 0 0 1px var(--wood-line),
    0 0 0 2px var(--brass-mid),
    0 4px 12px rgba(0,0,0,.65),
    inset 0 1px 0 rgba(255,233,168,.22);
}
.mm-counter__l{
  display:block;
  font-family:var(--font-display); font-weight:700; font-size:10px;
  letter-spacing:.2em; text-transform:uppercase;
  color:var(--brass-light); text-shadow:0 1px 0 rgba(0,0,0,.8);
}
.mm-counter__n{
  display:block; margin-top:2px;
  font-family:"Rye", Georgia, serif;
  font-size:clamp(22px, 2.7vw, 33px); line-height:1;
  color:var(--label-hi);
  font-variant-numeric:tabular-nums;   /* the count-up must not shuffle sideways */
  text-shadow:0 2px 0 #2A1806, 0 0 18px rgba(255,208,106,.35);
  /* the kick moves this element and nothing else, so it gets its own layer and
     the brass plate it sits in is never asked to re-lay-out around it */
  will-change:transform;
}

/* ── THE KICK ─────────────────────────────────────────────────────────────
   Owner, 2026-08-18: "Each cannon shot should turn the 'dollar amount' for
   raised so far green temporarily and have the text shake."

   ⚑ TWO CLASSES, ONE ANIMATION, AND THE PAIR IS THE POINT. Restarting a CSS
   animation normally means removing the class, READING offsetWidth to force a
   style flush, and adding it back — a synchronous layout on the frame of every
   press, at up to eleven presses a second, on an element inside a canvas stage.
   Two names carrying identical keyframes restart it for nothing: swapping the
   animation-name IS a new animation, so the engine runs it from zero without
   anybody touching the layout tree. js/mtmoney.js alternates them.

   ⚑ AND IT IS TRANSFORM + COLOUR ONLY. No margin, no padding, no font-size:
   the figure must not resize the plate it is screwed to, or a rapid volley
   makes the whole brass box breathe. The overshoot on the way back out (the
   1.06 at 62%) is what makes it read as a recoil rather than as a blink. */
@keyframes mm-kick{
    0%{ transform:translate3d(0,0,0) scale(1);         color:var(--label-hi); }
    7%{ transform:translate3d(-5px,-3px,0) scale(1.10); color:#8CF3A8; }
   17%{ transform:translate3d(5px,2px,0) scale(1.07);   color:#6FE894; }
   28%{ transform:translate3d(-4px,2px,0) scale(1.05);  color:#6FE894; }
   39%{ transform:translate3d(3px,-2px,0) scale(1.03);  color:#8CF3A8; }
   50%{ transform:translate3d(-2px,1px,0) scale(1.02);  color:#A7F7BE; }
   62%{ transform:translate3d(1px,0,0) scale(1.06);     color:#C6F9D5; }
   78%{ transform:translate3d(0,0,0) scale(1.01);       color:#E4F4DC; }
  100%{ transform:translate3d(0,0,0) scale(1);         color:var(--label-hi); }
}
.mm-counter__n.mm-kick-a{ animation:mm-kick 520ms cubic-bezier(.2,.85,.3,1) both; }
.mm-counter__n.mm-kick-b{ animation:mm-kicked 520ms cubic-bezier(.2,.85,.3,1) both; }
/* the twin. Identical by hand rather than by a shared name, because a shared
   name is exactly what would stop the retrigger working. */
@keyframes mm-kicked{
    0%{ transform:translate3d(0,0,0) scale(1);         color:var(--label-hi); }
    7%{ transform:translate3d(-5px,-3px,0) scale(1.10); color:#8CF3A8; }
   17%{ transform:translate3d(5px,2px,0) scale(1.07);   color:#6FE894; }
   28%{ transform:translate3d(-4px,2px,0) scale(1.05);  color:#6FE894; }
   39%{ transform:translate3d(3px,-2px,0) scale(1.03);  color:#8CF3A8; }
   50%{ transform:translate3d(-2px,1px,0) scale(1.02);  color:#A7F7BE; }
   62%{ transform:translate3d(1px,0,0) scale(1.06);     color:#C6F9D5; }
   78%{ transform:translate3d(0,0,0) scale(1.01);       color:#E4F4DC; }
  100%{ transform:translate3d(0,0,0) scale(1);         color:var(--label-hi); }
}
.mm-counter__d{
  display:block; margin-top:4px;
  font-size:11.5px; color:var(--label-dim); letter-spacing:.04em;
}
.mm-counter__d[hidden]{ display:none; }

/* ⛔ THE "SHOTS FIRED · PILE UNMOVED" READOUT IS GONE (owner, 2026-08-18:
   "'shots fired' and 'pile unmoved' is unnecessary"). Its element, its counter
   and its rule all went with it — a dead rule left behind is a rule somebody
   later restores an element to match. The honesty it was there to carry now
   lives in the brass tag over the cabinet, where it reads as a statement about
   donations rather than as a scoreboard for a toy. */

/* ── THE PLUNGER ──────────────────────────────────────────────────────────
   A real <button>, red enamel on a brass collar, sitting proud of its own
   shadow so that pressing it visibly travels. */
/* ⚑ 16px SAT THE PLUNGER ON THE FLOOR. The owner: "'fire' is a bit too low
   and clips through the floor in the game". The vault's floor line is drawn
   inside the canvas at the bottom of the room, so a control anchored 16px
   up from the stage edge overlaps it and reads as sunk INTO the boards
   rather than standing in front of them. It now clears the floor band. */
.mm-controls{
  position:absolute; left:0; right:0; bottom:13.5%;
  display:flex; justify-content:center;
  pointer-events:none;                  /* the bar is a rail, not a lid */
}
.mm-fire{
  pointer-events:auto;
  font-family:"Rye", Georgia, serif; font-size:22px; letter-spacing:.09em;
  color:#FFF2DC;
  padding:13px 38px 11px;
  border:none; border-radius:12px; cursor:pointer;
  /* The enamel is DOMED. A single vertical ramp is a flat card in red; the
     elliptical highlight across the upper third is what makes the plunger look
     like a turned cap you could press with your palm — the same trick the
     game's own toolbar buttons use, one gradient earlier in the stack. */
  background:
    radial-gradient(120% 74% at 50% -6%, rgba(255,255,255,.34) 0%, rgba(255,214,180,.10) 46%, rgba(255,255,255,0) 72%),
    linear-gradient(180deg,#F2764E 0%,#C9351A 36%,#8E1E0C 84%,#6B1408 100%);
  box-shadow:
    0 6px 0 #4A0D04,                     /* the barrel of the plunger */
    0 10px 16px rgba(0,0,0,.6),
    0 0 0 2px var(--wood-line),
    0 0 0 4px var(--brass-mid),
    inset 1px 1px 0 rgba(255,233,168,.5),
    inset -1px -2px 0 rgba(0,0,0,.35);
  text-shadow:0 2px 0 #5A1206, 0 0 16px rgba(255,140,80,.45);
  transition:filter .1s ease, transform .06s ease, box-shadow .06s ease;
}
.mm-fire:hover{ filter:brightness(1.1); }
.mm-fire:active,
.mm-fire.is-down{
  transform:translateY(5px);
  box-shadow:
    0 1px 0 #4A0D04,
    0 3px 7px rgba(0,0,0,.6),
    0 0 0 2px var(--wood-line),
    0 0 0 4px var(--brass-light),
    inset 1px 1px 0 rgba(255,233,168,.35);
}
.mm-fire:focus-visible{ outline:3px solid var(--brass-highlight); outline-offset:4px; }

/* ── The caption under the cabinet ─────────────────────────────────────────
   Scoped through .mm-game on purpose. This is a real <p>, and pay.css's
   `.plate p { font-size:17px }` outranks a bare class by one specificity point
   — so a component dropped inside somebody else's .plate has to out-specify the
   panel it is a guest in, or it silently inherits the panel's body copy. */
.mm-game .mm-note{
  margin:14px 0 0; text-align:center;
  font-family:var(--font-display); font-size:13.5px; font-style:italic; line-height:1.55;
  color:var(--brass-light); letter-spacing:.03em;
  text-shadow:0 1px 0 rgba(0,0,0,.7);
}

/* ── Phone ────────────────────────────────────────────────────────────────
   The plunger comes off the glass and becomes its own strip. Overlaid, it and
   the counter plate collide at about 380px — and the fix is not to shrink both
   until they fit, because a FIRE button you have to aim at is a worse toy.

   ⛔⛔⛔ THE PLUNGER USED TO VANISH ENTIRELY HERE, AND NOTHING IN THE MARKUP
   LOOKED WRONG. `position:static` returns .mm-controls to the flow — and it is
   the LAST CHILD of .mm-stage, whose height is fixed by `aspect-ratio` and is
   already consumed to the last pixel by `.mm-canvas{height:100%}`. A block box
   with aspect-ratio does not grow to fit its content; the content overflows.
   So the strip laid out at exactly the stage's bottom edge, `overflow:hidden`
   cut it off, and on EVERY phone the one control this game is about was not on
   the page. The rule was right, the box it was written against was wrong.

   THE FIX IS THE STAGE, NOT THE BUTTON. Under 700px the stage becomes a flex
   column: the canvas takes what is left after a plunger shelf of a KNOWN 68px,
   and the counter is anchored above that shelf so it is still bottom-right of
   the picture, which is where the owner asked for it. Deterministic — nothing
   here depends on measuring the button — and it needs no change to the markup,
   which matters because this component is assembled into a page it does not
   own. (js/mtmoney.js sizes its backing store from the CANVAS box for exactly
   this reason: read from the stage, the picture squashes by 68px.) */
@media (max-width:700px){
  .mm-frame{ padding:16px 18px 14px; border-radius:13px; }
  .mm-bulbs{ inset:8px; }
  /* the carving stays, at the width's own scale — an ornament that is dropped
     on a phone leaves the two edges of the picture unfinished, and this one
     costs a phone nothing to keep because it is a static rasterised layer */
  .mm-crest{ margin-bottom:-4px; }
  .mm-apron{ margin-top:-3px; }
  /* ⛔ HEIGHT, NOT RATIO — see the note on .mm-stage above. `aspect-ratio:4/3`
     with `min-height:300px` puts a 400px floor under the WIDTH, which is 95px
     more than a 375px phone has to give and is what was forcing the layout
     viewport open. An explicit height transfers nothing in either direction, so
     the box can never be wider than the frame it is in whatever the numbers say.
     `aspect-ratio:auto` is written out rather than left to cascade off the base
     rule, because the base rule is the thing being cancelled and a reader has to
     be able to see that it was cancelled deliberately.

     clamp() keeps what the ratio was for, and the numbers came DOWN with the
     desktop letterbox: 60vw is a wide picture at every phone width, the 232px
     floor stops the vault collapsing on a 320px handset, and the 300px ceiling
     stops a 700px tablet handing the stage more height than the picture wants.
     240px at 375px wide, of which the plunger shelf takes a known 62 — so the
     canvas is 178px and the whole cabinet, crest and apron included, is about
     the height the picture alone used to be. */
  .mm-stage{
    aspect-ratio:auto;
    height:clamp(232px, 60vw, 300px);
    display:flex; flex-direction:column;
  }
  .mm-canvas{ flex:1 1 0; width:100%; height:auto; min-height:0; }
  .mm-controls{
    position:static;
    flex:0 0 auto; height:62px; align-items:center;
    /* a wooden shelf the plunger is bolted to, not a gap under the picture */
    background:linear-gradient(180deg,#2C1B0C 0%,#170E05 100%);
    box-shadow:inset 0 1px 0 rgba(255,233,168,.16), inset 0 8px 14px rgba(0,0,0,.55);
  }
  .mm-counter{ right:10px; bottom:70px; padding:6px 11px 5px; }
}
@media (max-width:420px){
  .mm-bulb{ width:12px; height:12px; margin:-6px 0 0 -6px; }
  .mm-bulb::after{ width:9px; height:9px; margin:-4.5px 0 0 -4.5px; }
  .mm-fire{ font-size:19px; padding:12px 30px 10px; }
}

/* ── Reduced motion ───────────────────────────────────────────────────────
   The border stays LIT — a carnival frame with its bulbs off reads as a broken
   sign, not as a considerate one. It simply stops chasing. The canvas keeps
   working too: the cannon still aims and still fires, it just does not sweep on
   its own and everything it throws moves at a little over half speed. That part
   is js/mtmoney.js's business; this is only the frame. */
@media (prefers-reduced-motion: reduce){
  .mm-bulb::after{ animation:none; opacity:.66; }
  .mm-fire{ transition:none; }
  /* ⚑ THE FLASH STAYS, THE SHAKE GOES. The kick is FEEDBACK — it is how the
     figure answers a press — and removing it entirely would leave a reader who
     asked for less motion with a button that appears to do nothing to the one
     number on screen. So the colour runs and the transform does not: same two
     class names, same duration, keyframes with no movement in them. Redefining
     the names here rather than overriding animation-name on the classes is what
     keeps the two-class retrigger working under reduced motion too. */
  @keyframes mm-kick{
      0%{ color:var(--label-hi); }
     10%{ color:#8CF3A8; }
     55%{ color:#8CF3A8; }
    100%{ color:var(--label-hi); }
  }
  @keyframes mm-kicked{
      0%{ color:var(--label-hi); }
     10%{ color:#8CF3A8; }
     55%{ color:#8CF3A8; }
    100%{ color:var(--label-hi); }
  }
}

/* ══ THE VEGAS TUBE ═══════════════════════════════════════════════════════
   Owner: "needs to be centered and more stylized. Make it Vegas neon and
   wider."

   A brass nameplate is a plaque with a box around it; neon is a LIGHT SOURCE
   with nothing around it. So the box is gone and what is left is bent glass on
   a dark backer: a hot near-white letter core, the tube's own colour bleeding
   off it, and a wide soft bloom thrown onto the board behind. That is three
   text-shadows at three radii — a single coloured blur reads as a smudge, and
   the difference between the two is the whole difference between "glowing" and
   "neon".

   It is painted ONCE and only its opacity is animated, so the buzz costs a
   composite and never a repaint of the glyphs. */
.mm-vegas{
  /* position + isolation: the cash layer is an absolutely positioned child and
     the letters must sit ON it, not under it. `isolation` is deliberate — this
     box now stacks three things (cash, scrim, glass) and none of them may leak
     into the plate's own stacking order. */
  position:relative; isolation:isolate; overflow:hidden;
  /* ⛔⛔⛔ THIS ONE LINE WAS THE CONTRAST BUG, AND IT IS INHERITED FROM A RULE
     IN ANOTHER FILE. `.plate p` in css/pay.css sets
     `text-shadow:0 1px 0 rgba(0,0,0,.45)` — correct for the paragraphs it was
     written for, and this element is a <p> inside a .plate, so it got it too.
     ⭐⭐⭐ A text-shadow under a `background-clip:text` fill is painted ON TOP
     OF THE LETTER, not behind it: the clipped gradient paints in the element's
     BACKGROUND layer and the shadow paints in the TEXT layer above it. At 1px
     offset and zero blur that black-at-45% copy of the glyph covers all of its
     own letter except a 1px rim along every top edge — so the gold-and-silver
     bar survived only as an OUTLINE and the word rendered hollow, with the
     board showing through the middle of every stem.
     MEASURED, one page, one Chrome, flat white fill at dpr 3: plain text fill
     31 953 solid pixels vs background-clip:text 3 242. The 0.55 residual is
     1 − 0.45 to the digit, which is what named the culprit — no amount of
     re-colouring the board could have fixed a word that was being painted over.
     ⚠ AND IT IS WHY A NAÏVE CONTRAST PROBE CANNOT SEE IT: the natural way to
     isolate glyph ink is to re-render the text in a flat tag colour, and the
     natural way to do that is `-webkit-text-fill-color` + `text-shadow:none` —
     which SWITCHES THE DEFECT OFF. The control has to leave the shadow alone.
     See tools/contrast_probe.py. */
  /* ⚠ THE PADDING IS LOAD-BEARING NOW, NOT COSMETIC. The scrim behind the
     words is deep (.93) so the metal reads, and thin (.22) at the top and
     bottom so the notes read; those two thin bands only exist if there IS room
     above and below the line. At the old 9/11 padding they were ~6px and the
     board looked like a green smear. 20/22 gives ~16px of legible cash on each
     side of the tube. */
  margin:0 0 14px; padding:20px 22px 22px; text-align:center;
  border-radius:12px;
  /* ⚑ THE BOARD IS MONEY NOW. The flat brown gradient is gone; what is left
     here is the FALLBACK bed, in the same banknote green the cash layer paints,
     so a browser that never gets the SVG (or a future edit that removes the
     money header this backdrop borrows its notes from) still shows a green
     board rather than a bright hole. See the long note in part_mtmoney.html. */
  background:linear-gradient(180deg,#0B2416 0%,#123720 55%,#081C11 100%);
  /* The rim keeps its brass: a green board floating on brown wood with no metal
     edge reads as a sticker. The inner shadow is deeper than it was because the
     board is lighter than it was, and the routed channel is what seats it. */
  box-shadow:inset 0 2px 9px rgba(0,0,0,.9), 0 0 0 1px var(--wood-line),
             0 0 0 2px var(--brass-dark), 0 4px 12px rgba(0,0,0,.5);
}
/* The cash layer fills the box exactly and is never interactive. It is BEHIND
   the tube (z-index:0 vs the tube's 1) and inside the box's own overflow, so
   the `slice` crop is the box's rounded rectangle and nothing shows outside it. */
/* ⚠ THE SELECTOR IS DOUBLED ON PURPOSE, AND THAT IS THE WHOLE POINT OF THIS
   RULE. The shadow being cancelled comes from `.plate p` in css/pay.css, which
   is (0,1,1); a plain `.mm-vegas` is (0,1,0) and LOSES, silently — the
   declaration is there in the file, the computed style still reads
   `rgba(0,0,0,.45) 0px 1px 0px`, and the word stays hollow. `.mm-vegas.mm-vegas`
   is (0,2,0) and wins without `!important` and without this component having to
   name a class it does not own. The tube is listed separately because the
   inherited value would otherwise reach it through its own inheritance chain
   before this rule's value could. */
.mm-vegas.mm-vegas,
.mm-vegas.mm-vegas .mm-vegas__tube{ text-shadow:none; }

/* ⭐⭐⭐ AND IT IS PROMOTED, WHICH IS WORTH 112 DROPPED FRAMES IN THREE SECONDS.
   The tube above this carries `mm-shimmer`, which animates `background-position`
   on a `background-clip:text` fill — a REPAINT, every frame, by design and
   worth it. Unpromoted, that repaint drags this layer into the same paint pass,
   so ~90 banknotes and coins were being re-rasterised sixty times a second to
   move a highlight across six words.

   Measured with tools/lag_ab.py (arms interleaved A,B,B,A so machine drift
   cancels, verdict a sign test over temporally-adjacent pairs): dropped frames
   over five 1.4s samples went 116 -> 4, and no adjacent pair went the other
   way. The cash is completely static, so rasterising it once and compositing it
   thereafter is not a trade — it is simply the right answer. */
.mm-vegas__cash{
  position:absolute; inset:0; z-index:0; display:block; pointer-events:none;
  will-change:transform;
}
.mm-vegas__cashsvg{ display:block; width:100%; height:100%; }
/* Limelight, not Rye. Rye is 1880s WOOD TYPE — the right voice for a painted
   midway board and the wrong one for a gas tube. Limelight is deco display:
   high contrast, tight apertures, the shape a bent glass tube is actually made
   in, and the face a Vegas marquee is set in. */
@font-face{
  font-family:"Limelight";
  src:url("../assets/fonts/Limelight.ttf") format("truetype");
  font-weight:400; font-style:normal; font-display:swap;
}
.mm-vegas__tube{
  /* ⚑ ABOVE THE CASH. Without the position + z-index this span is an in-flow
     inline box and the absolutely positioned .mm-vegas__cash — a positioned
     element with z-index:0 — paints OVER it, which would hide the whole line.
     `position:relative` on a non-positioned inline is the minimum that puts it
     back in the positioned layer where it can be sorted above. */
  position:relative; z-index:1;
  display:block;
  font-family:"Limelight", "Rye", Georgia, serif;
  /* wider: it fills the cabinet rather than sitting in a chip at one end */
  font-size:clamp(13px, 2.1vw, 26px);
  letter-spacing:.055em; line-height:1.15;
  /* ⚑ GOLD AND SILVER, SHIMMERING. The letterface is a moving bar of polished
     metal — silver, gold, silver — travelling across the word, with the glow
     behind it split the same way: warm gold close in, cool silver further out.
     The travel is a BACKGROUND-POSITION animation on a background-clip:text
     fill, which the compositor can run on its own; animating the colours
     themselves would repaint every glyph on every frame. */
  color:#FFF7EC;
  /* ⚠ THE END STOPS WERE THE CONTRAST BUG, NOT THE MIDDLE. #8A7A55 is a dark
     olive-gold; against the old brown board it measured 4.8:1 and read as mud,
     because the only separation was luminance inside a single hue and a 14px
     gold drop-shadow fills that back in. Lifted to #B99A55 (6.1:1 against the
     new green bed) — still a shadowed edge of the metal bar, no longer a hole
     in the word. The bar's SHAPE is untouched: same eleven stops, same offsets,
     same travel. */
  background-image:linear-gradient(100deg,
      #B99A55 0%, #FFF3C8 12%, #F5C25A 22%, #FFFDF6 33%, #D9DEE6 42%,
      #FFFFFF 50%, #D9DEE6 58%, #FFFDF6 67%, #F5C25A 78%, #FFF3C8 88%, #B99A55 100%);
  background-size:260% 100%;
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent;
  /* ⚠ NOT text-shadow. Against a background-clip:text fill a text-shadow is
     painted ON TOP of the letter and buries the metal — the exact trap the
     coaster sign hit. drop-shadow runs on the finished raster, so the glow
     lands behind the shimmer where it belongs. */
  filter:
    drop-shadow(0 0 3px rgba(255,246,214,.95))
    drop-shadow(0 0 9px rgba(245,194,90,.85))
    drop-shadow(0 0 20px rgba(226,232,240,.55))
    drop-shadow(0 0 40px rgba(245,194,90,.35));
  animation:mm-shimmer 7s linear infinite;
}
@keyframes mm-shimmer{
  from { background-position:  140% 50% }
  to   { background-position: -140% 50% }
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .mm-vegas__tube{
    -webkit-text-fill-color:#FFF7EC; color:#FFF7EC; background-image:none;
    animation:none;
  }
}
@media (max-width:760px){
  .mm-vegas{ padding:13px 12px 15px; }
  .mm-vegas__tube{ letter-spacing:.03em; }
}
@media (prefers-reduced-motion: reduce){
  .mm-vegas__tube{ animation:none; }
}


/* ==== part_coastersign.css ==== */
/* ============================================================================
   THE MIDWAY MARQUEE  —  "HELP MAKE THIS GAME AMAZING"          (prefix: .cs-*)

   The head of the donate form. Not a banner and not a bulb-bordered board: a
   STREAMLINE MODERNE / early-Googie park marquee of about 1948-1955 — the
   chrome-and-speed-line object a post-war coaster or midway gate wore.

   ── THE RESEARCH (read first; this is the spec everything below was built to) ─
   Sources: Wikipedia, "Googie architecture"; the NPS's "Oklahoma Sign Makers and
   Endangered Signs"; aesdes.org, "Aesthetic Explorations: 1950's Signage"; the
   Neon Museum's "8 popular vintage neon sign designs and animations"; trade
   descriptions of channel-letter construction (Sign Source USA, Vintage Sign &
   Light, signsmanufacturing.com's channel-letter history).

   R1. STREAMLINE MODERNE IS HORIZONTAL. Its vocabulary is "rounded edges,
       horizontal SPEED LINES, machine-inspired design"; it ran from the mid-30s
       and "its influences still could be seen on commercial designs of the
       1950s". It reached parks directly — architect Richard Crowther put Deco
       and Streamline Moderne ticket booths into amusement parks, and "roller
       coaster stations and marquees were constructed in the Streamline Moderne
       style". THAT is the licence for this object existing at all.
       ⇒ The frame above and below the copy is a LADDER OF DECREASING HORIZONTAL
         RULES, not a border. It is the single most legible period cue and it is
         the one ornament that survives a 375px phone unchanged, because a
         horizontal rule only ever has to stretch.

   R2. GOOGIE SUPPLIES THE CROWN. From Lautner's 1949 coffee shop onward: swept
       BOOMERANGS ("a stylistic rendering of a directional energy field"),
       STARBURSTS ("a high-energy explosion" — the Las Vegas sign's), upswept
       roofs, cantilevers, acute angles, glass + steel + neon, all communicating
       "invisible forces of speed and energy" to somebody driving past.
       ⇒ A swept chrome boomerang rides the cabinet's top rail with an atomic
         starburst at its apex. Both are drawn as PATHS, never as characters —
         ✦ and ➤ are tofu the moment a font lacks them.

   R3. ⭐ THE FINDING THAT DECIDED THE WHOLE BUILD. 1950s signs used two
       different lighting technologies for two different jobs: "Neon provided
       highlights around words and shapes, while FLUORESCENT BULBS FILLED IN
       LETTERS and made the signs pop."
       ⇒ The letters are STEADY, bright and solid — they are the lit face of a
         channel letter, not a neon outline. The neon is the ORNAMENT: the tube
         in the speed ladders and the tube on the boomerang's leading edge.
         The old sign put five colours of neon glow on the words themselves and
         they went muddy; that was not a tuning problem, it was the wrong
         technology on the wrong part of the sign.

   R4. A CHANNEL LETTER IS THREE PARTS, and modelling all three is what makes it
       an object standing off the board instead of coloured type: a pigmented
       FACE; aluminium RETURNS — "the sides that create depth", deeper on the
       older signs "to house the glass tubes safely"; and a TRIM CAP, "a narrow
       strip of plastic or metal" bonding face to return along the edge.
       ⇒ face  = a chrome/cream gradient clipped to the glyphs
         trim  = a hairline dark -webkit-text-stroke ON the outline
         return= a chained drop-shadow() ramp, coral-red at the cap falling to
                 near-black — a red-painted can catching light at its top edge.

   R5. LAMPS CHASE, THEY DO NOT BLINK. Incandescent lamps were "wired in
       sequence to create a 'chasing' effect along a sign's border". A filament
       cools rather than switching off, so the dark end of the cycle is a warm
       ember. And a post-war marquee hung its lamps under the CANOPY, not right
       round the perimeter — a full bulb border is the older (1910s-20s) idiom
       and is exactly what made the previous sign read as generic.
       ⇒ ONE run, along the bottom chrome rail, travelling left-to-right (the
         same way the copy is raked).

   R6. THE PALETTE. Mid-century commercial colour is "bright artificial colours"
       and the atomic sub-style's "bright pastel colours, large geometric
       shapes"; in practice the park/diner quartet is TURQUOISE + CORAL-RED +
       CREAM + CHROME, with brass hardware. Turquoise enamel also happens to be
       the one strong field colour that sits on a dark espresso plate over
       bright green grass without fighting either, and cream letters over it
       give the highest contrast on the page.

   R7. WHAT WAS DELIBERATELY NOT TAKEN. A true Googie CANTILEVER hangs the
       cabinet off ONE raked pylon. It is the most characteristic mount of the
       era and it is refused here: this heading has to stay centred on its plate
       at every width from 375px up, and an asymmetric mast puts the words off
       the page's axis on a phone. Two steel pylons with chrome finials and
       brass collars are the period's other honest answer (the freestanding
       PYLON sign is itself an era staple per the Neon Museum) and they keep the
       composition symmetric. Written down because a rejected option that is not
       recorded gets "fixed" back in by the next pass.

   ── THE TYPEFACE ─────────────────────────────────────────────────────────────
   LILITA ONE, declared below (28 KB — the smallest face in the folder, and the
   only new asset this component adds).

   Rye was released explicitly for this sign, and it had to go: Rye is an 1880s
   American wood-type revival — Main Street, medicine show, the WRONG CENTURY by
   seventy years. Of the eight faces on hand:
     · Rye, Sancreek        1880s western wood type            — wrong era
     · PlayfairDisplay      didone book face                   — wrong register
     · AlfaSlabOne          Clarendon slab, 19th-c advertising — wrong era
     · Limelight            a 1930s Deco poster face, and the near miss. Its
                            hairline horizontals are its whole character, and a
                            hairline has NO SIDE WALL: a channel letter cut from
                            it would have a return you cannot see, and at the
                            22px phone floor the thin strokes disappear.
     · Bungee               a signage face, but a contemporary one — wide,
                            geometric, 119 KB, and its wide caps do not fit
                            "AMAZING" on a 375px board.
     · TitanOne             rounded, but cartoon-soft rather than machined.
     · LilitaOne            ✓ a heavy, slightly condensed advertising gothic
                            with softened corners — the streamlined letterform,
                            descended from exactly the mid-century fat-face
                            ad gothics this sign is quoting.
   The structural argument outranks the stylistic one: R4 says the letter must
   have GIRTH to show a return, and only the fat faces do. Among those LilitaOne
   is both the most period and the most compact, which is what buys the phone
   fit. It is RAKED, not italicised — there is no italic, and there was no
   italic in the sign painter's chase either: he skewed the standing type.

   ── HOW IT IS BUILT ──────────────────────────────────────────────────────────
   The words are a real <h2> holding two <span> lines. Everything else is CSS
   gradients plus ONE inline SVG (the boomerang + starburst). No images, no JS.

   PERFORMANCE. Only two properties are ever animated — `opacity` (lamps, neon,
   starburst) and a small `transform:scale` (lamps, starburst) — and both
   composite. Every gradient, filter and shadow in this file is STATIC, so each
   rasterises once and the animation only re-composites the finished layer.
   In particular the letters do NOT pulse: per R3 a fluorescent-filled letter is
   steady, which is both the period-correct answer and the free one.

   ── THE TWO TRAPS THIS COMPONENT HAS ALREADY FALLEN INTO ─────────────────────
   ⛔ text-shadow PAINTS ON TOP OF A background-clip:text FILL. CSS paints
      background (step 4) → text-shadow (step 7) → text, so with a transparent
      fill the shadows are the LAST thing drawn and an opaque keyline swallows
      the gradient. This sign shipped near-black letters on blue for a full
      review cycle because of it. ⇒ Nothing here uses text-shadow for form. The
      trim cap is `-webkit-text-stroke` (it sits ON the outline and leaves the
      interior alone) and the entire return + cast shadow + light-bleed is a
      `filter:drop-shadow()` chain, which runs over the element's FINISHED
      raster and therefore composites BEHIND the face.
   ⛔ ANY GLYPH A DEFAULT FONT MAY LACK RENDERS AS TOFU. Every mark on this sign
      that is not one of the 21 letters of the phrase is a stroked/filled PATH.

   ── WHY THE RETURN IS A CHAIN AND NOT A LIST ─────────────────────────────────
   `filter: A B` applies B to the RESULT of A, so chained drop-shadows compound:
   each one shadows the accumulated raster and lands one step further back. Six
   shadows of .035em therefore build a SOLID .175em extrusion whose visible
   bands run in listed order, nearest the glyph first. That is why the colours
   below are listed light → dark: the lit trim cap is nearest the face, and the
   can falls into its own shadow going down. A parallel (non-chained) list would
   have produced six separate ghosts.
   ============================================================================ */

/* Lilita One — see THE TYPEFACE above. give.css lives in website/css/, so the
   asset path is relative to that, not to this partial. */
@font-face{
  font-family:"Lilita One";
  src:url("../assets/fonts/LilitaOne.ttf") format("truetype");
  font-weight:400; font-style:normal; font-display:swap;
}

.cs-sign{
  /* ── THE ERA PALETTE (R6) ────────────────────────────────────────────────
     Turquoise enamel, coral neon, cream faces, chrome trim, brass hardware.
     Declared here so the whole sign re-tunes from one block — the inline SVG
     reads these same properties through style="" rather than repeating a hex,
     which is the only way that promise stays true. */
  --cs-teal-lip:#4FD6D0;   /* the rolled top lip catching the sky            */
  --cs-teal-hi:#1BA9AC;
  --cs-teal:#12878E;
  --cs-teal-dp:#0A6068;
  --cs-teal-lo:#053C46;    /* the foot, turning out of the light             */
  --cs-ink:#04222A;        /* the keyline / trim-cap colour, one step darker */

  --cs-coral:#FF4B37;      /* the neon, and the letter can                   */
  --cs-coral-hi:#FF8A6B;
  --cs-coral-core:#FFF0E9; /* the hot near-white gas inside the glass        */

  --cs-cream:#FFF6E2;
  --cs-gold:#FFD98A;

  /* Chrome is a HORIZON, not a grey: bright sky above the line, a dark band at
     it, a warm ground reflection below. Held as one custom property because
     three different objects (bezel, rails, pylons) are the same metal and must
     not drift into three near-greys. */
  --cs-chrome-face:linear-gradient(180deg,
      #FFFFFF 0%, #E6F1F5 16%, #B6CBD4 42%, #7A929C 49%,
      #566A73 52%, #C8DBE2 62%, #F6FBFD 84%, #A9BFC8 100%);

  --cs-lamp-hi:#FFFDF3;
  --cs-lamp:#FFF0C2;
  --cs-lamp-mid:#FFC85E;
  --cs-lamp-lo:#B87C18;

  /* ── THE LETTER CAN (R4) ─────────────────────────────────────────────────
     The chained return, held in one place because the base rule and the
     @supports rule both need it byte-identical and a copied filter chain is a
     copied filter chain that will diverge. Light → dark, nearest the glyph
     first; see "WHY THE RETURN IS A CHAIN" in the banner. */
  --cs-can:
    drop-shadow(0 .034em 0 #FF8A63)      /* the polished trim cap            */
    drop-shadow(0 .034em 0 #E2542F)      /* the can's red paint, lit         */
    drop-shadow(0 .034em 0 #B03A1F)
    drop-shadow(0 .034em 0 #7B2614)
    drop-shadow(0 .034em 0 #4C160C)
    drop-shadow(0 .034em 0 #260A06)      /* the can's own shadow at its foot */
    drop-shadow(0 .07em .05em rgba(0,0,0,.55))    /* cast onto the enamel    */
    drop-shadow(0 0 .55em rgba(255,240,203,.30)); /* the fluorescent bleed   */

  position:relative;
  max-width:640px;
  margin:0 auto 26px;
  text-align:center;
}

/* ── THE RIG ──────────────────────────────────────────────────────────────────
   Everything the sign owns lives INSIDE this box's padding — the crest and the
   starburst in the top pad, the pylon feet in the bottom, the rails in the
   sides. That is deliberate: the previous build hung its crown at top:-30px,
   outside the component's own box, where it overlapped the section above and
   was invisible to any `--clip` screenshot of the sign. An ornament that its
   own verification tool cannot photograph is an ornament nobody checked.

   THE HEADROOM IS DERIVED, not chosen. The crest is anchored by its BOTTOM
   EDGE to the TOP RAIL'S top edge — `bottom:100%` puts it on the bezel's top
   edge and `margin-bottom:13px` lifts it by exactly the rail's own 13px
   overhang — so the crown board STANDS on the rail at every width instead of
   floating at a distance that only happened to look right at one of them.
   Its height is then width * 124/320 (its viewBox aspect): 118px at the 304px
   the 50% clause reaches on a 608px bezel, +13 for the lift, +6 of air = 137. Anchoring by the bottom is what
   makes that one number correct everywhere; a `top` would have needed
   re-deriving for each clause of the width clamp and would have been wrong in
   between the clauses, where nobody looks. */
/* ⚑ 137/44 WAS SPACE FOR A PEDIMENT THAT NO LONGER EXISTS, and the owner
   counted it: "the 'poles' sticking too far up and downward isn't necessary and
   wastes space as well". The rig was 458px tall to carry a 277px sign. The top
   pad is now the star's window (96px at its widest clause) less the part that
   overlaps the rail, and the bottom is the pylon collar's own height and no
   more. */
.cs-rig{ position:relative; padding:56px 16px 20px; }

/* ── THE PYLONS (R7) ──────────────────────────────────────────────────────────
   Two steel uprights, chrome-faced, with a finial spike and a brass collar. The
   cabinet is bolted ACROSS them, so they are z 0 and the cabinet z 1: what
   shows is the spike above and the banded leg below.

   ⚠ THE LEG LENGTH IS LOAD-BEARING. The bottom rail hangs 14px BELOW the
   cabinet and is z 2, so the collar must clear it: the rig's 44px bottom pad
   less the rail's 14px overhang and its 26px height leaves the collar's 10px
   sitting just under the rail with 4px to spare. Shorten the pad and the collar
   is drawn behind the chrome and only a sliver survives — which is exactly what
   the old build did with its brass band. */
.cs-mast{
  position:absolute; z-index:0; top:52px; bottom:0; width:20px;
  border-radius:3px;
  background:
    /* the round of a drawn steel tube: one lit face between two dark arrises */
    linear-gradient(90deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,0) 20%,
                    rgba(255,255,255,.62) 40%, rgba(255,255,255,.10) 58%,
                    rgba(0,0,0,.20) 74%, rgba(0,0,0,.55) 100%),
    linear-gradient(180deg,#C3D5DC 0%, #8FA6B0 38%, #5D727A 100%);
  box-shadow:0 0 0 1px var(--cs-ink), 0 8px 15px rgba(0,0,0,.55);
}
.cs-mast--l{ left:0; }
.cs-mast--r{ right:0; }
/* the finial: a chrome spike, drawn as a clipped box so it is a SHAPE and not a
   character (see the tofu note in the banner) */
/* the finial. A SHALLOW swept cap, not a spike: at 20px wide a tall triangle
   reads as a nail head, and a nail is not hardware anybody bolts to a marquee.
   The 12% shoulders give it a cast profile instead of a sharpened one. */
.cs-mast::before{
  content:""; position:absolute; left:-4px; right:-4px; top:-13px; height:15px;
  clip-path:polygon(0% 100%, 12% 34%, 50% 0%, 88% 34%, 100% 100%);
  background:linear-gradient(180deg,#FFFFFF 0%, #D6E6EC 38%, #93A9B3 72%, #64797F 100%);
}
/* the brass collar, low enough to clear the bottom rail */
.cs-mast::after{
  content:""; position:absolute; left:-4px; right:-4px; bottom:6px; height:10px;
  border-radius:2px;
  background:linear-gradient(180deg,var(--brass-highlight) 0%, var(--brass-light) 34%,
                             var(--brass-mid) 62%, var(--brass-dark) 100%);
  box-shadow:0 0 0 1px var(--cs-ink), 0 2px 4px rgba(0,0,0,.55);
}

/* ── THE CHROMED SHELL ────────────────────────────────────────────────────────
   The cabinet's bezel is a real 7px band of METAL — a gradient with a horizon —
   rather than a stack of flat box-shadow rings. That is the whole difference
   between a frame that is modelled and a frame that is outlined, which is the
   note this rebuild is answering. Its cast shadow is what makes the marquee an
   object hung on the plate instead of a rectangle printed on it. */
.cs-bezel{
  position:relative; z-index:1;
  padding:7px; border-radius:46px;
  background:var(--cs-chrome-face);
  box-shadow:0 0 0 1.5px var(--cs-ink),
             inset 0 1px 0 rgba(255,255,255,.95),
             0 20px 34px rgba(0,0,0,.6);
}

/* ── THE CREST: BOOMERANG + STARBURST (R2) ────────────────────────────────────
   One SVG, anchored by its bottom edge so its size and its position stay tied
   together at every width (see THE RIG). The starburst is drawn INSIDE the same
   viewBox rather than as a second element for the same reason: a separately
   positioned burst would have had to chase the boomerang's apex through the
   width clamp, and would have missed it everywhere the clamp interpolates. */
.cs-crest{
  position:absolute; z-index:3; left:50%; bottom:100%;
  margin-bottom:-14px;                 /* the star straddles the top rail */
  width:clamp(64px, 13%, 88px); height:auto;
  transform:translateX(-50%);
  pointer-events:none;
}
/* the burst breathes on opacity + a small scale; both composite */
.cs-burst{
  transform-box:fill-box; transform-origin:center;
  animation:cs-twinkle 5.2s ease-in-out infinite;
}
@keyframes cs-twinkle{
  0%,100%{ opacity:1;   transform:scale(1);    }
  50%    { opacity:.62; transform:scale(.915); }
}

/* ── THE TWO CHROME RAILS ─────────────────────────────────────────────────────
   The frame "above and below": a chrome trim band crossing the cabinet at the
   top and at the bottom and CANTILEVERING 16px past each end, which is the
   detail that reads as applied hardware rather than as a border. They are z 2,
   over the cabinet and over the pylons — a trim band passes in front of both. */
.cs-rail{
  position:absolute; z-index:2; left:-16px; right:-16px; height:26px;
  border-radius:13px;
  background:var(--cs-chrome-face);
  box-shadow:0 0 0 1.5px var(--cs-ink),
             inset 0 1px 0 rgba(255,255,255,.9),
             0 4px 9px rgba(0,0,0,.5);
}
.cs-rail--cap  { top:-13px;    }
.cs-rail--skirt{ bottom:-14px; }

/* THE SPEARS. Two-tone side trim with pointed ends — the 1950s automotive
   spear, the same shop and the same year as the sign. Clip-path polygons,
   because a point is a SHAPE; there is no arrow character involved.

   ⚠ THERE ARE TWO, FLANKING THE CROWN, and that is not decoration for its own
   sake. One spear running the whole rail put a long flat red bar directly under
   the boomerang: at 570px the 30px points are 5% of its length, so the taper —
   the entire reason a spear is a spear — was invisible and it read as a stripe.
   Split either side of the crest each spear is ~150px, the points are a fifth
   of it, and the shape declares itself. A tapered ornament has a maximum length
   past which it has stopped being tapered. */
.cs-spear{
  position:absolute; top:50%; height:12px; margin-top:-6px;
  clip-path:polygon(0% 50%, 30px 0%, calc(100% - 30px) 0%, 100% 50%,
                    calc(100% - 30px) 100%, 30px 100%);
  /* enamel, not a flat fill: a lit top edge, the body colour, a dark foot.
     A clip-path clips the element's shadows too, so the modelling has to live
     in the BACKGROUND — an inset highlight would have been sliced off at the
     points, which is the one place the trim reads as an object. */
  background:linear-gradient(180deg, #FFC9B4 0%, var(--cs-coral-hi) 16%,
                             var(--cs-coral) 46%, #D6331F 78%, #8C1C0E 100%);
}
/* The crest is centred and clamps at 50% of the bezel, so its widest reach is
   25% either side of centre (the 320px ceiling only ever makes it NARROWER in
   percentage terms), and the crown BOARD is 85% of that — 21.25%. The spears
   stop at 21%, which leaves clear rail between trim and crown at every width
   the clamp can produce. */
.cs-spear--l{ left:30px; right:79%; }
.cs-spear--r{ left:79%;  right:30px; }

/* ── THE LAMP RUN (R5) ────────────────────────────────────────────────────────
   Fifteen lamps set into the bottom rail, chasing LEFT TO RIGHT — the same way
   the copy is raked, so the sign has one direction of travel rather than two.

   THE DELAY IS POSITIVE ON PURPOSE. Lamp i's phase is (t - i*k), so it reaches
   the lit part of the cycle at t = i*k: later as i grows, i.e. the wave moves
   with the DOM order. A negative delay runs the wave the other way, which is
   the bug the previous build worked around by numbering its bottom run
   backwards in the markup. k = .066s against a 1.05s cycle advances the phase
   6.3% per lamp, so a wave spans ~16 lamps and 15 lamps carry ONE group
   travelling the length of the sign. */
.cs-lamps{
  position:absolute; inset:0 24px; display:flex;
  align-items:center; justify-content:space-between; pointer-events:none;
}
.cs-lamp{
  display:block; width:9px; height:9px; border-radius:50%;
  background:radial-gradient(circle at 34% 30%, #FFFFFF 0%, var(--cs-lamp) 36%,
                             var(--cs-lamp-mid) 72%, var(--cs-lamp-lo) 100%);
  /* the socket ring is a shadow, not a border, so it never changes the box size
     and the run keeps its measured pitch */
  box-shadow:0 0 0 1.5px rgba(22,10,2,.85),
             0 0 0 2.5px rgba(190,130,40,.9),
             0 0 6px 1px rgba(255,214,120,.85),
             0 0 13px 3px rgba(255,190,90,.42);
  animation:cs-chase 1.05s linear infinite;
  animation-delay:calc(var(--cs-i, 0) * .066s);
}
/* A filament does not switch off, it COOLS (R5). The floor is a warm ember at
   .32 and the return to full is a fast ramp over the last 6%, not a step. */
@keyframes cs-chase{
  0%, 6%{ opacity:1;   transform:scale(1);   }
  32%   { opacity:.32; transform:scale(.86); }
  94%   { opacity:.32; transform:scale(.86); }
  100%  { opacity:1;   transform:scale(1);   }
}

/* ── THE CABINET ──────────────────────────────────────────────────────────────
   Turquoise enamel over steel, so it is SHEENY: a lit rolled lip along the top,
   a broad soft specular across the upper third, a cool bloom from above, a
   darkened cartouche through the middle for the cream letters to be bright
   AGAINST, and a foot that turns out of the light. A flat field is the one
   thing that makes a painted sign look like CSS.

   The double inset ring is the hand-painted pinstripe: a 2px dark keyline with
   a gold line just inside it. Inset shadows paint first-listed on top, so the
   dark 0-2px band covers the gold 0-4px band and what survives is gold from
   2px to 4px — a 2px pinstripe. Reverse the order and the gold vanishes. */
.cs-cab{
  position:relative; z-index:1;
  padding:22px 28px 20px;
  border-radius:40px;
  background:linear-gradient(180deg, var(--cs-teal-hi) 0%, var(--cs-teal) 30%,
                             var(--cs-teal-dp) 70%, var(--cs-teal-lo) 100%);
  box-shadow:
    inset 0 0 0 2px var(--cs-ink),
    inset 0 0 0 4px rgba(255,214,140,.55),
    inset 0 3px 0 rgba(120,255,246,.30),      /* the lit rolled lip           */
    inset 0 -14px 26px rgba(0,0,0,.42);       /* the foot leaving the light   */
}
.cs-cab::before{
  content:""; position:absolute; inset:0; z-index:1; border-radius:inherit;
  pointer-events:none;
  background:
    linear-gradient(177deg, rgba(255,255,255,.22) 0%, rgba(255,255,255,.06) 24%,
                    rgba(255,255,255,0) 46%),
    radial-gradient(120% 72% at 50% -12%, rgba(150,255,248,.26) 0%,
                    rgba(150,255,248,0) 62%),
    radial-gradient(64% 54% at 50% 52%, rgba(2,22,28,.34) 0%, rgba(2,22,28,0) 74%);
}

/* ── THE SPEED LADDERS (R1) ───────────────────────────────────────────────────
   Three horizontal rules above the copy and three below, widest OUTERMOST so
   the pair opens away from the words and frames them. Every rule is a plain
   percentage width, which is why this ornament needs no phone variant at all:
   it only ever stretches.

   The middle rule of each ladder is the NEON. The two outer ones are polished
   chrome with a dark under-edge — unlit trim, so the eye can tell which of the
   three is actually a light. */
.cs-speed{ position:relative; z-index:2; display:block; }
.cs-speed--top{ margin:0 0 16px; }
.cs-speed--bot{ margin:16px 0 0;  }
.cs-rule{
  display:block; margin:0 auto 7px; height:5px; border-radius:3px;
  background:linear-gradient(180deg,#FFFFFF 0%, #E4F0F5 26%, #A9C0CA 46%,
                             #63787F 55%, #DDEBF1 78%, #FBFDFE 100%);
  box-shadow:0 1px 0 rgba(0,26,32,.62), 0 -1px 0 rgba(255,255,255,.18);
}
.cs-rule:last-child{ margin-bottom:0; }
.cs-rule--wide{ width:100%; }
.cs-rule--mid { width:74%;  }
.cs-rule--nar { width:48%;  }

/* ── A LENGTH OF GLASS ────────────────────────────────────────────────────────
   Real neon is not a coloured blur. It is a HOT NEAR-WHITE CORE inside a wide
   coloured halo — the gas is white-hot down the middle of the tube and only the
   fringe of the discharge, plus the glass itself, carries the colour. So the
   bar is three parts, and all three are in the one static paint:
     · the fill is a vertical gradient — coral at the tube's rim, cream-white
       through the middle 45-58%, deeper coral at the bottom rim;
     · the tight box-shadow is the near-tube bloom;
     · the wide one is the halo thrown onto the enamel.
   Only its OPACITY breathes, so the whole thing rasterises once. */
.cs-rule--neon{
  position:relative; height:6px; border-radius:3px;
  background:linear-gradient(180deg, var(--cs-coral) 0%, var(--cs-coral-hi) 26%,
                             var(--cs-coral-core) 46%, var(--cs-coral-core) 58%,
                             var(--cs-coral) 82%, #C22B1B 100%);
  box-shadow:0 0 7px rgba(255,75,55,.9),
             0 0 20px rgba(255,75,55,.42),
             0 0 34px rgba(255,110,70,.20);
  animation:cs-hum 6.2s ease-in-out infinite;
}
/* the electrodes — the chrome caps where the tube enters its housings */
.cs-rule--neon::before,
.cs-rule--neon::after{
  content:""; position:absolute; top:-2.5px; width:10px; height:10px;
  border-radius:50%;
  background:radial-gradient(circle at 38% 32%, #FFFFFF 0%, #DCEAF0 44%, #7A929C 100%);
  box-shadow:0 0 0 1px var(--cs-ink);
}
.cs-rule--neon::before{ left:-7px;  }
.cs-rule--neon::after { right:-7px; }
@keyframes cs-hum{ 0%,100%{ opacity:1 } 50%{ opacity:.74 } }
/* the two ladders breathe out of phase, so the board never dims as a whole */
.cs-speed--bot .cs-rule--neon{ animation-delay:-3.1s; }

/* ── THE COPY ─────────────────────────────────────────────────────────────────
   A real <h2> holding two line spans, so the heading announces exactly one
   phrase and the break is a DECISION rather than a measurement — the natural
   wrap point of a display face moves across a ~100px band of viewport width,
   and the composition has to be the same object at 375px and at 640px.

   THE RAKE IS ON THE WORDS ALONE. Skewing the ladders with them would tilt the
   speed lines, and a tilted speed line is not a speed line — R1's whole point
   is that the motif is HORIZONTAL. skewX(-9deg) moves a point right above the
   centre line and left below it: tops forward, feet back, a forward lean, which
   is how the type stood in a raked chase.
   CLEARANCE: the skew displaces a glyph by .158 x its distance from the block's
   vertical centre. Two lines at font-size F are ~2.25F tall, so the worst case
   is .158 * 1.13F = .178F — 7.5px at the 42px ceiling, 4.3px at the 24px phone
   size. The cabinet's side padding is 40px / 20px. Both clear it several times
   over. */
.cs-words{
  position:relative; z-index:2;
  margin:0; padding:0;
  transform:skewX(-9deg);
  font-family:"Lilita One", "Trebuchet MS", "Segoe UI", sans-serif;
  font-weight:400; font-size:clamp(22px, 7.6vw, 58px); line-height:1.02;
  letter-spacing:.035em; word-spacing:.04em; text-transform:uppercase;
  /* skeuo.css hands every h1/h2/h3 --title-gold plus a gold emboss text-shadow,
     and BOTH inherit into the lines. Both are cancelled here and the shadow is
     cancelled again on .cs-line, because an inherited text-shadow over a
     background-clip:text fill is the exact trap in the banner. */
  color:var(--cs-cream); text-shadow:none;
}
.cs-line{
  display:block; width:fit-content; max-width:100%;
  margin-left:auto; margin-right:auto;
  text-shadow:none;
  /* the trim cap: on the outline, interior untouched */
  -webkit-text-stroke-width:.024em;
  -webkit-text-stroke-color:var(--cs-ink);
  paint-order:stroke fill;
  filter:var(--cs-can);
}
.cs-line + .cs-line{ margin-top:.06em; }
/* The payoff line is set larger, the way a hand-lettered board always is. */
.cs-line--key{ font-size:1.1em; }

/* THE CHROMED FACE. Guarded, because without background-clip:text a transparent
   fill is an invisible word. The fallback is the solid cream letter above with
   the same trim cap and the same red can, which is a perfectly good sign — and
   the stroke is a second net: even if a browser advertised the clip and then
   failed to paint it, the glyph outlines and the whole return still render. The
   word can be plain. It cannot be invisible.

   The stops are a chrome HORIZON: bright sky down to 42%, the dark band of the
   ground line at 49%, then a warm gold reflection below it (which is also how
   the letters keep a thread of the cabinet's brass). */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .cs-line{
    background-image:linear-gradient(180deg,
        #FFFFFF 0%, #F6FCFF 18%, #BFD8E4 42%, #4E6E7C 49%,
        #FFF6E2 55%, var(--cs-gold) 76%, #FFFFFF 100%);
    -webkit-background-clip:text; background-clip:text;
    -webkit-text-fill-color:transparent;
    color:transparent;
    /* ⛔ and NOT a text-shadow — see the banner. The filter is unchanged from
       the base rule, restated only because this rule replaces nothing else. */
    text-shadow:none;
    filter:var(--cs-can);
  }
}

/* ── THE BUILDER'S PLATE ──────────────────────────────────────────────────────
   Hung under the cabinet on two chrome straps, exactly where a coaster carries
   its "Philadelphia Toboggan Co. · 1964". The straps reach 14px up into a 10px
   gap, so their top 4px is drawn behind the rig — which is what makes them read
   as hanging FROM it rather than sitting under it. */
.cs-plate{ position:relative; margin:10px 0 0; }
.cs-plate__tag{
  position:relative; display:inline-block;
  background:linear-gradient(180deg,#0B4C55 0%, #04252C 100%);
  border:2px solid #9FB6C0; border-radius:6px;
  padding:5px 16px;
  color:var(--cs-cream);
  font-family:var(--font-display); font-weight:700; font-size:10.5px;
  letter-spacing:.18em; text-transform:uppercase; white-space:nowrap;
  text-shadow:0 -1px 0 #000, 0 1px 0 rgba(255,246,226,.16);
  box-shadow:0 4px 8px rgba(0,0,0,.6), inset 0 0 9px rgba(0,0,0,.55),
             inset 0 1px 0 rgba(255,246,226,.22);
}
.cs-plate__tag::before,
.cs-plate__tag::after{
  content:""; position:absolute; top:-14px; width:4px; height:15px;
  border-radius:1px;
  background:linear-gradient(90deg,#5D727A 0%, #FFFFFF 46%, #7A929C 100%);
  box-shadow:0 0 0 1px var(--cs-ink);
}
.cs-plate__tag::before{ left:26px;  }
.cs-plate__tag::after { right:26px; }

/* ── PHONE ────────────────────────────────────────────────────────────────────
   Everything shrinks; NOTHING is removed. The speed ladders need no rule here
   at all — they are percentages — which is precisely why they were chosen as
   the frame (R1).

   THE FIT AT 375px, measured rather than assumed:
     viewport 375 - 2x16 (.gv-plate pad) - 2x1 (plate border)      = 341
     - 2x12 (.cs-rig pad) - 2x7 (bezel) - 2x20 (cabinet pad)       = 239 of copy
   against "HELP MAKE THIS" and "GAME AMAZING" set in Lilita One at the 24.75px
   clamp value (6.6vw). The measurement is taken with --probe in the same page
   load as the screenshot, so the number and the picture describe one state.
   The headroom follows the crest the same way it does on desktop: at the 224px
   ceiling the SVG is 224 x 124/320 = 87px, plus the phone rail's 10px lift = 97,
   +3 of air = 100. */
@media (max-width:600px){
  .cs-sign{ margin-bottom:20px; max-width:none; }
  .cs-rig{ padding:40px 12px 16px; }
  .cs-bezel{ padding:5px; border-radius:28px; }
  .cs-cab{ padding:26px 20px 24px; border-radius:24px; }
  .cs-crest{ width:clamp(52px, 16%, 70px); margin-bottom:-10px; }
  .cs-rail{ height:20px; border-radius:10px; left:-11px; right:-11px; }
  .cs-rail--cap  { top:-10px;    }
  .cs-rail--skirt{ bottom:-10px; }
  .cs-spear{ height:9px; margin-top:-4.5px;
             clip-path:polygon(0% 50%, 16px 0%, calc(100% - 16px) 0%, 100% 50%,
                               calc(100% - 16px) 100%, 16px 100%); }
  .cs-spear--l{ left:14px; right:81%; }
  .cs-spear--r{ left:81%;  right:14px; }
  .cs-lamps{ inset:0 16px; }
  .cs-lamp{ width:7px; height:7px; }
  .cs-speed--top{ margin-bottom:14px; }
  .cs-speed--bot{ margin-top:14px;    }
  .cs-rule{ height:4px; margin-bottom:6px; }
  .cs-rule--neon{ height:5px; }
  .cs-rule--neon::before, .cs-rule--neon::after{ width:8px; height:8px; top:-2px; }
  .cs-rule--neon::before{ left:-6px; } .cs-rule--neon::after{ right:-6px; }
  .cs-mast{ width:15px; top:40px; }
  .cs-mast::before{ top:-10px; height:12px; }
  .cs-mast::after{ height:8px; bottom:5px; }
  .cs-plate__tag{ font-size:9.5px; letter-spacing:.13em; padding:4px 12px; }
  .cs-plate__tag::before{ left:18px; } .cs-plate__tag::after{ right:18px; }
}
/* The plate is nowrap on purpose — a cast builder's plate that wraps to two
   lines stops reading as a plaque — so at the narrowest widths the long half of
   the company name goes instead of the plate's shape. */
@media (max-width:390px){
  .cs-plate__co{ display:none; }
}

/* ── REDUCED MOTION ───────────────────────────────────────────────────────────
   The chase stops with every lamp LIT, the tubes stop breathing at full
   brightness and the starburst holds. That is the sign with its chaser motor
   switched off, which is a real state of a real marquee — never the sign with
   its lights out, which would be removing the feature rather than the motion.
   Only the lamps and the burst are ever MOVED by an animation, so only those
   two need the transform reset: clearing transform across the whole list would
   also take the copy's 9-degree rake with it, and the rake is layout. */
@media (prefers-reduced-motion:reduce){
  .cs-lamp, .cs-rule--neon, .cs-burst{
    animation:none !important;
    opacity:1 !important;
  }
  .cs-lamp, .cs-burst{ transform:none !important; }
}


/* ==== part_chips.css ==== */
/* ============================================================================
   Isometricland — donate.html, COMPONENT 5: THE AMOUNT SHELF  ("gv-" prefix)

   Seven painted-enamel plates on a prize shelf, each with a pile of cash that
   grows with the figure. Everything here is namespaced gv-chip* / gv-cash*
   EXCEPT the two contract names the payment code owns:

     .chip            js/donate.js selects '.chip' and toggles aria-pressed
     [data-amount]    js/donate.js reads it as dollars

   So every chip carries BOTH class="chip" (theirs) and class="gv-chip" (mine),
   and every rule below that has to beat pay.css is written as a TWO-CLASS
   selector, .chip.gv-chip — it wins on SPECIFICITY, not on which stylesheet the
   integrator happens to link last. What is overridden from pay.css:
       .chip                      background, box-shadow, padding, border-radius,
                                  min-width, font-size, color, text-shadow,
                                  transition, display
       .chip:hover                box-shadow, filter
       .chip:active               transform
       .chip[aria-pressed="true"] background, colour, text-shadow, box-shadow
       .chip:focus-visible        outline, outline-offset
   pay.css's `.chips` flex row is NOT used at all — this component brings its own
   container, .gv-chips, so nothing has to be un-styled to make room.

   ── EVERY WIDTH BELOW IS ARITHMETIC, NOT A GUESS ─────────────────────────
   Rye's advance widths were read out of assets/fonts/Rye.ttf (2048 upem), not
   estimated:  "$500" = 2.6699 em, "$1,000" = 3.5488 em.  So at the default
   19px the widest of the six plain figures needs 51.5 px and the grand one
   needs 68.6 px, and every flex-basis / min-width in this file is
       text + gap + pile + horizontal padding,  rounded UP.
   The previous revision put all seven on one 130px basis; "$1,000" + its pile
   + padding measures 144.6 px there, so the grand chip overflowed its own
   plate by ~11 px at EVERY desktop width. That is why the $1,000 chip now has
   a basis of its own — which is also the better design, because on a prize
   shelf the grand prize is on the bigger plate.
   ============================================================================ */

/* ── THE SHELF ────────────────────────────────────────────────────────────
   The chips live inside .jar, which pay.css caps at 520px — a sensible width
   for a coin slot and a text field, and far too narrow for seven plates. The
   shelf therefore breaks out of its parent and re-centres on it: margin-left
   50% puts its left edge at the parent's centre, the translate pulls it back by
   half its own width. The width is clamped to 100% at the low end so a phone
   can never make the shelf NARROWER than the column it sits in.

   ⚠ THE 144px OF SLACK IS NOT A ROUND NUMBER, IT IS A SUM. The shelf must stay
   inside .plate's content box, and 100vw does NOT know about any of the things
   between them:
       .pay padding      2 × 20 = 40
       .plate padding    2 × 38 = 76
       .plate border     2 ×  1 =  2
       classic scrollbar        ≈ 17   ← 100vw INCLUDES it; layout width doesn't
                               ─────
                                  135
   The previous 120px was under that total, so on a Windows desktop with a
   classic scrollbar at ~1040px the shelf came out ~11px wider than the panel
   it lives in and pushed a horizontal scrollbar onto the page — which widens
   nothing and only makes the overflow permanent. 144 clears the sum with 9px
   to spare. Below 760px pay.css shrinks both paddings, so the sum drops to 70
   and this constant is merely conservative; there the max(100%, …) floor is
   what governs. The 1002px cap is .plate's true interior at the 1120px page
   maximum (1120 − 40 − 76 − 2), not the 1004 that was written here before.
   If this block is ever moved out of .jar, the two breakout lines can simply be
   deleted; nothing else depends on them. */
.gv-chips{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap:12px 9px;
  /* ⛔ THE BREAKOUT IS GONE, and the reason is a rule worth keeping: this used
     to read `width:max(100%, min(1002px, calc(100vw - 144px)))` with the 144
     derived as .pay's padding + .plate's padding. That arithmetic was correct
     when it was written and WRONG the moment the plate stopped being the full
     width of .pay — the shelf then measured itself against the VIEWPORT while
     the panel it sits in measured itself against a max-width, and the row hung
     21px past the panel on each side. An invented constant cannot track a
     layout it does not read. The row now takes its width from its container,
     which is the only number that is always right. (Owner: "The below
     'donation' buttons are too wide and peek out past the panel.") */
  width:100%;
  margin:0 0 22px;
  /* room for the hover lift and the lit chip's halo to breathe — nothing here
     clips, but a flush row reads cramped on a shelf */
  padding:8px 0 12px;
}

/* ── ONE PLATE ────────────────────────────────────────────────────────────
   Seven across at the widest: 6 × 129 + 158 + 6 × 9 = 986, inside the 998 the
   shelf has there. Below that they wrap naturally and the rows stay centred,
   so the last row lands under the middle of the one above it. */
.gv-chip.chip{
  position:relative;
  /* "They can be a bit more skinny" — but not so skinny that the figure and
     its pile stop fitting. 88px was measured and REJECTED: every chip reported
     scrollWidth 5px over clientWidth and the $500/$1,000 content sat 1-2px
     PAST the plate edge. 98px is the measured floor with air in it. Seven
     across: 6 x 102 + 132 + 6 x 9 = 798, inside the 820px column, and the
     decorative ring each chip draws 5px proud of itself still clears it. */
  /* ⭐ THE WIDTH IS INTRINSIC, NOT A TABLE OF CONSTANTS. Every earlier revision
     of this file computed a flex-basis per breakpoint from Rye's advance widths
     — and every one of them was wrong at some band, because the moment the
     font-size or the pile changes in a media query the arithmetic has to be
     redone by hand and silently isn't. Measured at 1000/820/600px the $100 and
     $500 plates had 0 and -1px of padding: the figure was touching the edge.
     `min-width:fit-content` makes "never narrower than what is inside it" a
     property of the box instead of a number somebody has to maintain. The
     basis below is only the RHYTHM — the shelf's preferred beat — and content
     always wins over it. */
  flex:0 1 102px; min-width:fit-content; max-width:136px;
  display:flex; align-items:center; justify-content:center; gap:7px;
  padding:11px 9px 12px; min-height:66px;
  border:none; border-radius:11px; cursor:pointer;
  touch-action:manipulation;              /* no 300ms tap delay on a phone */
  -webkit-tap-highlight-color:transparent;
  font-family:"Rye", Georgia, serif; font-size:16.5px; line-height:1;
  white-space:nowrap;

  /* Four painted layers, front to back:
       1-2  two brass SCREWS at the upper corners. The comment under the bevel
            claims the plate is "screwed to the shelf"; these are what make that
            claim true in pixels instead of in prose.
       3    the enamel SHEEN. A radial dome, not a linear wash — painted tin
            catches the light in one place. It is authored to die out by 28% of
            the plate's height, which is ABOVE the figure's cap band (~36%), so
            it cannot lift the background under the label and quietly spend the
            contrast that was measured against the paint.
       4    the enamel itself, break high so the plate reads domed. */
  background:
    radial-gradient(circle 2.6px at 11px 11px,
      var(--brass-highlight,#FFE9A8) 0%, var(--brass-mid,#C99230) 42%,
      var(--brass-dark,#7E5A12) 76%, rgba(20,10,3,.7) 93%, rgba(20,10,3,0) 100%),
    radial-gradient(circle 2.6px at calc(100% - 11px) 11px,
      var(--brass-highlight,#FFE9A8) 0%, var(--brass-mid,#C99230) 42%,
      var(--brass-dark,#7E5A12) 76%, rgba(20,10,3,.7) 93%, rgba(20,10,3,0) 100%),
    radial-gradient(118% 62% at 50% -18%,
      var(--gv-sh1) 0%, var(--gv-sh2) 46%, rgba(255,255,255,0) 74%),
    linear-gradient(180deg, var(--gv-a) 0%, var(--gv-a) 36%, var(--gv-b) 100%);
  color:var(--gv-ink);
  text-shadow:var(--gv-ink-sh);

  /* The bevel, working outward: a painted keyline in the plate's own dark tone,
     then the cabinet's wood line, then a brass rim — the plate is screwed to
     the shelf, not floating on the page. The inset stops give it a lit top edge
     and a shaded lower lip. */
  box-shadow:
    inset 0 1.6px 0 rgba(255,255,255,.42),
    inset 0 -2.4px 0 rgba(0,0,0,.34),
    inset 0 0 14px rgba(0,0,0,.22),
    0 0 0 1.5px var(--gv-dk),
    0 0 0 2.5px var(--wood-line),
    0 0 0 4px var(--brass-dark),
    0 4px 0 -1px rgba(0,0,0,.35),
    0 7px 13px rgba(0,0,0,.5);

  transition:transform .09s ease, filter .14s ease, box-shadow .14s ease;
}

/* The PINSTRIPE — a hand-painted line set in from the edge, with a dark hair
   just outside it. This is the single detail that separates a fairground
   enamel plate from a rectangle with a gradient, and it is the reason the
   gloss moved into the background layers above: the pseudo-element was worth
   more as a painted line than as a sheen a gradient could do anyway.
   The ring sits at 4.5–5.8px; content starts at the 9px padding, so it never
   crowds the figure. */
.gv-chip.chip::before{
  content:""; position:absolute; inset:4.5px; pointer-events:none;
  border-radius:7px;
  border:1.3px solid var(--gv-line);
  box-shadow:0 0 0 1px rgba(0,0,0,.26);
}

/* The halo that lights when the plate is chosen. Spread-only shadow on a
   transparent box OUTSIDE the chip, so it can never wash across the face and
   quietly drop the label's contrast. */
.gv-chip.chip::after{
  content:""; position:absolute; inset:-5px; border-radius:16px;
  pointer-events:none; opacity:0;
  box-shadow:0 0 18px 3px var(--gv-glow);
  transition:opacity .18s ease;
}

.gv-chip__fig{ position:relative; flex:none; letter-spacing:.01em; }

/* The pile. A FIXED box, so all seven piles stand on one baseline — they differ
   in HEIGHT, which is the whole point, never in where they stand.
   ⚠ Every size below is EXACTLY 4:3, matching the 64×48 viewBox. A box of any
   other ratio letterboxes the artwork and silently moves the ground line, so
   the piles stop sharing one baseline — which is the only thing this box is
   here to guarantee. */
.gv-chip__pile{
  position:relative; flex:none; display:block;
  width:40px; height:30px;
  filter:drop-shadow(0 1px 1px rgba(0,0,0,.4));
}
/* the top prize gets a bigger stage */
.gv-chip--1000 .gv-chip__pile{ width:56px; height:42px; }
/* …and a bigger plate to stand it on: "$1,000" is 3.5488 em of Rye, half again
   as wide as "$500", and it is the one figure that cannot be allowed to wrap
   or to push its pile off the enamel. */
.gv-chip.chip.gv-chip--1000{ flex-basis:132px; min-width:fit-content; max-width:164px; }

/* The sprite sheet is geometry, not content: it must occupy no space, and it
   must NOT be display:none — some engines stop resolving <use> into a hidden
   subtree, and the piles vanish everywhere at once. */
.gv-cash-sprite{ position:absolute; width:0; height:0; overflow:hidden; pointer-events:none; }

/* ── STATES ───────────────────────────────────────────────────────────────
   hover lifts the plate and warms the brass rim;
   active presses it into the shelf;
   [aria-pressed] is LIT AND PUSHED IN — the plate keeps its own colour, because
   the label's contrast was measured against that colour and must survive being
   chosen. The light comes from the brass ring and the halo, never from washing
   the paint out.
   ⚠ ORDER MATTERS AND IS NOT ARBITRARY. :hover, :active and [aria-pressed] all
   land on 0-3-0, so the cascade is decided by source order alone: pressed must
   come after hover (a chosen plate is not merely a hovered one) and :active
   must come after BOTH, or clicking the already-chosen chip gives no press at
   all — the one interaction most likely to be tried twice. */
.gv-chip.chip:hover{
  transform:translateY(-2px);
  filter:saturate(1.06) brightness(1.07);
  box-shadow:
    inset 0 1.6px 0 rgba(255,255,255,.5),
    inset 0 -2.4px 0 rgba(0,0,0,.3),
    inset 0 0 14px rgba(0,0,0,.18),
    0 0 0 1.5px var(--gv-dk),
    0 0 0 2.5px var(--wood-line),
    0 0 0 4px var(--brass-light),
    0 6px 0 -1px rgba(0,0,0,.35),
    0 11px 18px rgba(0,0,0,.5);
}

.gv-chip.chip[aria-pressed="true"]{
  transform:translateY(2px);
  filter:saturate(1.1);
  /* ⚠ background / color / text-shadow are RESTATED here and are not
     redundant. pay.css's .chip[aria-pressed="true"] repaints the chosen chip
     in the page accent (--acc-pa…pd, a brass ramp) with dark ink. Leaving
     these three out does not "inherit the plate's colour" — it hands the
     selected state straight back to pay.css, and the chosen plate turns gold
     whichever of the seven it is, taking its measured contrast with it. */
  background:
    radial-gradient(circle 2.6px at 11px 11px,
      var(--brass-highlight,#FFE9A8) 0%, var(--brass-mid,#C99230) 42%,
      var(--brass-dark,#7E5A12) 76%, rgba(20,10,3,.7) 93%, rgba(20,10,3,0) 100%),
    radial-gradient(circle 2.6px at calc(100% - 11px) 11px,
      var(--brass-highlight,#FFE9A8) 0%, var(--brass-mid,#C99230) 42%,
      var(--brass-dark,#7E5A12) 76%, rgba(20,10,3,.7) 93%, rgba(20,10,3,0) 100%),
    radial-gradient(118% 62% at 50% -18%,
      var(--gv-sh1) 0%, var(--gv-sh2) 46%, rgba(255,255,255,0) 74%),
    linear-gradient(180deg, var(--gv-a) 0%, var(--gv-a) 33%, var(--gv-b) 100%);
  color:var(--gv-ink);
  text-shadow:var(--gv-ink-sh);
  box-shadow:
    inset 0 3px 7px rgba(0,0,0,.45),
    inset 0 -1.5px 0 rgba(255,255,255,.28),
    0 0 0 1.5px var(--gv-dk),
    0 0 0 2.5px var(--wood-line),
    0 0 0 4.5px var(--brass-highlight),
    0 1px 0 -1px rgba(0,0,0,.35),
    0 3px 8px rgba(0,0,0,.55);
}
.gv-chip.chip[aria-pressed="true"]:hover{ transform:translateY(1px); filter:saturate(1.14) brightness(1.05); }
.gv-chip.chip[aria-pressed="true"]::before{ border-color:var(--gv-line-lit); }
.gv-chip.chip[aria-pressed="true"]::after{
  opacity:1; animation:gv-chip-lamp 2.6s ease-in-out infinite;
}
@keyframes gv-chip-lamp{ 0%,100%{ opacity:.62 } 50%{ opacity:1 } }

/* last, so it beats both of the above — see the ORDER note */
.gv-chip.chip:active,
.gv-chip.chip[aria-pressed="true"]:active{
  transform:translateY(3px); filter:saturate(1.02) brightness(.95);
}

/* A real ring, outside the brass rim, in the one colour legible against both
   the espresso panel behind the shelf and all seven enamels. */
.gv-chip.chip:focus-visible{
  outline:3px solid var(--brass-highlight);
  outline-offset:5px;
}

/* ── THE SEVEN COLOURS ────────────────────────────────────────────────────
   A prize shelf: saturated, genuinely different hues — red, orange, yellow,
   green, teal, blue, purple. --gv-a / --gv-b are the enamel, --gv-dk the
   painted keyline, --gv-line the pinstripe, --gv-glow what the plate throws
   when it is lit, --gv-sh1/2 how hard the sheen sits on that paint.

   ⚠ THE INK IS MEASURED AGAINST THE PAINT THE LABEL ACTUALLY SITS ON, WHICH IS
   NOT THE MID TONE. The previous revision computed WCAG contrast against each
   plate's midpoint and reported 4.8–8.2 across the board. But the gradient is
   pure --gv-a for its top 36%, and the figure's cap band starts at about 36%
   of the plate's height — so the top of every glyph is on A, not on the mid
   tone, and A is the LIGHTEST the plate ever gets. Measured against A the old
   set read 3.39 (teal), 3.68 (green), 4.17 (red) and 3.80 (orange's dark ink on
   B): four of the seven were under 4.5:1 exactly where the type is. Re-solved
   in linear light — hue and saturation held, luminance moved — the set now
   reads, ink against A / against B:
       $3    5.40 / 8.36      $10   7.28 / 4.99      $25  10.47 / 6.32
       $50   5.41 / 8.88      $100  5.37 / 9.07      $500  6.32 / 10.15
       $1000 5.62 / 9.80
   Every figure clears 4.5:1 at BOTH ends of its own gradient, and the selected
   state only saturates and sinks the plate — it never lightens it — so none of
   them can lose that by being chosen. Yellow and blue/purple were already
   clear and were left exactly as they were. */
.gv-chip--3{    --gv-a:#B83824; --gv-b:#8E1E12; --gv-dk:#4E0D05; --gv-glow:rgba(255,120,86,.55); }
.gv-chip--10{   --gv-a:#EE9528; --gv-b:#DD7313; --gv-dk:#6B3304; --gv-glow:rgba(255,178,74,.55); }
.gv-chip--25{   --gv-a:#F2C62F; --gv-b:#C99510; --gv-dk:#775205; --gv-glow:rgba(255,222,110,.6); }
.gv-chip--50{   --gv-a:#2A733E; --gv-b:#175023; --gv-dk:#0C3216; --gv-glow:rgba(102,214,132,.5); }
.gv-chip--100{  --gv-a:#14707E; --gv-b:#0A4B58; --gv-dk:#04303A; --gv-glow:rgba(86,214,232,.5); }
.gv-chip--500{  --gv-a:#3358A8; --gv-b:#1E3A78; --gv-dk:#0E2048; --gv-glow:rgba(112,150,255,.5); }
.gv-chip--1000{ --gv-a:#8B44A8; --gv-b:#5E2478; --gv-dk:#380C4A; --gv-glow:rgba(212,124,255,.55); }

/* cream ink + cream pinstripe — the five dark enamels */
.gv-chip--3, .gv-chip--50, .gv-chip--100, .gv-chip--500, .gv-chip--1000{
  --gv-ink:#FFF7DD;
  --gv-ink-sh:0 1.5px 0 rgba(0,0,0,.6), 0 2px 5px rgba(0,0,0,.45);
  --gv-line:rgba(255,247,221,.3);
  --gv-line-lit:rgba(255,247,221,.55);
  --gv-sh1:rgba(255,255,255,.4);
  --gv-sh2:rgba(255,255,255,.12);
}
/* dark ink + dark pinstripe — the two light enamels. Cream on that orange
   measures 2.5:1, which is not a judgement call, it is unreadable; these get
   the emboss the other way up, exactly as .btn--donate does in skeuo.css, and
   the sheen comes down or it swallows the figure's own highlight. */
.gv-chip--10, .gv-chip--25{
  --gv-ink:#2A1806;
  --gv-ink-sh:0 1px 0 rgba(255,255,255,.4);
  --gv-line:rgba(42,24,6,.34);
  --gv-line-lit:rgba(42,24,6,.6);
  --gv-sh1:rgba(255,255,255,.26);
  --gv-sh2:rgba(255,255,255,.07);
}
/* on the light plates the dark pinstripe wants a light hair outside it, not a
   dark one — the same relief, read the other way up */
.gv-chip--10.chip::before, .gv-chip--25.chip::before{
  box-shadow:0 0 0 1px rgba(255,255,255,.22);
}

/* ── WIDTHS ───────────────────────────────────────────────────────────────
   Wrapping is left to flexbox and the rows stay centred; what is pinned here
   is that NOTHING is ever squashed and no pile is ever clipped, at any width.
   Each band's min-width is the measured content of its widest figure, rounded
   up — text (Rye advances × font-size) + gap + pile + horizontal padding:

     band          font  pad  gap  pile   "$500" needs   "$1,000" needs
     default        19    18    7  48/56     124.5 ≤129      149.6 ≤158
     ≤1065px        20    20    8  52/60     134.2 ≤150      160.2 ≤178
     ≤520px         18    16    6  44/48     114.8 ≤124      135.0 ≤138
     ≤339px         17    14    5  36/40     101.1 ≤106      120.4 ≤124

   The last band exists because 320px is still a real screen: pay.css leaves
   250px of interior there, and the previous 124/130 pair plus the gap came to
   263 — one chip per row, on the narrowest phone in circulation. 106 + 124 + 8
   = 238 fits with room. */
@media (max-width:1065px){
  .gv-chip.chip{ flex-basis:104px; min-width:fit-content; max-width:140px;
                 font-size:17.5px; padding:11px 9px 12px; gap:7px; min-height:64px; }
  .gv-chip.chip.gv-chip--1000{ flex-basis:140px; min-width:fit-content; max-width:172px; }
  .gv-chip__pile{ width:52px; height:39px; }
  .gv-chip--1000 .gv-chip__pile{ width:60px; height:45px; }
}
@media (max-width:520px){
  .gv-chips{ gap:11px 9px; }
  .gv-chip.chip{ flex-basis:124px; min-width:fit-content; max-width:190px;
                 font-size:18px; padding:10px 8px 11px; gap:6px; min-height:58px; }
  .gv-chip.chip.gv-chip--1000{ flex-basis:138px; min-width:fit-content; max-width:210px; }
  .gv-chip__pile{ width:44px; height:33px; }
  .gv-chip--1000 .gv-chip__pile{ width:48px; height:36px; }
}
@media (max-width:339px){
  .gv-chips{ gap:10px 8px; }
  .gv-chip.chip{ flex-basis:106px; min-width:fit-content; max-width:170px;
                 font-size:17px; padding:9px 7px 10px; gap:5px; min-height:52px; }
  .gv-chip.chip.gv-chip--1000{ flex-basis:124px; min-width:fit-content; max-width:190px; }
  .gv-chip__pile{ width:36px; height:27px; }
  .gv-chip--1000 .gv-chip__pile{ width:40px; height:30px; }
  .gv-chip.chip::before{ inset:3.5px; border-radius:6px; }
}

/* ── REDUCED MOTION ───────────────────────────────────────────────────────
   Nothing is removed: the lift, the press and the lamp all still happen and
   still mean what they meant. The TRAVEL between states stops being animated,
   and the lamp breathes on a fourteen-second cycle instead of a three. */
@media (prefers-reduced-motion: reduce){
  .gv-chip.chip{ transition-duration:.01ms; }
  .gv-chip.chip::after{ transition-duration:.01ms; }
  .gv-chip.chip[aria-pressed="true"]::after{ animation-duration:14s; }
}


/* ==== part_continue.css ==== */
/* ============================================================================
   COMPONENT 6 — THE CONTINUE BUTTON + THE ENTRY CARDS   ("gc-" namespace)

   Two objects off the same midway:

     THE MARQUEE      the primary action, built as a small painted sign — a wood
                      board, brass sockets, a ring of COLOURED chasing bulbs,
                      and the word itself set in Rye (the game's own wordmark
                      face) with a different colour on every glyph. Point at it
                      and the glyphs strike into neon, left to right, the way a
                      real tube sign warms up.
     THE ENTRY CARDS  the two questions and the leaderboard opt-in, built as
                      hand-painted entry cards: a riveted nameplate over a
                      brass-lipped recess, with the subject of the question
                      engraved into the metal at the right-hand end of the slot.

   Everything here is CSS. There is no script, which matters more than it looks:
   js/donate.js OWNS the button's label — it rewrites .btn__label's textContent
   to "Continue with $25" and back on every amount change — so any per-letter
   markup this component ships is destroyed the first time someone picks a chip.
   The colouring therefore has to survive its own text being replaced. See
   THE TWO-STATE LABEL below for how, and for why the seam is invisible.

   EVERY SELECTOR IS PREFIXED. There is not one bare element selector in this
   file (no h1, no p, no button, no svg, no input): every rule is anchored to a
   .gc-* ancestor, so nothing here can reach the other five components on the
   page. There are no SVG <defs> and therefore no ids at all — the pictograms
   are bare stroked paths, which is also why they cannot collide with
   COMPONENT 5's gv-cash-* gradients living in the same document.
   ============================================================================ */

/* ── The eight bulb colours ────────────────────────────────────────────────
   ONE spectral run — red, orange, gold, green, teal, blue, violet, magenta —
   and the order is load-bearing, not decorative. When the label's text grows
   past the word "Continue" the colour bands stop landing on letter boundaries
   (see below), so some glyph WILL be split between two bands. Two ADJACENT
   hues split across one letter reads as painted shading; red split against
   green reads as a rendering bug. Keeping the run spectral means the worst
   case the layout can produce is still pretty. Magenta wraps back to red for
   exactly the same reason.

   --gc-cN  the painted (unlit) enamel colour
   --gc-nN  the hot core when the tube strikes — the hue blown out to near-white
   --gc-gN  the far halo — the same hue carrying alpha, so it can bloom         */
.gc-continue-rig,
.gc-fields,
.gc-entry {
  --gc-c1:#F4432E; --gc-n1:#FFC9BC; --gc-g1:rgba(244, 67, 46,.75);
  --gc-c2:#FF8A2A; --gc-n2:#FFDFB4; --gc-g2:rgba(255,138, 42,.72);
  --gc-c3:#FFC93C; --gc-n3:#FFF3C4; --gc-g3:rgba(255,201, 60,.70);
  --gc-c4:#79D34E; --gc-n4:#DCF8C6; --gc-g4:rgba(121,211, 78,.70);
  --gc-c5:#35CFC0; --gc-n5:#C9FBF3; --gc-g5:rgba( 53,207,192,.70);
  --gc-c6:#4FA6FF; --gc-n6:#CFE6FF; --gc-g6:rgba( 79,166,255,.72);
  --gc-c7:#A87BFF; --gc-n7:#E2D3FF; --gc-g7:rgba(168,123,255,.72);
  --gc-c8:#FF63B8; --gc-n8:#FFD1EA; --gc-g8:rgba(255, 99,184,.72);
}

/* ══════════════════════════════════════════════════════════════════════════
   PART A — THE MARQUEE
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The board the sign is mounted on ─────────────────────────────────────
   The bulbs need something to be screwed INTO, or they read as dots floating
   around a button. This is the same rim recipe the ticket uses in pay.css
   (hairline, brass, hairline) so the sign belongs to the same cabinet.
   The 21px padding is not arbitrary: it is the lane the bulb ring runs in.

   isolation:isolate is load-bearing, not tidiness. Three things stack inside
   this box — the sockets (0), the bulbs (1) and the button (2) — and without
   an isolation the button's z-index:2 competes with whatever else on the page
   happens to be positioned, which is exactly how a primary action ends up
   painting over somebody else's dialog. */
.gc-continue-rig{
  position:relative;
  isolation:isolate;
  display:block;
  width:100%;
  max-width:382px;              /* 340px button + the two 21px bulb lanes */
  margin:0 auto;
  padding:21px;
  border-radius:22px;
  background:
    linear-gradient(90deg,
      rgba(0,0,0,0) 0%, rgba(0,0,0,.10) 6%, rgba(0,0,0,0) 13%,
      rgba(0,0,0,.09) 26%, rgba(0,0,0,0) 38%, rgba(0,0,0,.08) 52%,
      rgba(0,0,0,0) 63%, rgba(0,0,0,.10) 78%, rgba(0,0,0,0) 100%),
    linear-gradient(180deg,#3A2513 0%,#2A180B 42%,#1A0F05 100%);
  box-shadow:
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-mid),
    0 0 0 4px var(--wood-line),
    inset 0 1px 0 rgba(255,233,168,.16),
    inset 0 0 26px rgba(255,176,74,.07),   /* what the bulbs throw on the wood */
    inset 0 -2px 6px rgba(0,0,0,.55),
    0 14px 30px rgba(0,0,0,.55);
  transition:box-shadow .30s ease;
}
/* Pointing at the sign turns the ambient up: the board is being lit by its own
   bulbs, so it cannot stay the same colour when they go full on. */
.gc-continue-rig:hover,
.gc-continue-rig:focus-within{
  box-shadow:
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-light),
    0 0 0 4px var(--wood-line),
    inset 0 1px 0 rgba(255,233,168,.22),
    inset 0 0 30px rgba(255,176,74,.15),
    inset 0 -2px 6px rgba(0,0,0,.55),
    0 14px 34px rgba(0,0,0,.58);
}

/* ── The sockets (static) ─────────────────────────────────────────────────
   A brass cup with a dark throat at every bulb position. It never animates —
   a socket is screwed to the board — so this whole layer costs one paint at
   load and nothing afterwards, and it is the difference between "bulbs" and
   "dots". The tiles are the SAME four lanes at the SAME sizes and positions as
   the mask below, and each gradient is centred at 50% 50% of its own tile
   rather than at a hand-typed pixel, so socket and bulb cannot drift apart. */
.gc-continue-rig::after{
  content:"";
  position:absolute; inset:1px;
  border-radius:21px;
  pointer-events:none;
  z-index:0;
  background-image:
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 0 4.2px, rgba(0,0,0,.60) 4.8px, rgba(0,0,0,.60) 5.7px, rgba(255,233,168,.20) 6.4px, rgba(255,233,168,0) 7.7px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 0 4.2px, rgba(0,0,0,.60) 4.8px, rgba(0,0,0,.60) 5.7px, rgba(255,233,168,.20) 6.4px, rgba(255,233,168,0) 7.7px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 0 4.2px, rgba(0,0,0,.60) 4.8px, rgba(0,0,0,.60) 5.7px, rgba(255,233,168,.20) 6.4px, rgba(255,233,168,0) 7.7px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 0 4.2px, rgba(0,0,0,.60) 4.8px, rgba(0,0,0,.60) 5.7px, rgba(255,233,168,.20) 6.4px, rgba(255,233,168,0) 7.7px);
  background-size:18px 20px, 18px 20px, 20px 18px, 20px 18px;
  background-repeat:repeat-x, repeat-x, repeat-y, repeat-y;
  background-position:0 0, 0 100%, 0 0, 100% 0;
}

/* ── The chasing bulbs ────────────────────────────────────────────────────
   The bulbs DO NOT MOVE — light does. That is the whole trick, and it is the
   difference between a marquee and a barber pole: a fixed dot MASK punches the
   glass, and a coloured gradient travels underneath it, so every bulb takes
   the colour that is passing through it at that moment. Sliding the dots
   themselves would have been far easier and completely wrong.

   Four background layers pair 1:1 with four mask layers — top, bottom, left,
   right — so each edge travels in its own direction and the chase runs
   CLOCKWISE around the frame instead of everything sliding right at once.

   steps(8) is deliberate twice over. Real chase lights hop one socket at a
   time, and a stepped animation repaints about seven times a second instead of
   sixty — which is how this honours the project's performance contract without
   owning a frame loop at all.

   VERIFIED, not assumed. Layer 0's colour tile is 144px wide with a hard stop
   every 18px; the mask tile is 18px wide with its dot at 50% 50%, i.e. x=9,
   27, 45 ... Each of those falls in the middle of one 18px colour band, so a
   bulb is never split across two colours. The animation's four end positions
   (+144x, -144x, -144y, +144y) send top->right, right->down, bottom->left,
   left->up: clockwise. The dot's soft skirt dies at 8.6px from a centre 10px
   inside the frame, so no bulb light reaches past the 21px padding lane. */
.gc-continue-rig::before{
  content:"";
  position:absolute; inset:1px;
  border-radius:21px;
  pointer-events:none;
  z-index:1;

  /* the travelling colour — one 144px cycle = 8 bulbs at an 18px socket pitch */
  background-image:
    linear-gradient(90deg,
      var(--gc-c1) 0px,   var(--gc-c1) 18px,  var(--gc-c2) 18px,  var(--gc-c2) 36px,
      var(--gc-c3) 36px,  var(--gc-c3) 54px,  var(--gc-c4) 54px,  var(--gc-c4) 72px,
      var(--gc-c5) 72px,  var(--gc-c5) 90px,  var(--gc-c6) 90px,  var(--gc-c6) 108px,
      var(--gc-c7) 108px, var(--gc-c7) 126px, var(--gc-c8) 126px, var(--gc-c8) 144px),
    linear-gradient(90deg,
      var(--gc-c1) 0px,   var(--gc-c1) 18px,  var(--gc-c2) 18px,  var(--gc-c2) 36px,
      var(--gc-c3) 36px,  var(--gc-c3) 54px,  var(--gc-c4) 54px,  var(--gc-c4) 72px,
      var(--gc-c5) 72px,  var(--gc-c5) 90px,  var(--gc-c6) 90px,  var(--gc-c6) 108px,
      var(--gc-c7) 108px, var(--gc-c7) 126px, var(--gc-c8) 126px, var(--gc-c8) 144px),
    linear-gradient(180deg,
      var(--gc-c1) 0px,   var(--gc-c1) 18px,  var(--gc-c2) 18px,  var(--gc-c2) 36px,
      var(--gc-c3) 36px,  var(--gc-c3) 54px,  var(--gc-c4) 54px,  var(--gc-c4) 72px,
      var(--gc-c5) 72px,  var(--gc-c5) 90px,  var(--gc-c6) 90px,  var(--gc-c6) 108px,
      var(--gc-c7) 108px, var(--gc-c7) 126px, var(--gc-c8) 126px, var(--gc-c8) 144px),
    linear-gradient(180deg,
      var(--gc-c1) 0px,   var(--gc-c1) 18px,  var(--gc-c2) 18px,  var(--gc-c2) 36px,
      var(--gc-c3) 36px,  var(--gc-c3) 54px,  var(--gc-c4) 54px,  var(--gc-c4) 72px,
      var(--gc-c5) 72px,  var(--gc-c5) 90px,  var(--gc-c6) 90px,  var(--gc-c6) 108px,
      var(--gc-c7) 108px, var(--gc-c7) 126px, var(--gc-c8) 126px, var(--gc-c8) 144px);
  background-size:144px 20px, 144px 20px, 20px 144px, 20px 144px;
  background-repeat:repeat-x, repeat-x, repeat-y, repeat-y;
  background-position:0 0, 0 100%, 0 0, 100% 0;

  /* The glass. A hard core with a long soft skirt gives every bulb a halo in
     ITS OWN colour for nothing — a filter could only ever tint them all alike. */
  -webkit-mask-image:
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px);
  mask-image:
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px),
    radial-gradient(circle at 50% 50%, rgba(0,0,0,1) 0, rgba(0,0,0,1) 2.4px, rgba(0,0,0,.92) 3.2px, rgba(0,0,0,.22) 5.3px, rgba(0,0,0,0) 8.6px);
  -webkit-mask-size:18px 20px, 18px 20px, 20px 18px, 20px 18px;
  mask-size:18px 20px, 18px 20px, 20px 18px, 20px 18px;
  -webkit-mask-position:0 0, 0 100%, 0 0, 100% 0;
  mask-position:0 0, 0 100%, 0 0, 100% 0;
  -webkit-mask-repeat:repeat-x, repeat-x, repeat-y, repeat-y;
  mask-repeat:repeat-x, repeat-x, repeat-y, repeat-y;

  animation:gc-chase 1.15s steps(8) infinite;
  opacity:.92;
}

/* One step = one 18px socket. Top runs right, right runs down, bottom runs
   left, left runs up — clockwise. */
@keyframes gc-chase{
  from{ background-position:0 0,      0 100%,       0 0,      100% 0; }
  to  { background-position:144px 0, -144px 100%,   0 -144px, 100% 144px; }
}

/* Pointing at the sign speeds the chase up and turns the bulbs full on.
   The trigger is the BOARD, not the button: the bulbs are part of the control
   even though they are not the thing you click. */
.gc-continue-rig:hover::before,
.gc-continue-rig:focus-within::before{
  animation-duration:.62s;
  opacity:1;
}

/* ── The button plate ─────────────────────────────────────────────────────
   .btn--pay hands this button the page's GOLD accent, and eight saturated
   enamel colours on a bright gold plate is a mess — the gold glyph disappears
   into it outright. The plate is re-tuned to near-black lacquer so every bulb
   colour reads, and the brass bevel .btn already draws is kept and thickened,
   so the affordance ("this is the big one") gets stronger, not weaker. The
   first background layer is the varnish: a specular sheen down the top third,
   which is what tells you the sign face is lacquered and not matte card.

   WHY THIS IS A CONTAINER, AND WHY IT SETS ITS OWN WIDTH.
   The label is sized in cqi off this box (see below). container-type:inline-size
   is only legal when the box's inline size does not depend on its contents, so
   the width is stated here rather than borrowed from pay.css's `.act .btn`
   rule — otherwise dropping this markup anywhere but inside .act would collapse
   the button to its own padding. The two agree exactly (both 100%/340px), so
   nothing about the current page moves.

   overflow:clip is the never-spill net, and it is NOT the fit mechanism — the
   sizing below is. It exists because a font that has not finished swapping, or
   an amount nobody predicted, must degrade to a clipped word inside the button
   and never to Rye running out across the page. overflow-clip-margin gives the
   neon bloom (26px at its widest) the room it needs so the net cannot eat the
   glow it is meant to protect. */
.gc-continue.btn{
  --plate-a:#3E2614; --plate-b:#26160A; --plate-c:#170D04; --plate-d:#0D0602;
  position:relative;
  z-index:2;
  container-type:inline-size;
  width:100%;
  max-width:340px;
  padding:16px 12px;
  min-height:60px;
  border-radius:12px;
  overflow:clip;
  overflow-clip-margin:18px;
  background:
    linear-gradient(180deg,
      rgba(255,240,205,.15) 0%, rgba(255,240,205,.04) 17%, rgba(255,240,205,0) 36%),
    linear-gradient(180deg,
      var(--plate-a) 0%, var(--plate-b) 38%, var(--plate-c) 82%, var(--plate-d) 100%);
  box-shadow:
    0 4px 6px rgba(0,0,0,.65),
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-mid),
    inset 0 0 0 1px rgba(255,233,168,.12),     /* the painted pinstripe */
    inset 1px 1px 0 rgba(255,233,168,.42),
    inset 0 -2px 8px rgba(0,0,0,.65);
  transition:box-shadow .18s ease, transform .06s ease, filter .18s ease;
}
/* .btn brightens its whole plate on hover; here the LIGHT is supposed to be
   the letters, so the plate stays put and only its rim answers. */
.gc-continue.btn:hover{
  filter:none;
  box-shadow:
    0 4px 6px rgba(0,0,0,.65),
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-highlight),
    0 0 18px rgba(255,208,106,.30),
    inset 0 0 0 1px rgba(255,233,168,.20),
    inset 1px 1px 0 rgba(255,247,221,.60),
    inset 0 -2px 8px rgba(0,0,0,.65);
}
.gc-continue.btn:active{ transform:translateY(1px); filter:none; }
.gc-continue.btn:focus-visible{ outline:3px solid var(--brass-highlight); outline-offset:5px; }

/* ── THE TWO-STATE LABEL ──────────────────────────────────────────────────
   js/donate.js does this, and there is no negotiating with it:

       continueBtn.querySelector('.btn__label').textContent = 'Continue with $25';

   so any <span> per letter this component ships is gone the moment an amount is
   chosen. The colour therefore lives in the ELEMENT, not in the markup: a
   hard-stopped gradient painted through background-clip:text, which colours
   whatever text happens to be inside it.

   WHERE THE STOPS COME FROM. They are not eyeballed, and they are not eight
   equal twelfths. They are Rye's own advance widths for C-o-n-t-i-n-u-e, read
   out of assets/fonts/Rye.ttf (unitsPerEm 2048; the face carries neither a
   `kern` table nor GPOS, so advance width IS the whole story):

       C 1320   o 1375   n 1385   t  895
       i  716   n 1385   u 1389   e 1220     total 9685 units = 4.72900em

   Cumulative, as a fraction of the word: 13.629 / 27.827 / 42.127 / 51.368 /
   58.761 / 73.061 / 87.403 / 100 %. Those ARE the stops, and the tile is
   4.729em wide — the exact width of the word — so on the label's initial text
   every band lands dead on a letter boundary. Measured: 0 of 8 glyphs split.

   When the label grows, the tile simply repeats. Two consequences, both worth
   knowing before anyone "fixes" this:
     * the word "Continue" is coloured IDENTICALLY before and after the rewrite,
       because it is always the first tile. The change is seamless.
     * the tail (" with $25") keeps the eight-colour rhythm but is no longer
       letter-aligned — measured 6 of 17 glyphs split, worst case an "i" halved.
       Hence the spectral colour order above: a split glyph is only ever split
       between two neighbouring hues, and the 0.35 % feather on each stop turns
       that seam into a paint overlap rather than a razor cut.

   ⚠ WHAT ACTUALLY FITS — AND WHY THIS IS SIZED IN cqi AND NOT IN vw.
   The first draft used clamp(15px, 4.6vw, 27px) on the claim that 4.6vw "keeps
   Continue with $1,000 inside a 320px phone". It does not, three ways over.
   A vw unit cannot see the chrome standing between the viewport and this box,
   and on this page that chrome is 100px per row before the button's own padding:
   .pay 20+20, .plate 34px 38px (which has NO mobile override anywhere in
   pay.css or skeuo.css), then the rig's 21+21 and the button's own. Measured
   through the real chain, and against Rye's real advances:

       viewport   .plate    rig content   BUTTON CONTENT ( = 100cqi )
         320px     204px       172px          148px
         375px     259px       227px          203px
         414px     298px       266px          242px
        >=620px    ...         340px          316px   (max-width pins it here)

   Longest strings donate.js can actually produce (money() emits no thousands
   separator, and MAX_CENTS is 2 500 000, so the chip ladder tops out at $1000
   and the #ownamount box at $25000 / $24999.99):

       "Continue"                4.7290em
       "Continue with $25"       9.4556em
       "Continue with $1000"    10.7866em
       "Continue with $25000"   11.5986em
       "Continue with $24999.99" 13.4204em   <- the real worst case

   13.4204em must fit in 100cqi, so the size ceiling is 100/13.4204 = 7.45cqi.
   7.3cqi is used, which leaves 6px of slack at the widest (23.07px font,
   309.6px of text in a 316px box) and holds at every step down to a 360px
   phone. Below ~330px the 12px floor takes over and the pathological decimal
   case overruns by ~13px — that, and only that, is what overflow:clip is for.
   The old 27px ceiling overflowed the button by 9px on a 1440px DESKTOP.

   The first declaration is the fallback for anything without container query
   units; the second wins wherever they exist. Both are checked against the
   same table.

   ⚠ text-shadow MUST be none here. .btn sets an emboss shadow that this label
   inherits, and text-shadow paints ABOVE an element's background — with the
   fill transparent that shadow lands on top of the clipped gradient as black
   smears across every letter. The emboss is restored below as a drop-shadow
   inside the filter chain, which composites in the right order.               */
.gc-continue .gc-continue__label{
  display:inline-block;
  min-width:0;                         /* so the flex item can be clipped, not spill */
  max-width:100%;
  font-family:"Rye", Georgia, serif;
  font-weight:400;
  font-size:clamp(12px, 3.6vw, 22px);  /* fallback: no container query units */
  font-size:clamp(12px, 7.3cqi, 24px); /* the real rule — see the table above */
  line-height:1.06;
  letter-spacing:0;                    /* letter-spacing would shift every band off its glyph */
  white-space:nowrap;
  text-shadow:none;                    /* see the warning above — this one is load-bearing */

  background-image:
    /* [0] the strike — a bright band swept across on hover; see @keyframes gc-strike */
    linear-gradient(96deg, rgba(255,255,255,0) 38%, rgba(255,255,255,.60) 50%, rgba(255,255,255,0) 62%),
    /* [1] the paint — one tile per "Continue", stops at Rye's real boundaries */
    linear-gradient(90deg,
      var(--gc-c1)  0%,     var(--gc-c1) 13.279%,
      var(--gc-c2) 13.979%, var(--gc-c2) 27.477%,
      var(--gc-c3) 28.177%, var(--gc-c3) 41.777%,
      var(--gc-c4) 42.477%, var(--gc-c4) 51.018%,
      var(--gc-c5) 51.718%, var(--gc-c5) 58.411%,
      var(--gc-c6) 59.111%, var(--gc-c6) 72.711%,
      var(--gc-c7) 73.411%, var(--gc-c7) 87.053%,
      var(--gc-c8) 87.753%, var(--gc-c8) 100%);
  background-size:60% 100%, 4.729em 100%;
  background-repeat:no-repeat, repeat;
  background-position:-150% 0, 0 0;
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  -webkit-text-fill-color:transparent;

  /* Unlit. The filter list is written with zero-strength members in the same
     ORDER as the lit state below, because a filter only interpolates against a
     matching list — mismatched lists snap on instead of glowing up.
     brightness(.88), not .84: at .84 the red glyph fell to 3.46:1 against the
     lacquer, which is inside AA-large only by a hair. .88 puts it near 3.9:1
     and the unlit-to-lit jump is still 25 % plus three glows. */
  filter:
    brightness(.88) saturate(.94)
    drop-shadow(0 1px 0 rgba(0,0,0,.9))
    drop-shadow(0 0 0 rgba(255,246,220,0))
    drop-shadow(0 0 0 rgba(255,150, 90,0));
  transition:filter .30s ease;
}

/* Ignition. The pointer anywhere on the button lights it; so does the keyboard. */
.gc-continue:hover .gc-continue__label,
.gc-continue:focus-visible .gc-continue__label{
  filter:
    brightness(1.10) saturate(1.10)
    drop-shadow(0 1px 0 rgba(0,0,0,.9))
    drop-shadow(0 0 4px rgba(255,246,220,.55))
    drop-shadow(0 0 15px rgba(255,150, 90,.42));
  animation:gc-strike .62s cubic-bezier(.3,.7,.4,1) 1 both;
}

/* The strike sweep, which is what "lights up left to right" looks like when
   there are no per-letter spans left to stagger. Layer [1] is pinned at 0 0 in
   BOTH keyframes — if the paint tile moved, the letters would change colour as
   the light passed over them.
   The band is 60 % of the label wide, so at -150 % its right edge sits exactly
   on the label's left edge and at 250 % its left edge sits exactly on the
   right edge: the sweep enters and leaves completely, with no clipped start. */
@keyframes gc-strike{
  from{ background-position:-150% 0, 0 0; }
  to  { background-position: 250% 0, 0 0; }
}

/* ── Per-letter glyphs (the pristine, pre-rewrite state) ──────────────────
   These spans exist so that the first thing anyone sees is the real article:
   eight glyphs, eight solid colours, each striking into its own neon on its
   own beat. They paint on top of the gradient — same colours, same order — so
   when donate.js throws them away nothing about the word visibly changes. The
   gradient underneath was already saying the same thing.

   The selectors cycle every eight (:nth-child(8n+k)) rather than matching 1..8,
   so if the integrator ever adopts the optional JS helper the colouring runs
   correctly through a label of any length. The trailing .sr-only twin is
   nth-child(9) and matches no .gc-ltr rule, so it costs the cycle nothing.    */
.gc-continue .gc-ltr{
  color:var(--gc-c);
  -webkit-text-fill-color:var(--gc-c);
  text-shadow:0 1px 0 rgba(0,0,0,.85);
  transition:color .26s ease, -webkit-text-fill-color .26s ease, text-shadow .26s ease;
}
.gc-continue:hover .gc-ltr,
.gc-continue:focus-visible .gc-ltr{
  color:var(--gc-n);
  -webkit-text-fill-color:var(--gc-n);
  /* core, tube, bloom — the three things you actually see in a lit neon letter */
  text-shadow:
    0 0  1px var(--gc-n),
    0 0  6px var(--gc-c),
    0 0 14px var(--gc-c),
    0 0 26px var(--gc-g);
  /* the sign warms up left to right, capped at ten sockets so a long label
     never takes longer to light than a pointer typically rests. The var()
     carries its own fallback: a span that somehow arrives without --gc-i
     lights immediately rather than dropping the declaration. */
  transition-delay:calc(min(var(--gc-i, 0), 10) * 38ms);
}

.gc-continue .gc-ltr:nth-child(8n+1){ --gc-c:var(--gc-c1); --gc-n:var(--gc-n1); --gc-g:var(--gc-g1); }
.gc-continue .gc-ltr:nth-child(8n+2){ --gc-c:var(--gc-c2); --gc-n:var(--gc-n2); --gc-g:var(--gc-g2); }
.gc-continue .gc-ltr:nth-child(8n+3){ --gc-c:var(--gc-c3); --gc-n:var(--gc-n3); --gc-g:var(--gc-g3); }
.gc-continue .gc-ltr:nth-child(8n+4){ --gc-c:var(--gc-c4); --gc-n:var(--gc-n4); --gc-g:var(--gc-g4); }
.gc-continue .gc-ltr:nth-child(8n+5){ --gc-c:var(--gc-c5); --gc-n:var(--gc-n5); --gc-g:var(--gc-g5); }
.gc-continue .gc-ltr:nth-child(8n+6){ --gc-c:var(--gc-c6); --gc-n:var(--gc-n6); --gc-g:var(--gc-g6); }
.gc-continue .gc-ltr:nth-child(8n+7){ --gc-c:var(--gc-c7); --gc-n:var(--gc-n7); --gc-g:var(--gc-g7); }
.gc-continue .gc-ltr:nth-child(8n+8){ --gc-c:var(--gc-c8); --gc-n:var(--gc-n8); --gc-g:var(--gc-g8); }

/* The visible label is a decorative rendering of a word; the word itself rides
   along as an .sr-only twin inside the same span (skeuo.css carries that class
   for precisely this), so the button's accessible name stays "Continue" and
   never "C o n t i n u e". */

/* ── Fallbacks ────────────────────────────────────────────────────────────
   background-clip:text is the one modern thing this component leans on. Where
   it is missing the letters must not vanish, so the fill goes back to solid
   brass — the cabinet's own --brass-light, not a hex invented for the occasion;
   the per-letter spans use plain colour and carry on unaffected. */
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .gc-continue .gc-continue__label{
    background-image:none;
    color:var(--brass-light);
    -webkit-text-fill-color:var(--brass-light);
  }
}
/* Forced colours: hand the whole thing back to the OS. A marquee that survives
   by being pretty is worth less than a button somebody can read. */
@media (forced-colors: active){
  .gc-continue .gc-continue__label{
    background-image:none; filter:none;
    color:CanvasText; -webkit-text-fill-color:CanvasText;
  }
  .gc-continue .gc-ltr{ color:CanvasText; -webkit-text-fill-color:CanvasText; text-shadow:none; }
  .gc-continue-rig::before,
  .gc-continue-rig::after{ display:none; }
}

/* While donate.js is talking to the Worker it adds .paying and swaps the label
   to "One moment...". A sign that keeps advertising itself mid-transaction reads
   as a page that did not hear the click, so it dims and slows instead. */
.gc-continue.paying .gc-continue__label{ animation:none; }
.gc-continue-rig:has(.paying)::before{ animation-duration:2.4s; opacity:.55; }

/* ══════════════════════════════════════════════════════════════════════════
   PART B — THE ENTRY CARDS
   ══════════════════════════════════════════════════════════════════════════ */

.gc-fields{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:20px;
  margin:6px 0 0;
}
@media (max-width:620px){ .gc-fields{ grid-template-columns:1fr; gap:18px; } }

/* .gc-card sits ON TOP of pay.css's .field, which already gives it block
   display and a bottom margin. Every rule below is qualified with a .gc-*
   ancestor so it wins on SPECIFICITY rather than on stylesheet order —
   give.css must not care whether it is concatenated before or after pay.css. */
.gc-card{ position:relative; }
/* .field carries a 16px bottom margin; inside a grid the gap owns the spacing. */
.gc-fields .gc-card{ margin:0; }

/* The painted nameplate, riveted to the top edge of the recess below it. Rye
   at label size is the point: these two questions are the only place on the
   page where a donor writes back to the owner, and they should look like the
   sign over a booth rather than like a form field.

   ⚠ display:block + width:max-content, NOT inline-block. The plate tucks under
   itself with margin-bottom:-10px, and on an inline-block that -10px fights the
   line box's strut: an atomic inline sits on the parent's baseline and the
   parent's font reserves descender space beneath it, so the real overlap comes
   out as -10px plus however much descender the inherited font happens to have.
   A block box has no line box and no strut, so -10px means -10px. */
.gc-card__plate{
  position:relative;
  z-index:2;
  display:block;
  width:max-content;
  max-width:100%;
  margin:0 0 -10px 13px;
  padding:5px 26px;
  border-radius:9px 9px 8px 8px;
  background:linear-gradient(180deg,#4E3117 0%,#331D0B 55%,#241407 100%);
  box-shadow:
    0 0 0 1px var(--wood-line),
    0 0 0 2px var(--brass-mid),
    0 3px 5px rgba(0,0,0,.6),
    inset 0 1px 0 rgba(255,233,168,.30);
}
/* the two screws holding it on */
.gc-card__plate::before,
.gc-card__plate::after{
  content:"";
  position:absolute; top:50%; width:6px; height:6px; margin-top:-3px;
  border-radius:50%;
  background:radial-gradient(circle at 35% 30%, #FFE9A8 0%, #C99230 45%, #6B4A10 100%);
  box-shadow:0 1px 0 rgba(0,0,0,.7);
}
.gc-card__plate::before{ left:9px; }
.gc-card__plate::after { right:9px; }

.gc-card .field__label{
  display:block; margin:0;
  font-family:"Rye", Georgia, serif;
  font-weight:400; font-size:15px; letter-spacing:.02em;
  text-transform:none;
  color:var(--title-gold);
  text-shadow:0 -1px 0 #000, 0 1px 0 rgba(255,233,168,.22);
}

/* The recess. The lip is brass and the hole is deep — the game's own sunk
   window, given a heavier rim because an entry card has one. */
.gc-card__well{
  position:relative;
  display:block;
  border-radius:11px;
  background:linear-gradient(180deg,#0D0702 0%,#1D1106 58%,#2B1A08 100%);
  box-shadow:
    inset 0 3px 7px rgba(0,0,0,.95),
    inset 0 -1px 0 rgba(255,233,168,.10),
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-dark),
    0 0 0 4px var(--wood-line),
    0 3px 7px rgba(0,0,0,.45);
  transition:box-shadow .16s ease;
}
/* The input keeps its site class — donate.js finds it by id and pay.css styles
   the class — and is stripped back to bare text here so that the WELL is the
   thing you see rather than a second box inside it. */
.gc-card .gc-card__well .field__input{
  position:relative;
  z-index:1;
  width:100%;
  background:none;
  box-shadow:none;
  border:none;
  border-radius:11px;
  padding:16px 15px 13px;
  font-family:var(--font-body); font-size:15.5px; line-height:1.35;
  color:var(--label-hi);
}
/* ⚠ The gutter belongs to the PICTOGRAM, not to the class. The first draft put
   56px of right padding on every .gc-card input, including the two in the entry
   box that carry no mark — so Name and Location each lost a fifth of their
   width to a hole with nothing in it. Ask whether the mark is actually there. */
.gc-card__well:has(.gc-card__mark) .field__input{ padding-right:56px; }
/* inside the entry box the cards are ~212px, so the mark and its gutter shrink
   with them rather than eating the placeholder */
.gc-entry .gc-card__well:has(.gc-card__mark) .field__input{ padding-right:46px; }
.gc-entry .gc-card__mark{ width:34px; height:23px; margin-top:-11px; right:10px; }

.gc-card .gc-card__well .field__input:focus{ outline:none; box-shadow:none; }
/* #7A6A52 is what pay.css uses, and against this well it measures 3.46:1 —
   thin for a placeholder that is carrying real instruction ("Any flat ride,
   roller coaster, or beyond!" is the answer prompt, not decoration). #8B7A5E
   is 4.45:1 and still unmistakably a placeholder rather than a value. */
.gc-card .gc-card__well .field__input::placeholder{ color:#8B7A5E; font-style:italic; }
.gc-card .gc-card__well .field__input:disabled{ color:#6C5B44; }
/* Chrome paints its own opaque autofill plate straight over the recess and no
   background rule can beat it — only an inset shadow the size of the box can.
   Without this, one saved-form suggestion turns a brass-lipped slot into a
   flat blue rectangle. */
.gc-card .gc-card__well .field__input:-webkit-autofill{
  -webkit-text-fill-color:var(--label-hi);
  -webkit-box-shadow:inset 0 0 0 1000px #1D1106;
  caret-color:var(--label-hi);
}

/* Lit. The brass lip goes bright, the recess catches a warm glow, and the
   engraved pictogram at the end of the slot lights with it — one gesture in
   three parts, so the field reads as switched ON and not merely outlined. */
.gc-card__well:focus-within{
  box-shadow:
    inset 0 3px 7px rgba(0,0,0,.95),
    inset 0 0 14px rgba(255,180,70,.14),
    0 0 0 1px var(--wood-line),
    0 0 0 3px var(--brass-highlight),
    0 0 0 4px var(--wood-line),
    0 0 16px rgba(255,208,106,.45),
    0 3px 7px rgba(0,0,0,.45);
}

/* ── The engraved pictogram ───────────────────────────────────────────────
   Cut into the metal at the right-hand end of the slot: a dark stroke with a
   one-pixel warm edge under it, which is all "engraved" has ever been. Drawn
   as SVG paths and never as an emoji or a font glyph — an emoji is a different
   picture on every machine, and this site ships no icon face.

   40x27 against a 48x32 viewBox is not a square ratio; preserveAspectRatio
   defaults to xMidYMid meet, so the art scales uniformly by min(40/48, 27/32)
   = 0.8333 and centres, losing 0.17px of vertical letterbox. Deliberate: the
   alternative is stretching a wheel into an ellipse. */
.gc-card__mark{
  position:absolute;
  right:12px; top:50%;
  width:40px; height:27px;
  margin-top:-13px;
  pointer-events:none;
  color:#7C5825;
  opacity:.85;
  filter:drop-shadow(0 1px 0 rgba(255,233,168,.13));
  transition:color .18s ease, opacity .18s ease, filter .18s ease;
}
.gc-card__well:focus-within .gc-card__mark{
  color:var(--brass-highlight);
  opacity:1;
  filter:drop-shadow(0 0 7px rgba(255,208,106,.6));
}

.gc-fields__note{
  margin:14px 0 22px;
  font-size:13px; line-height:1.5; color:var(--label-dim);
  text-align:left;
}
.gc-fields__note b{ color:var(--brass-light); font-weight:400; }

/* ── The entry form ───────────────────────────────────────────────────────
   Everything about the leaderboard, boxed as one object: a pinned card with a
   dashed rule printed inside its rim, the way a county-fair entry slip is. It
   is a box on purpose — this is the only part of the form that PUBLISHES
   anything, and it should look like a separate decision.

   ⚠ text-align:left is not cosmetic. This box lands inside .jar, which is
   text-align:center for the chips and the coin slot. pay.css's .check and
   .field opt out explicitly, so the fields looked right — but .gc-entry__tag
   and .gc-entry__note inherit, and both were quietly centring themselves in the
   middle of a left-aligned form. Set it once on the box rather than patching
   each descendant, which is how the next one gets missed. */
.gc-entry{
  position:relative;
  margin:4px 0 22px;
  padding:20px 22px 16px;
  border-radius:15px;
  text-align:left;
  background:linear-gradient(180deg,#35220F 0%,#241507 60%,#1A0F05 100%);
  box-shadow:
    0 0 0 1px var(--wood-line),
    0 0 0 2px var(--brass-dark),
    inset 0 1px 0 rgba(255,233,168,.16),
    0 6px 16px rgba(0,0,0,.45);
}
.gc-entry::before{
  content:"";
  position:absolute; inset:6px;
  border:1px dashed rgba(201,146,48,.40);
  border-radius:10px;
  pointer-events:none;
}
.gc-entry .gc-entry__tag{ margin:0 0 12px; }

/* pay.css's .check is already a real restyled checkbox — it only needs the
   weight of a headline here, because that is what it is. */
.gc-entry .gc-entry__optin{ margin:0 0 4px; align-items:center; }
.gc-entry .gc-entry__optin .check__text b{
  font-family:"Rye", Georgia, serif;
  font-weight:400; font-size:16.5px; color:var(--title-gold);
  text-shadow:0 1px 0 rgba(0,0,0,.7);
}

/* The sub-questions. pay.css dims them until the box is ticked and indents
   them 32px; inside a card that indent runs them straight into the dashed
   rule, so the rail is redrawn as a brass margin instead.
   The .is-on rule below is NOT redundant: the shorthand `border-left` here
   overwrites the border-left-color that pay.css's `.reveal.is-on` sets, and the
   two selectors tie on specificity, so the lit rail has to be restated at a
   weight that wins whichever order the sheets are concatenated in. */
.gc-entry .reveal{
  margin:12px 0 0;
  padding:14px 16px 2px;
  border-left:3px solid var(--brass-dark);
  border-radius:0 11px 11px 0;
  background:rgba(0,0,0,.30);
}
.gc-entry .reveal.is-on{ border-left-color:var(--accent); }
.gc-entry .gc-fields{ margin:0; gap:16px; }
.gc-entry .check{ margin:0 0 11px; }
.gc-entry__note{
  margin:10px 0 0;
  font-size:12.5px; line-height:1.5; color:var(--label-dim);
}
.gc-entry__note b{ color:var(--brass-light); font-weight:400; }

@media (max-width:560px){
  .gc-continue-rig{ padding:16px; border-radius:19px; }
  .gc-continue-rig::before,
  .gc-continue-rig::after{ border-radius:17px; }
  .gc-entry{ padding:16px 14px 12px; }
  .gc-card__plate{ padding:5px 22px; margin-left:10px; }
}

/* ── Motion ───────────────────────────────────────────────────────────────
   Reduced motion keeps every feature and removes the travel: the bulbs stay
   coloured and lit, they simply stop chasing, and the sign still strikes into
   neon — all eight letters at once, over a slow third of a second, instead of
   running along the word. Nothing here is switched off.

   ⚠ The label is given .34s rather than being lumped in with the .01ms list
   below. It was in that list, which made the paragraph above false: the glow
   snapped on instantly while the per-letter spans faded over .34s, so the two
   halves of the same effect disagreed. A cross-fade of colour is not travel and
   is not what prefers-reduced-motion is asking to be rid of. */
@media (prefers-reduced-motion: reduce){
  .gc-continue-rig::before,
  .gc-continue-rig:hover::before,
  .gc-continue-rig:focus-within::before{ animation:none; opacity:1; }
  .gc-continue:hover .gc-continue__label,
  .gc-continue:focus-visible .gc-continue__label{ animation:none; }
  .gc-continue .gc-ltr{ transition-duration:.34s; }
  .gc-continue:hover .gc-ltr,
  .gc-continue:focus-visible .gc-ltr{ transition-delay:0s; }
  .gc-continue .gc-continue__label{ transition-duration:.34s; }
  /* .gc-continue.btn, not .gc-continue: the transition it is cancelling is set
     by `.gc-continue.btn`, and a one-class selector loses that tie outright —
     a reduced-motion override that is quietly out-specified is worse than none,
     because it reads as handled. */
  .gc-card__well,
  .gc-card__mark,
  .gc-continue-rig,
  .gc-continue.btn{ transition-duration:.01ms; }
}


/* ==== part_leaderboard.css ==== */
/* ============================================================================
   THE LEADERBOARD — the midway prize board at the foot of the donate page.

   The object is a HALL-OF-FAME PLAQUE screwed to the cabinet: brass rim, four
   rivets, engraved column heads, and a row of espresso plaques under them. It
   is deliberately the same family as pay.css's .board (the /board page) rather
   than a second look — same espresso row, same brass top three, same Rye
   numerals — but it is the dressed version, because this one has to persuade
   somebody standing at the counter with their card out.

   Everything reads from the page accent (--accent*, set once on .pay), so the
   board would re-tint red on the buy page without a line changing here.

   TWO THINGS THIS FILE IS RESPONSIBLE FOR THAT ARE EASY TO MISS:

   1. THE EMPTY STATE IS THE DEFAULT STATE. .lb-ghost and .lb-empty are visible
      until JavaScript hides them. Every rule below therefore has to look
      finished with no data at all — that is the state the owner opens today.

   2. ONE COLUMN DEFINITION, TWO CONSUMERS. The legend and the rows must agree
      to the pixel or the heads sit over the wrong cells, so the track list is a
      single custom property (--lb-cols) that both read. Editing it in one place
      moves both; editing it in two places is how they drift.
   ============================================================================ */

.lb-plate{
  position:relative;
  max-width:960px; margin:26px auto 0;
  padding:30px 34px 28px;
  /* the one column definition — see note 2 in the header */
  --lb-cols:62px minmax(0,1.5fr) minmax(0,1.15fr) minmax(0,1.15fr) minmax(92px,auto);
  --lb-gap:16px;
}

/* Four brass rivets, one per corner — the plaque is SCREWED to the cabinet.
   Radial gradients rather than four elements: it is pure decoration and it must
   not cost the row layout a node. */
.lb-plate::before{
  content:""; position:absolute; inset:9px; border-radius:8px; pointer-events:none;
  background:
    radial-gradient(circle 3.5px at 0    0,    var(--accent-hi) 0 38%, var(--accent-mid) 40% 72%, rgba(0,0,0,.55) 74%, transparent 76%),
    radial-gradient(circle 3.5px at 100% 0,    var(--accent-hi) 0 38%, var(--accent-mid) 40% 72%, rgba(0,0,0,.55) 74%, transparent 76%),
    radial-gradient(circle 3.5px at 0    100%, var(--accent-hi) 0 38%, var(--accent-mid) 40% 72%, rgba(0,0,0,.55) 74%, transparent 76%),
    radial-gradient(circle 3.5px at 100% 100%, var(--accent-hi) 0 38%, var(--accent-mid) 40% 72%, rgba(0,0,0,.55) 74%, transparent 76%);
  background-repeat:no-repeat;
}

/* ══ THE HEAD ═════════════════════════════════════════════════════════════ */
.lb-head{ text-align:center; margin:0 0 22px; }
.lb-tag{ margin-bottom:12px; }
/* Rye, because this is signage. The rest of the page sets its headings in the
   display stack; a board hung over a midway is painted, and Rye is the painted
   voice the mark and the marquee already use. */
.lb-title{
  margin:0 0 8px;
  font-family:"Rye",Georgia,serif; font-weight:400;
  font-size:clamp(27px,4.2vw,42px); line-height:1.05;
  color:var(--title-gold);
  text-shadow:0 2px 0 var(--emboss-dark), 0 3px 7px rgba(0,0,0,.55),
              0 0 26px rgba(245,194,90,.24);
}
.lb-sub{
  margin:0 auto !important; max-width:56ch;
  font-size:15.5px !important; line-height:1.62 !important; color:var(--label-dim);
  text-shadow:0 1px 0 rgba(0,0,0,.5);
}

/* ══ THE ENGRAVED COLUMN HEADS ════════════════════════════════════════════ */
.lb-legend{
  display:grid; grid-template-columns:var(--lb-cols); gap:0 var(--lb-gap);
  align-items:end;
  padding:0 18px 8px; margin:0 0 10px;
  border-bottom:1px solid rgba(201,146,48,.42);
  box-shadow:0 1px 0 rgba(0,0,0,.5);
}
.lb-legend__c{
  font-family:var(--font-display); font-weight:700; font-size:10.5px;
  letter-spacing:.19em; text-transform:uppercase;
  color:var(--accent); text-shadow:0 1px 0 var(--emboss-dark);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.lb-legend__c:first-child{ text-align:center; }
.lb-legend__c--amt{ text-align:right; }

/* ══ THE ROWS ═════════════════════════════════════════════════════════════ */
.lb-list{ margin:0; padding:0; list-style:none; }

.lb-row{
  display:grid; grid-template-columns:var(--lb-cols); gap:0 var(--lb-gap);
  align-items:center;
  padding:13px 18px; margin-bottom:8px; border-radius:10px;
  background:linear-gradient(180deg,#2a1a0c,#180d05);
  box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--brass-dark), var(--shadow-drop);
  transition:box-shadow .14s ease, transform .14s ease;
}
.lb-row:last-child{ margin-bottom:0; }
/* A row is a person, so it answers when you point at it. Lift only — a colour
   change would fight the brass the top three already wear. */
.lb-row:hover{
  transform:translateY(-2px);
  box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--accent-mid),
             0 8px 18px rgba(0,0,0,.5);
}
/* The top three wear brass, the way a midway board would. Same three steps as
   pay.css's .board__row--n, so a donor moving between the two pages sees the
   same medal. */
.lb-row--1{ box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--accent-hi), 0 0 18px rgba(255,208,106,.42), var(--shadow-drop); }
.lb-row--2{ box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--accent),    0 0 11px rgba(255,208,106,.24), var(--shadow-drop); }
.lb-row--3{ box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--accent-mid), var(--shadow-drop); }
.lb-row--1:hover,.lb-row--2:hover,.lb-row--3:hover{
  box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--accent-hi),
             0 0 22px rgba(255,208,106,.5), 0 8px 18px rgba(0,0,0,.5);
}

/* ── rank + medallion ──────────────────────────────────────────────────── */
.lb-rank{
  position:relative; display:flex; align-items:center; justify-content:center;
  min-height:40px;
}
.lb-rank__n{
  position:relative; z-index:1;
  font-family:"Rye",Georgia,serif; font-size:24px; line-height:1;
  color:var(--accent); text-shadow:0 2px 0 #2A1806;
}
.lb-row--1 .lb-rank__n,
.lb-row--2 .lb-rank__n,
.lb-row--3 .lb-rank__n{ color:var(--accent-hi); }
/* The medallion sits BEHIND the numeral and is drawn, never typed. */
.lb-rosette{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:44px; height:44px; fill:none; stroke:var(--accent-mid);
  stroke-width:1.1; opacity:.62; pointer-events:none;
}
.lb-rosette__ticks{ stroke-width:2.6; stroke-linecap:round; }
.lb-rosette__ring--in{ opacity:.55; }
.lb-row--1 .lb-rosette{ stroke:var(--accent-hi); opacity:.95; }
.lb-row--2 .lb-rosette{ stroke:var(--accent);    opacity:.8;  }

/* ── who ───────────────────────────────────────────────────────────────── */
.lb-who{ min-width:0; }
.lb-name{
  font-family:var(--font-display); font-weight:700; font-size:16.5px; line-height:1.3;
  color:var(--label-hi); text-shadow:0 1px 0 var(--emboss-dark);
  /* a name typed by a stranger can be one 120-character word; it wraps rather
     than pushing the amount column off the plaque */
  overflow-wrap:anywhere; word-break:break-word;
}
.lb-from{
  display:flex; align-items:center; gap:5px; margin-top:2px;
  font-size:12.5px; color:var(--label-dim); min-width:0;
}
.lb-from__v{ overflow-wrap:anywhere; min-width:0; }

/* ── the two write-ins ─────────────────────────────────────────────────── */
.lb-fav{
  display:flex; align-items:flex-start; gap:6px; min-width:0;
  font-size:13.5px; line-height:1.42; color:var(--accent);
}
.lb-fav .lb-i{ margin-top:2px; }
.lb-fav__v{ overflow-wrap:anywhere; min-width:0; }
/* Not everyone answers the questions. A blank cell reads as a broken cell, so
   an unanswered one keeps its place with a dash and steps back in colour. */
.lb-fav--none{ color:var(--label-dim); opacity:.5; }

/* ── the figure ────────────────────────────────────────────────────────── */
.lb-amt{
  font-family:"Rye",Georgia,serif; font-size:23px; line-height:1.1;
  color:var(--label-hi); white-space:nowrap; text-align:right;
  text-shadow:0 2px 0 #2A1806;
}
/* "I gave, don't print the number" is a choice, not a gap — it gets a warm
   line rather than an empty column. */
.lb-amt--hidden{
  font-family:var(--font-body); font-size:12.5px; letter-spacing:.07em;
  text-transform:uppercase; color:var(--label-dim); text-shadow:none;
  white-space:normal;
}

/* ══ THE ICONS ════════════════════════════════════════════════════════════ */
.lb-i{
  flex:none; fill:none; stroke:currentColor; stroke-width:1.55;
  stroke-linecap:round; stroke-linejoin:round; overflow:visible;
}
.lb-i--star{ fill:var(--accent); stroke:var(--accent-hi); stroke-width:1.1; }
.lb-i--chev{ stroke-width:2; transition:transform .18s ease; }
.lb-ico-defs[hidden]{ display:none; }

/* ══ THE GHOSTS — the board's structure, with nobody on it ════════════════ */
.lb-ghost{
  /* The stack dissolves downward so six rows read as "and so on" instead of as
     six rows that stopped. */
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 44%,rgba(0,0,0,.25) 88%,transparent 100%);
          mask-image:linear-gradient(180deg,#000 0%,#000 44%,rgba(0,0,0,.25) 88%,transparent 100%);
}
.lb-ghost[hidden]{ display:none; }
.lb-row--ghost{
  background:rgba(0,0,0,.26);
  box-shadow:0 0 0 1px rgba(201,146,48,.16), inset 0 0 0 1px rgba(0,0,0,.35);
  border:1px dashed rgba(201,146,48,.30);
  padding:12px 17px;
}
.lb-row--ghost:hover{ transform:none;
  box-shadow:0 0 0 1px rgba(201,146,48,.16), inset 0 0 0 1px rgba(0,0,0,.35); }
.lb-row--ghost .lb-rank__n{ color:var(--accent-mid); opacity:.5; }
/* the worked example: the field names, in the field's own place */
.lb-row--sample .lb-name{ color:var(--label); opacity:.62; }
.lb-row--sample .lb-from,
.lb-row--sample .lb-fav{ opacity:.5; }
.lb-amt--ghost{
  font-family:var(--font-body); font-size:12.5px; letter-spacing:.07em;
  text-transform:uppercase; color:var(--label-dim); opacity:.6; text-shadow:none;
}

/* A ghosted value: a brass-lit bar with a slow sweep across it, so the empty
   board looks like a board WAITING rather than a board that failed to load. */
.lb-bar{
  display:block; width:var(--lb-w,70%); height:9px; border-radius:5px;
  background:
    linear-gradient(90deg, rgba(201,146,48,.06) 0%, rgba(245,194,90,.20) 45%,
                           rgba(201,146,48,.06) 90%),
    rgba(255,255,255,.045);
  background-size:220% 100%, 100% 100%;
  box-shadow:inset 0 0 0 1px rgba(201,146,48,.14);
  animation:lb-sweep 3.6s linear infinite;
}
.lb-bar + .lb-bar{ margin-top:7px; }
.lb-bar--sm{ height:7px; opacity:.7; }
.lb-bar--amt{ height:13px; margin-left:auto; max-width:78px; }
@keyframes lb-sweep{ from{ background-position:120% 0, 0 0 } to{ background-position:-120% 0, 0 0 } }
/* the rows are staggered so the sweep travels down the board, not across it */
.lb-row--ghost:nth-child(2) .lb-bar{ animation-delay:.18s }
.lb-row--ghost:nth-child(3) .lb-bar{ animation-delay:.36s }
.lb-row--ghost:nth-child(4) .lb-bar{ animation-delay:.54s }
.lb-row--ghost:nth-child(5) .lb-bar{ animation-delay:.72s }
.lb-row--ghost:nth-child(6) .lb-bar{ animation-delay:.90s }

/* ══ THE INVITATION ═══════════════════════════════════════════════════════ */
.lb-empty{
  display:flex; align-items:center; justify-content:center; gap:10px;
  /* 64ch, not 52: the sentence is 57 characters and a 52ch box breaks it after
     "could be", which leaves the offer hanging on a line of its own. */
  margin:6px auto 0 !important; max-width:64ch;
  padding:15px 22px; border-radius:11px; text-align:center;
  background:linear-gradient(180deg,rgba(58,36,16,.85),rgba(27,17,8,.85));
  box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--accent-mid),
             0 0 22px rgba(255,208,106,.13) inset;
  font-size:15.5px !important; line-height:1.5 !important; color:var(--label-hi);
  text-shadow:0 1px 0 var(--emboss-dark);
}
.lb-empty[hidden]{ display:none; }
.lb-empty .lb-i--star{ filter:drop-shadow(0 0 6px rgba(255,208,106,.55)); }

/* ══ THE DOOR TO EVERYONE ELSE ════════════════════════════════════════════ */
.lb-foot{
  display:flex; flex-direction:column; align-items:center; gap:9px;
  margin-top:18px;
}
.lb-more{ min-width:230px; }
.lb-more[hidden]{ display:none; }
.lb-more[aria-expanded="true"] .lb-i--chev{ transform:rotate(180deg); }
.lb-count{
  margin:0 !important; font-size:12.5px !important; color:var(--label-dim);
  letter-spacing:.05em;
}

.lb-scroll{
  position:relative; margin-top:14px;
  max-height:clamp(300px,52vh,560px);
  overflow-y:auto; overscroll-behavior:contain;
  /* the 34px foot is room for .lb-fade to sit in at maximum scroll, so the last
     donor on the board is never read through a gradient */
  padding:0 12px 34px; border-radius:11px;
  background:rgba(0,0,0,.34);
  box-shadow:inset 0 3px 9px rgba(0,0,0,.7),
             0 0 0 1px var(--wood-line), 0 0 0 2px var(--brass-dark);
  /* a scroll region a keyboard can reach must SHOW that it has been reached */
  scrollbar-width:thin; scrollbar-color:var(--accent-mid) rgba(0,0,0,.5);
}
.lb-scroll[hidden]{ display:none; }
.lb-scroll:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:3px; }
.lb-scroll::-webkit-scrollbar{ width:12px; }
.lb-scroll::-webkit-scrollbar-track{
  background:rgba(0,0,0,.5); border-radius:9px;
  box-shadow:inset 0 0 0 1px var(--wood-line);
}
.lb-scroll::-webkit-scrollbar-thumb{
  border-radius:9px; border:2px solid transparent; background-clip:padding-box;
  background-color:var(--accent-mid);
  box-shadow:inset 0 1px 0 rgba(255,233,168,.6), inset 0 -1px 0 rgba(0,0,0,.4);
}
.lb-scroll::-webkit-scrollbar-thumb:hover{ background-color:var(--accent); }
/* The caption stays put while the names go past it, so a reader forty rows in
   still knows what box they are inside. */
.lb-restcap{
  position:sticky; top:0; z-index:2;
  margin:0 -12px 10px !important; padding:9px 18px;
  background:linear-gradient(180deg,#1B1108 62%,rgba(27,17,8,0) 100%);
  font-family:var(--font-display); font-weight:700; font-size:10.5px !important;
  letter-spacing:.19em; text-transform:uppercase; color:var(--accent);
  text-shadow:0 1px 0 var(--emboss-dark);
}
.lb-list--rest{ padding-top:2px; }
/* Sticky rather than absolute: an absolutely-positioned child of a scroll
   container scrolls away with the content, which is exactly the one thing this
   must not do. The negative top margin means it occupies no flow height, so it
   cannot push the list taller than the box it is meant to bound. */
.lb-fade{
  position:sticky; bottom:0; height:36px; margin:-36px -12px 0;
  pointer-events:none;
  background:linear-gradient(180deg, rgba(20,11,4,0) 0%, rgba(20,11,4,.78) 62%,
                                     rgba(20,11,4,.95) 100%);
}

/* ══ PHONE ════════════════════════════════════════════════════════════════
   A five-column table does not survive 375px, so it stops being one: the rank
   keeps its own narrow column and everything else stacks under the name in
   reading order — who, where, ride, park, gift. The column heads go with it,
   which is why every cell carries its own .sr-only name. */
@media (max-width:720px){
  .lb-plate{ padding:24px 16px 22px; border-radius:10px; }
  .lb-plate::before{ inset:7px; }
  .lb-legend{ display:none; }

  .lb-row{ grid-template-columns:42px minmax(0,1fr); gap:0 12px; padding:12px 13px;
           align-items:start; }
  .lb-rank{ min-height:0; padding-top:2px; }
  .lb-rank__n{ font-size:20px; }
  .lb-rosette{ width:34px; height:34px; }
  .lb-fav, .lb-amt{ grid-column:2; }
  .lb-fav{ margin-top:5px; }
  /* The dash exists to hold a COLUMN open, and there are no columns down here —
     it would just be a stray line in a stack. The .sr-only "not given" goes
     with it, which is right: an unanswered optional question is not news. */
  .lb-fav--none{ display:none; }
  .lb-amt{ text-align:left; margin-top:7px; font-size:20px; }
  /* Both of these are small-caps prose, not a figure, so they must NOT inherit
     the 20px the numeral gets — and they have to be restated here because the
     rule above them has the same specificity and comes later. */
  .lb-amt--hidden, .lb-amt--ghost{ font-size:12px; }
  .lb-bar--amt{ margin-left:0; }

  .lb-sub{ font-size:14.5px !important; }
  .lb-empty{ font-size:14.5px !important; padding:14px 16px; gap:8px; }
  .lb-more{ min-width:0; width:100%; max-width:320px; }
  .lb-scroll{ max-height:clamp(280px,58vh,520px); padding:0 8px 10px; }
  .lb-restcap{ margin-left:-8px !important; margin-right:-8px !important; padding:8px 13px; }
}

/* ══ REDUCED MOTION ═══════════════════════════════════════════════════════
   The sweep is the only thing moving here, and it is decoration on an empty
   state. Nothing is lost by stopping it, so it stops. */
@media (prefers-reduced-motion: reduce){
  .lb-bar{ animation:none; background-position:50% 0, 0 0; }
  .lb-row, .lb-i--chev{ transition:none; }
  .lb-row:hover{ transform:none; }
}

/* ══ THE PROSCENIUM ═══════════════════════════════════════════════════════
   Owner: "make the header of this theatrical with a retracted curtain."

   The first pass hung the curtain as a BAND above the title, which is a banner
   with a picture of a curtain on it. A curtain that has been retracted is a
   FRAME: the drapes are pulled aside and what you see between them is the thing
   they were covering. So the stage is the header's background and the title
   plays ON it, in the opening the drapes have made.

   The SVG is `preserveAspectRatio="none"` on purpose — it is scenery, not a
   diagram, and stretching the velvet to the header's proportions is exactly
   what a real cloth does when it is hung on a wider rod. */
.lb-head{
  position:relative;
  margin:0 0 26px;
  padding:74px 20px 34px;              /* the valance owns the top */
  border-radius:14px;
  overflow:hidden;
  box-shadow:inset 0 0 0 1px var(--wood-line),
             inset 0 0 0 2px var(--brass-dark),
             inset 0 -30px 46px rgba(0,0,0,.55);
}
.lb-stage{ position:absolute; inset:0; z-index:0; }
.lb-curtain{ display:block; width:100%; height:100%; }
.lb-head > .lb-title,
.lb-head > .lb-sub{ position:relative; z-index:1; }
.lb-title{
  /* lit from the front, the way a title on a stage is */
  text-shadow:0 2px 0 #2A1806, 0 3px 10px rgba(0,0,0,.8),
              0 0 30px rgba(245,194,90,.45);
}
.lb-sub{
  color:#F7EAD2 !important;
  text-shadow:0 1px 0 rgba(0,0,0,.85), 0 2px 8px rgba(0,0,0,.6);
}
@media (max-width:760px){
  .lb-head{ padding:52px 14px 24px; }
}


/* ==== part_celebrate.css ==== */
/* ============================================================================
   THE CELEBRATIONS  ("cel-")
   The rising balloons and the fireworks, both in the page BACKGROUND.

   There is exactly one rule here, because the effect itself is drawn -- this
   file's whole job is to hang one canvas in the right place in the stack and
   guarantee it can never touch the page. Everything that moves lives in
   js/celebrate.js, and the element below does not exist until that file is
   asked for a burst.

   ONE SELECTOR, ONE CLASS, NO BARE ELEMENT SELECTORS. Nothing in this file can
   reach another component: there is no `canvas {}`, no `body {}`, no id, and
   the only class is prefixed. (The reverse is defended in js/celebrate.js: if
   somebody else's partial ships a bare `canvas {}` that lands on this element
   and knocks it out of position:fixed, the script notices at creation time and
   inlines the essentials, which beat any stylesheet rule.)
   ============================================================================ */

/* -- THE LAYER --------------------------------------------------------------
   z-index:-1, and the value is reasoned rather than picked:

     * the grass is the BODY background, and a body background with no html
       background is propagated to the root canvas, which paints FIRST -- before
       any child of any kind, negative z-index included;
     * every .plate, the jar and the whole form are in-flow BLOCK boxes, which
       paint AFTER all negative-z-index positioned children;
     * the site header is position:sticky with z-index:50 and stays clear of
       all of this either way.

   So the painting order is exactly what the brief asks for:

       grass  <  .cel-layer (-1)  <  the plates and the form  <  header (50)

   z-index:0 or auto would paint the balloons straight OVER the card fields,
   which is the one thing this must never do.

   THE NEIGHBOUR IN THE NEGATIVE LAYER. The amount chips (gv-) hang
   `body::after` at z-index:-2: a fixed veil that turns the grass to night in
   proportion to how much has been given, because a coloured spark -- or a
   falling gold coin -- over bright green is a smudge. Since 2026-08-20 it is
   DECLARED ALWAYS, at opacity 0, and lit by `html.is-dusk` carrying a `--dusk`
   depth; the element is therefore in this census at every moment rather than
   only during a show, which changes nothing about the ORDER but does mean the
   list below is now the page's permanent shape. -2 sorts before -1, so it is

       grass  <  dusk veil (-2)  <  .cel-layer (-1)  <  the plates  <  header (50)

   and the shells land in front of the night, which is the point. That is the
   whole census: two negative layers, and a third would have to be reconciled
   against this list rather than picked.

   The load-bearing precondition, checked against the ASSEMBLED skeuo.css +
   pay.css + give.css rather than assumed: NOTHING between this canvas and the
   root creates a stacking context. body sets only margin / font / colour /
   background / line-height, <main class="pay"> sets only max-width / margin /
   padding, and .plate sets only background / border / box-shadow / padding.
   A single `position:relative` or `isolation:isolate` or `filter` or
   `opacity:.99` or `will-change` on body or on a wrapper would bury this whole
   layer underneath that element's own background, and it would fail SILENTLY --
   the canvas would still be there, still animating, still costing frames, and
   painting nothing a person can see. If anyone ever adds one, this rule is the
   thing that breaks, and js/celebrate.js's fallback cannot save it (the
   fallback restores position, not the stacking order).

   position:fixed + inset:0 also means it can never add a scrollbar: it is out
   of flow, so it contributes nothing to the document's scrollable overflow. */
.cel-layer{
  position:fixed;
  inset:0;
  /* NOT redundant with inset:0. A canvas is a REPLACED element, and an
     absolutely positioned replaced element with auto width and both left and
     right set uses its INTRINSIC width -- i.e. the backing-store attribute,
     which is the viewport times the device pixel ratio. Without these two the
     layer renders at 2x the viewport on any retina screen. */
  width:100%;
  height:100%;
  display:block;
  /* It is scenery. It never eats a click, a tap, a drag or a focus ring, and
     the form underneath behaves exactly as if it were not there. */
  pointer-events:none;
  z-index:-1;
}

/* Somebody who has asked their system for less motion still gets the
   celebration -- fewer balloons, drifting; occasional soft bursts with no
   flicker (celebrate.js drops the two shapes that twinkle, slows the flash to
   a third of its speed and never launches more than one shell at a time). This
   last touch is the one part that belongs in CSS: the whole layer is pulled
   back so nothing behind the reading area competes with it. */
@media (prefers-reduced-motion: reduce){
  .cel-layer{ opacity:.72; }
}

/* Confetti does not print.
   !important because js/celebrate.js can inline the layout properties as a
   fallback (see FALLBACK_CSS there), and an inline declaration outranks a
   stylesheet one. `display` is deliberately absent from that fallback so this
   rule would win anyway -- the !important is the belt to that pair of braces,
   and it costs nothing because no other rule in the site sets this property on
   this element. */
@media print{
  .cel-layer{ display:none !important; }
}


/* ==== part_zz_integration.css ==== */
/* ============================================================================
   INTEGRATION — the joins between the six components.
   Nothing here invents an object; it is only the mortar: the widths the midway
   pieces need that pay.css never anticipated, the sincere paragraph, and the
   one small link out to the terms.
   ============================================================================ */

/* The hero no longer carries a brass eyebrow and two dashed copy slots, so it
   no longer needs the padding that was holding them apart. The money header
   brings its own. */
.pay__hero--money{ padding:26px 0 4px; }

/* ── THE FORM PLATE ────────────────────────────────────────────────────────
   pay.css caps .jar at 520px, which was right when the jar held four chips and
   two questions. It now holds SEVEN chips with piles of cash on them and a sign
   the width of the plate, and 520px squeezes the chips into a column. The plate
   is the frame; the jar is just the form inside it. */
.gv-plate{ max-width:960px; margin:0 auto; padding-top:0; }
.gv-plate .jar{
  /* 820, not 720: the shelf of seven chips measures 774 at the width where the
     figures and their piles stop overflowing, and the plate's content box is
     960 - 2x38 = 884. So the row fits in one line AND stays a comfortable 32px
     inside the panel on each side, which is the whole of the owner's note. */
  max-width:820px;
}

/* ── THE SINCERE PARAGRAPH ─────────────────────────────────────────────────
   The one moment on this page that is not a joke, so it is set like one: bigger
   than body copy, centred, generously leaded, and given air on both sides. The
   three words the owner capitalised are the sentence's whole spine — they are
   lit in brass rather than merely bolded, because on this cabinet brass IS the
   emphasis and a heavier grey would just look like a heavier grey. */
.gv-sincere{
  max-width:62ch; margin:22px auto 30px; text-align:center;
  font-size:18.5px; line-height:1.75; color:var(--label);
  text-shadow:0 1px 0 rgba(0,0,0,.5);
}
.gv-sincere b{
  color:var(--brass-highlight); font-weight:700; letter-spacing:.04em;
  text-shadow:0 1px 0 var(--emboss-dark), 0 0 14px rgba(255,208,106,.35);
}

/* ── "SOMETHING ELSE" ──────────────────────────────────────────────────────
   It sits directly under a shelf of seven painted chips, so it needs to read as
   the SAME kind of control rather than a stray form field — a little more room
   above it, and a label with the chips' voice. */
.gv-own{ margin:6px 0 26px; }
.gv-own label{
  font-family:var(--font-display); font-weight:700; font-size:13px;
  letter-spacing:.06em; text-transform:uppercase; color:var(--brass-light);
  text-shadow:0 1px 0 var(--emboss-dark);
}

/* ── THE SMALL PRINT ───────────────────────────────────────────────────────
   Everything legal lives on /terms, and this is the door to it. It is small on
   purpose and it is NOT hidden on purpose: it is the last line inside the plate
   that takes the money, which is exactly where a person looks for it. */
.gv-smallprint{
  /* It now sits on the GRASS rather than inside the plate — the leaderboard
     moved in above it — and dim cream on bright green is the one pairing this
     palette cannot do as loose text. So it gets its own little dark plate,
     which is also how every other small label on this site survives the lawn. */
  display:block; width:fit-content; margin:24px auto 0 !important; text-align:center;
  padding:8px 16px; border-radius:20px;
  background:rgba(20,10,3,.72); box-shadow:0 0 0 1px rgba(126,90,18,.55), var(--shadow-drop);
  font-size:13px; color:var(--label);
}
.gv-smallprint a{
  color:var(--brass-light); text-decoration:underline; text-underline-offset:3px;
}
.gv-smallprint a:hover{ color:var(--brass-highlight); }

/* ── THE CONTINUE BUTTON, SIZED TO ITS OWN SIGN ────────────────────────────
   The marquee rig is 340px wide and the word inside it measured 128px — a lit
   frame around a whisper. Both grow together, and the ceiling is still set by
   the longest label the script can write into it: "Continue with $1,000" is
   300px at 27px, so it is 356px at 32px, which is why the rig goes to 404 and
   not to 380. Verified by measuring the long label, not by looking at the
   short one. */
.act .gc-continue-rig{ width:100%; max-width:404px; }
.act .gc-continue.btn{ max-width:none; }
.gc-continue .gc-continue__label{ font-size:clamp(15px, 4.6vw, 32px); }

/* ── THE PICTOGRAM AND THE PLACEHOLDER WERE SHARING A SEAT ─────────────────
   The etched coaster hill and Ferris wheel sit against the right edge of their
   field. At 375px "Any flat ride, roller coaster, or beyond!" runs straight
   under the hill and the last word is read through a drawing. The well keeps
   the room; only the text gets out of the way. */
.gc-card__well .field__input{ padding-right:44px; }

/* ── THE CONTINUE BUTTON, SIZED TO ITS OWN SIGN ────────────────────────────
   The marquee rig is 340px wide and the word inside it measured 128px — a lit
   frame around a whisper. Both grow together, and the ceiling is still set by
   the longest label the script can write into it: "Continue with $1,000" is
   300px at 27px, so it is 356px at 32px, which is why the rig goes to 404 and
   not to 380. Verified by measuring the long label, not by looking at the
   short one. */
.act .gc-continue-rig{ width:100%; max-width:404px; }
.act .gc-continue.btn{ max-width:none; }
.gc-continue .gc-continue__label{ font-size:clamp(15px, 4.6vw, 32px); }

/* ── THE PICTOGRAM AND THE PLACEHOLDER WERE SHARING A SEAT ─────────────────
   The etched coaster hill and Ferris wheel sit against the right edge of their
   field. At 375px "Any flat ride, roller coaster, or beyond!" runs straight
   under the hill and the last word is read through a drawing. The well keeps
   the room; only the text gets out of the way. */
.gc-card__well .field__input{ padding-right:44px; }

/* ── THE CONTINUE BUTTON, SIZED TO ITS OWN SIGN ────────────────────────────
   The marquee rig is 340px wide and the word inside it measured 128px — a lit
   frame around a whisper. Both grow together, and the ceiling is still set by
   the longest label the script can write into it: "Continue with $1,000" is
   300px at 27px, so it is 356px at 32px, which is why the rig goes to 404 and
   not to 380. Verified by measuring the long label, not by looking at the
   short one. */
.act .gc-continue-rig{ width:100%; max-width:404px; }
.act .gc-continue.btn{ max-width:none; }
.gc-continue .gc-continue__label{ font-size:clamp(15px, 4.6vw, 32px); }

/* ── THE PICTOGRAM AND THE PLACEHOLDER WERE SHARING A SEAT ─────────────────
   The etched coaster hill and Ferris wheel sit against the right edge of their
   field. At 375px "Any flat ride, roller coaster, or beyond!" runs straight
   under the hill and the last word is read through a drawing. The well keeps
   the room; only the text gets out of the way. */
.gc-card__well .field__input{ padding-right:44px; }

/* ── THE CONTINUE BUTTON, SIZED TO ITS OWN SIGN ────────────────────────────
   The marquee rig is 340px wide and the word inside it measured 128px — a lit
   frame around a whisper. Both grow together, and the ceiling is still set by
   the longest label the script can write into it: "Continue with $1,000" is
   300px at 27px, so it is 356px at 32px, which is why the rig goes to 404 and
   not to 380. Verified by measuring the long label, not by looking at the
   short one. */
.act .gc-continue-rig{ width:100%; max-width:404px; }
.act .gc-continue.btn{ max-width:none; }
.gc-continue .gc-continue__label{ font-size:clamp(15px, 4.6vw, 32px); }

/* ── THE PICTOGRAM AND THE PLACEHOLDER WERE SHARING A SEAT ─────────────────
   The etched coaster hill and Ferris wheel sit against the right edge of their
   field. At 375px "Any flat ride, roller coaster, or beyond!" runs straight
   under the hill and the last word is read through a drawing. The well keeps
   the room; only the text gets out of the way. */
.gc-card__well .field__input{ padding-right:44px; }

/* ── THE CONTINUE BUTTON, SIZED TO ITS OWN SIGN ────────────────────────────
   The marquee rig is 340px wide and the word inside it measured 128px — a lit
   frame around a whisper. Both grow together, and the ceiling is still set by
   the longest label the script can write into it: "Continue with $1,000" is
   300px at 27px, so it is 356px at 32px, which is why the rig goes to 404 and
   not to 380. Verified by measuring the long label, not by looking at the
   short one. */
.act .gc-continue-rig{ width:100%; max-width:404px; }
.act .gc-continue.btn{ max-width:none; }
.gc-continue .gc-continue__label{ font-size:clamp(15px, 4.6vw, 32px); }

/* ── THE PICTOGRAM AND THE PLACEHOLDER WERE SHARING A SEAT ─────────────────
   The etched coaster hill and Ferris wheel sit against the right edge of their
   field. At 375px "Any flat ride, roller coaster, or beyond!" runs straight
   under the hill and the last word is read through a drawing. The well keeps
   the room; only the text gets out of the way. */
.gc-card__well .field__input{ padding-right:44px; }

@media (max-width:760px){
  .gv-plate{ padding-left:16px; padding-right:16px; }
  .gv-sincere{ font-size:16.5px; margin:18px auto 24px; }
}

/* ── DUSK ──────────────────────────────────────────────────────────────────
   js/donate.js puts .is-dusk on <html> and a DEPTH in `--dusk` whenever the
   chosen amount has earned one, and takes both off when it falls back below.

   The reason is contrast, and it is the whole difference between a firework
   and a smudge: this page's background is the game's own grass, the brightest
   surface on the site, and a coloured spark over bright green is a coloured
   spark you cannot see. Real fireworks need a night to happen in. So the sky
   goes deep blue for as long as the show lasts.

   ⭐⭐ IT IS A DIMMER NOW, NOT A SWITCH (owner, 2026-08-20: "Start darkening
   the background at $100 ... $500 moneybags and various jewels ... $1,000 -
   then the fireworks and baloons appear alllll over"). The ladder in
   js/celebrate.js carries the depth for each rung — .30 at $100, .58 at $500,
   .82 at $1,000 — and hands it back to donate.js, which writes it here. One
   table decides how dark the page goes and what falls out of the sky, so the
   two can never drift apart.

   ⚠ AND THE VEIL IS ALWAYS DECLARED, AT opacity 0, RATHER THAN BEING CREATED
   BY THE CLASS. That is what makes it a dimmer: a `transition` needs a FROM
   state, and an element that comes into existence with the class has none, so
   the old `animation: gv-dusk .9s both` was the only way to fade it in — and
   an animation with `fill: both` outranks a transition, which would have
   frozen every later depth change into a JUMP. Declared-and-transparent costs
   nothing: an `opacity: 0` element is not painted, and nothing here promotes
   it to a layer of its own.

   It is a fixed veil rather than a filter on <body>: a filter would repaint
   every plate, every bulb and the entire Mt. Money canvas on the way in and
   out. The plates sit above it untouched. z-index -2 keeps it behind all page
   content and behind js/celebrate.js's canvas at -1, so the shells and the
   falling money land in FRONT of the night, which is the point. */
/* ⚠ SCOPED WITH :has(), BECAUSE css/give.css IS ALSO LOADED BY buy.html.
   The rule below used to be `html.is-dusk body::after`, which was self-scoping:
   only js/donate.js sets that class, so the element simply never existed
   anywhere else. Declaring it always — which is what makes the depth
   transition possible at all — gave the BUY page a full-viewport fixed
   pseudo-element it has no use for. It would have painted nothing (opacity 0 is
   not painted and promotes no layer), but a shared stylesheet quietly adding a
   fixed box to a page that never asked for one is the kind of thing that is
   discovered much later, by something else breaking.
   `.pay--give` is the donate page's own <main> class; buy.html carries
   `.pay--buy`. Support for :has() is universal in the browsers this site
   already requires for :is() and background-clip:text, and if it were ever
   missing the failure is the veil not appearing — the page stays bright, the
   money still falls. */
body:has(.pay--give)::after{
  content:""; position:fixed; inset:0; z-index:-2; pointer-events:none;
  background:
    radial-gradient(120% 80% at 50% 108%, rgba(12,22,58,0) 0%, rgba(9,16,44,.34) 55%, rgba(6,11,32,.62) 100%),
    linear-gradient(180deg, rgba(6,10,30,.78) 0%, rgba(10,18,48,.66) 46%, rgba(8,14,38,.50) 100%);
  opacity:0;
  transition:opacity .85s ease;
}
/* The fallback is the old full-strength night, so a page that somehow sets the
   class without the property still gets a sky rather than nothing. */
html.is-dusk body:has(.pay--give)::after{ opacity:var(--dusk, 1); }

/* The grass is still down there, so the plates get a touch more lift to keep
   their edges from dissolving into the blue. */
html.is-dusk .plate,
html.is-dusk .gv-plate{ box-shadow:0 0 4px 1px rgba(0,0,0,.5) inset, 0 0 40px rgba(0,0,0,.55), var(--shadow-deep); }

@media (prefers-reduced-motion: reduce){
  /* No fade — but the veil still arrives, because somebody who asked for less
     motion has not asked for a page where the gold is invisible. */
  body:has(.pay--give)::after{ transition:none; }
}


/* ══ HIS PARAGRAPH, IN THE SIGN'S OWN MATERIAL ════════════════════════════
   Owner: "in its own panel - teal like the above streamlined sign with a chrome
   outline, same vein." The colours are read from the sign's own custom
   properties where they exist, so the two objects cannot drift apart, with the
   sign's measured values as the fallback for the case where this panel is ever
   used on a page the sign is not on. */
.gv-teal{
  max-width:640px; margin:6px auto 26px; padding:20px 30px 22px;
  border-radius:26px; text-align:center;
  background:linear-gradient(180deg, var(--cs-teal-hi,#3E97A4) 0%, var(--cs-teal,#2C7E8C) 32%,
                             var(--cs-teal-dp,#1E626F) 72%, var(--cs-teal-lo,#154C57) 100%);
  box-shadow:
    inset 0 0 0 2px var(--cs-ink,#04222A),
    inset 0 2px 0 3px rgba(255,255,255,.30),
    0 0 0 3px #D7E2E6,                    /* the chrome bezel */
    0 0 0 5px #7E939C,
    0 0 0 6px var(--cs-ink,#04222A),
    0 8px 20px rgba(0,0,0,.5);
}
.gv-teal .gv-sincere{
  margin:0; max-width:none; color:#F4FBFC;
  text-shadow:0 1px 0 rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.35);
}
.gv-teal .gv-sincere b{
  color:#FFE9A8;
  text-shadow:0 1px 0 rgba(0,0,0,.6), 0 0 14px rgba(255,208,106,.5);
}
@media (max-width:760px){ .gv-teal{ padding:16px 18px 18px; border-radius:20px; } }

/* ══ CONTRAST ═════════════════════════════════════════════════════════════
   Owner: "Favorite ride, Favorite Park, and entry form need actual contrast."
   They were espresso ink on an espresso panel — technically a recess, visually
   one flat brown field. The well goes darker than the panel it sits in, the lip
   goes brighter, and the typed value and its placeholder both come up. */
.gc-card__well,
.gv-plate .field__input,
.gv-plate .gc-card__well .field__input{
  background:linear-gradient(180deg,#070400 0%,#170C02 100%) !important;
}
.gv-plate .field__input{
  color:#FFF7DD !important;
  box-shadow:inset 0 2px 6px rgba(0,0,0,.95),
             0 0 0 1px var(--wood-line),
             0 0 0 2px var(--brass-mid) !important;
}
.gv-plate .field__input::placeholder{ color:#D7C4A0 !important; opacity:1; }
.gv-plate .field__input:focus{
  box-shadow:inset 0 2px 6px rgba(0,0,0,.95),
             0 0 0 1px var(--wood-line),
             0 0 0 2px var(--brass-highlight),
             0 0 12px rgba(255,208,106,.55) !important;
}
.gv-plate .field__label,
.gv-plate .gc-card__plate .field__label{ color:#FFF7DD !important; }
/* the leaderboard block is its own object again, not a shade of the panel */
.gv-plate .gc-entry{
  background:linear-gradient(180deg,#241505 0%,#150C03 100%);
  box-shadow:0 0 0 1px var(--wood-line), 0 0 0 2px var(--brass-mid),
             inset 0 1px 0 rgba(255,233,168,.14), var(--shadow-drop);
}
.gv-plate .check__text b{ color:#FFF7DD; }


/* ══ FOUR PLATES, FOUR COLOURS ════════════════════════════════════════════
   Owner: "Favorite ride, Favorite Park, Name, Location - can these buttons
   please have different colors."

   Each little nameplate is now its own enamel, keyed off the field it labels
   rather than off its position, so the colours cannot be shuffled by a markup
   change. They are the chips' palette one step deeper, so the two rows read as
   the same cabinet rather than as two different sites: ride red, park green,
   name blue, location violet. The ink flips to dark on the light plates, which
   is the only way any of them stays legible. */
.gv-plate .gc-card__plate{
  background:linear-gradient(180deg, var(--gv-p-hi) 0%, var(--gv-p) 46%, var(--gv-p-lo) 100%) !important;
  box-shadow:0 2px 3px rgba(0,0,0,.5), 0 0 0 1px var(--wood-line),
             0 0 0 2px var(--gv-p-rim), inset 0 1px 0 rgba(255,255,255,.42) !important;
}
.gv-plate .gc-card__plate .field__label{
  color:var(--gv-p-ink) !important;
  text-shadow:0 1px 0 rgba(255,255,255,.30) !important;
}
.gv-plate label[for="fav-ride"]     { --gv-p-hi:#E4685A; --gv-p:#C4372B; --gv-p-lo:#8E1D14;
                                      --gv-p-rim:#FFC9BC; --gv-p-ink:#FFF3EE; }
.gv-plate label[for="fav-park"]     { --gv-p-hi:#63C46E; --gv-p:#2F9440; --gv-p-lo:#186327;
                                      --gv-p-rim:#CFF3D2; --gv-p-ink:#F2FFF3; }
.gv-plate label[for="display-name"] { --gv-p-hi:#5FB6E8; --gv-p:#1E7FBE; --gv-p-lo:#0E5183;
                                      --gv-p-rim:#CDEBFF; --gv-p-ink:#F1FAFF; }
.gv-plate label[for="location"]     { --gv-p-hi:#B98CE8; --gv-p:#8348C4; --gv-p-lo:#552A87;
                                      --gv-p-rim:#E7D6FF; --gv-p-ink:#F8F2FF; }

/* the envelope sits ON the label line, not above it */
.gv-emaillbl{ display:inline-flex !important; align-items:center; gap:8px; }
.gv-env{ display:block; flex:none; filter:drop-shadow(0 1px 1px rgba(0,0,0,.6)); }

/* ══ THE MARQUEE FITS THE BIGGEST AMOUNT ══════════════════════════════════
   Owner: "the continue with 'marquee' shouldn't cut off with larger amounts.
   we want large amounts."

   MEASURED, not guessed: js/donate.js writes "Continue with <amount>" into the
   label, and the largest the form accepts is $25,000. That string measures
   338px at the 32px ceiling, and the old 404px rig gave the label a 294px box —
   it overflowed by 44px. 338 + the button's 2x22 padding is 382, plus the two
   21px bulb lanes is 424; 480 leaves real air and still fits the column. */
.act .gc-continue-rig{ max-width:480px; }
.gc-continue .gc-continue__label{ white-space:nowrap; }

/* ══ THE TITLE COMES DOWN OFF THE DRAPES ══════════════════════════════════
   Owner: "move 'The Leaderboard' and below tagline lower just a bit into the
   negative space, not intersecting into the upper drapes of the curtain."
   The drapes are widest at the top and fall away as they gather, so dropping
   the title moves it into the part of the opening that is actually open. */
.lb-head{ padding-top:112px !important; padding-bottom:40px !important; }
@media (max-width:760px){
  .lb-head{ padding-top:78px !important; padding-bottom:28px !important; }
}

/* ══ THE REFUSAL ══════════════════════════════════════════════════════════
   Owner: "when nothing is 'typed' or 'selected' and continue is pressed -
   notify the user 'pick or type an amount above' where the 'continue' button
   is, turning the lights and text red temporarily, then reverting back to the
   color."

   Both halves are one class on the rig, and both revert on their own: the bulb
   ring's colours are custom properties, so overriding the eight of them turns
   every lamp red without touching the gradient that draws them, and the label's
   own eight-colour band is overridden by a flat red for the duration. */
.gv-alarm{
  margin:10px 0 0 !important; text-align:center;
  font-family:var(--font-display); font-weight:700; font-size:14.5px;
  letter-spacing:.03em; color:#FF9E8E;
  text-shadow:0 1px 0 rgba(0,0,0,.8), 0 0 14px rgba(255,60,40,.5);
  animation:gv-alarm-in .22s ease both;
}
.gv-alarm[hidden]{ display:none; }
@keyframes gv-alarm-in{ from{ opacity:0; transform:translateY(-4px) } to{ opacity:1; transform:none } }

.gc-continue-rig.is-alarm{
  --gc-c1:#FF3B2F; --gc-c2:#FF3B2F; --gc-c3:#FF3B2F; --gc-c4:#FF3B2F;
  --gc-c5:#FF3B2F; --gc-c6:#FF3B2F; --gc-c7:#FF3B2F; --gc-c8:#FF3B2F;
  animation:gv-alarm-shake .5s ease both;
}
.gc-continue-rig.is-alarm .gc-continue__label{
  background-image:none !important;
  -webkit-text-fill-color:#FF6A5A !important;
  color:#FF6A5A !important;
}
.gc-continue-rig.is-alarm .gc-ltr{
  color:#FF6A5A !important; -webkit-text-fill-color:#FF6A5A !important;
}
.gc-continue-rig.is-alarm .gc-continue.btn{
  box-shadow:0 0 0 2px #FF3B2F, 0 0 22px rgba(255,59,47,.55) !important;
}
@keyframes gv-alarm-shake{
  0%,100%{ transform:translateX(0) } 18%{ transform:translateX(-6px) }
  38%{ transform:translateX(5px) } 58%{ transform:translateX(-4px) }
  78%{ transform:translateX(3px) }
}
@media (prefers-reduced-motion: reduce){
  .gc-continue-rig.is-alarm{ animation:none; }
  .gv-alarm{ animation:none; }
}

/* the amounts are their own block now, above both steps */
.gv-amounts{ margin:0; }

/* ══ THE LAG ══════════════════════════════════════════════════════════════
   Owner: "The Mt money minigame can lag sometimes… The big 'donate' can lag
   too. make sure these are all correct."

   ⭐ THE HEADER WAS THE EXPENSIVE ONE, AND IT WAS THE GLINT. The word DONATE is
   seven stacked <text> layers, two feGaussianBlur filters and ~4,200 shapes
   (186 notes and 49 coins, each a dozen primitives) inside a clipPath. All of
   that is STATIC and costs one rasterisation — until something animates inside
   the same SVG. `mh-glint` slid a <rect> across a <mask>, and a mask is not a
   composited layer: moving it re-rasterised the entire header, filters and all,
   sixty times a second, for a highlight most people never noticed. Turning it
   off makes the header free after first paint. The gold, the money and the
   keyline are untouched.

   The same reasoning retires the ghost-row shimmer: `lb-sweep` animates
   background-position, which is a repaint (not a composite), on 25 placeholder
   bars that exist only to show the shape of an empty board. */
.mh-shine{ animation:none !important; }
.lb-ghost [class*="lb-"]{ animation:none !important; }

/* The tube keeps its shimmer — that one the owner asked for — but pays less for
   it. Four chained drop-shadows meant four blur passes per frame over animating
   text; two carry the same read. */
.mm-vegas__tube{
  filter:
    drop-shadow(0 0 3px rgba(255,246,214,.95))
    drop-shadow(0 0 14px rgba(245,194,90,.75)) !important;
  will-change:background-position;
}

/* ⚑ AND THE GAME GETS ITS OWN LAYER. `contain:paint` promises the browser that
   nothing inside .mm-stage paints outside it, so a repaint anywhere else on
   this very busy page cannot drag the canvas into the same paint pass — and the
   canvas, which is redrawing every frame, cannot dirty its neighbours either.
   It is the cheapest possible fix for "the minigame can lag sometimes": the
   loop was already capped and the pile already baked; what it lacked was
   isolation from everything else moving on the page. */
.mm-stage{ contain:paint; }
.mm-canvas{ will-change:transform; }
