/* =============================================================================
 * style.css - shell, HUD, loading + welcome screens and menu chrome.
 * All gameplay art is drawn on the canvas; this file is only the UI around it.
 * No image or font files: icons are inline SVG, the wordmark is system type
 * with a stroke, and the paper background is two CSS gradients.
 * ========================================================================== */

:root {
  --ink:       #2b3a67;   /* deep navy - outlines and text */
  /* Secondary text. Was #6b7aa3, which measured 3.8:1 to 4.25:1 on white -
     under the 4.5:1 needed at the small sizes it is actually used at (skin
     names, the /200 suffix, the best line). Darkened until every one of those
     clears it, which is a shade the eye barely registers as different. */
  --ink-soft:  #56679a;
  --paper:     #f2fbff;
  --grid:      #cbeaff;

  --blue:      #3aa9ff;
  --blue-dk:   #1b7fd4;
  --green:     #4dd865;
  --green-dk:  #2fa845;
  --cash:      #3fbf6a;
  --cash-dk:   #1e7a45;
  --cash-lt:   #9ff0be;
  --yellow:    #ffc93c;
  --yellow-dk: #d99a06;
  --coral:     #ff7b7b;
  --coral-dk:  #d9484f;
  --violet:    #a78bfa;
  --violet-dk: #7c5ce0;
  --mint:      #8ee6c8;
  --pink:      #ff8fc8;

  --panel:     #ffffff;
  --panel-2:   #f2f6fb;

  --r-lg: 28px;
  --r-md: 18px;
  --r-sm: 13px;

  --safe-t: env(safe-area-inset-top, 0px);

  /* Where the water bar starts and how tall it is: right under the top row
     (SKINS, cash, pause), never above 56px, where on a narrow phone the pause
     button still hangs down into the bar's row. It used to sit lower, under a
     bounce counter that grew with the viewport; the counter is gone. The
     tutorial line and the story line are placed below it off the same
     numbers, so none of the three can land on another. */
  --bar-top: 56px;
  --bar-h: 64px;
  /* The hearts: one heart's box and the space between two. They sit centred
     over the water bar, 3px above it (.hud-hearts); on an upright phone they
     get a row of their own and --bar-top steps down to 92px to make it. */
  --heart-w: 34px;
  --heart-h: 31px;
  --heart-gap: 5px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* ========================== SCROLLBARS ==============================
   Drawn, not inherited. A stock grey Windows scrollbar down the side of a
   hand-drawn cartoon panel is the single most obvious seam between "a game"
   and "a web page with a game in it", and the shop has a permanent one.

   Two syntaxes, because there is no single standard: Chrome, Edge, Brave,
   Safari and every Poki-relevant mobile browser take the ::-webkit-scrollbar
   pseudo-elements, while Firefox ignores those entirely and honours only
   scrollbar-width / scrollbar-color. Both are set, so it is styled everywhere
   and merely thin-and-tinted on Firefox rather than wrong.

   The thumb is a rounded pill in the same violet as the reward buttons, on a
   faint ink track - the same shape language as every other bar in the game
   (the load bar, the run bar, the offer timer). */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--violet-dk) rgba(43,58,103,0.10);
}
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track {
  background: rgba(43,58,103,0.09);
  border-radius: 999px;
  /* Insets the track from the ends so it reads as a floating pill rather than
     a channel cut into the panel edge. */
  margin: 4px 0;
}
::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: linear-gradient(180deg, #a78bfa 0%, #7c5ce0 100%);
  /* Transparent border + padding-box clip is what puts real breathing room
     between the thumb and the track; a plain inset would just shrink it. */
  border: 2px solid transparent;
  background-clip: padding-box;
  min-height: 34px;              /* never a dot on a long list */
}
::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #b9a3ff 0%, #8b6cf0 100%);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:active {
  background: linear-gradient(180deg, #8b6cf0 0%, #6845d0 100%);
  background-clip: padding-box;
}
::-webkit-scrollbar-corner { background: transparent; }

/* Over a dark card the ink track disappears, so those get a light one. */
.screen-solid ::-webkit-scrollbar-track,
.cf-card ::-webkit-scrollbar-track { background: rgba(255,255,255,0.16); }

html, body {
  margin: 0; padding: 0;
  width: 100%; height: 100%;
  overflow: hidden;
  /* Warm near-black: what shows behind the canvas on a resize is the
     bottom of the tower, not a summer sky. */
  background: #1a0a0e;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  color: var(--ink);
  user-select: none; -webkit-user-select: none;
  overscroll-behavior: none;
}

#app { position: fixed; inset: 0; touch-action: none; }
#game { display: block; width: 100%; height: 100%; }
#ui { position: absolute; inset: 0; pointer-events: none; }

.hidden { display: none !important; }

/* Shared icon behaviour: SVGs inherit the button's colour. */
.ico { display: block; width: 100%; height: 100%; fill: currentColor; stroke: currentColor; }
.ico[viewBox] { pointer-events: none; }
.ico path[fill="none"], .ico rect[fill="none"] { fill: none; }

/* ============================== HUD ================================== */
.hud {
  position: absolute;
  top: calc(10px + var(--safe-t));
  left: calc(12px + var(--safe-l));
  right: calc(12px + var(--safe-r));
  display: none;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.hud.active { display: flex; }


.hud-side { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.hud-right { justify-content: flex-end; margin-left: auto; }

/* SKINS, in the top-left of the run. It has to be noticed without being
   another thing to read mid-jump, so it glows rather than talks: a soft pink
   pulse, and a shine that runs across it every few seconds. */
.hud-skins {
  pointer-events: auto;
  position: relative;
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px;
  padding: 5px 14px 5px 9px;
  border-radius: 999px;
  border: 3px solid var(--ink);
  /* The flat colour under the gradient is what the text is measured against. */
  background-color: #6a4bd6;
  background-image:
    linear-gradient(115deg, transparent 38%, rgba(255,255,255,0.55) 50%, transparent 62%),
    linear-gradient(180deg, #9b7ff5 0%, #5e3fcf 100%);
  background-size: 260% 100%, 100% 100%;
  background-repeat: no-repeat;
  background-position: 130% 0, 0 0;
  color: #fff;
  font: inherit;
  font-weight: 900;
  font-size: clamp(12px, 2.6vw, 15px);
  letter-spacing: 0.08em;
  text-shadow: 0 1px 0 rgba(20,14,60,0.45);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  box-shadow: 0 3px 0 rgba(43,58,103,0.3), 0 0 8px 2px rgba(255,143,200,0.5);
}
.hud-skins .ico-shirt { width: 22px; height: 22px; flex: 0 0 auto; fill: #fff; stroke: var(--ink); }
.hud-skins:active { transform: translateY(2px); }
.hud-skins:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }
/* Something in the shop they can afford right now. */
.hud-skins-dot {
  position: absolute; top: -5px; right: -5px;
  width: 15px; height: 15px;
  border-radius: 50%;
  background: var(--coral-dk);
  border: 2.5px solid #fff;
  box-shadow: 0 0 0 1.5px var(--ink);
  display: none;
}
.hud-skins-dot.on { display: block; animation: dotPop 1.2s ease-in-out infinite; }
@keyframes dotPop { 0%, 70%, 100% { transform: scale(1); } 80% { transform: scale(1.3); } }
/* A narrow phone: the row has the score, the cash, pause and the sound
   column to fit as well, so the pill tightens up - but keeps the word. */
@media (max-width: 360px) {
  .hud-skins { padding: 5px 10px 5px 7px; gap: 4px; letter-spacing: 0.04em; }
  .hud-skins .ico-shirt { width: 19px; height: 19px; }
}
.pill {
  display: inline-flex; align-items: center; gap: 7px;
  background: rgba(255,255,255,0.94);
  border: 3px solid var(--ink);
  border-radius: 999px;
  padding: 5px 13px;
  font-weight: 800;
  font-size: clamp(12px, 2.6vw, 16px);
  box-shadow: 0 3px 0 rgba(43,58,103,0.25);
  white-space: nowrap;
}
.pill.big { font-size: clamp(15px, 3.2vw, 20px); padding: 7px 16px; }
.pill-cash { color: var(--cash-dk); }

/* Hearts: pink with the ink outline everything in the HUD wears; a lost one
   stays in its place, emptied. Pink, never red - red is the hazards'.

   Centred on top of the water bar, 3px above it: the tally sits on the
   climb it belongs to, and the corner row keeps only SKINS, the cash and
   pause. Out of the row's flow - positioned against #hud, which is 10px
   down and 12px in - so the room they take is never taken from SKINS or the
   cash pill, and SKINS keeps its word at every heart count. The bar runs
   10px..62px in on a phone and 70px..70px from 700px up, so under 700px its
   centre is 26px left of the HUD's: hence right: 52px. Their top comes off
   --bar-top, so hearts and bar can never drift apart. Taps go through. */
.hud-hearts {
  position: absolute;
  top: calc(var(--bar-top) - var(--heart-h) - 13px);
  left: 0; right: 52px;
  width: max-content;
  margin: 0 auto;
  display: flex; align-items: center;
  gap: var(--heart-gap);
  pointer-events: none;
}
@media (min-width: 700px) {
  .hud-hearts { right: 0; }
}
.hud-hearts .heart {
  width: var(--heart-w); height: var(--heart-h);
  overflow: visible;                 /* the outline's outer edge, unclipped */
  fill: #ff6fa3;
  stroke: var(--ink); stroke-width: 2.2; stroke-linejoin: round;
  filter: drop-shadow(0 2px 0 rgba(43,58,103,0.25));
  transform-origin: 50% 60%;
  transition: fill 0.2s;
}
.hud-hearts .heart.lost { fill: rgba(255,255,255,0.35); }
.hud-hearts .heart.bump { animation: heartLost 0.4s cubic-bezier(.25,1.7,.5,1); }
@keyframes heartLost {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.35); }
  100% { transform: scale(1); }
}
/* A phone: a size down - still half as big again as the 19-21px they were. */
@media (max-width: 599px) {
  :root { --heart-w: 30px; --heart-h: 28px; --heart-gap: 4px; }
}
/* An upright phone: SKINS and the cash pill already meet over the bar's
   middle, so the hearts get a row of their own under the corner row
   (61-89px, 4px under the SKINS ledge) and the bar steps down under them.
   Everything placed off --bar-top moves with it: the bar, her bubble inside
   it, the tutorial line and the story line.
   A phone on its side keeps the bar at 56px - its height is the game's -
   and from 560px wide its top row has room for 30px hearts between SKINS
   and the cash. Only a landscape screen under 560px wide takes the row. */
@media (max-width: 559px), (max-width: 599px) and (min-height: 501px) {
  :root { --bar-top: 92px; }
}
/* But a short phone on its side under 560px wide (550x400, 520x320) keeps
   the bar at 56px - its height is the game's - with the hearts a size down
   (4 = 105px) between SKINS and the cash. Game.resize reads --bar-top, so
   the camera stays in step. */
@media (max-width: 559px) and (max-height: 500px) and (min-aspect-ratio: 13/10) {
  :root { --bar-top: 56px; --heart-w: 24px; --heart-h: 22px; --heart-gap: 3px; }
}

/* One-shot bump, retriggered from JS whenever a HUD number changes. */
.bump { animation: bump 0.26s cubic-bezier(.25,1.7,.5,1); }
@keyframes bump {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}
/* The cash pill is the whole bank now, so it runs to five digits, and on the
   narrowest phones SKINS, the cash and pause need the room: the pill gives up
   some padding. Totals from 10,000 up are written short there too ("12K",
   see UI.tickHUD). */
@media (max-width: 380px) {
  .hud .pill-cash { padding: 4px 8px; gap: 4px; }
}
/* 320-wide phones: every pixel of the row is spoken for, so everything in it
   gives a little more. SKINS keeps its word - it only tightens. */
@media (max-width: 340px) {
  .hud .hud-skins { padding: 5px 8px 5px 6px; gap: 3px; letter-spacing: 0.02em; font-size: 11.5px; }
  .hud .hud-skins .ico-shirt { width: 17px; height: 17px; }
  .hud .pill-cash .cash-ico { width: 18px; height: 12px; }
}

/* Cash note icon - a little green banknote, matching the pickups on canvas. */
.cash-ico {
  width: 22px; height: 14px; flex: 0 0 auto;
  border-radius: 3px;
  background: var(--cash);
  border: 2px solid var(--cash-dk);
  position: relative;
  display: inline-block;
}
.cash-ico::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: var(--cash-lt);
  box-shadow: 0 0 0 1.5px rgba(255,255,255,0.7);
}
.cash-ico.small { width: 18px; height: 12px; margin-right: 5px; }

.icon-btn {
  pointer-events: auto;
  width: 46px; height: 46px;
  border-radius: var(--r-sm);
  background: #fff;
  border: 3px solid var(--ink);
  box-shadow: 0 4px 0 rgba(43,58,103,0.3);
  cursor: pointer;
  display: grid; place-items: center;
  padding: 11px;
  color: var(--coral);
}
.icon-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(43,58,103,0.3); }
.ico-pause { stroke: none; }

/* Audio has one home for the whole game: top right, above every screen,
   never moving. Sitting outside #hud is what makes that work - the HUD is
   hidden the moment a panel opens, and this must stay reachable there.
   During play the HUD's right group is padded to make room for it.

   Effects and music are two switches, stacked in a column. A column is what
   keeps the rest of this file true: every clearance below - the HUD's right
   padding, the menu cash pill's margin - is written against the width of ONE
   button, and a second button beside it would have meant re-tuning all of it
   at three breakpoints for no gain. */
.audio-fixed {
  position: absolute;
  top: calc(10px + var(--safe-t));
  right: calc(12px + var(--safe-r));
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.audio-fixed .icon-btn { color: var(--violet-dk); }
.audio-fixed .ico-sound { fill: currentColor; stroke: currentColor; }
.audio-fixed .ico-music { fill: currentColor; stroke: currentColor; }
/* Outline only, unlike the two above it - a filled house at this size turns
   into a blob, and the roofline is what makes it read as Home. */
.audio-fixed .ico-home { fill: none; stroke: currentColor; }
/* Home is hidden on the home screen and on the loading splash. A control that
   takes you where you already are is noise in a three-button column. It stays
   up everywhere else, including over the pause and game-over panels, because
   that is the whole point of this column: it does not move. */
body.at-home #btnHome { display: none; }
.hud .hud-right { padding-right: 54px; }
/* The menu's cash pill shares that corner, so it steps aside too. */
.menu-cash { margin-right: 54px; }

@media (max-width: 460px) {
  .audio-fixed .icon-btn { width: 40px; height: 40px; padding: 9px; }
  .audio-fixed { gap: 6px; }
  .hud .hud-right { padding-right: 46px; }
  /* .menu-cash sizing lives with its base rule further down - a media query
     up here loses to a plain rule below it, media queries add no specificity. */

  /* A narrow phone: tighter paddings keep SKINS clear of the cash pill and
     pause clear of the sound column. */
  .pill { padding: 5px 10px; gap: 5px; }
  .hud { gap: 6px; }
  .hud-side { gap: 6px; }
}

/* Folds and narrow embeds. Still not enough room at 280, so the round buttons
   come down as well. */
@media (max-width: 340px) {
  /* 40px, not the 36px this used to be. The shrink was to stop the HUD row
     overflowing, but the tighter pill paddings below recovered enough width
     that the buttons no longer have to pay for it - measured down to 280px,
     where the row still fits with the two corner buttons 4px apart. 36px is
     under a fingertip, and these are the only two controls during play. */
  .icon-btn, .audio-fixed .icon-btn { width: 40px; height: 40px; padding: 9px; }
  .hud .hud-right { padding-right: 44px; }
  .pill { font-size: 11px; padding: 4px 8px; }
  .cash-ico { width: 18px; height: 12px; }
}

/* Progress: the water bar (js/fluidbar.js). Never "Level N" - a tube filling
   up with his head riding the front of it, and her waiting at the far end.

   As long as the screen allows: every floor moves the water by the same
   share of the tube, so the longer the tube, the further each floor visibly
   carries him - early on, when a floor is a fraction of a percent, that is
   the difference between progress you can see and progress you cannot.

   On a phone it runs from the left edge - nothing sits under SKINS - to just
   short of the sound column down the right (40px wide there, 12px in from
   the edge). A wider screen centres it and stops at 560px, where it would
   start turning into a banner. Sits under the score, clear of its stroke. */
.progress {
  position: absolute;
  top: calc(var(--bar-top) + var(--safe-t));
  left: calc(10px + var(--safe-l));
  right: calc(62px + var(--safe-r));
  max-width: 560px;
  margin: 0 auto;
  height: var(--bar-h);
  display: none;
  pointer-events: none;
}
@media (min-width: 700px) {
  /* Room to spare: truly centred, the same clearance each side. */
  .progress { left: calc(70px + var(--safe-l)); right: calc(70px + var(--safe-r)); }
}
.progress.active { display: block; }
.fluid-bar { display: block; width: 100%; height: 100%; }

/* Her voice from the cage (UI.tickSay): a small speech bubble hung under her
   badge at the far end of the water bar, its tail pointing up at her mouth.
   DOM rather than canvas, so the words are crisp at any DPI and in any of the
   game's alphabets.

   Placed with plain numbers because the badge never moves inside the bar:
   the bar is always --bar-h (64px) tall, so FluidBar always draws her badge
   19px round with its centre 22px in from the bar's right edge, the rim's low
   point about 60px down, and her mouth 27px in from that edge. Right-aligned
   to the bar, the bubble grows LEFT into the open sky under it - never out
   over the sound column, which the bar already stops short of. */
.bar-say {
  display: none;
  position: absolute;
  top: calc(100% + 3px);              /* the tail's tip lands on the rim */
  right: 0;
  width: max-content;
  max-width: min(200px, 58vw);
  padding: 4px 9px 5px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  background: #fff;
  /* The berry of the heart on her badge: 6.5:1 on white. Her pink from the
     prologue's shouts (#e5397d) is only 4:1 - too faint at 12px. */
  color: #b0245a;
  font-size: 12px;
  font-weight: 900;
  line-height: 1.12;
  letter-spacing: 0.02em;
  text-align: center;
  text-wrap: balance;
  box-shadow: 0 2px 0 rgba(43,58,103,0.25);
  pointer-events: none;
  /* It pops out of the tail's tip, so it grows out of her mouth. */
  transform-origin: calc(100% - 27px) -6px;
}
.bar-say.show { display: block; animation: barSay 2.5s linear both; }
/* The tail: an ink triangle, and a white one inside it whose base also covers
   the border where they meet, so bubble and tail read as one shape. */
.bar-say::before,
.bar-say::after {
  content: "";
  position: absolute;
  bottom: 100%;
  width: 0;
  height: 0;
  border-style: solid;
  border-color: transparent;
}
.bar-say::before { right: 18px; border-width: 0 7px 8px; border-bottom-color: var(--ink); }
.bar-say::after  { right: 20px; border-width: 0 5px 6px; border-bottom-color: #fff; }
/* Act two: the monster's badge talks back, in its own colours - the plum of
   the badge and its near-black rim. Red stays on his KILL ribbon. */
.bar-say.boss { background: #3a2a3e; border-color: #1b1420; color: #fff; }
.bar-say.boss::before { border-bottom-color: #1b1420; }
.bar-say.boss::after  { border-bottom-color: #3a2a3e; }
/* A wide screen has the room for a size up. */
@media (min-width: 700px) {
  .bar-say { font-size: 13px; padding: 5px 11px 6px; max-width: 240px; }
}
/* Pops in, holds, lifts off. The length is set per line (UI.sayShow). */
@keyframes barSay {
  0%   { opacity: 0; transform: scale(0.3); }
  7%   { opacity: 1; transform: scale(1.08); }
  12%  { opacity: 1; transform: scale(1); }
  88%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-3px) scale(0.96); }
}
/* Reduced motion: no pop and no lift - it only fades in and out. */
@keyframes barSayFade {
  0%   { opacity: 0; }
  7%   { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .bar-say.show { animation-name: barSayFade; }
}
/* Belt and braces: the rescue and the finale keep the bar on screen, and
   UI.tickSay already takes the bubble down there. */
body.in-rescue .bar-say,
body.in-finale .bar-say { display: none; }

/* The prologue: nothing of the HUD belongs over it. The one thing the player
   may want - to skip it - is the small SKIP in the corner (.skip-btn). */
body.in-prologue .hud,
body.in-prologue .progress,
body.in-prologue #btnHome { display: none; }
/* "TAP TO START": the first-visit prologue opens on the two of them walking
   under the moon and waits for this. Big, low in the frame and breathing, so
   nobody misses that the game has loaded and is waiting for them - and the
   tap it asks for is also the one that lets the story's sound play. */
.skip-hint {
  position: absolute;
  left: 50%;
  /* Centred in the bottom letterbox bar, clear of the two of them: the bar is
     max(10% of the height, half of what a 1.3 : 1 frame leaves) - the same
     sum as Game.proBarPx. */
  top: calc(100% - max(10vh, (100vh - 130vw) / 2) / 2);
  transform: translate(-50%, -50%);
  padding: 0 16px;
  box-sizing: border-box;
  width: max-content;
  max-width: 92vw;
  text-align: center;
  color: #ffffff;
  font-weight: 900;
  font-size: clamp(22px, min(6.6vw, 7vh), 44px);
  letter-spacing: 0.06em;
  line-height: 1.1;
  -webkit-text-stroke: 6px #2a3170;
  paint-order: stroke fill;
  filter: drop-shadow(0 0 14px rgba(255, 150, 200, 0.55)) drop-shadow(0 4px 0 rgba(20, 16, 50, 0.4));
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 34;
}
body.in-prologue .skip-hint.is-start {
  visibility: visible;
  /* Up at once. Half a second of nothing before the prompt read as the
     game not having noticed the player was there. */
  animation: startIn 0.3s ease-out 0s both, startBreathe 1.5s ease-in-out 0.3s infinite;
}
@keyframes startIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes startBreathe {
  0%, 100% { opacity: 1;   transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 0.7; transform: translate(-50%, -50%) scale(1.07); }
}

/* SKIP: the way out of the prologue. Deliberately small and tucked into the
   bottom-left corner - there for the player who wants it, never in the
   picture for the one who is watching. Up for the whole prologue, from its
   first frame, so someone who has seen it on another device never sits
   through it again.

   Small to look at, not to hit: the ::before pads the touch target out to
   about 44px without drawing anything, so a thumb on a phone still finds it. */
.skip-btn {
  display: none;
  position: absolute;
  left: calc(10px + var(--safe-l));
  bottom: calc(10px + var(--safe-b));
  z-index: 35;
  padding: 5px 11px;
  border: 2px solid rgba(255, 255, 255, 0.5);
  border-radius: 999px;
  background: rgba(20, 16, 50, 0.42);
  color: rgba(255, 255, 255, 0.88);
  font-family: inherit;
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: 0.08em;
  cursor: pointer;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}
.skip-btn::before {
  content: "";
  position: absolute;
  inset: -10px -8px;
}
.skip-btn:hover { background: rgba(20, 16, 50, 0.62); color: #fff; }
.skip-btn:active { transform: scale(0.94); }
.skip-btn:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }
body.in-prologue .skip-btn { display: block; }
/* The rescue on floor 130: the same SKIP, and nothing to press but it -
   the bar stays, running out at her. */
body.in-rescue .skip-btn,
body.in-finale .skip-btn { display: block; }
body.in-rescue .hud,
body.in-rescue #btnHome,
body.in-finale .hud,
body.in-finale #btnHome { display: none; }
body.screen-up .skip-btn { display: none; }   /* never over a panel (UI.showScreen) */

/* Tutorial banner - the one control, spelled out over the first hops. */
.tap-hint {
  position: absolute;
  /* Never higher than the water bar's bottom edge plus half its own height:
     on a short landscape phone 20% lands on top of the bar. */
  left: 50%; top: max(20%, calc(var(--bar-top) + var(--bar-h) + 34px + var(--safe-t)));
  transform: translate(-50%, -50%);
  padding: 10px 22px;
  font-size: clamp(17px, 3.6vw, 34px);
  font-weight: 900;
  letter-spacing: 0.01em;
  color: #5c6cc4;
  -webkit-text-stroke: 7px #fff;
  paint-order: stroke fill;
  opacity: 0;
  transition: opacity 0.25s;
  text-align: center;
  white-space: nowrap;
  max-width: 94vw;
}
/* The desktop wording is long; let it wrap rather than run off a phone. */
@media (max-width: 620px) {
  .tap-hint { white-space: normal; line-height: 1.15; max-width: 88vw; }
}
.tap-hint.show { opacity: 1; animation: pulse 1.15s ease-in-out infinite; }
/* After the first tap it stays up for the rest of level 1, but smaller, dimmer
   and still, so it reads as a reminder rather than an instruction. */
.tap-hint.show.calm {
  opacity: 0.62;
  animation: none;
  font-size: clamp(13px, 2.4vw, 22px);
  -webkit-text-stroke-width: 5px;
  transition: opacity 0.4s, font-size 0.4s;
}
/* The tutorial's freeze: the world has stopped and is waiting for this, so
   it says so at full size, calm or not. */
.tap-hint.show.freeze,
.tap-hint.show.calm.freeze {
  opacity: 1;
  font-size: clamp(20px, 4.6vw, 38px);
  -webkit-text-stroke-width: 7px;
  animation: none;
  transition: opacity 0.25s, font-size 0.25s;
}
/* ...and pulsing once a tap would count (Game.tutorFreezeT past its wait). */
.tap-hint.show.freeze.ready { animation: pulse 0.8s ease-in-out infinite; }
/* A tap that came before the guide could have been seen: the banner shakes
   its head, and nothing else happens. */
.tap-hint.show.freeze.nudge { animation: hintNudge 0.35s ease-in-out; }
@keyframes hintNudge {
  0%, 100% { transform: translate(-50%, -50%); }
  25%      { transform: translate(calc(-50% - 9px), -50%); }
  75%      { transform: translate(calc(-50% + 9px), -50%); }
}
@media (prefers-reduced-motion: reduce) {
  .tap-hint.show.freeze, .tap-hint.show.freeze.ready, .tap-hint.show.freeze.nudge { animation: none; }
}
@keyframes pulse {
  0%,100% { transform: translate(-50%,-50%) scale(1); }
  50%     { transform: translate(-50%,-50%) scale(1.055); }
}

/* ============================== ADS ==================================
   An ad is a full takeover and has to be the top layer, full stop.

   Without this the level-clear overlay (z-index 30) painted straight over the
   placeholder card: LEVEL COMPLETE, the stars, the cash and a dead DOUBLE CASH
   button all sitting on top of the ad. The overlay is not a .screen, so it is
   not in the stack showScreen() manages, and the stub card is switched on with
   a raw classList call that never goes through showScreen at all.

   Belt and braces, because the z-index alone only protects the STUB. A real
   portal SDK renders its ad outside our DOM entirely, where our stacking order
   means nothing, so `body.in-ad` also hides every floating thing we own for
   the duration. Set from Ads.onAdStart / onAdEnd. */
#screen-ad { z-index: 90; }

body.in-ad .story-card,
body.in-ad .skip-hint,
body.in-ad .skip-btn,
body.in-ad .audio-fixed,
body.in-prologue .audio-fixed,
body.offer-up .audio-fixed,
body.loading-up .audio-fixed,   /* (pass 30) no sound buttons on the loading screen */   /* (pass 28) no corner buttons on the portal skin offer */   /* (pass 27) no sound buttons during the intro */
body.in-ad .toast,
body.in-ad .hud,
body.in-ad .progress { visibility: hidden; }

/* ============================ STORY CARD =============================
   One line, once, over the live world while he is still flying. It never
   waits for a tap and never blocks anything - the player is mid-launch and
   the card is gone before the first platform arrives. */
.story-card {
  position: absolute;
  left: 50%; top: max(34%, calc(var(--bar-top) + var(--bar-h) + 88px + var(--safe-t)));
  transform: translate(-50%, -50%);
  padding: 0 16px;
  /* Sized to the line, not to the room left of centre: an absolute box at
     left: 50% only ever gets half the screen, which stacked a short line
     into a column four or five words tall. Balanced when it does wrap. */
  box-sizing: border-box;
  width: max-content;
  max-width: 92vw;
  text-wrap: balance;
  text-align: center;
  font-size: clamp(19px, 4.6vw, 40px);
  font-weight: 900;
  letter-spacing: 0.03em;
  line-height: 1.08;
  color: #ff4d7d;
  -webkit-text-stroke: 9px #fff;
  paint-order: stroke fill;
  filter: drop-shadow(0 5px 0 rgba(20,32,66,0.22));
  opacity: 0;
  pointer-events: none;
  z-index: 32;
}
/* 5.6 seconds: long enough to be read twice and felt once. The words pop in
   one after another, bob in a wave while it holds - a cheer, not a caption -
   and the line floats up as it goes, the way he is about to. */
.story-card.show { animation: storyCard 5.6s linear both; }
@keyframes storyCard {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.92); }
  5%   { opacity: 1; transform: translate(-50%, -50%) scale(1.04); }
  9%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  89%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, calc(-50% - 46px)) scale(1.06); }
}
.story-card .sw { display: inline-block; }
.story-card.show .sw {
  animation:
    swIn 0.55s cubic-bezier(.2, 1.7, .4, 1) calc(var(--i) * 95ms) both,
    swBob 1.3s ease-in-out calc(0.75s + var(--i) * 120ms) infinite;
}
@keyframes swIn {
  0%   { opacity: 0; transform: translateY(28px) scale(0.3) rotate(-8deg); }
  100% { opacity: 1; transform: none; }
}
@keyframes swBob {
  0%, 100% { transform: translateY(0) rotate(0); }
  50%      { transform: translateY(-6px) rotate(-2deg); }
}
@media (prefers-reduced-motion: reduce) {
  .story-card.show { animation: none; opacity: 1; }
  .story-card.show .sw { animation: none; }
}

/* ========================== OUT OF HEARTS ============================
   No panel, no card, no title: a light dim over the floor (he is lying where
   he fell) and two buttons of exactly the same size - REVIVE (the rewarded
   one, purple, with the video mark) and "Tap to continue playing" (the free
   way on, in the standard light style). Poki: the standard continue is shown
   at the same time as the reward and is at least as big. The rest of the
   screen still carries on as well (UI.continueFloor).
   Two classes deep: this sits above `.screen` and `.btn` in the file. */
.screen.screen-revive {
  background: radial-gradient(120% 90% at 50% 46%,
              rgba(20,16,40,0.18) 0%, rgba(12,10,26,0.62) 100%);
  justify-content: center;
  gap: 14px;
  cursor: pointer;
}
.screen-revive .revive-btn,
.screen-revive .revive-continue {
  width: min(280px, 84vw);
  min-height: 54px;
  padding: 11px 16px;
  margin: 0;
  font-size: 15px;
  line-height: 1.2;
}
/* The entrance is a class UI puts on both as the offer opens
   (onOutOfLives) and takes off again once it has played. As a rule on the
   buttons themselves it owned their animation: the press pop had to replace
   it, and once the pop was taken off again the entrance would start over,
   sending both buttons out and back in under a tap that came before the
   offer counts (UI.offerArmed). Held at its end it also kept the press from
   showing. */
.screen-revive .revive-btn.entering,
.screen-revive .revive-continue.entering {
  animation: reviveIn 0.32s cubic-bezier(.18,1.5,.42,1) both;
}
/* The free one: the label is longer than REVIVE, so a little tighter, and
   allowed to take two lines in the longer languages rather than overflow. */
.screen-revive .revive-continue {
  letter-spacing: 0.02em;
  white-space: normal;
  text-align: center;
}
@keyframes reviveIn {
  from { opacity: 0; transform: translateY(24px) scale(0.92); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .screen-revive .revive-btn.entering, .screen-revive .revive-continue.entering { animation: none; }
}

/* ============================ SCREENS ================================ */
.screen {
  position: absolute; inset: 0;
  display: none;
  flex-direction: column;
  align-items: center; justify-content: center;
  padding: calc(16px + var(--safe-t)) calc(16px + var(--safe-r)) calc(16px + var(--safe-b)) calc(16px + var(--safe-l));
  pointer-events: auto;
  overflow-y: auto;
  /* Explicit, because overflow-y:auto on its own makes the OTHER axis auto
     too. Title art that leans in past the edges (it used to, on purpose) left
     the title screen draggable 30px sideways on a 380px phone - the
     first screen of the game, wobbling under the thumb. Screens scroll
     vertically or not at all. */
  overflow-x: hidden;
}
.screen.active { display: flex; }
/* A vignette rather than a flat wash: the panel sits in the bright middle and
   the corners fall away, so the eye lands on the card instead of the tint. */
.screen-dim {
  /* Neutral, warm-dark: a navy tint read as blue fog over the hell floors. */
  background: radial-gradient(125% 95% at 50% 44%,
              rgba(30,18,24,0.34) 0%, rgba(10,5,8,0.76) 100%);
  backdrop-filter: blur(3px);
}
.screen-solid { background: #10141f; }

/* Graph paper, like a sketchbook - used by loading and the welcome screen. */
.screen-paper {
  background:
    linear-gradient(var(--grid) 1.2px, transparent 1.2px) 0 0 / 30px 30px,
    linear-gradient(90deg, var(--grid) 1.2px, transparent 1.2px) 0 0 / 30px 30px,
    var(--paper);
}

/* ============================== BRAND ================================ */
.brand {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  margin-bottom: 18px;
  flex: 0 0 auto;
}

/* The title art (UI.paintBrandScene): him, the monster, her in the cage.
   One box with a fixed shape, 460 x 270, and every layer placed inside it in
   percentages of that box, so the picture scales as one piece and the
   characters never drift apart on a different screen. */
.brand-art {
  position: relative;
  /* Sized by the HEIGHT that is left as well as the width. The menu is one
     column - art, wordmark, play button, level plate, tiles - and everything
     but the art needs about 560px of it; the art takes what remains, so on a
     720px laptop screen it gives way instead of pushing the menu into a
     scroll. 1.7 is the art's width-to-height ratio. */
  width: min(92vw, 460px, calc((100vh - 560px) * 1.7));
  aspect-ratio: 460 / 270;
  /* The wordmark's glyphs stand taller than its tight line box. */
  margin-bottom: 14px;
}
/* His way to her, over the monster's head. The dashes march toward the cage. */
.brand-arc {
  position: absolute; inset: 0; width: 100%; height: 100%;
  overflow: visible;
}
.brand-arc path { animation: arcMarch 1.1s linear infinite; }
@keyframes arcMarch { to { stroke-dashoffset: -28; } }

.bs {
  position: absolute; display: block;
  filter: drop-shadow(0 3px 0 rgba(43,58,103,0.16));
}
/* Each one moves the way the character would: he bobs in mid-leap, the
   monster breathes, the cage swings from its chain. Rotations and origins
   live inside the keyframes and rules together - an animation replaces the
   rule's transform outright. */
.bs-hero {
  left: 0.87%; top: 37.04%; width: 32.61%; height: 55.56%;
  transform-origin: 50% 60%;
  animation: bsLeap 2.4s ease-in-out infinite;
}
.bs-monster {
  left: 39.13%; top: 11.11%; width: 39.13%; height: 88.89%;
  transform-origin: 50% 95%;
  animation: bsLoom 3.2s ease-in-out infinite;
}
.bs-cage {
  left: 71.74%; top: 0; width: 28.26%; height: 100%;
  transform-origin: 50% 0%;
  animation: bsSway 2.8s ease-in-out infinite;
  /* The chain fades out going up, into the sky, rather than stopping dead at
     the picture's edge - which on the home screen is mid-air under the title.
     The cage itself starts at 45% down, well clear of the fade. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 34%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 34%);
}
/* The heart at the top of his way to her - the reason for the climb. */
.bs-heart {
  position: absolute; left: 47.7%; top: 2.7%; width: 6.6%;
  overflow: visible;
  fill: var(--pink); stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round;
  animation: bsBeat 1.2s ease-in-out infinite;
}
@keyframes bsLeap {
  0%, 100% { transform: translate(0, 0) rotate(-4deg); }
  50%      { transform: translate(4%, -7%) rotate(2deg); }
}
@keyframes bsLoom {
  0%, 100% { transform: scale(1, 1); }
  50%      { transform: scale(1.025, 0.982); }
}
@keyframes bsSway {
  0%, 100% { transform: rotate(-2.5deg); }
  50%      { transform: rotate(2.5deg); }
}
@keyframes bsBeat {
  0%, 60%, 100% { transform: scale(1); }
  12%           { transform: scale(1.28); }
  24%           { transform: scale(0.94); }
  36%           { transform: scale(1.14); }
}

/* four-point sparkles */
.spark {
  position: absolute;
  width: 20px; height: 20px;
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%);
  animation: twinkle 2.4s ease-in-out infinite;
}
/* Sized with the art rather than in pixels, so they stay sparkles when the
   picture shrinks on a short screen. */
.spark-1 { background: var(--pink);   right: 1%;   top: 2%;   width: 5.2%; height: auto; aspect-ratio: 1; }
.spark-2 { background: var(--yellow); left: 6%;    top: 14%;  width: 4.3%; height: auto; aspect-ratio: 1; animation-delay: .7s; }
@keyframes twinkle { 0%,100% { transform: scale(1) rotate(0deg); opacity: .95; } 50% { transform: scale(.7) rotate(45deg); opacity: .55; } }

.wordmark {
  margin: 0;
  display: flex; flex-direction: column; align-items: center;
  line-height: 0.86;
  font-weight: 900;
  letter-spacing: -0.005em;
  font-size: clamp(38px, 11vw, 74px);
  color: #4cb8f5;
  -webkit-text-stroke: 9px var(--ink);
  paint-order: stroke fill;
  filter: drop-shadow(0 5px 0 rgba(43,58,103,0.18));
}
/* ---- the wordmark acts out the title ------------------------------------
   RESCUE is him: every few seconds it crouches and springs, the bounce the
   whole game is made of. HER! is her, in pink: its letters rattle in a
   ripple, the way she shakes the bars of the cage.

   The cycle is deliberately mostly REST. About two thirds of it is a still
   logo with the faintest float, and each move is over in under half a
   second. A logo that moves constantly stops reading as a logo and starts
   reading as a loading spinner, and it is on screen behind every menu.

   Anything a letter needs at rest goes inside its keyframes: an animation
   replaces the rule's transform outright, so a transform left up in the rule
   would be dropped the moment the animation starts. */
.wordmark .wm-1 {
  transform-origin: 50% 100%;
  animation: wmLeap var(--wm-cycle) cubic-bezier(.3,0,.4,1) infinite;
}
.wordmark .wm-2 {
  color: #ff5f9e;
  transform: rotate(-1.5deg) translateX(4px);
}
.wordmark { --wm-cycle: 3.6s; animation: wmBreathe var(--wm-cycle) linear infinite; }
.wm-ch { display: inline-block; }

/* RESCUE: crouch, spring, land - squash on the way down, stretch on the way
   up, the same shape the doll makes on every bounce. */
@keyframes wmLeap {
  0%, 55%   { transform: translateY(0)     scale(1, 1); }
  61%       { transform: translateY(4px)   scale(1.06, 0.9); }
  68%       { transform: translateY(-16px) scale(0.96, 1.06); }
  75%       { transform: translateY(2px)   scale(1.04, 0.96); }
  81%, 100% { transform: translateY(0)     scale(1, 1); }
}

/* HER!: a quick shake per letter, rippling across the word. Hung from the
   bottom edge, so the letters rock on the line rather than float. */
.wordmark .wm-2 .wm-ch {
  transform-origin: 50% 100%;
  animation: wmRattle var(--wm-cycle) linear infinite;
  animation-delay: calc(var(--i) * 45ms);
}
@keyframes wmRattle {
  0%, 20%   { transform: translate(0, 0)     rotate(0deg); }
  22%       { transform: translate(-2px, 0)  rotate(-5deg); }
  24%       { transform: translate(2px, -1px) rotate(5deg); }
  26%       { transform: translate(-2px, 0)  rotate(-4deg); }
  28%       { transform: translate(1px, 0)   rotate(2deg); }
  30%, 100% { transform: translate(0, 0)     rotate(0deg); }
}

/* Two jobs in one track on the whole lockup, because both want `transform`
   and the last animation to set it would win.

   The first two thirds are a slow breath. Without it the logo is genuinely
   motionless for seconds at a time between moves, which is the static look
   this whole thing exists to kill - the leap should be the moment the eye
   catches, not the only moment anything moves.

   The rest is RESCUE landing: four frames, linear, barely two pixels.
   Punctuation on the landing, not a move of its own. */
@keyframes wmBreathe {
  0%   { transform: translate(0, 0);     animation-timing-function: ease-in-out; }
  32%  { transform: translate(0, -5px);  animation-timing-function: ease-in-out; }
  64%  { transform: translate(0, 0);     animation-timing-function: linear; }
  68%  { transform: translate(-3px, 2px); }
  70%  { transform: translate(3px, -2px); }
  72%  { transform: translate(-2px, 1px); }
  76%  { transform: translate(1px, 0);   animation-timing-function: ease-out; }
  100% { transform: translate(0, 0); }
}

/* A light passing across the lockup every few seconds. Mostly off - it is
   one sweep in five and a half seconds - so it reads as a glint on the logo
   rather than a shimmer that never stops. Painted over the whole brand box,
   art included, because a highlight that stops at the edge of the type is
   the one thing that gives away that the two are separate elements. */
.brand::after {
  content: '';
  position: absolute; inset: -6% -5%;
  pointer-events: none;
  background: linear-gradient(104deg,
              rgba(255,255,255,0) 42%, rgba(255,255,255,0.58) 50%, rgba(255,255,255,0) 58%);
  background-size: 260% 100%;
  animation: brandShine 5.4s linear infinite;
}
@keyframes brandShine {
  0%, 60%   { background-position: 100% 0; opacity: 0; }
  62%       { background-position: 100% 0; opacity: 1; }
  78%       { background-position: 0% 0;   opacity: 1; }
  80%, 100% { background-position: 0% 0;   opacity: 0; }
}

.brand-lg .wordmark { font-size: clamp(44px, 13vw, 88px); }
/* The loading screen has only the bar under its wordmark, so its art can
   keep more of the height. */
.brand-lg .brand-art { width: min(92vw, 460px, calc((100vh - 420px) * 1.7)); }

@media (prefers-reduced-motion: reduce) {
  .wordmark, .wordmark .wm-1, .wordmark .wm-2 .wm-ch { animation: none; }
  .bs-hero, .bs-monster, .bs-cage, .bs-heart, .brand-arc path { animation: none; }
  .bs-hero { transform: rotate(-4deg); }
  .brand::after { display: none; }
}

/* ============================= LOADING =============================== */
.load-box { width: min(80vw, 380px); text-align: center; margin-top: 36px; }
.load-bar {
  height: 26px;
  background: #fff;
  border: 4px solid var(--ink);
  border-radius: 999px;
  padding: 4px;
  box-shadow: 0 4px 0 rgba(43,58,103,0.2);
}
.load-fill {
  height: 100%; width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--pink), #ffb3dc);
  transition: width 0.25s ease-out;
}
.load-label {
  margin: 14px 0 0;
  font-size: clamp(16px, 3.4vw, 24px);
  font-weight: 900;
  letter-spacing: 0.34em;
  color: var(--ink-soft);
}
.build-tag {
  position: absolute; left: 10px; bottom: 8px;
  margin: 0; font-size: 10px; color: var(--ink-soft); opacity: 0.5;
}

/* ====================== THE MENU'S WORLD =============================
   The title screen used to be a column of controls on blank graph paper. It
   is now the game's own place: two towers holding the frame open, hers lit
   all the way up on the right, clouds crossing between them, hearts coming
   up off the bottom and a warm glow behind the logo.

   All of it is decoration and none of it is in the way. The layer is
   z-index -1, which paints it above the paper and behind every control
   without touching their stacking, and #screen-menu is isolated so that
   negative index resolves inside the screen instead of escaping behind it.
   Nothing here takes a pointer event. */
#screen-menu { isolation: isolate; }
.menu-decor {
  position: absolute; inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
/* Corners falling away, so the middle of the screen - where every control
   is - reads as the lit part of the picture. */
.menu-decor::after {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(125% 92% at 50% 44%,
              rgba(255,255,255,0) 38%, rgba(108,150,196,0.20) 100%);
}

/* The warm light behind the logo: the one thing on the screen that is not
   drawn with an outline, which is what keeps the paper from reading flat. */
.md-glow {
  position: absolute; left: 50%; top: 24%;
  width: min(128vw, 880px); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle,
              rgba(255,143,200,0.30) 0%, rgba(255,201,60,0.17) 36%,
              rgba(255,255,255,0) 68%);
  animation: mdGlow 7.5s ease-in-out infinite;
}
@keyframes mdGlow {
  0%, 100% { transform: translate(-50%, -50%) scale(1);    opacity: 0.85; }
  50%      { transform: translate(-50%, -50%) scale(1.13); opacity: 1; }
}

/* The towers. Windows are a dot grid tiled at one storey a row, so a tower
   is one element however tall the viewport makes it. */
.md-tower {
  position: absolute; bottom: 0;
  width: clamp(52px, 8vw, 104px);
  border-radius: 16px 16px 0 0;
  background:
    radial-gradient(circle at 50% 50%, rgba(255,255,255,0.9) 0 2.6px, rgba(255,255,255,0) 3.1px)
      0 0 / 22px 27px,
    linear-gradient(180deg, #d3e9fc 0%, #a8ccef 100%);
  box-shadow: inset 0 0 0 3px rgba(43,58,103,0.09);
  opacity: 0.6;
}
/* Hers: the tall one, and the lit one. */
.md-tower-r { right: 3.5%; height: clamp(190px, 46vh, 460px); }
.md-tower-l { left: 3.5%;  height: clamp(130px, 30vh, 320px); width: clamp(44px, 6.4vw, 84px); }
/* The lights coming on and going off up the building, on a slow breath. A
   second, coarser grid offset from the first, so the lit windows are a
   scatter rather than every other one. */
.md-tower::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background:
    radial-gradient(circle at 50% 50%, rgba(255,201,60,0.95) 0 2.7px, rgba(255,255,255,0) 3.2px)
      0 0 / 44px 81px,
    radial-gradient(circle at 50% 50%, rgba(255,201,60,0.7) 0 2.7px, rgba(255,255,255,0) 3.2px)
      11px 40px / 66px 108px;
  animation: mdLights 4.2s ease-in-out infinite;
}
.md-tower-l::after { animation-duration: 5.6s; animation-delay: -2s; }
@keyframes mdLights { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }

/* Clouds: one element each, the puffs are its own shadows. */
.md-cloud {
  position: absolute; left: 0;
  width: 62px; height: 30px;
  border-radius: 999px;
  background: #ffffff;
  opacity: 0.72;
  box-shadow:
     26px  7px 0 -5px #ffffff,
    -27px  9px 0 -7px #ffffff,
      7px -11px 0 -3px #ffffff;
  animation: mdDrift 52s linear infinite;
}
/* Sized rather than scaled: the drift owns `transform`, and a scale left in
   the rule would be dropped the moment the animation started. */
.md-cloud-1 { top: 13%; width: 78px; height: 37px; animation-duration: 58s; animation-delay:  -6s; }
.md-cloud-2 { top: 31%; width: 52px; height: 25px; animation-duration: 74s; animation-delay: -34s; opacity: 0.55; }
.md-cloud-3 { top: 57%; width: 66px; height: 31px; animation-duration: 64s; animation-delay: -50s; opacity: 0.45; }
/* Moved, not laid out again: a cloud crossing the screen must not cost the
   browser a layout pass on every frame of a menu the game is idling behind. */
@keyframes mdDrift {
  from { transform: translateX(-26vw); }
  to   { transform: translateX(126vw); }
}

/* Hearts, coming up off the bottom of the screen. */
.md-heart {
  position: absolute; bottom: -8%;
  width: 17px; height: 15px;
  opacity: 0;
  animation: mdFloat 13s ease-in-out infinite;
}
.md-heart::before, .md-heart::after {
  content: '';
  position: absolute; top: 0; left: 4px;
  width: 9px; height: 15px;
  border-radius: 9px 9px 0 0;
  background: var(--pink);
  transform: rotate(-45deg);
  transform-origin: 0 100%;
}
.md-heart::after {
  left: auto; right: 4px;
  transform: rotate(45deg);
  transform-origin: 100% 100%;
}
.md-heart-1 { left: 12%; animation-duration: 12s;   animation-delay: -1s; }
.md-heart-2 { left: 31%; animation-duration: 15.5s; animation-delay: -7s; }
.md-heart-3 { left: 68%; animation-duration: 13.5s; animation-delay: -4s; }
.md-heart-4 { left: 86%; animation-duration: 17s;   animation-delay: -11s; }
@keyframes mdFloat {
  0%   { transform: translate(0, 0)        scale(0.6) rotate(-10deg); opacity: 0; }
  10%  { opacity: 0.7; }
  25%  { transform: translate(15px, -22vh) scale(0.8) rotate(9deg); }
  50%  { transform: translate(-11px, -44vh) scale(0.95) rotate(-7deg); }
  75%  { transform: translate(13px, -66vh) scale(1) rotate(10deg); opacity: 0.45; }
  100% { transform: translate(0, -88vh)    scale(1.08) rotate(0deg); opacity: 0; }
}

/* On a phone the controls already fill the width, so the towers slide most
   of the way off the edges: still framing the screen, no longer standing
   behind the water bar. */
@media (max-width: 560px) {
  .md-tower { opacity: 0.42; }
  .md-tower-r { right: -3%; }
  .md-tower-l { left: -4%; }
  .md-twinkle-3 { display: none; }
}

/* A few four-point sparkles in the empty sky, the same shape as the ones on
   the title art. */
.md-twinkle {
  position: absolute;
  width: 16px; height: 16px;
  background: var(--yellow);
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%);
  animation: twinkle 3.2s ease-in-out infinite;
}
.md-twinkle-1 { left: 18%; top: 17%; }
.md-twinkle-2 { right: 21%; top: 27%; background: var(--pink); width: 13px; height: 13px; animation-delay: -1.1s; }
.md-twinkle-3 { left: 27%; top: 62%; width: 11px; height: 11px; animation-delay: -2.2s; }

/* ---- the menu arriving ------------------------------------------------
   Every row comes in on its own beat instead of the whole screen appearing
   at once. It is over in half a second and it only runs when the screen is
   switched on, which is exactly when a player is deciding whether the game
   is worth their time. */
/* The picture does not come in: it IS the screen, and it is already moving. */
#screen-menu.active .menu-logo    { animation: menuIn 0.5s cubic-bezier(.2,1.35,.4,1) both; }
#screen-menu.active .menu-actions { animation: menuIn 0.5s cubic-bezier(.2,1.35,.4,1) 0.09s both; }
#screen-menu.active .menu-journey { animation: menuIn 0.45s cubic-bezier(.2,1.35,.4,1) 0.17s both; }
#screen-menu.active .menu-best,
#screen-menu.active .menu-cash    { animation: menuFade 0.5s ease-out 0.28s both; }
@keyframes menuIn {
  from { opacity: 0; transform: translateY(26px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}
@keyframes menuFade { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .md-glow, .md-cloud, .md-heart, .md-twinkle, .md-tower::after { animation: none; }
  .md-heart { opacity: 0.5; }
  #screen-menu.active .menu-logo, #screen-menu.active .menu-actions,
  #screen-menu.active .menu-journey, #screen-menu.active .menu-best,
  #screen-menu.active .menu-cash { animation: none; }
}

/* ============================ WELCOME ================================ */

/* Cash sits where it sits in-game: a pill in the top-right corner, level with
   the sound button beside it.

   It takes the sound button's `top` AND its height rather than being nudged
   into place with a hand-tuned offset. Matching the height is what does the
   work: two border-box elements of equal height sharing a `top` have their
   centres on the same line by construction, so they stay aligned at every
   breakpoint - including the two narrow ones below, where the button shrinks
   and a tuned offset would quietly drift out of true again.

   `right` matches the button's too, so the clearance between them lives in
   exactly one number (margin-right) instead of being split across two. */
.menu-cash {
  position: absolute;
  top: calc(10px + var(--safe-t)); right: calc(12px + var(--safe-r));
  margin-right: 58px;              /* 46px button + 12px gap */
  height: 46px;
  font-size: clamp(14px, 3vw, 18px);
  padding: 0 15px;
}
/* The button shrinks on narrow screens, so the pill shrinks with it. These
   live HERE, after the base rule, not up in the blocks that resize the button:
   a media query carries no extra specificity, so one placed earlier in the
   file loses to the plain .menu-cash rule above and silently does nothing. */
@media (max-width: 460px) {
  .menu-cash { height: 40px; margin-right: 50px; }   /* 40 + 10 */
}
@media (max-width: 340px) {
  .menu-cash { height: 40px; margin-right: 48px; }   /* 40 + 8 */
  /* The reward button carries the most in one row - glyph, label, pips and the
     AD stamp - and on a 280px fold its contents clipped by a couple of pixels. */
  .btn-reward { padding-left: 9px; padding-right: 9px; }
  .rv-ico { width: 24px; height: 18px; }
  .ad-badge { font-size: 9px; padding: 2px 4px; }
}

/* Under the play button: where he is on the way to her (the water bar, see
   fluidbar.js). It took the place of a level picker, and keeps its footprint -
   the menu is sized to the viewport height and has no room to spare. */
.play-stack { position: relative; display: flex; flex-direction: column; align-items: center; }

.menu-journey {
  /* Clear of the button: it carries a bloom and a pulse ring. */
  margin-top: 14px;
  position: relative;
  z-index: 2;
  /* Long for the same reason as the in-game bar: a longer tube shows each
     floor as more water. */
  width: min(430px, 88vw);
  height: 56px;
  /* A picture, not a control: taps near the play button go to the button. */
  pointer-events: none;
}

/* Their record, on a card of its own rather than loose type on the paper.
   It is the only number on the title screen, and a pill with a star on it
   reads as a trophy at a glance - which is what it is. */
.best-line {
  display: inline-flex; align-items: center; gap: 7px;
  /* Tighter than the loose line it replaced. The menu column is sized to the
     viewport and on a 1024x768 laptop it already used every pixel of it, so
     the pill has to give back what its padding and border take or the title
     screen starts to scroll - which reads as broken. */
  margin: 8px 0 10px;
  padding: 4px 15px 4px 12px;
  font-size: clamp(11px, 2.5vw, 13px);
  font-weight: 800;
  letter-spacing: 0.09em;
  color: var(--ink-soft);
  background: rgba(255,255,255,0.88);
  border: 3px solid rgba(43,58,103,0.13);
  border-radius: 999px;
  box-shadow: 0 3px 0 rgba(43,58,103,0.08);
}
.best-line b { color: var(--ink); font-size: 1.35em; margin-left: 2px; }
.best-star {
  width: 15px; height: 15px;
  flex: 0 0 auto;
  background: var(--yellow);
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 92%, 50% 70%, 21% 92%, 32% 57%, 2% 35%, 39% 35%);
}

/* the big yellow play button */
/* The single most important pixel area in the game.
   It has to be the biggest thing on the menu and it has to move. A player who
   opens the game and sees one large breathing button knows what to do without
   reading a word, and the idle animation is not decoration: a still button on
   a still menu is the most common way a first session ends at the menu. */
.play-btn {
  pointer-events: auto;
  width: clamp(150px, 38vw, 196px);
  height: clamp(126px, 32vw, 164px);
  padding: clamp(28px, 7vw, 38px);
  border-radius: 34px;
  background: var(--yellow);
  border: 6px solid var(--ink);
  /* The second shadow is a coloured bloom, not a drop shadow - it makes the
     button the brightest thing on screen even against the pale sky. */
  box-shadow: 0 10px 0 var(--yellow-dk), 0 20px 38px -12px rgba(232,168,20,0.66);
  color: #fff;
  cursor: pointer;
  display: grid; place-items: center;
  transition: transform .07s, box-shadow .07s;
  transform-origin: center bottom;
  animation: playIdle 1.75s ease-in-out infinite;
  position: relative;        /* the pulse ring anchors to this box */
  flex: 0 0 auto;            /* never let a short viewport squash it */
}
/* Squash and stretch rather than a plain scale: it lands like a body, and the
   hold at the bottom of the cycle is what stops it reading as a throb. */
@keyframes playIdle {
  0%, 62%, 100% { transform: scale(1, 1) translateY(0); }
  74%           { transform: scale(1.055, 0.945) translateY(3px); }
  86%           { transform: scale(0.975, 1.05) translateY(-7px); }
  94%           { transform: scale(1.01, 0.99) translateY(0); }
}
/* A ring that pulses out from under it, so the eye is pulled there even in
   peripheral vision. Purely decorative, so it never eats a tap.

   Anchored to the BUTTON with inset:0 rather than drawn at hand-computed
   coordinates on the stack. The button is a flex item, and in a short viewport
   (landscape phones) the column shrinks it below its stated height; the ring
   was not a flex item, kept its full size, and ended up a fat halo sitting low
   and to one side of a squashed button. inset:0 cannot drift. */
.play-btn::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  border: 5px solid var(--yellow);
  pointer-events: none;
  animation: playRing 1.75s ease-out infinite;
}
@keyframes playRing {
  0%, 55% { opacity: 0; transform: scale(1); }
  70%     { opacity: 0.55; transform: scale(1); }
  100%    { opacity: 0; transform: scale(1.42); }
}
.play-btn:active {
  transform: translateY(6px) scale(0.97);
  box-shadow: 0 2px 0 var(--yellow-dk);
  animation: none;
}
/* The same for the press UI marks on pointerdown (.pressable.pressing),
   which is the one a phone sees: touch browsers hold :active back while
   they decide whether the finger is a scroll. Left running, the idle owned
   transform and the button never went down under a thumb. */
.play-btn.pressing { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .play-btn { animation: none; }
  .play-btn::after { display: none; }
}

/* Landscape phones. The menu is a tall column and 360px of height is not
   enough for it at full size - it overflowed and started scrolling, which on
   a main menu reads as broken. Everything shrinks together so the proportions
   hold; the play button stays the biggest thing on screen either way. */
@media (max-height: 440px) {
  /* The screen's own 16px top and bottom padding is a third of the shortfall
     on its own at this height. */
  .screen { padding-top: calc(8px + var(--safe-t)); padding-bottom: calc(8px + var(--safe-b)); }
  .brand { margin-bottom: 4px; }
  .wordmark { font-size: clamp(26px, 7vw, 40px); -webkit-text-stroke-width: 6px; }
  .brand-lg .wordmark { font-size: clamp(28px, 8vw, 46px); }
  .play-btn {
    width: clamp(104px, 26vw, 132px);
    height: clamp(84px, 21vw, 108px);
    padding: clamp(18px, 5vw, 24px);
  }
  .menu-journey { margin-top: 8px; height: 50px; }
  .best-line { margin: 5px 0 4px; }
  .tile-btn { width: 48px; height: 48px; padding: 11px; }
}
.play-btn .ico-play { stroke: var(--ink); }
.play-btn:focus-visible, .tile-btn:focus-visible { outline: 4px solid var(--blue); outline-offset: 4px; }

.tile-row { display: flex; gap: 12px; }
.tile-btn {
  pointer-events: auto;
  width: 62px; height: 62px; padding: 13px;
  border-radius: var(--r-sm);
  background: var(--coral);
  border: 4px solid var(--ink);
  box-shadow: 0 6px 0 var(--coral-dk);
  color: #fff;
  cursor: pointer;
  display: grid; place-items: center;
}
/* A wiggle every few seconds, the two of them a beat apart. Most of the
   cycle is rest - it is there to say the buttons are alive, not to compete
   with the play button, which is the one thing on the menu that should be
   moving when the eye lands. */
.tile-btn { animation: tileIdle 6.5s ease-in-out infinite; }
.tile-row .tile-btn:nth-child(2) { animation-delay: 0.45s; }
@keyframes tileIdle {
  0%, 84%, 100% { transform: rotate(0deg); }
  88%           { transform: rotate(-5deg) translateY(-2px); }
  92%           { transform: rotate(4deg); }
  96%           { transform: rotate(-2deg); }
}
/* An animation beats a plain declaration, so the idle has to stand down for
   the press states or the button would stop answering the finger. */
.tile-btn:active,
.tile-btn.pressing { animation: none; }
.tile-btn:active { transform: translateY(5px); box-shadow: 0 1px 0 var(--coral-dk); }
@media (prefers-reduced-motion: reduce) { .tile-btn { animation: none; } }
.tile-btn .ico-shirt { fill: #fff; stroke: var(--ink); }
.tile-btn .ico-sound { fill: #fff; stroke: #fff; }
/* Ink, not white. The tile is coral, and white on coral measures 2.5:1 - fine
   for the shirt glyph next to it, which reads as a shape, but not for two
   letters that have to be read as letters. */
/* A touch darker than --ink: on coral the standard ink lands at 4.4:1, just
   under the bar, and this clears it at 5.6:1. */
.ico-lang { font-size: 17px; font-weight: 900; letter-spacing: 0.02em; color: #1e2a4d; }

/* Muted state. The speaker drops its waves and takes a cross where they were.
   The note has nothing to remove, so it gets struck through instead - and the
   strike is drawn twice, once fat in the button's own white and once thin in
   the ink on top. That white pass punches a gap through the note underneath,
   which is what stops a beamed pair plus a diagonal from collapsing into an
   unreadable scribble at 40px. */
.snd-slash { display: none; }
.muted .snd-wave { display: none; }
.muted .snd-slash { display: block; }
.audio-fixed .ico-music .slash-cut { stroke: #fff; }
/* Off is a real state, not a styling flourish: dim the whole control so it is
   obvious at arm's length which of the two is switched off. Grey enough to
   read as disabled beside the live violet, dark enough to clear 3:1 on the
   white button - the obvious lighter grey lands at 3.02:1, which is inside the
   measurement noise of passing. */
.audio-fixed .icon-btn.muted { color: #7c84ab; }


/* ============================= PANELS ================================ */
/* --- panels ------------------------------------------------------------
   Deliberately NOT the heavy-black-outline cartoon style the canvas art
   uses. Outlines that thick on UI read as a template; depth here comes from
   layered soft shadows and a hairline, the way shipped mobile games do it. */
.panel {
  width: min(100%, 400px);
  background: linear-gradient(180deg, #ffffff 0%, #f7fafd 100%);
  /* Under the gradient, which is opaque and hides it. It exists so the panel
     has a real background-color: a gradient alone computes as transparent, and
     anything measuring text contrast walks up looking for a colour, misses
     every panel in the game and lands on the sky behind them. */
  background-color: #ffffff;
  border: 0;
  border-radius: 26px;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.9) inset,
    0 0 0 1px rgba(43,58,103,0.10),
    0 18px 40px -12px rgba(20,32,66,0.42),
    0 4px 12px -4px rgba(20,32,66,0.20);
  padding: 26px 22px 20px;
  text-align: center;
  animation: pop 0.26s cubic-bezier(.2,1.25,.4,1);
  max-height: 100%;
  overflow-y: auto;
}
@keyframes pop {
  from { transform: scale(0.94) translateY(10px); opacity: 0; }
  to   { transform: scale(1) translateY(0); opacity: 1; }
}

/* A coloured header band instead of a title floating on white. It gives the
   panel a shape, carries the win/lose state in colour rather than in wording,
   and stops the whole thing reading like a form. */
.panel-headed { padding: 0; overflow: hidden; }

/* The band's bottom edge is a shallow arc rather than a straight cut. It is
   one line of border-radius and it is most of the difference between "card
   with a coloured strip" and something that looks designed. */
.panel-head {
  padding: 22px 22px 24px;
  text-align: center;
  color: #fff;
  position: relative;
  border-radius: 0 0 50% 50% / 0 0 26px 26px;
  box-shadow: 0 9px 20px -10px rgba(20,32,66,0.55);
}
/* Faint rays behind the title - reads as celebration without a single asset. */
.panel-head::before {
  content: ''; position: absolute; inset: 0;
  background: repeating-conic-gradient(from 0deg at 50% 22%,
              rgba(255,255,255,0.10) 0deg 9deg, rgba(255,255,255,0) 9deg 18deg);
  -webkit-mask-image: radial-gradient(70% 78% at 50% 24%, #000 0%, transparent 72%);
          mask-image: radial-gradient(70% 78% at 50% 24%, #000 0%, transparent 72%);
  border-radius: inherit;
  pointer-events: none;
}
.panel-head > * { position: relative; z-index: 1; }

.head-win  { background: linear-gradient(180deg, #3ddb8a 0%, #22b56a 62%, #1a9d5b 100%); }
.head-fail { background: linear-gradient(180deg, #ff7b88 0%, #e8505f 62%, #d13f50 100%); }
.panel-body { padding: 16px 22px 18px; }

/* White is for a title sitting ON a coloured band. A plain panel - pause, shop
   - has a white body, so it keeps the ink colour or the heading disappears. */
.panel h2 {
  margin: 0 0 4px;
  line-height: 1.05;
  font-size: clamp(21px, 5.0vw, 30px);
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--ink);
}
.panel-head h2 {
  color: #fff;
  text-shadow: 0 2px 0 rgba(20,32,66,0.18);
}
.panel-head .subtitle {
  margin: 0;
  color: rgba(255,255,255,0.85);
  font-weight: 700; font-size: 11.5px;
  letter-spacing: 0.16em; text-transform: uppercase; opacity: 1;
}

/* --- star rating -------------------------------------------------------
   Perfect landings were tracked but never rewarded. Across 200 levels a
   three-star chase is what turns "cleared" into "cleared properly", and it
   costs one number in the save file. */
.stars {
  display: flex; justify-content: center; align-items: center;
  gap: 10px; margin-top: 14px; margin-bottom: -2px;
}
.star {
  width: 30px; height: 30px;
  /* Unearned stars are a socket punched into the band, not a pale copy of an
     earned one - at a glance you count what you got, not what is missing. */
  background: rgba(10,36,24,0.26);
  box-shadow: 0 1px 0 rgba(255,255,255,0.22);
  clip-path: polygon(50% 0%, 62% 36%, 100% 38%, 70% 60%, 81% 98%, 50% 75%, 19% 98%, 30% 60%, 0% 38%, 38% 36%);
  transform: scale(0.84);
}
.head-fail .star { background: rgba(58,12,20,0.26); }
.star:nth-child(2) { width: 38px; height: 38px; margin-top: -8px; }
.star.on {
  background: linear-gradient(180deg, #ffe27a 0%, #ffcb2e 55%, #f5a623 100%);
  filter:
    drop-shadow(0 0 9px rgba(255,214,80,0.75))
    drop-shadow(0 2px 3px rgba(20,32,66,0.30));
  box-shadow: none;
  animation: starPop 0.44s cubic-bezier(.2,1.7,.4,1) both;
}
/* The stagger is driven from JS so each star can carry its own sound and
   confetti puff; no CSS delay, or the two offsets compound. */
@keyframes starPop {
  0%   { transform: scale(0.2) rotate(-40deg); opacity: 0; }
  70%  { transform: scale(1.2) rotate(6deg); opacity: 1; }
  100% { transform: scale(1) rotate(0deg); opacity: 1; }
}

/* ======================= LEVEL CLEAR SCENE ===========================
   A full-screen celebration rather than a card. The confetti canvas sits
   behind everything, the character dances on the left of a two-column row,
   and the numbers and buttons run down the right. Below 720px the row
   collapses and the dancer shrinks to a band above the numbers. */
.screen-win {
  background: radial-gradient(120% 100% at 50% 30%,
              rgba(26,38,64,0.62) 0%, rgba(10,15,28,0.90) 100%);
  backdrop-filter: blur(5px);
  padding: 0;
}

.win-confetti {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}

.win-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: min(100%, 720px);
  max-height: 100%;
  padding: 14px 16px;
  overflow-y: auto;
  animation: winIn 0.4s cubic-bezier(.2,1.2,.35,1) both;
}
@keyframes winIn {
  from { transform: translateY(18px) scale(0.96); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* Tilted banner, like a sticker slapped on the screen. */
.win-banner {
  transform: rotate(-2.2deg);
  background: linear-gradient(180deg, #3ddb8a, #1fae65);
  background-color: #2ec477;      /* see the note on .panel */
  border-radius: 14px;
  padding: 9px 30px 11px;
  box-shadow: 0 10px 22px -10px rgba(10,40,24,0.8), 0 2px 0 rgba(255,255,255,0.4) inset;
  text-align: center;
}
.win-banner h2 {
  margin: 0;
  color: #fff;
  font-size: clamp(23px, 5.6vw, 40px);
  font-weight: 900;
  letter-spacing: 0.05em;
  line-height: 1.02;
  text-shadow: 0 3px 0 rgba(12,60,36,0.34);
}
/* The whole climb, full, under the title: his head has reached her. */
.win-bar { width: min(300px, 76vw); height: 54px; margin: 6px auto 0; }

/* Bigger than in a header band, and the socket has to be scoped to :not(.on)
   or it wins on specificity and greys out the earned stars too. */
.screen-win .stars { margin-top: 12px; gap: 14px; }
.screen-win .star:not(.on) { background: rgba(255,255,255,0.19); box-shadow: none; }
.screen-win .star { width: 42px; height: 42px; }
.screen-win .star:nth-child(2) { width: 54px; height: 54px; margin-top: -10px; }

.win-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin-top: 6px;
}

.win-doll {
  flex: 0 0 38%;
  width: 38%;
  height: auto;
  aspect-ratio: 5 / 6;
  pointer-events: none;
}

.win-side {
  flex: 1 1 auto;
  min-width: 0;
  background: linear-gradient(180deg, #ffffff, #f5f9fd);
  background-color: #fafcfe;      /* see the note on .panel */
  border-radius: 22px;
  padding: 14px 16px 14px;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.9) inset,
    0 18px 40px -14px rgba(8,16,36,0.7);
}
.win-side .score-list { margin-bottom: 10px; }

/* The hero number. Cash is the currency the meta layer runs on, so it is the
   one figure that gets the size. */
.win-hero {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  background: linear-gradient(180deg, #eafbf1, #d8f5e5);
  background-color: #e1f8eb;      /* see the note on .panel */
  border-radius: 14px;
  padding: 8px 14px;
  margin-bottom: 6px;
}
.win-hero-label {
  font-weight: 800; font-size: 11px;
  letter-spacing: 0.14em; text-transform: uppercase;
  /* One shade down from #2b8055, which measured 4.36:1 on the mint plate -
     close enough to pass by eye, under the bar by measurement. */
  color: #256f4a;
}
.win-hero-value { display: inline-flex; align-items: center; gap: 8px; }
.win-hero-value b {
  font-size: clamp(26px, 5.4vw, 38px);
  font-weight: 900;
  color: #128f4f;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.cash-ico.big { width: 28px; height: 18px; border-width: 2.5px; }

.win-best {
  margin: 0 2px 12px;
  display: flex; justify-content: space-between;
  font-size: 12px; font-weight: 700;
  color: var(--ink-soft); opacity: 0.72;
}

@media (max-width: 720px) {
  .win-row { flex-direction: column; gap: 4px; }
  .win-doll { flex: 0 0 auto; width: 42%; max-width: 190px; }
  .win-side { width: 100%; }
}
@media (max-height: 560px) {
  .win-doll { display: none; }
  .win-banner { padding: 6px 22px 8px; }
  .screen-win .stars { margin-top: 6px; }
}

/* --- "how far you got", under the water bar in the game-over card ------ */
.run-bar-cap {
  margin: 0;
  font-weight: 800; font-size: 11px;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255,255,255,0.9);
}
.run-bar-cap span:first-child { font-size: 15px; }

/* Rows on hairline dividers rather than inside a boxed sub-panel: one less
   frame, and the numbers carry the hierarchy instead of the border. */
.score-list {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0 2px 18px;
  text-align: left;
}
.score-line {
  display: flex; justify-content: space-between; align-items: center;
  font-weight: 600; font-size: 13.5px;
  padding: 9px 2px;
  color: var(--ink-soft);
  border-bottom: 1px solid rgba(43,58,103,0.09);
}
.score-line:last-child { border-bottom: 0; }
.score-line b {
  color: var(--ink); font-size: 18px; font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.score-line.total { padding-top: 4px; }
.score-line.total b { color: var(--blue); font-size: 30px; font-weight: 800; line-height: 1; }
.score-line.cash b { color: #1aa35c; }
.score-line.small { font-size: 12px; opacity: 0.6; padding: 7px 2px 2px; }
.score-line.small b { font-size: 14px; font-weight: 700; }

/* ============================ BUTTONS ================================ */
/* --- buttons -----------------------------------------------------------
   Solid fills with a top-light gradient and a tight coloured rim, pressing
   1-2px rather than 5. Thick black outlines and a 5px hard drop shadow are
   the tell of the generic cartoon UI kit; this keeps the weight but not the
   costume. */
.btn {
  pointer-events: auto;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%;
  font-family: inherit;
  font-weight: 800;
  font-size: 15.5px;
  letter-spacing: 0.07em;
  color: #fff;
  background-image: linear-gradient(180deg, rgba(255,255,255,0.22), rgba(255,255,255,0) 55%);
  background-color: var(--blue);
  border: 0;
  border-radius: 16px;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.35) inset,
    0 -2px 0 rgba(0,0,0,0.14) inset,
    0 4px 12px -2px rgba(20,32,66,0.30);
  padding: 15px 16px;
  cursor: pointer;
  margin-bottom: 10px;
  transition: transform .08s ease, box-shadow .08s ease, filter .08s ease;
}
.btn:hover:not(:disabled) { filter: brightness(1.06); }
.btn:active:not(:disabled) {
  transform: translateY(2px);
  box-shadow: 0 -2px 0 rgba(0,0,0,0.14) inset, 0 1px 4px -1px rgba(20,32,66,0.35);
}
.btn:disabled { opacity: 0.45; cursor: default; }
.btn:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }

/* --- press feedback ---------------------------------------------------
   Driven from JS on pointerdown/up rather than :active, so it fires on the
   frame the finger lands (touch delays :active by up to 100ms) and so the
   release can overshoot on the way back instead of snapping. */
.pressable { transition: transform .07s ease, box-shadow .07s ease, filter .07s ease; }
.pressable.pressing {
  transform: translateY(2px) scale(0.972);
  filter: brightness(0.96);
}
.pressable.popped { animation: btnPop 0.24s cubic-bezier(.2,1.8,.4,1); }
@keyframes btnPop {
  0%   { transform: translateY(2px) scale(0.972); }
  55%  { transform: translateY(-1px) scale(1.035); }
  100% { transform: none; }
}
/* Refused: a short horizontal shake, the standard "no" in a game UI. */
.pressable.denied { animation: btnDeny 0.34s ease; }
@keyframes btnDeny {
  0%, 100% { transform: none; }
  15% { transform: translateX(-6px) rotate(-1deg); }
  38% { transform: translateX(5px) rotate(1deg); }
  62% { transform: translateX(-3px); }
  84% { transform: translateX(2px); }
}

.btn-big { font-size: clamp(17px, 4.2vw, 20px); padding: 18px 16px; }

/* Try Again while it counts down. Kept full-opacity rather than greyed: it is
   arriving, not unavailable, and the digit says so. */
.btn.counting {
  opacity: 1;
  background-color: #9fb3c9;
  font-size: clamp(22px, 5.5vw, 30px);
  font-variant-numeric: tabular-nums;
  cursor: default;
}
/* ...and the rewarded continue is the live option in the meantime. */
/* Fetching the ad: the play icon turns over so the button is plainly busy
   rather than dead. */
.btn-reward.loading { animation: none; opacity: 0.85; cursor: progress; }
.btn-reward.loading .rv-ico { animation: rvSpin 0.9s linear infinite; }
@keyframes rvSpin { to { transform: rotate(360deg); } }

.btn-primary   { background-color: #2ec46f; }
.btn-secondary {
  background-color: #eef2f8; color: var(--ink);
  box-shadow: 0 0 0 1px rgba(43,58,103,0.10) inset, 0 3px 8px -3px rgba(20,32,66,0.25);
}
.btn-ghost { background-color: var(--panel-2); color: var(--ink); }

.btn-buy { background-color: var(--cash); color: #fff; }
/* Out of reach. A flat grayscale filter made these read as broken controls;
   they are not broken, they are priced. A quiet outlined pill says "later"
   where a grey slab says "disabled". */
.btn-buy.too-poor {
  filter: none;
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.16);
  box-shadow: none;
  color: #9fb0d8;
}
.btn-buy.too-poor .cash-ico { opacity: 0.6; }

.btn-row { display: flex; gap: 9px; }
.icon-only { width: 56px; flex: 0 0 56px; padding: 12px 16px; }
.icon-only .ico-sound { fill: var(--ink); stroke: var(--ink); }

/* --- rewarded-video buttons -----------------------------------------
   Every one of these opens an ad, so every one carries the video glyph and
   an AD badge. A rewarded button that looks like a normal button is the
   fastest way to lose a player's trust. */
/* A shade deeper than the old #8b6cf0. Its label is 15px, below the size where
   the relaxed large-text contrast rule applies, and white measured 3.8:1 on
   the lighter purple. This clears 4.5:1 and still reads as the same colour. */
.btn-reward {
  background-color: #7a57e8;
  padding-right: 12px;
}
.rv-ico {
  width: 30px; height: 22px; flex: 0 0 auto;
  fill: #fff; stroke: #fff;
}
.rv-text { flex: 1 1 auto; text-align: center; }

/* Dark plate rather than a white wash. At 10px the old translucent-white badge
   measured 2.5:1 against the button under it; the same white letters on a dark
   plate clear 4.5:1 and the badge reads as a stamp rather than a smudge. */
.ad-badge {
  flex: 0 0 auto;
  font-size: 10px; font-weight: 900; letter-spacing: 0.08em;
  color: #fff;
  background: rgba(20,14,48,0.42);
  border: 2px solid rgba(255,255,255,0.42);
  border-radius: 6px;
  padding: 2px 6px;
  line-height: 1.2;
}

/* ============================== SHOP =================================
   A stage and a rack, on warm paper like every other panel in the game.

   It used to be the one dark panel - deep indigo, violet cards - because
   most of the catalogue is made of light (auras, sparks, glow) and light
   only reads against something dark. That part still holds, so the dark is
   kept to the one place a skin is shown moving: the stage's window, a
   little piece of her city's sky at dusk, navy going gold at the horizon.
   Everything round it is cream and white with ink type. Violet is left to
   the rewarded button, its chips and its pips, which wear it on every
   screen of the game.

   The stage shows ONE skin, big and moving, with the one thing to do about
   it: wear it, buy it, or watch the ads that unlock it (pips count them).
   The rack beside it (under it, on a phone) is every skin as a small card
   with its price or its ads on a chip, and its tier as a thin tab of colour
   along the top rather than a card painted in it. Tap a card and it steps
   up onto the stage.

   Sized to sit IN the screen rather than cover it: at most 720 x 660, which
   on a desktop holds all eighteen cards, six across, with no scroll. A
   phone keeps the full height - the rack needs it - and gets four across.

   Nothing in here moves on its own but the stage's canvas (UI.startShopAnim),
   and nothing behind it is blurred: see the dim. Scoped to #screen-shop so
   none of it leaks into other panels; the id also outranks the .panel rules
   further up. */
#screen-shop .shop-panel {
  width: min(100%, 720px);
  max-height: min(100%, 660px);
  display: flex; flex-direction: column;
  overflow: hidden;
  padding: 14px 14px 12px;
  background: linear-gradient(180deg, #fff7ed 0%, #ffeedd 100%);
  /* See the note on .panel: a gradient alone computes as transparent. */
  background-color: #fff7ed;
  box-shadow:
    0 1px 0 rgba(255,255,255,0.9) inset,
    0 0 0 1px rgba(43,58,103,0.12),
    0 18px 40px -14px rgba(16,7,11,0.55);
}
/* The shop's dim: a warm tint and NO blur. A backdrop blur is worked out
   again, over the whole screen, on every frame that anything under it or
   inside it changes - and the stage repaints thirty times a second. The
   deeper tint does the blur's job of pushing the world back. */
#screen-shop.screen-dim {
  background: radial-gradient(125% 95% at 50% 44%,
              rgba(52,24,32,0.44) 0%, rgba(16,7,11,0.80) 100%);
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}
.shop-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px; flex: 0 0 auto;
}
/* min-width:0 so the title gives way before the cash pill is pushed out of
   the panel on a 320px phone; the ellipsis is the backstop. Ink on the
   paper (.panel h2), a size down from the other panels: the panel is. */
#screen-shop .shop-head h2 {
  margin: 0; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: clamp(20px, 3.6vw, 26px);
  letter-spacing: 0.08em;
}
.shop-wallet { display: inline-flex; align-items: center; flex: 0 0 auto; }
@media (max-width: 340px) {
  #screen-shop .shop-head h2 { font-size: 18px; letter-spacing: 0.04em; }
  #screen-shop .shop-wallet .pill { padding: 4px 9px; gap: 5px; }
}
/* The page's violet scrollbar would be the one purple stripe on the paper,
   so in here it is ink. */
#screen-shop .skin-grid { scrollbar-color: rgba(43,58,103,0.38) rgba(43,58,103,0.08); }
#screen-shop ::-webkit-scrollbar-thumb {
  background: rgba(43,58,103,0.34);
  background-clip: padding-box;
}
#screen-shop ::-webkit-scrollbar-thumb:hover,
#screen-shop ::-webkit-scrollbar-thumb:active {
  background: rgba(43,58,103,0.50);
  background-clip: padding-box;
}

.shop-body {
  flex: 1 1 auto; min-height: 0;
  display: grid;
  grid-template-columns: minmax(200px, 34%) 1fr;
  gap: 12px;
  margin-bottom: 10px;
}

/* ---- rarity -------------------------------------------------------------
   One colour per tier, spent sparingly: the tab along the top of a card,
   the ring round the stage's window, the light behind him in it, and the
   tier word. Stone -> cobalt -> violet -> gold, the order players read as
   ascending. None is the gems' cyan or the kisses' pink - those belong to
   the pickups - and red stays with the hazards. --tier-ink is the same hue
   dark enough for 10px type on the paper (4.5:1 or better). Literal rgba
   rather than color-mix(): this ships to phone browsers a couple of years
   behind. */
.rar-common    { --tier: #b9b0a6; --tier-ink: #6f675f; --tier-glow: rgba(255,232,196,0.30); }
.rar-rare      { --tier: #3d7fe6; --tier-ink: #2a64c7; --tier-glow: rgba(96,150,255,0.46); }
.rar-epic      { --tier: #8b5cf6; --tier-ink: #6d3fd9; --tier-glow: rgba(160,118,255,0.46); }
.rar-legendary { --tier: #f0b429; --tier-ink: #946100; --tier-glow: rgba(255,198,84,0.52); }

/* ---- the stage ----------------------------------------------------------
   A white card: the window on top, and under it, on the paper, the tier,
   the name, the pips and the one action - so no type sits on the dark. */
.shop-stage {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  gap: 8px;
  min-height: 0;
  padding: 8px 8px 10px;
  border-radius: 18px;
  background: #ffffff;
  box-shadow: 0 0 0 1px rgba(43,58,103,0.12) inset, 0 3px 0 rgba(43,58,103,0.08);
}
/* The window. Its sky - a few stars, navy going gold at the horizon, and
   his tier's light behind him - is the canvas's CSS background, painted
   once; only the doll in front of it is redrawn. It takes the stage's full
   width and whatever height the rack leaves, and object-position keeps him
   standing on the horizon whatever shape that makes it. The ring is his
   tier. aspect-ratio is for the layouts that leave the width to it (the
   phone strip, the short screen). */
.stage-cv {
  width: 100%; max-width: 100%;
  height: min(240px, 32vh);
  aspect-ratio: 260 / 300;
  flex: 1 1 auto; min-height: 0;
  display: block;
  object-fit: contain;
  object-position: 50% 100%;
  border-radius: 13px;
  background:
    radial-gradient(2px 2px at 18% 14%, rgba(255,255,255,0.9), rgba(255,255,255,0)),
    radial-gradient(2px 2px at 74% 9%,  rgba(255,255,255,0.75), rgba(255,255,255,0)),
    radial-gradient(2px 2px at 86% 30%, rgba(255,255,255,0.6), rgba(255,255,255,0)),
    radial-gradient(48% 38% at 50% 64%, var(--tier-glow) 0%, rgba(0,0,0,0) 72%),
    radial-gradient(80% 26% at 50% 100%, rgba(255,176,120,0.55) 0%, rgba(255,176,120,0) 70%),
    linear-gradient(180deg, #1d2a52 0%, #2c416f 56%, #d9956c 88%, #ffd29e 100%);
  background-color: #2c416f;
  box-shadow: 0 0 0 2px var(--tier);
}
.stage-info {
  position: relative;
  width: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.stage-tier {
  font-size: 10px; font-weight: 900; letter-spacing: 0.18em;
  color: var(--tier-ink);
}
.stage-name {
  font-size: clamp(17px, 2.6vw, 22px); font-weight: 900; line-height: 1.05;
  color: var(--ink);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The ads it takes, as pips: a lit one per ad watched. Nothing to translate,
   and a row filling up reads as progress at a glance. Lit in the rewarded
   violet, the colour of the button that fills them. */
.stage-pips { display: flex; gap: 6px; min-height: 9px; margin: 3px 0 2px; }
.stage-pips i {
  width: 10px; height: 10px; border-radius: 50%;
  background: rgba(43,58,103,0.08);
  box-shadow: 0 0 0 1.5px rgba(43,58,103,0.26) inset;
}
.stage-pips i.on { background: #7a57e8; box-shadow: 0 0 0 1.5px #5b3fd0 inset; }
/* Never taller than BACK (below): this can be WATCH AD, and a rewarded
   button is never bigger than the standard one beside it. */
#screen-shop .stage-act {
  width: 100%; margin: 4px 0 0;
  min-height: 46px;
  padding-top: 10px; padding-bottom: 10px;
  font-size: 15px;
}
#screen-shop .stage-act .buy-word { margin-right: 8px; }
#screen-shop .stage-act .rv-text { white-space: nowrap; }
/* BUY is the cash green, a shade deep enough for white 15px type (4.8:1). */
#screen-shop .stage-act.btn-buy { background-color: #1a8246; }
/* Out of reach, on paper: an outlined pill in ink, the price still legible.
   (The shared .too-poor rule was drawn for the old dark panel.) */
#screen-shop .stage-act.btn-buy.too-poor {
  background: #ffffff;
  color: var(--ink-soft);
  box-shadow: 0 0 0 1.5px rgba(43,58,103,0.20) inset;
}
/* Worn: a state, not an action - flat, in green, with a ring. */
#screen-shop .stage-act.is-worn {
  background: rgba(63,191,106,0.12);
  color: #1e7a45;
  box-shadow: 0 0 0 2px #3fbf6a inset;
  border-color: transparent;
  opacity: 1;
}
#screen-shop .stage-act.no-ad { font-size: 13px; opacity: 0.75; }
/* A skin opens: the window flares in its tier's light and pops. */
.shop-stage.burst .stage-cv {
  animation: stagePop 0.6s cubic-bezier(.2,1.5,.4,1);
}
@keyframes stagePop {
  0%   { transform: scale(0.94); }
  100% { transform: scale(1); }
}

/* ---- the rack ----------------------------------------------------------- */
.shop-rack { display: flex; flex-direction: column; min-height: 0; }
.skin-grid {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: grid;
  /* Six across beside the stage on a desktop - eighteen skins in three
     rows, the whole rack in view - and four on a phone. */
  grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  align-content: start;
  gap: 6px;
  /* Room at the top for the selected card's lift. */
  padding: 4px 5px 6px 3px;
}
.shop-sec {
  grid-column: 1 / -1;
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 2px 2px 0;
}
.shop-sec:not(:first-child) { margin-top: 6px; }
.shop-sec-title { font-size: 11px; font-weight: 900; letter-spacing: 0.14em; color: var(--ink); }
.shop-sec-count { font-size: 11px; font-weight: 800; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.shop-sec-count b { font-weight: 900; color: var(--ink); }
.shop-sec-count.full, .shop-sec-count.full b { color: #8a5a00; }

/* A card: white, an ink hairline, and its tier as a tab of colour along the
   top edge. The picture sits in a warm well and is painted ONCE, when the
   rack is built (UI.shopTile) - the moving version is the stage's job. */
.skin-tile {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 5px 3px 5px;
  border: 0; border-radius: 12px;
  font: inherit; color: var(--ink); cursor: pointer;
  background: #ffffff;
  box-shadow: 0 0 0 1px rgba(43,58,103,0.14) inset, 0 2px 0 rgba(43,58,103,0.08);
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}
.skin-tile::before {
  content: '';
  position: absolute; left: 12px; right: 12px; top: 0;
  height: 3px; border-radius: 0 0 3px 3px;
  background: var(--tier);
}
.skin-tile:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px; }
.skin-tile.selected {
  transform: translateY(-2px);
  box-shadow: 0 0 0 2.5px var(--ink) inset, 0 5px 10px -5px rgba(43,58,103,0.45);
}
.skin-tile.equipped:not(.selected) { box-shadow: 0 0 0 2px #3fbf6a inset, 0 2px 0 rgba(43,58,103,0.08); }
.tile-cv {
  width: 100%; height: auto; aspect-ratio: 96 / 112; display: block;
  border-radius: 8px;
  background: linear-gradient(180deg, #fff8ef 0%, #ffe8d4 100%);
}
.tile-name {
  max-width: 100%;
  font-size: 10.5px; font-weight: 800; line-height: 1.2;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tile-chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  min-height: 17px; padding: 1px 7px;
  border-radius: 999px;
  font-size: 10px; font-weight: 900; line-height: 1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tile-chip .cash-ico.small { width: 13px; height: 9px; margin-right: 2px; border-width: 1.5px; border-radius: 2px; }
.tile-chip .cash-ico::after { width: 3px; height: 3px; margin: -1.5px 0 0 -1.5px; box-shadow: 0 0 0 1px rgba(255,255,255,0.7); }
/* A price in reach turns solid green - BUY's green; out of reach it is an
   outline. Cash is the one thing in here that is green. */
.chip-cash { background: rgba(63,191,106,0.12); color: #1e7a45; box-shadow: 0 0 0 1px rgba(30,122,69,0.32) inset; }
.chip-cash.can { background: #1a8246; color: #fff; box-shadow: none; }
/* Ads: a play mark and watched/needed in the rewarded violet - as ink on
   white rather than a violet slab, so twelve of them do not turn the rack
   purple again. */
.chip-ad { background: #ffffff; color: #5b3fd0; box-shadow: 0 0 0 1.5px rgba(122,87,232,0.55) inset; }
.chip-play {
  width: 0; height: 0; margin-right: 1px;
  border-left: 6px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent;
}
.chip-owned { background: rgba(43,58,103,0.08); color: var(--ink-soft); letter-spacing: 0.06em; }
/* Worn: a green disc with a tick. */
.chip-worn {
  width: 22px; min-height: 19px; padding: 0;
  background: linear-gradient(180deg, #58e08a 0%, #24a95c 100%);
  position: relative;
}
.chip-worn::after {
  content: ''; position: absolute; left: 8px; top: 3px;
  width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2.2px 2.2px 0;
  transform: rotate(42deg);
}


/* The sound column (.audio-fixed: 46px wide, 12px in from the right, three
   buttons deep) stands over the panel's top-right corner whenever the panel
   runs to within ~60px of the screen's edge - 720 + 2 x 70, so up to about
   860px wide. On a phone only the wallet is in its way; wider, the rack's
   top-right is too, so the whole panel keeps clear of it. */
@media (max-width: 860px) {
  #screen-shop .shop-head { padding-right: 44px; }
}
@media (min-width: 601px) and (max-width: 860px) {
  #screen-shop .shop-panel { padding-right: 60px; }
  #screen-shop .shop-head { padding-right: 0; }
}
/* ---- a phone: the stage becomes a strip over the rack --------------------
   The window on the left, the name and the action beside it, so the rack
   keeps most of the height. The panel takes the full height here - the
   rack needs it - and the cards go four across, none under 64px. */
@media (max-width: 600px) {
  #screen-shop .shop-panel { max-height: 100%; }
  .shop-body { grid-template-columns: 1fr; grid-template-rows: auto 1fr; gap: 10px; }
  .shop-stage { flex-direction: row; align-items: center; justify-content: flex-start; gap: 10px; padding: 6px 12px 6px 6px; }
  .stage-cv { width: auto; height: min(134px, 22vh); flex: 0 0 auto; }
  .stage-info { flex: 1 1 auto; min-width: 0; align-items: flex-start; }
  #screen-shop .stage-act { min-height: 44px; font-size: 13px; letter-spacing: 0.03em; padding-left: 8px; padding-right: 8px; gap: 6px; }
  #screen-shop .stage-act .rv-ico { width: 24px; height: 18px; }
  #screen-shop .stage-act .ad-badge { padding: 2px 3px; font-size: 9px; letter-spacing: 0.03em; }
  .skin-grid { grid-template-columns: repeat(auto-fill, minmax(66px, 1fr)); }
}
/* ---- a short screen (a phone on its side) ------------------------------
   Whatever the width, the stage stays BESIDE the rack - stacked, the rack
   got nine pixels - and everything tightens. The two buttons stay at a
   fingertip's 44px.
   After the phone block, so it wins over it. */
@media (max-height: 420px) {
  #screen-shop .shop-panel { padding-top: 10px; padding-bottom: 10px; }
  .shop-head { margin-bottom: 6px; }
  #screen-shop .shop-head h2 { font-size: 18px; }
  #screen-shop .shop-wallet .pill { font-size: 15px; padding: 4px 12px; }
  .shop-body { grid-template-columns: minmax(150px, 32%) 1fr; grid-template-rows: none; gap: 10px; margin-bottom: 8px; }
  .shop-stage { flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px; padding: 4px 6px 8px; }
  .stage-cv { height: auto; width: auto; max-width: 100%; flex: 1 1 0; min-height: 56px; }
  .stage-info { flex: 0 0 auto; align-items: center; }     /* the phone block grows it */
  .stage-name { font-size: 16px; }
  .stage-pips { margin: 2px 0 1px; }
  #screen-shop .stage-act { min-height: 44px; margin-top: 2px; padding-top: 7px; padding-bottom: 7px; font-size: 13px; }
  .skin-grid { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: 6px; }
  .tile-name { font-size: 10px; }
}
/* ...and on a phone on its side the sound column stands over the rack's
   top-right: the panel keeps clear of it, as it does from 601px up. */
@media (max-height: 420px) and (max-width: 600px) {
  #screen-shop .shop-panel { padding-right: 60px; }
  #screen-shop .shop-head { padding-right: 0; }
}

/* ============================ AD CARD ================================ */
.ad-box { text-align: center; color: #fff; width: min(100%, 340px); }
.ad-label { font-size: 13px; letter-spacing: 0.24em; font-weight: 800; margin: 0 0 14px; opacity: 0.85; }
.ad-bar { height: 8px; background: rgba(255,255,255,0.18); border-radius: 999px; overflow: hidden; }
.ad-bar-fill { height: 100%; width: 0; background: var(--yellow); border-radius: 999px; }
.ad-note { margin: 14px 0 0; font-size: 11px; opacity: 0.45; }

/* ============================== TOAST ================================ */
.toast {
  position: absolute;
  left: 50%; bottom: calc(26px + var(--safe-b));
  transform: translate(-50%, 20px);
  background: var(--ink); color: #fff;
  font-weight: 800; font-size: 14px;
  padding: 10px 18px; border-radius: 999px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  max-width: 88vw; text-align: center;
  z-index: 5;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ====================== short-viewport tuning ======================== */
@media (max-height: 620px) {
  .brand-lg .brand-art { width: min(62vw, 270px, calc((100vh - 340px) * 1.7)); }
  .wordmark, .brand-lg .wordmark { font-size: clamp(30px, 8vw, 46px); -webkit-text-stroke-width: 7px; }
  .brand { margin-bottom: 10px; }
  .play-btn { height: 78px; width: 98px; padding: 18px; }
  .menu-journey { margin-top: 6px; height: 52px; }
  /* The record pill hands back what its padding and border cost. A landscape
     phone has 360px of height for the whole column and it already used every
     one of them, so the trophy has to be free. */
  .best-line { margin: 3px 0 4px; padding: 2px 12px 2px 10px; border-width: 2px; }
  .best-star { width: 13px; height: 13px; }
  .tile-btn { width: 50px; height: 50px; padding: 12px; }
  .load-box { margin-top: 20px; }

  .panel { padding: 15px 15px 12px; }
  /* ...but a headed panel's band must still run edge to edge. */
  .panel.panel-headed { padding: 0; }
  .panel-head { padding: 16px 18px 20px; }
  .panel-body { padding: 12px 16px 14px; }
  .star { width: 26px; height: 26px; }
  .star:nth-child(2) { width: 33px; height: 33px; }
  .subtitle { margin-bottom: 9px; }
  .btn { padding: 11px 12px; margin-bottom: 7px; font-size: 15px; }
  .btn-big { padding: 13px; }
  .score-line { padding: 2px 0; font-size: 13px; }
}
/* Landscape phones. Down here, after the block above, because both match and
   the later one wins. The column leaves the art very little height, so it
   gets what there is - and on the shortest screens, where that would be a
   thumbnail nobody could read, it steps aside for the play button. */
@media (max-height: 440px) {
  .brand-lg .brand-art { width: min(34vw, 180px, calc((100vh - 330px) * 1.7)); margin-bottom: 4px; }
}
@media (max-height: 420px) {
  .brand-lg .brand-art { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  /* The hearts' pop outranks the plain .bump, so it is named as well. */
  .panel, .tap-hint.show, .spark, .bump, .hud-hearts .heart.bump,
  .btn-reward.urging, .pressable.popped { animation: none; }
  /* The SKINS glow holds at its brightest instead of pulsing. */
  .hud-skins, .hud-skins-dot.on { animation: none; }
  .hud-skins { box-shadow: 0 3px 0 rgba(43,58,103,0.3), 0 0 14px 5px rgba(255,143,200,0.75); }
  body.in-prologue .skip-hint.is-start { animation: none; opacity: 1; }
  /* The shop's window neither flares nor pops (its doll holds still as
     well: UI.startShopAnim). */
  .shop-stage.burst .stage-cv { animation: none; }
  /* starPop starts from opacity 0 with fill-mode both, so it has to be replaced
     rather than merely stopped - otherwise the earned stars never appear. */
  .star.on { animation: none; transform: scale(1); opacity: 1; }
}


/* ==========================================================================
   VISUAL PASS (prototype): the menu on her rosy dusk rooftop - the world the
   story opens on - instead of pale blue graph paper, so PLAY opens into the
   same world. The graph paper stays, as faint white lines.
   ========================================================================== */
/* A blue-to-rose-to-peach sundown rather than the violet it was: the
   player found the purple unlovely, and her rooftop is meant to be the rosy
   end of the tower. */
.screen-paper {
  background:
    linear-gradient(rgba(255,255,255,.06) 1.2px, transparent 1.2px) 0 0 / 30px 30px,
    linear-gradient(90deg, rgba(255,255,255,.06) 1.2px, transparent 1.2px) 0 0 / 30px 30px,
    linear-gradient(180deg, #4d68b8 0%, #7f93d6 20%, #e59cbc 44%, #ff9fb0 60%, #ffc3a0 74%, #ffe1ba 88%);
  background-color: #7f93d6;
}
/* Centred on the picture now, not on a logo at the top. */
.md-glow {
  top: 50%;
  background: radial-gradient(circle, rgba(255,236,196,.70) 0%, rgba(255,176,122,.34) 26%, rgba(255,154,106,0) 60%);
}
.menu-decor::after {
  background: radial-gradient(125% 92% at 50% 40%, rgba(18,24,58,0) 40%, rgba(18,24,58,.32) 100%);
}
.md-tower {
  background:
    radial-gradient(circle at 50% 50%, rgba(255,230,176,.55) 0 2.6px, rgba(255,230,176,0) 3.1px) 0 0 / 22px 27px,
    linear-gradient(180deg, #3c4a80 0%, #252d57 100%);
  box-shadow: inset 0 0 0 3px rgba(14,18,44,.35);
  opacity: .95;
}
@media (max-width: 560px) { .md-tower { opacity: .8; } }
.md-cloud {
  background: #ffe3ee; opacity: .82;
  box-shadow: 26px 7px 0 -5px #ffe3ee, -27px 9px 0 -7px #ffe3ee, 7px -11px 0 -3px #ffe3ee;
}
.wordmark { filter: drop-shadow(0 6px 0 rgba(16,22,56,.45)); }
/* The two small buttons beside PLAY: white, like the sound buttons in the
   corner, so the one coloured button on the roof is PLAY. The shirt is the
   pink the SKINS button glows in during a run. The language letters keep
   their ink (.ico-lang). */
.tile-btn {
  background: #fff;
  box-shadow: 0 5px 0 rgba(10,14,40,.55);
}
.tile-btn:active { box-shadow: 0 1px 0 rgba(10,14,40,.55); }
.tile-btn .ico-shirt { fill: var(--pink); }
.load-label { color: var(--ink); }
.build-tag { color: rgba(43,58,103,.55); }

/* ==========================================================================
   THE HOME SCREEN, PICTURE FIRST
   The game's story is one picture - him leaping at the monster, her in the
   cage the monster guards - and on the home screen that picture is now most
   of the screen, standing on a rooftop that runs out to both edges with the
   sun going down behind the monster. The title sits above it, smaller, and
   PLAY and the way up to her sit on the roof below.

   Three layouts off one set of markup: landscape (the picture 460 x 270, the
   title on one line when the screen is wide enough), portrait (the same
   characters set out in a 360 x 480 box, the cage above the monster), and a
   phone on its side (the picture on the left, the title and PLAY beside it).
   Everything is scoped to #screen-menu so it outranks the older short-screen
   rules higher up; the loading splash keeps its own layout.
   ========================================================================== */

/* Title, picture, dock. The picture takes whatever the other two leave; the
   corner row (BEST, cash, the sound column) is out of the flow. */
#screen-menu.active {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas: "logo" "stage" "dock";
  justify-items: center;
  row-gap: clamp(6px, 1.2vh, 12px);
  /* A two-line title starts under the corner row: 46px tall from 10px down
     (40px under 461px wide). */
  padding-top: calc(64px + var(--safe-t));
  padding-bottom: calc(14px + var(--safe-b));
}
@media (max-width: 460px) {
  #screen-menu.active { padding-top: calc(58px + var(--safe-t)); }
}

#screen-menu .menu-logo { grid-area: logo; position: relative; z-index: 1; }
#screen-menu .wordmark {
  font-size: clamp(34px, min(12.5vw, 6vh), 64px);
  -webkit-text-stroke-width: 0.15em;
  filter: drop-shadow(0 0.09em 0 rgba(16,22,56,0.45));
}
/* A wide screen has room for the title on one line, level with the corner
   row, and every pixel that saves goes to the picture. */
@media (min-aspect-ratio: 9/10) and (min-width: 760px) {
  #screen-menu.active { padding-top: calc(14px + var(--safe-t)); }
  #screen-menu .wordmark {
    flex-direction: row; align-items: baseline; gap: 0.26em;
    font-size: clamp(34px, 6.4vh, 66px);
  }
}

/* The picture fits whatever width and height the title and the dock leave
   it (cqw / cqh), rather than a guessed number of pixels for everything else.
   Layout containment also makes anything that spills out of it (the sun's
   halo) ink rather than scrollable overflow. */
#screen-menu .menu-stage {
  grid-area: stage;
  align-self: stretch; justify-self: stretch;
  min-height: 0;
  display: flex; align-items: center; justify-content: center;
  container-type: size;
}
#screen-menu .brand-art {
  --roof-y: 94.8%;            /* the monster's feet: his deck's top, 256 of 270 */
  display: block;
  flex: 0 0 auto;
  margin: 0;
  aspect-ratio: 460 / 270;
  /* Browsers without container units: the viewport less the title and dock. */
  width: min(94vw, 1180px, calc((100vh - 270px) * 1.7037));
  width: min(100cqw, 1180px, 100cqh * 1.7037);
}
#screen-menu .arc-tall { display: none; }
/* Its own layer: the marching dashes repaint every frame, and nothing else
   should be repainted with them. */
#screen-menu .brand-arc { will-change: transform; }
/* A heavier swing at this size: the cage is two thirds of the screen tall. */
#screen-menu .bs-cage { animation-duration: 3.4s; }

/* The sun going down behind the monster: the one soft light in the picture,
   and what he stands out against. Breathes on opacity only. */
#screen-menu .bs-sun {
  position: absolute; left: 58.7%; top: 41%;
  width: 64%; aspect-ratio: 1;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle closest-side,
    #fff4dc 0, #fff4dc 49%, rgba(255,232,196,0.75) 51%,
    rgba(255,214,176,0.32) 68%, rgba(255,200,170,0) 100%);
  animation: bsSun 7s ease-in-out infinite;
  pointer-events: none;
}
@keyframes bsSun { 0%, 100% { opacity: 0.85; } 50% { opacity: 1; } }

/* The city behind the roof: two hazy rows of blocks, one tile repeated, faded
   out at the sides so it never needs to reach past the picture. */
#screen-menu .bs-city {
  position: absolute; left: -3%; right: -3%;
  height: 22%; top: calc(var(--roof-y) - 22%);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 120'%3E%3Cpath fill='rgba(58,70,128,0.32)' d='M0 120V64H40V46H86V72H138V36H160V22H172V36H202V66H260V48H310V82H350V34H386V58H446V76H494V42H526V18H536V42H568V62H624V50H674V80H718V44H764V64H800V120Z'/%3E%3Cpath fill='rgba(36,43,85,0.5)' d='M0 120V92H70V84H130V98H210V80H274V94H344V86H430V100H496V82H572V96H656V88H730V92H800V120Z'/%3E%3C/svg%3E") repeat-x 50% 100% / auto 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14%, #000 86%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 14%, #000 86%, transparent 100%);
  pointer-events: none;
}

/* The rooftop they are all on, out to both edges of the screen and down to
   the bottom, where the dock stands on it. Each band is a box-shadow spread
   past the screen, trimmed with clip-path: a shadow is paint only, so it can
   reach the edges without making the menu scroll. Three bands, three boxes -
   a clip on the parent would clip its children. */
#screen-menu .bs-roof {
  position: absolute; left: 0; right: 0; top: var(--roof-y); height: 0;
  pointer-events: none;
}
#screen-menu .bs-roof::before,
#screen-menu .bs-roof::after,
#screen-menu .bs-ledge {
  content: ''; position: absolute; left: 0; right: 0; display: block;
}
/* The lit rim along the edge - the sun is behind them. */
#screen-menu .bs-roof::before {
  top: 0; height: 3px;
  background: #ffd6b8;
  box-shadow: 0 0 0 100vmax #ffd6b8;
  clip-path: inset(0 -100vmax);
}
/* The parapet. */
#screen-menu .bs-ledge {
  top: 3px; height: clamp(8px, 1.1vw, 14px);
  background: #3b4677;
  box-shadow: 0 0 0 100vmax #3b4677;
  clip-path: inset(0 -100vmax);
}
/* The roof itself, to the bottom of the screen. */
#screen-menu .bs-roof::after {
  top: calc(3px + clamp(8px, 1.1vw, 14px)); height: 1px;
  background: #242b55;
  box-shadow: 0 0 0 100vmax #242b55;
  clip-path: inset(0 -100vmax -100vmax);
}

/* ---- the dock, on the roof ---- */
#screen-menu .menu-dock {
  grid-area: dock;
  position: relative; z-index: 1;     /* over the roof's paint */
  width: min(600px, 100%);
  display: flex; flex-direction: column; align-items: center;
  gap: 16px;                           /* clear of PLAY's 8px ledge */
}
#screen-menu .menu-actions {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(12px, 1.6vw, 20px);
}
#screen-menu .menu-actions .tile-btn:last-child { animation-delay: 0.45s; }
#screen-menu .tile-btn { width: 58px; height: 58px; padding: 13px; }
#screen-menu .menu-journey {
  margin: 0;
  width: min(560px, 100%);
  height: clamp(48px, 6.2vh, 58px);
}
/* Still the one thing to press, and still the brightest thing on the roof,
   but a pill with the word on it rather than a block: the picture is what
   the eye lands on now. White letters in the ink outline, like the title. */
#screen-menu .play-btn {
  width: auto;
  min-width: clamp(176px, 15vw, 230px);
  height: clamp(60px, 8vh, 76px);
  padding: 0 clamp(22px, 2.2vw, 34px);
  border-radius: 999px;
  border-width: 5px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.4em;
  font: inherit;
  font-size: clamp(22px, 3.3vh, 32px);
  font-weight: 900;
  letter-spacing: 0.06em;
  line-height: 1;
  color: #fff;
  -webkit-text-stroke: 0.2em var(--ink);
  paint-order: stroke fill;
  box-shadow: 0 8px 0 var(--yellow-dk), 0 16px 34px -10px rgba(255,190,60,0.6);
}
#screen-menu .play-btn:active { box-shadow: 0 2px 0 var(--yellow-dk); }
#screen-menu .play-btn .ico-play { width: 1em; height: 1em; flex: 0 0 auto; }
/* The ring grows more up and down than sideways, so it does not run over
   the two buttons beside it. */
#screen-menu .play-btn::after { animation-name: playRingPill; }
@keyframes playRingPill {
  0%, 55% { opacity: 0; transform: scale(1); }
  70%     { opacity: 0.55; transform: scale(1); }
  100%    { opacity: 0; transform: scale(1.1, 1.32); }
}

/* ---- the corner row ---- */
#screen-menu .menu-cash { z-index: 2; }
/* Their record, the same pill as the cash across from it. */
#screen-menu .menu-best {
  position: absolute; z-index: 2;
  top: calc(10px + var(--safe-t)); left: calc(12px + var(--safe-l));
  height: 46px; margin: 0;
  padding: 0 15px 0 12px;
  font-size: clamp(12px, 2.6vw, 15px);
  color: var(--ink-soft);
  background: rgba(255,255,255,0.94);
  border: 3px solid var(--ink);
  box-shadow: 0 3px 0 rgba(43,58,103,0.25);
}
@media (max-width: 460px) {
  #screen-menu .menu-best { height: 40px; padding: 0 11px 0 9px; }
}

/* The towers only frame a wide screen. Anything squarer and they stand
   behind the picture, where the city already is. */
@media (max-aspect-ratio: 3/2) {
  .md-tower { display: none; }
}
/* They stand behind the rooftop now, which hides their lower part, so they
   are taller by about the roof's height - and a little hazier, as the far
   side of the city is. */
#screen-menu .md-tower-r { height: clamp(190px, 66vh, 660px); opacity: .85; }
#screen-menu .md-tower-l { height: clamp(130px, 48vh, 480px); opacity: .85; }
/* The third cloud passes over the sky, not behind the sun's disc. */
#screen-menu .md-cloud-3 { top: 38%; }

/* ---- portrait: the same characters, set out tall ------------------------
   Positions are the painted layers' own sizes (hero 150 x 150, monster
   180 x 240, cage 130 x 270) placed in a 360 x 480 box: the monster on the
   roof at the bottom, her cage hanging over him, and him leaping up at her
   from the bottom left. The arc runs from his fist to her hand. */
@media (max-aspect-ratio: 9/10) {
  #screen-menu .brand-art {
    --roof-y: 97.08%;          /* 466 of 480 */
    aspect-ratio: 360 / 480;
    width: min(92vw, calc((100vh - 330px) * 0.75));
    width: min(100cqw, 100cqh * 0.75);
  }
  #screen-menu .arc-wide { display: none; }
  #screen-menu .arc-tall { display: block; }
  #screen-menu .bs-hero    { left: 1.11%;  top: 54.58%; width: 41.67%; height: 31.25%; }
  #screen-menu .bs-monster { left: 37.78%; top: 50%;    width: 50%;    height: 50%; }
  #screen-menu .bs-cage    { left: 62.78%; top: 0;      width: 36.11%; height: 56.25%; }
  #screen-menu .bs-heart   { left: 44.9%;  top: 23.6%;  width: 8.44%; }
  #screen-menu .bs-sun     { left: 62.8%;  top: 70%;    width: 80%; }
  #screen-menu .bs-city    { height: 18%;  top: calc(var(--roof-y) - 18%); }
  #screen-menu .menu-dock  { width: 100%; }
  #screen-menu .menu-journey { width: 100%; height: clamp(46px, 6vh, 56px); }
  #screen-menu .menu-actions { gap: 14px; }
  #screen-menu .play-btn {
    min-width: clamp(160px, 44vw, 220px);
    height: clamp(56px, 7.4vh, 70px);
    font-size: clamp(20px, 2.9vh, 28px);
  }
  #screen-menu .tile-btn { width: 54px; height: 54px; padding: 12px; }
}

/* ---- a phone on its side: the picture left, the rest beside it ---------
   Stacked, the picture got a strip 130px tall. Side by side it keeps the
   whole height, and the title and dock sit centred in the right column,
   below the sound column in the corner. */
@media (max-height: 500px) and (min-aspect-ratio: 13/10) {
  #screen-menu.active {
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto auto minmax(0, 1fr);
    grid-template-areas: "stage ." "stage logo" "stage dock" "stage .";
    column-gap: 16px; row-gap: 10px;
    padding-top: calc(8px + var(--safe-t));
    padding-bottom: calc(8px + var(--safe-b));
  }
  #screen-menu .wordmark {
    flex-direction: column; gap: 0;
    font-size: clamp(26px, 8.5vh, 40px);
  }
  #screen-menu .brand-art {
    width: min(54vw, calc((100vh - 24px) * 1.7037));
    width: min(100cqw, 100cqh * 1.7037);
  }
  #screen-menu .menu-dock { width: 100%; gap: 12px; }
  #screen-menu .menu-journey { width: 100%; height: 46px; }
  #screen-menu .menu-actions { gap: 12px; }
  #screen-menu .play-btn { min-width: 150px; height: 54px; font-size: 21px; border-width: 4px; padding: 0 18px; }
  #screen-menu .tile-btn { width: 48px; height: 48px; padding: 10px; border-width: 3px; }
  .md-tower { display: none; }
}

/* 320-wide phones: the PLAY row is 296px at the sizes above, in 288. */
@media (max-width: 340px) {
  #screen-menu .menu-actions { gap: 10px; }
  #screen-menu .play-btn { min-width: 150px; padding: 0 18px; }
  #screen-menu .tile-btn { width: 48px; height: 48px; padding: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  #screen-menu .bs-sun { animation: none; opacity: 1; }
}

/* =============================================================================
 * dir_sky.css - ROOFTOP SKY: one look for every menu, button and label.
 *
 * Load AFTER css/style.css (a <link> at the end of <head>, or paste this whole
 * file onto the end of style.css). No markup changes are needed.
 *
 * The idea in one line: every panel is a small piece of the home screen.
 *   - its background is her sunset sky, pale blue at the top going rose and
 *     then peach, with the hazy city standing on a navy roof along the bottom
 *     and a peach sun-rim on the roof's edge - the same roof PLAY stands on;
 *   - the one main action on a screen is PLAY's yellow pill (white letters in
 *     an ink outline, a dark-yellow ledge);
 *   - every other button is a white tile or pill with the ink outline and a
 *     hard navy ledge, like the two tiles beside PLAY;
 *   - titles are lettered like the wordmark (white or gold in a thick ink
 *     outline with a hard drop);
 *   - violet means "this plays an ad" and nothing else; green means cash (and
 *     the skin you are wearing: WEARING, its tick, its card's ring); pink
 *     means her (hearts); no red anywhere - red is hazards;
 *   - navy plates are messages from the game (the toast); white pills are
 *     things you press.
 *
 * Depth is always a solid ledge straight down, never a blur. Nothing here uses
 * backdrop-filter, and the only soft thing is the sun in the panel sky, which
 * is a static background layer. Everything that loops animates transform or
 * opacity only; the one-shot stage flare and a button's press ledge animate
 * box-shadow. All of it stops under prefers-reduced-motion (at the end).
 *
 * No !important anywhere: every override matches or beats the specificity of
 * the rule it replaces (the comments after the selectors give the weight).
 * ========================================================================== */


/* ============================== TOKENS ===================================
   Prefixed --sky- so nothing collides with style.css. The existing --ink,
   --yellow, --yellow-dk, --pink, --blue and --r-* tokens are reused as-is. */
:root {
  /* The panel sky: the home screen's dusk, lifted most of the way to white so
     ink type reads anywhere on it (ink measures 7.4:1 at the palest-blue top,
     9.9:1 at the bottom). Secondary ink (--ink-soft) does NOT pass up here
     (3.7:1), so text that sits straight on the sky is always full ink. */
  --sky-1: #c9d3f2;          /* pale evening blue */
  --sky-2: #e6d4ec;          /* lilac */
  --sky-3: #fbd7e1;          /* rose */
  --sky-4: #ffe3d3;          /* peach */
  --sky-5: #fff0df;          /* the glow at the horizon */
  --sky-grad: linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 26%, var(--sky-3) 50%, var(--sky-4) 74%, var(--sky-5) 100%);

  /* Her rooftop, exactly the home screen's three bands. */
  --sky-rim:    #ffd6b8;     /* the lit edge */
  --sky-roof-2: #3b4677;     /* the parapet */
  --sky-roof:   #242b55;     /* the roof */

  /* Ledges. Under white things on a light surface: the parapet navy, solid.
     Under anything over the live world or a dark scrim: the home tiles' own
     translucent near-black, which reads on her pink sky and on floor 1's hell. */
  --sky-ledge:    #3b4677;
  --sky-ledge-dk: rgba(10,14,40,0.55);
  --sky-drop:     rgba(16,22,56,0.45);   /* the wordmark's hard drop */

  /* One job per colour. */
  --sky-love:    #ff5f9e;    /* her: every heart, the story line */
  --sky-ad:      #7a57e8;    /* rewarded ads only (white 15px = 4.8:1) */
  --sky-ad-dk:   #5b3fd0;
  --sky-ad-lt:   #f1ecff;
  --sky-cash:    #1a8246;    /* BUY fill (white 15px = 4.9:1) */
  --sky-cash-dk: #0f5c33;
  --sky-cash-ink:#1e7a45;    /* money written on white (5.4:1) */
  --sky-cash-lt: #e6f7ec;
  --sky-gold:    #ffcb2e;
  --sky-focus-lt:#1666b5;    /* focus ring on the pale panels: 3.9:1 on the
                                palest sky (the pale --blue is 1.7:1 there) */

  /* The coral tokens are retired: red is the hazards' colour. Anything that
     still points at them paints in ink / roof navy instead of red. */
  --coral:    #2b3a67;
  --coral-dk: #242b55;
}


/* =============================== SCROLLBARS ==============================
   Violet was the scrollbar colour; violet now means "ad". The thumb is the
   secondary ink on a faint ink track - a quiet pill that matches the rest.
   On the dark end-of-run scrim it is the peach roof rim instead.
   Both syntaxes, as in style.css: current Chrome and Firefox obey
   scrollbar-color, older WebKit the pseudo-elements. */
* { scrollbar-color: #56679a rgba(43,58,103,0.12); }
::-webkit-scrollbar-track { background: rgba(43,58,103,0.10); }
::-webkit-scrollbar-thumb {
  background: #56679a;
  border: 3px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover  { background: var(--ink);      background-clip: padding-box; }
::-webkit-scrollbar-thumb:active { background: var(--sky-roof); background-clip: padding-box; }
#screen-shop .skin-grid { scrollbar-color: #56679a rgba(43,58,103,0.10); }
#screen-shop ::-webkit-scrollbar-thumb { background: #56679a; background-clip: padding-box; }
#screen-shop ::-webkit-scrollbar-thumb:hover,
#screen-shop ::-webkit-scrollbar-thumb:active { background: var(--ink); background-clip: padding-box; }
.screen-win, .screen-win * { scrollbar-color: var(--sky-rim) rgba(255,255,255,0.14); }
.screen-win ::-webkit-scrollbar-track { background: rgba(255,255,255,0.14); }
.screen-win ::-webkit-scrollbar-thumb,
.screen-win ::-webkit-scrollbar-thumb:hover { background: var(--sky-rim); background-clip: padding-box; }


/* ================================ SCRIMS =================================
   What sits between a panel and the game. A dusk tint - plum in the middle
   going to night navy at the corners - and NO blur. The old pause and
   end-of-run screens ran a backdrop blur over a canvas that redraws every
   frame (the frosted-glass look is also half of what read as "AI"). The tint
   is lighter in the middle than it was, so the world stays readable round a
   light panel. */
.screen-dim,
.screen-win {
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}
#screen-pause.screen-dim {                                   /* (1,1,0) */
  background: radial-gradient(125% 95% at 50% 46%,
              rgba(62,34,78,0.30) 0%, rgba(18,16,46,0.74) 100%);
}
.screen.screen-win {                                          /* beats .screen-win */
  background: radial-gradient(120% 100% at 50% 30%,
              rgba(70,44,104,0.66) 0%, rgba(14,14,40,0.92) 100%);
}
.screen.screen-revive {                                       /* (0,2,0), later */
  background: radial-gradient(120% 90% at 50% 46%,
              rgba(52,26,64,0.18) 0%, rgba(14,12,34,0.64) 100%);
}
/* The shop keeps its own warm tint (style.css, #screen-shop.screen-dim). */


/* ============================== THE SKY PANEL ============================
   The panel is a window onto her rooftop at sundown:
     - the sky runs pale blue -> lilac -> rose -> peach, top to bottom;
     - two small clouds either side of the title (painted, not moving);
     - along the bottom, the city's hazy blocks stand on her roof: a peach
       sun-rim, the parapet, the navy roof - the same three bands as the
       home screen - with the sun going down behind them;
     - the whole card has the ink outline and stands on a hard dark ledge,
       so the roof reads as the panel's base.
   All of it is background layers on the one element: no extra markup, no
   pseudo-element, nothing that repaints. Layers are listed top-most first.

   The three roof bands all start 4px down, under the panel's 4px ink border,
   and overlap downwards: each still shows only its own strip (roof 10,
   parapet 5, rim 3), but a band's edge always lands on the next band's
   colour. Placed edge to edge, a sub-pixel gap let the pale sky through as a
   light hairline across the roof. The city is the home screen's skyline in
   its rose-plum haze (in navy it read as a grey zigzag), and its tile keeps
   a transparent row top and bottom so repeating it bleeds no line. */
:root {
  --sky-roof-h: 18px;       /* rim 3 + parapet 5 + roof 10 */
  --sky-city-h: 22px;
  --sky-bands:
    linear-gradient(var(--sky-roof), var(--sky-roof)) left 0 bottom -4px / 100% 14px no-repeat,
    linear-gradient(var(--sky-roof-2), var(--sky-roof-2)) left 0 bottom -4px / 100% 19px no-repeat,
    linear-gradient(var(--sky-rim), var(--sky-rim)) left 0 bottom -4px / 100% 22px no-repeat;
  /* the city on the roof (the home screen's own skyline tile) */
  --sky-city:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -3 800 126'%3E%3Cpath fill='rgba(178,104,158,0.34)' d='M0 120V64H40V46H86V72H138V36H160V22H172V36H202V66H260V48H310V82H350V34H386V58H446V76H494V42H526V18H536V42H568V62H624V50H674V80H718V44H764V64H800V120Z'/%3E%3Cpath fill='rgba(92,64,128,0.52)' d='M0 120V92H70V84H130V98H210V80H274V94H344V86H430V100H496V82H572V96H656V88H730V92H800V120Z'/%3E%3C/svg%3E") 50% calc(100% - 18px) / auto var(--sky-city-h) repeat-x;
  --sky-upper:
    /* the sun going down behind the city, centred on the rim */
    radial-gradient(circle closest-side, #fff6e4 0, #fff6e4 58%, rgba(255,236,208,0.55) 72%, rgba(255,226,200,0) 100%) 50% calc(100% + 66px) / 168px 168px no-repeat,
    /* two clouds */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 30'%3E%3Cpath d='M10 28h44a8 8 0 0 0 0-16 11 11 0 0 0-20.5-5A9 9 0 0 0 17 13a8 8 0 0 0-7 15z' fill='%23fff5f8'/%3E%3C/svg%3E") left 14px top 16px / 50px auto no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 30'%3E%3Cpath d='M10 28h44a8 8 0 0 0 0-16 11 11 0 0 0-20.5-5A9 9 0 0 0 17 13a8 8 0 0 0-7 15z' fill='%23fff5f8' fill-opacity='0.8'/%3E%3C/svg%3E") right 20px top 40px / 34px auto no-repeat,
    /* the sky */
    var(--sky-grad);
  --sky-layers: var(--sky-bands), var(--sky-city), var(--sky-upper);
}

.panel {                                                      /* (0,1,0), later */
  width: min(100%, 380px);
  background: var(--sky-layers);
  background-color: var(--sky-3);   /* a real colour under the layers (see style.css .panel) */
  border: 4px solid var(--ink);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 0 var(--sky-ledge-dk);
  padding: 18px 18px calc(var(--sky-roof-h) + 26px);
  animation: skyPanelIn 0.36s cubic-bezier(.2,1.35,.4,1) both;
}
@keyframes skyPanelIn {
  from { opacity: 0; transform: translateY(26px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

/* Panel titles: the wordmark's lettering - RESCUE's italic blue in a thick
   ink outline with its hard drop, tilted a touch like HER!. (Upright white
   they read as another typeface's headings.) The outline carries the
   contrast: ink on the palest sky is 7.4:1. Bigger on a phone: 30px at
   320 wide, 35px at 390 (the phone-on-its-side size is set further down). */
.panel h2 {
  margin: 0 0 14px;
  font-size: clamp(30px, 9vw, 44px);
  font-style: italic;
  font-weight: 900;
  letter-spacing: 0.05em;
  line-height: 1;
  text-transform: uppercase;
  color: #4cb8f5;
  -webkit-text-stroke: 0.15em var(--ink);
  paint-order: stroke fill;
  filter: drop-shadow(0 0.09em 0 var(--sky-drop));
  transform: rotate(-1.5deg);
}


/* ================================ BUTTONS ================================
   One recipe for every word button: a pill with the 4px ink outline and a
   solid ledge straight down. Each variant only changes the fill, the label
   and the ledge colour, carried in three custom properties so the pressed
   state can be written once:
     --sky-lh     ledge height
     --sky-lc     ledge colour (follows the fill)
     --sky-shine  a flat white band inside the top edge on coloured fills
   Pressed: the button sinks by its ledge less 1px and the ledge drops to 1px
   (the "clunk" PLAY and the tiles have). Released: the existing .popped
   bounce, restarted from the sunk position. */
.btn {                                                        /* base = secondary */
  --sky-lh: 5px;
  --sky-lc: var(--sky-ledge);
  --sky-shine: inset 0 0 0 transparent;
  min-height: 50px;
  padding: 10px 16px;
  margin-bottom: 12px;
  font-weight: 900;
  font-size: 16px;
  letter-spacing: 0.06em;
  line-height: 1.1;
  color: var(--ink);
  background-color: #ffffff;
  background-image: none;
  border: 4px solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--sky-shine), 0 var(--sky-lh) 0 var(--sky-lc);
  text-shadow: none;
  -webkit-text-stroke: 0;
  transition: transform .07s ease, box-shadow .07s ease;
}
/* Hover (mouse only): each fill shifts a shade. Written per variant - a
   generic hover colour would outrank .btn-buy and paint BUY white. The
   coloured ones go DEEPER, so the white label keeps its contrast. */
.btn:hover:not(:disabled) { filter: none; }
.btn.btn-secondary:hover:not(:disabled),
.btn.counting:hover:not(:disabled),
.btn.btn-ghost:hover:not(:disabled) { background-color: #fffaf4; }

/* The one main action: PLAY's yellow, PLAY's lettering. Ink on yellow is
   7.2:1 - the outline is what carries the white letters. */
.btn.btn-primary {
  --sky-lh: 6px;
  --sky-lc: var(--yellow-dk);
  --sky-shine: inset 0 3px 0 rgba(255,255,255,0.45);
  background-color: var(--yellow);
  color: #fff;
  -webkit-text-stroke: 0.2em var(--ink);
  paint-order: stroke fill;
}
.btn.btn-primary:hover:not(:disabled) { background-color: #ffd257; }

.btn.btn-secondary { background-color: #ffffff; color: var(--ink); }
.btn.btn-big {
  --sky-lh: 7px;
  min-height: 60px;
  padding: 10px 18px;
  font-size: clamp(19px, 5vw, 23px);
}

/* Rewarded: violet, the film mark and the AD stamp - always recognisable,
   never bigger than the standard button next to it (Poki). White on this
   violet is 4.8:1 by itself; the thin ink outline only matches the family. */
.btn.btn-reward {
  --sky-lc: var(--sky-ad-dk);
  --sky-shine: inset 0 3px 0 rgba(255,255,255,0.28);
  background-color: var(--sky-ad);
  color: #fff;
  -webkit-text-stroke: 0.12em var(--ink);
  paint-order: stroke fill;
  padding-right: 12px;
  /* The film mark is 22px tall: with the standard 10px padding it made this
     button taller than a plain word button at every min-height below 50px.
     At 6px it is the min-height that sets the height, as for the others. */
  padding-top: 6px;
  padding-bottom: 6px;
}
.btn.btn-reward:hover:not(:disabled) { background-color: #6f4ce0; }   /* white 5.5:1 */
/* A long label may wrap (УДВОИТЬ ДЕНЬГИ on a phone). Two lines at 1.02 plus
   this padding still came out 3px taller than the SKINS button under it, so
   the end screen trims it further (.win-side .btn.btn-reward, below). */
.btn.btn-reward .rv-text { line-height: 1.02; }
/* Film mark, word and AD stamp read as one label, centred together like
   PLAY's triangle and word (the word stretched across the pill and left the
   mark and the stamp stranded at the two ends, like a form row). It may
   still shrink, so a long word wraps. */
.btn.btn-reward .rv-text { flex: 0 1 auto; }                   /* (0,3,0) */
.btn .rv-ico { fill: #fff; stroke: #fff; }
.btn .ad-badge {
  -webkit-text-stroke: 0;
  color: var(--sky-ad-dk);
  background: #ffffff;
  border: 2px solid var(--ink);
  border-radius: 6px;
  box-shadow: 0 2px 0 rgba(10,14,40,0.35);
  padding: 1px 5px;
  font-size: 10px;
  font-weight: 900;
  letter-spacing: 0.08em;
  line-height: 1.2;
  transform: rotate(-4deg);
}

/* Cash: BUY is the one green button. */
.btn.btn-buy {
  --sky-lc: var(--sky-cash-dk);
  --sky-shine: inset 0 3px 0 rgba(255,255,255,0.25);
  background-color: var(--sky-cash);
  color: #fff;
  -webkit-text-stroke: 0.12em var(--ink);
  paint-order: stroke fill;
}
.btn.btn-buy:hover:not(:disabled) { background-color: #177a41; }       /* white 5.4:1 */
/* Not enough cash yet: a plain white button with the price still legible
   (ink-soft on white, 5.5:1). Still pressable - it shakes its head. */
.btn.btn-buy.too-poor {
  --sky-lc: var(--sky-ledge);
  --sky-shine: inset 0 0 0 transparent;
  background: #ffffff;
  color: #56679a;
  -webkit-text-stroke: 0;
  border-color: var(--ink);
}
.btn.btn-buy.too-poor .cash-ico { opacity: 0.6; }
.btn.btn-buy.too-poor:hover:not(:disabled) { background-color: #fffaf4; }

/* Dead in the current game, kept safe: same as the secondary. */
.btn.btn-ghost,
.btn.counting { background-color: #ffffff; color: var(--ink); -webkit-text-stroke: 0; }
.btn.counting { font-size: clamp(20px, 5vw, 26px); font-variant-numeric: tabular-nums; }

.btn:disabled { opacity: 0.5; }

/* Pressed - for the mouse (:active) and for touch (.pressing, set by UI on
   pointerdown). Written against #ui so it also outranks the shop's
   id-scoped button rules. */
#ui .btn:active:not(:disabled),
#ui .btn.pressable.pressing {                                  /* (1,3,0) */
  transform: translateY(calc(var(--sky-lh) - 1px));
  box-shadow: var(--sky-shine), 0 1px 0 var(--sky-lc);
  filter: none;
}

/* The DOUBLE CASH nudge: a little hop and landing squash, then a long rest -
   transform only (it was an endless animated box-shadow). A hop rather than
   a wiggle: the button runs the panel's full width, and any rotation swings
   its ends about. Listed before the pop and the shake so a press on it still
   bounces and a refusal still shakes. */
.btn.btn-reward.urging { animation: skyUrge 2.2s ease-in-out infinite; transform-origin: 50% 100%; }
@keyframes skyUrge {
  0%, 62%, 100% { transform: none; }
  72% { transform: translateY(-2px) scale(1.01, 1.02); }
  82% { transform: translateY(1px) scale(1.02, 0.95); }
  90% { transform: none; }
}
#ui .btn.btn-reward.urging.pressing { animation: none; }
.btn.btn-reward.loading { animation: none; opacity: 0.85; cursor: progress; }

.btn.pressable.popped { animation: skyPop 0.26s cubic-bezier(.2,1.8,.4,1); }
@keyframes skyPop {
  0%   { transform: translateY(3px); }
  55%  { transform: translateY(-1px) scale(1.035); }
  100% { transform: none; }
}
.btn.pressable.denied { animation: btnDeny 0.34s ease; }

.btn-row { display: flex; gap: 10px; }


/* ======================= ICONS ON THE WORD BUTTONS =======================
   The home screen speaks in pictures (a play mark, a shirt, "EN"); the old
   panels were words only. Each button gets its picture as a ::before, drawn
   in the house's two inks - the ink outline and one colour: her pink for
   the shirt, his blue for the restart arrow, the lit windows' yellow for
   home. Background images, so no markup changes and nothing to theme. */
#btnResume::before,
#btnNext::before,
#btnPauseShop::before,
#btnSumShop::before,
#btnPauseRestart::before,
#btnPauseMenu::before,
#btnSumMenu::before {
  content: '';
  flex: 0 0 auto;
  width: 1.15em; height: 1.15em;
  background: center / contain no-repeat;
}
/* play: PLAY's own triangle, white in ink */
#btnResume::before,
#btnNext[data-i18n="summary.next"]::before {
  width: 1.2em; height: 1.2em;
  margin-left: -0.2em;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.4 4.9 19 11.1a1 1 0 0 1 0 1.8L8.4 19.1A1 1 0 0 1 7 18.2V5.8a1 1 0 0 1 1.4-.9z' fill='%23fff' stroke='%232b3a67' stroke-width='2.6' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* shirt: the home tile's, pink in ink */
#btnPauseShop::before,
#btnSumShop::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 3.5 12 6l3-2.5 5 2.6-1.7 4.2-2.1-.8V20H7.8v-10.5l-2.1.8L4 6.1z' fill='%23ff8fc8' stroke='%232b3a67' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* restart: a round arrow, blue on an ink under-stroke (the house's double
   stroke, as on the home screen's dashed path) */
#btnPauseRestart::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-2 -2 28 28' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg stroke='%232b3a67' stroke-width='5.4'%3E%3Cpath d='M1.5 3.5v6h6'/%3E%3Cpath d='M3.8 14.8a8.6 8.6 0 1 0 2-9L1.5 9.5'/%3E%3C/g%3E%3Cg stroke='%234cb8f5' stroke-width='2.3'%3E%3Cpath d='M1.5 3.5v6h6'/%3E%3Cpath d='M3.8 14.8a8.6 8.6 0 1 0 2-9L1.5 9.5'/%3E%3C/g%3E%3C/svg%3E");
}
/* home: a yellow house in ink */
#btnPauseMenu::before,
#btnSumMenu::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='%232b3a67' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M5.6 10.2v9.6h4.6v-5.2h3.6v5.2h4.6v-9.6L12 4.8z' fill='%23ffc93c' stroke-width='2.2'/%3E%3Cpath d='M2.8 11.6 12 3.9l9.2 7.7' fill='none' stroke-width='2.6'/%3E%3C/svg%3E");
}
/* home on the yellow button (the final summary's MENU): a white house */
#btnNext[data-i18n="summary.menu"]::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='%232b3a67' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M5.6 10.2v9.6h4.6v-5.2h3.6v5.2h4.6v-9.6L12 4.8z' fill='%23fff' stroke-width='2.4'/%3E%3Cpath d='M2.8 11.6 12 3.9l9.2 7.7' fill='none' stroke-width='2.8'/%3E%3C/svg%3E");
}


/* ================================= PAUSE =================================
   The screen the player called "AI-looking". Now: the sky window with the
   title lettered like the logo, RESUME as a big PLAY
   pill that breathes like PLAY, and under it three picture tiles - SKINS,
   RESTART, MENU - cut like the home screen's tiles, standing above the city
   on her roof. A 3-column grid; the markup's .btn-row wrapper steps out of
   the way (display: contents) so its two buttons join the row. */
#screen-pause .panel {                                         /* (1,1,0) */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 10px;
  align-items: stretch;
}
#screen-pause .panel h2,
#screen-pause #btnResume { grid-column: 1 / -1; }
/* The title's box hugs its word: stretched to the full row, the slap's 1.35
   scale pushed it 30px past the panel, which scrolls (overflow auto), and a
   horizontal scrollbar flashed up under every pause for 0.4s. */
#screen-pause .panel h2 { justify-self: center; }
#screen-pause .btn-row { display: contents; }
#screen-pause .panel .btn { margin: 0; }

/* The title slaps on like a sticker, once, as the panel arrives. */
#screen-pause.active .panel h2 {
  animation: skySlap 0.42s cubic-bezier(.2,1.35,.4,1) 0.06s both;
}
@keyframes skySlap {
  0%   { opacity: 0; transform: rotate(-8deg) scale(1.35); }
  100% { opacity: 1; transform: rotate(-1.5deg) scale(1); }
}
/* No sparkles either side of the word: "✦ TITLE ✦" is the stock ornament
   that read as AI; the clouds already dress the panel. */

#screen-pause #btnResume {
  margin-bottom: 16px;
  transform-origin: 50% 100%;
  gap: 0.35em;
}
/* RESUME breathes the way PLAY does: a squash and a hop, then a long rest.
   It is about twice PLAY's width, so the sideways swell is a third of
   PLAY's (1.2%, about 2px a side): a full-width bar swelling 6px a side
   wobbled like jelly. The up-and-down is PLAY's. */
#screen-pause.active #btnResume {                              /* (2,1,0) */
  animation: skyIdle 2.4s ease-in-out 0.7s infinite;
}
@keyframes skyIdle {
  0%, 64%, 100% { transform: none; }
  74% { transform: translateY(2px) scale(1.012, 0.95); }
  86% { transform: translateY(-4px) scale(0.996, 1.035); }
  94% { transform: scale(1.004, 0.99); }
}
#screen-pause.active #btnResume.pressing { animation: none; }            /* (2,2,0) */
#screen-pause.active #btnResume.pressable.popped { animation: skyPop 0.26s cubic-bezier(.2,1.8,.4,1); }

/* The three tiles: picture over word, the home tiles' squircle grown a word. */
#screen-pause .panel .btn-secondary {
  flex-direction: column;
  gap: 4px;
  min-height: 74px;
  padding: 8px 4px 7px;
  border-radius: var(--r-md);
  font-size: 12px;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
#screen-pause .panel .btn-secondary::before { width: 30px; height: 30px; }


/* ============================ END OF THE CLIMB ===========================
   The summary after floor 200. The title banner is a strip of the home
   screen: the title in gold on the sky, and under it the full water bar on
   a slim navy roof - where the bar lives on the home screen - with his head
   and the badge rising above the roofline into the sky, as they do there.
   (A 64px roof made two thirds of the banner a dark slab and ran into the
   title's outline.) The bands overlap under the border, as on the panels.
   The numbers and buttons sit in a sky panel on her roof, like pause. */
.win-banner {
  transform: rotate(-2deg);
  background:
    linear-gradient(var(--sky-roof), var(--sky-roof)) left 0 bottom -4px / 100% 38px no-repeat,
    linear-gradient(var(--sky-roof-2), var(--sky-roof-2)) left 0 bottom -4px / 100% 42px no-repeat,
    linear-gradient(var(--sky-rim), var(--sky-rim)) left 0 bottom -4px / 100% 45px no-repeat,
    var(--sky-grad);
  background-color: var(--sky-2);
  border: 4px solid var(--ink);
  border-radius: var(--r-md);
  box-shadow: 0 6px 0 var(--sky-ledge-dk);
  padding: 10px 26px 6px;
}
.win-banner h2 {
  font-style: italic;                /* the wordmark's lean, in gold */
  color: var(--yellow);
  -webkit-text-stroke: 0.15em var(--ink);
  paint-order: stroke fill;
  text-shadow: none;
  filter: drop-shadow(0 0.08em 0 var(--sky-drop));
  letter-spacing: 0.05em;
  padding-bottom: 2px;
}
.win-bar { margin-top: 2px; }
/* On a phone the centred banner ran under the sound column in the top-right
   corner (it did before this file too: its last letters were hidden behind
   the music button). It now keeps a column's width clear on BOTH sides, so
   it stays centred over the stars and the panel (clear on the right only,
   it sat 29px left of them), and the title and the bar size down to the
   room that leaves. The bar reads its own width every frame (FluidBar), so
   a narrower box is all it needs. */
@media (max-width: 580px) {
  .win-banner {
    margin-left: 58px;
    margin-right: 58px;
    max-width: calc(100% - 116px);
    padding: 8px 14px 6px;
  }
  .win-banner h2 { font-size: clamp(18px, 5.2vw, 30px); letter-spacing: 0.03em; }
  .win-bar { width: min(300px, 100%); }
}
/* ...and up to 720px the column layout still puts the banner under that
   column: CAMPAÑA COMPLETADA ran 17px under it at 600 wide, 8px at 680.
   Same clearance, and the title a size down so it stays on one line. */
@media (min-width: 581px) and (max-width: 720px) {
  .win-banner { margin-right: 58px; max-width: calc(100% - 58px); }
  .win-banner h2 { font-size: min(5vw, 36px); }
}

/* Stars: gold in the ink outline with a white glint and a hard ledge, like
   everything else - the clip-path version could not take an outline, and
   its 9px glow was a blur. An unearned one is an empty socket outlined in
   the roof's peach rim. */
.screen-win .star {
  clip-path: none;
  box-shadow: none;
  background: center / contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-6 -6 112 112'%3E%3Cpath d='M50 0 62 36 100 38 70 60 81 98 50 75 19 98 30 60 0 38 38 36z' fill='%23ffffff' fill-opacity='0.14' stroke='%23ffd6b8' stroke-opacity='0.8' stroke-width='6' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* (0,3,0): style.css's own :not(.on) rule is a `background` shorthand at
   this weight, which would wipe the socket picture. */
.screen-win .star:not(.on) {
  background: center / contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-6 -6 112 112'%3E%3Cpath d='M50 0 62 36 100 38 70 60 81 98 50 75 19 98 30 60 0 38 38 36z' fill='%23ffffff' fill-opacity='0.14' stroke='%23ffd6b8' stroke-opacity='0.8' stroke-width='6' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.screen-win .star.on {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-6 -6 112 112'%3E%3Cpath d='M50 0 62 36 100 38 70 60 81 98 50 75 19 98 30 60 0 38 38 36z' fill='%23ffcb2e' stroke='%232b3a67' stroke-width='7' stroke-linejoin='round'/%3E%3Cpath d='M38 46 45 30' stroke='%23fff' stroke-opacity='0.8' stroke-width='6' stroke-linecap='round'/%3E%3C/svg%3E");
  filter: drop-shadow(0 3px 0 rgba(10,14,40,0.55));
}

.win-side {
  background: var(--sky-layers);
  background-color: var(--sky-3);
  border: 4px solid var(--ink);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 0 var(--sky-ledge-dk);
  padding: 14px 16px calc(var(--sky-roof-h) + 22px);
}
/* No city on this one: the screen has no height to spare to lift the last
   buttons off the roof, and the rooftops poked out round the ends and under
   the ledge of SKINS / MENU. Roof, sun, clouds and sky only. */
.screen-win .win-side {                                        /* (0,2,0) */
  background: var(--sky-bands), var(--sky-upper);
  background-color: var(--sky-3);
}
.win-side .score-list { margin: 0 0 10px; }

/* Score rows: little white pills, the home screen's BEST pill. */
.score-line {
  margin: 0 0 7px;
  padding: 5px 14px;
  background: #ffffff;
  border: 3px solid var(--ink);
  border-radius: 999px;
  box-shadow: 0 3px 0 var(--sky-ledge);
  color: #56679a;                               /* 5.5:1 on the white pill */
  font-size: 11.5px;
  font-weight: 900;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.score-line:last-child { border-bottom: 3px solid var(--ink); margin-bottom: 0; }
.score-line b { color: var(--ink); font-size: 19px; font-weight: 900; letter-spacing: 0; }

/* The cash won: a mint plate, and the number lettered like a title - green
   in the ink outline (numbers 26px and up get the stroke). */
.win-hero {
  background: var(--sky-cash-lt);
  border: 3px solid var(--ink);
  border-radius: var(--r-md);
  box-shadow: 0 3px 0 var(--sky-ledge);
  padding: 7px 14px;
  margin-bottom: 8px;
}
.win-hero-label { color: var(--sky-cash-ink); font-weight: 900; }   /* 4.8:1 */
.win-hero-value b {
  color: #3fbf6a;
  -webkit-text-stroke: 0.14em var(--ink);
  paint-order: stroke fill;
  filter: drop-shadow(0 0.07em 0 var(--sky-drop));
}
/* BEST: full ink now (it was faded to 3.1:1), and one centred caption line
   ("BEST 58") - spread to the two edges it read as leftover form layout. */
.win-best {
  opacity: 1;
  color: var(--ink);
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 4px 14px;
  justify-content: center;
  gap: 0.6em;
}
/* The main button: a notch less spacing and side padding, so the Russian
   СЛЕДУЮЩИЙ УРОВЕНЬ stays on one line beside its play mark (it wrapped and
   grew the button to 79px); on a phone the word also follows the width
   down to 17px (the old floor was 19px), so it fits from 340px up. */
.win-side .btn.btn-big { letter-spacing: 0.02em; padding-left: 12px; padding-right: 12px; font-size: clamp(17px, 5vw, 23px); }
/* DOUBLE CASH: a label that wraps (УДВОИТЬ ДЕНЬГИ at 320-389px) sits in 2px
   padding with a line-height of 1, so two lines (16px: 32 + 4 + 8 outline =
   44) stay inside the standard button's 50px (46px on short screens), and
   the rewarded button is never taller than SKINS under it. On one line the
   min-height sets the height, as before. */
.win-side .btn.btn-reward { padding-top: 2px; padding-bottom: 2px; }
.win-side .btn.btn-reward .rv-text { line-height: 1; }
.win-side .btn-row .btn { margin-bottom: 0; }
/* SKINS and MENU share a row (every clear but the last): with the icons and
   the outline, СКИНЫ + МЕНЮ needed 270px of a 256px row at 320 wide and the
   pair ran out of the panel. Let them shrink; the words still fit. */
.win-side .btn-row .btn { min-width: 0; padding-left: 10px; padding-right: 10px; }


/* ============================ OUT OF HEARTS ==============================
   No panel on purpose (the floor stays in view). The two choices are the
   same size and cut from the same sheet: REVIVE violet with the film mark
   and AD stamp, "tap to continue" a white pill. Same outline, same ledge;
   only the free label can grow (two lines in the longer languages). */
.screen-revive .revive-btn,
.screen-revive .revive-continue {
  --sky-lc: var(--sky-ledge-dk);
  border-radius: 999px;
}
.screen-revive .revive-btn { --sky-lc: var(--sky-ad-dk); }
/* Uppercase like every other label (it was the one sentence-case button).
   Russian takes two lines, so this one may be the taller of the two - never
   the ad. */
.screen-revive .revive-continue { font-size: 14px; letter-spacing: 0.05em; text-transform: uppercase; }
/* What happened, without a word to translate: the HUD's heart, broken, in
   pale pink and the ink outline, over the two choices. On a real game over
   the camera sits on an empty pad and the two pills floated in a void.
   Static, and taps go through it to the screen (which carries on). */
#screen-revive::before {                                       /* a flex item above the buttons */
  content: '';
  flex: 0 0 auto;
  width: 74px; height: 68px;
  margin-bottom: 2px;
  background: center / contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1.5 -1 27 24'%3E%3Cpath d='M12 20.4S3.6 15.2 1.9 10.3C.6 6.6 2.8 2.6 6.7 2.6c2.2 0 3.9 1.2 5.3 3 1.4-1.8 3.1-3 5.3-3 3.9 0 6.1 4 4.8 7.7C20.4 15.2 12 20.4 12 20.4z' fill='%23ffd3e3' stroke='%232b3a67' stroke-width='2.2' stroke-linejoin='round'/%3E%3Cpath d='M12.4 6.6 10.6 10l2.6 1.6-2 4.2' fill='none' stroke='%232b3a67' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  filter: drop-shadow(0 3px 0 var(--sky-drop));
  pointer-events: none;
}


/* ================================= SHOP ==================================
   Pass 3's cream paper stays (it is the peach end of the same sky). What
   changes is that it now wears the family: the ink outline and the dark
   ledge, a slim roof along its foot, the lettered title, ink-outlined cards,
   and the same buttons as everywhere else. Scoped with the id, like the
   rules it replaces. */
#screen-shop .shop-panel {                                     /* (1,1,0) */
  background:                          /* bands overlap under the border, as on the panels */
    linear-gradient(var(--sky-roof), var(--sky-roof)) left 0 bottom -4px / 100% 11px no-repeat,
    linear-gradient(var(--sky-roof-2), var(--sky-roof-2)) left 0 bottom -4px / 100% 15px no-repeat,
    linear-gradient(var(--sky-rim), var(--sky-rim)) left 0 bottom -4px / 100% 18px no-repeat,
    linear-gradient(180deg, #fff7ed 0%, #ffeedd 100%);
  background-color: #fff7ed;
  border: 4px solid var(--ink);
  box-shadow: 0 8px 0 var(--sky-ledge-dk);
  padding-bottom: 26px;                        /* 14px roof + 12px air */
}
/* The header takes her sky, as pause and the end screen do, and fades to
   the cream by 120px, before the section labels and the rack (secondary ink
   does not pass on the sky). A phone on its side keeps the plain cream and
   its slimmer roof (below). */
@media (min-height: 421px) {
  #screen-shop .shop-panel {                                   /* (1,1,0) */
    background:
      linear-gradient(var(--sky-roof), var(--sky-roof)) left 0 bottom -4px / 100% 11px no-repeat,
      linear-gradient(var(--sky-roof-2), var(--sky-roof-2)) left 0 bottom -4px / 100% 15px no-repeat,
      linear-gradient(var(--sky-rim), var(--sky-rim)) left 0 bottom -4px / 100% 18px no-repeat,
      linear-gradient(180deg, var(--sky-1) 0, var(--sky-2) 28px, var(--sky-3) 56px, var(--sky-4) 84px, #fff7ed 120px, #ffeedd 100%);
  }
}
#screen-shop .shop-head h2 {                                   /* (1,1,1) */
  font-style: italic;                /* the panel titles' lettering */
  color: #4cb8f5;
  -webkit-text-stroke: 0.15em var(--ink);
  paint-order: stroke fill;
  filter: drop-shadow(0 0.08em 0 var(--sky-drop));
  transform: rotate(-1.5deg);
  /* Room inside the clipping box for the outline (the title ellipsizes),
     and 10px on the right for the italic lean. */
  padding: 3px 10px 5px 3px;
  line-height: 1;
  /* A title, not a caption: at 20px it was smaller than the wallet's number
     beside it (PAUSED is 28-40px). SKINS / СКИНЫ are short in every
     language; the small screens keep their 18px (restated just below). */
  font-size: clamp(24px, 3.6vw, 32px);
}
@media (max-width: 340px), (max-height: 420px) {
  #screen-shop .shop-head h2 { font-size: 18px; }
}
#screen-shop .pill { box-shadow: 0 3px 0 var(--sky-ledge); }

/* The stage card: white, outlined, standing on its ledge. The window keeps
   its tier ring, now inside an ink one (a box-shadow: the canvas keeps its
   drawing box). */
.shop-stage {
  border-radius: var(--r-md);
  box-shadow: inset 0 0 0 3px var(--ink), 0 4px 0 var(--sky-ledge);
}
.stage-cv { box-shadow: 0 0 0 2px var(--tier), 0 0 0 4.5px var(--ink); }
/* (pass 14h) the flare: p14b.css .stage-ring (opacity, not box-shadow) */
.stage-pips i { box-shadow: 0 0 0 2px rgba(43,58,103,0.35) inset; }
/* The name clips for its ellipsis, and at line-height 1.05 that cut the
   tails off g, y and p (Voltage). Room below, taken back by the margin. */
.stage-name { padding-bottom: 0.2em; margin-bottom: -0.2em; }

/* The stage's one action and BACK are twins (46px, 3px outline, 4px ledge),
   so WATCH AD can never stand taller than BACK. The padding is trimmed so the
   film mark (22px) plus the outline stays inside the 46px. */
#screen-shop .stage-act {                                      /* (1,1,0) */
  --sky-lh: 4px;
  border-width: 3px;
  padding-top: 7px;
  padding-bottom: 7px;
}
/* (pass 31) BUY, BUY out of reach, their presses and hovers: the one button system
   ("PASS 31: ONE BUTTON SYSTEM", the end of this file) - BUY is a primary now. */
/* WEARING: a state, not an action - a mint button already pressed flat. */
#screen-shop .stage-act.is-worn {                                           /* (1,2,0) */
  background: var(--sky-cash-lt);
  color: var(--sky-cash-ink);                  /* 4.8:1 */
  -webkit-text-stroke: 0;
  border-color: var(--ink);
  box-shadow: 0 1px 0 var(--sky-ledge);
  transform: translateY(3px);
  opacity: 1;
}
/* No ad to be had: flat violet tint, violet words (6.1:1), no ledge. The
   sentence is long ("No ad available right now"), so it may take two
   centred lines instead of running out across the outline on both sides. */
#screen-shop .stage-act.no-ad {                                             /* (1,2,0) */
  background: var(--sky-ad-lt);
  color: var(--sky-ad-dk);
  -webkit-text-stroke: 0;
  box-shadow: none;
  opacity: 1;
  font-size: 12px;
  letter-spacing: 0.02em;
  padding-left: 8px;
  padding-right: 8px;
}
#screen-shop .stage-act.no-ad .rv-text { white-space: normal; line-height: 1.05; text-align: center; }   /* (1,3,0) */

/* The stage's column beside the sound column (sound, music, home: 12px in
   from the right, down to 146px with its ledge). On an upright phone the
   stage card ran 18px under it; it now stops 6px short, as if the column
   stood in a notch cut for it. The rack starts below the column and keeps
   the full width. The column is 40px wide up to 460px and
   46px above. */
@media (max-width: 600px) and (min-height: 421px) {
  #screen-shop .shop-stage { margin-right: 24px; padding-right: 8px; }     /* (1,1,0) */
}
@media (min-width: 461px) and (max-width: 600px) and (min-height: 421px) {
  #screen-shop .shop-stage { margin-right: 30px; }
}

/* The cards: white, a 2px ink ring (inset, so the card and its picture keep
   their size), a small ledge; the tier stays a tab of colour along the top.
   Selected: lifted, warm, with a heavier ring. */
.skin-tile {
  border-radius: var(--r-sm);
  box-shadow: inset 0 0 0 2px var(--ink), 0 3px 0 var(--sky-ledge);
}
/* Selected: PLAY's yellow, the one thing in focus - the pale wash only
   showed as a thin strip under the picture and the worn card's green ring
   was louder. Name ink on it is 7.2:1. */
.skin-tile.selected {
  transform: translateY(-3px);
  background: var(--yellow);
  box-shadow: inset 0 0 0 3px var(--ink), 0 5px 0 var(--sky-ledge);
}
/* The one you are wearing: the ink ring with a green one inside it - the
   green of its tick and of WEARING on the stage. */
.skin-tile.equipped:not(.selected) { box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 4.5px #3fbf6a, 0 3px 0 var(--sky-ledge); }
.skin-tile::before { top: 2px; }
.skin-grid { padding-top: 6px; }

/* Epic was the ad violet: on an epic ad skin (Celestial) the ring, the tier
   word, WATCH AD and the pips all merged. It is a magenta-purple now, about
   40 degrees off the ad violet - still read as "epic"; its ink 6.3:1 on
   white. */
.rar-epic { --tier: #c026d3; --tier-ink: #a21caf; --tier-glow: rgba(192,38,211,0.40); }

/* Chips: small flat stickers, each in its one colour. */
.chip-cash     { background: var(--sky-cash-lt); color: var(--sky-cash-ink); box-shadow: 0 0 0 1.5px var(--sky-cash-ink) inset; }
.chip-cash.can { background: var(--sky-cash); color: #fff; box-shadow: 0 0 0 1.5px var(--sky-cash-dk) inset; }
.chip-ad       { background: #ffffff; color: var(--sky-ad-dk); box-shadow: 0 0 0 1.5px var(--sky-ad) inset; }
.chip-owned    { background: #eef0f6; color: #56679a; }
.chip-worn     { background: #3fbf6a; box-shadow: 0 0 0 1.5px var(--sky-cash-dk) inset; }


/* ================================== HUD ==================================
   Over the live game, so every ledge is the home tiles' dark one (it reads on
   her pink sky and on floor 1's hell alike). Paint only: no size, padding or
   gap changes, so the narrow-phone fitting and the hearts move are untouched.

   SKINS was a violet pill with a glow and a sweep that ran for the whole
   game (an animated box-shadow and background, repainting every frame).
   It is now the home screen's SKINS tile as a pill: white, the pink shirt,
   ink word - and a small wiggle every seven seconds to be noticed. */
.hud-skins {                                                   /* (0,1,0), later */
  background: #ffffff;
  color: var(--ink);
  text-shadow: none;
  box-shadow: 0 3px 0 var(--sky-ledge-dk);
  animation: skyNudge 7s ease-in-out 3s infinite;
}
.hud-skins .ico-shirt { fill: var(--pink); stroke: var(--ink); }
.hud-skins:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--sky-ledge-dk); animation: none; }
@keyframes skyNudge {
  0%, 86%, 100% { transform: none; }
  89% { transform: rotate(-4deg) translateY(-1px); }
  92% { transform: rotate(3deg); }
  95% { transform: rotate(-1.5deg); }
}
/* "Something here you can afford": a yellow sticker dot (it was red). */
.hud-skins-dot {
  background: var(--yellow);
  border: 2.5px solid var(--ink);
  box-shadow: 0 2px 0 var(--sky-ledge-dk);
}
.hud-skins-dot.on { animation: skyDot 2.4s ease-in-out infinite; }
@keyframes skyDot {
  0%, 76%, 100% { transform: none; }
  84% { transform: scale(1.3); }
  92% { transform: scale(0.94); }
}

/* Pills (cash in the HUD, the menu's corner pills): solid white. */
.pill { background: #ffffff; box-shadow: 0 3px 0 var(--sky-ledge-dk); }
/* The HUD's cash pill stands as tall as SKINS beside it (44px; it was 31-36
   between a 44px SKINS and a 46px pause, and looked like a smaller part).
   40px on a 320px phone, where pause is 40. */
.hud .pill-cash { min-height: 44px; }                          /* (0,2,0), later */
@media (max-width: 340px) { .hud .pill-cash { min-height: 40px; } }
#screen-menu .menu-best { background: #ffffff; box-shadow: 0 3px 0 var(--sky-ledge-dk); }  /* (1,1,0) */

/* Hearts: her pink, the one pink for hearts everywhere, in the ink outline
   with the hard drop. A lost one stays in place as pale paper. (Size and
   position belong to the hearts change; only paint here.) */
.hud-hearts .heart {                                           /* (0,2,0), later */
  fill: var(--sky-love);
  stroke: var(--ink);
  filter: drop-shadow(0 2px 0 var(--sky-drop));
}
.hud-hearts .heart.lost { fill: rgba(255,247,237,0.45); }

/* Her speech bubble under the bar: already white in an ink outline, but its
   ledge was the pale ink - over the live world it takes the HUD's dark one. */
.bar-say { box-shadow: 0 2px 0 var(--sky-ledge-dk); }

/* Pause (and the sound column below): ink glyphs on white. The pause bars
   were coral - red-family, and 2.5:1. */
.icon-btn {
  color: var(--ink);
  background: #ffffff;
  box-shadow: 0 4px 0 var(--sky-ledge-dk);
}
.icon-btn:active,
.icon-btn.pressable.pressing {                                 /* (0,3,0) */
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--sky-ledge-dk);
  filter: none;
}
/* The squircles are 40px on a phone (the sound column up to 460px wide,
   pause at 340px and under): a clear ring 2px round each makes the finger's
   target 44px without changing the look. The column's gap is 6px and pause
   is 4px from it at 320, so the rings never meet. (-5px: an absolute box is
   placed from the padding edge, inside the 3px outline.) */
.icon-btn { position: relative; }
.icon-btn::after { content: ''; position: absolute; inset: -5px; }


/* ============================= SOUND COLUMN ==============================
   Effects, music and home: the same white squircles in ink (they were violet,
   which now means "ad"). Muted keeps the grey-blue and the strike. */
.audio-fixed .icon-btn { color: var(--ink); }                 /* (0,2,0), later */
/* (pass 10a) Vibration: the column's next switch, only where a phone can
   buzz (Android - UI.init takes away its `hidden`). An outline, like Home;
   off takes the music's two-pass strike. */
#btnBuzz[hidden] { display: none; }
.audio-fixed .ico-buzz { fill: none; stroke: currentColor; }
.audio-fixed .ico-buzz .slash-cut { stroke: #fff; }


/* ================================= TOAST =================================
   A message, not a button: a small navy plate of her roof with the peach
   rim along its top, white words (13.5:1), the ink outline and a ledge.
   (Dressed as a white pill it was the white-button recipe exactly, and in
   the shop it landed on BACK, which then seemed to turn into a "Not enough
   cash" button.) Sized to its words: at left 50% it only ever got half the
   screen, and wrapped into a two-line blob on a phone. */
.toast {
  width: max-content;
  max-width: 88vw;
  text-align: center;
  background: var(--sky-roof);
  color: #ffffff;
  border: 3px solid var(--ink);
  box-shadow: inset 0 2px 0 var(--sky-rim), 0 4px 0 var(--sky-ledge-dk);
  font-weight: 900;
  padding: 9px 18px;
}
/* Over the shop and the end screen, the foot of the screen is where their
   buttons are (BACK; NEXT and DOUBLE CASH), so the note floats in the middle
   instead, for its 1.7s. #toast follows every section in #ui, hence ~. */
#screen-shop.active ~ .toast,
#screen-summary.active ~ .toast { bottom: auto; top: 50%; transform: translate(-50%, -30%); }   /* (1,2,0) */
#screen-shop.active ~ .toast.show,
#screen-summary.active ~ .toast.show { transform: translate(-50%, -50%); }                     /* (1,3,0) */
/* An upright end screen with the dancing doll: the panel's top edge sits
   near mid-screen there, so the note goes up into the doll's space. */
@media (max-width: 720px) and (min-height: 721px) {
  #screen-summary.active ~ .toast { top: calc((100vh - 230px) / 2); }
}
/* A wider screen puts the doll beside the panel (38% of the row, 16px
   padding, 720px at most), and mid-screen was the panel's cash row. The
   note floats over the doll instead. */
@media (min-width: 721px) and (min-height: 561px) {
  #screen-summary.active ~ .toast {
    left: calc((100vw - min(100vw, 720px)) / 2 + 16px + (min(100vw, 720px) - 32px) * 0.19);
    max-width: calc((min(100vw, 720px) - 32px) * 0.38);
  }
}
/* The first-visit prologue: the foot of the screen is TAP TO START (and
   "Progress will not be saved" landed right on it). Up in the city sky,
   over the two of them. */
body.in-prologue .toast { bottom: auto; top: 40%; transform: translate(-50%, -30%); }   /* (0,2,1) */
body.in-prologue .toast.show { transform: translate(-50%, -50%); }
/* The home screen: its foot is the navy roof and PLAY, where a navy plate
   disappeared. It floats over the pink sky instead. */
#screen-menu.active ~ .toast { bottom: auto; top: 44%; transform: translate(-50%, -30%); }
#screen-menu.active ~ .toast.show { transform: translate(-50%, -50%); }
/* A phone on its side: mid-screen is PLAY, and the picture is the left
   column (1.25fr : 1fr, 16px sides and gap; 1fr : 1fr up to 640px wide).
   The note floats in the middle of the picture. */
@media (max-height: 500px) and (min-aspect-ratio: 13/10) {
  #screen-menu.active ~ .toast {
    top: 50%;
    left: calc(16px + var(--safe-l) + (100vw - 48px - var(--safe-l) - var(--safe-r)) * 0.2778);
    max-width: calc((100vw - 48px - var(--safe-l) - var(--safe-r)) * 0.5556);
  }
}
@media (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 640px) {
  #screen-menu.active ~ .toast {
    left: calc(16px + var(--safe-l) + (100vw - 48px - var(--safe-l) - var(--safe-r)) * 0.25);
    max-width: calc((100vw - 48px - var(--safe-l) - var(--safe-r)) * 0.5);
  }
}
/* The two-column end screen (below): mid-screen is NEXT itself, so the note
   goes to the foot of the left column, under the stars. The sums follow the
   grid: 10px + 62px padding, 12px gap, 0.85fr / 1.15fr (8px, 0.62fr / 1.38fr
   under 700px wide). A phone on its side under 560px wide gets the same
   layout (see the end screen below). */
@media (max-height: 560px) and (min-width: 560px), (max-height: 500px) and (min-aspect-ratio: 13/10) {
  #screen-summary.active ~ .toast {
    top: auto;
    bottom: calc(14px + var(--safe-b));
    left: calc((100vw - min(100vw, 820px)) / 2 + 10px + var(--safe-l) + (min(100vw, 820px) - 84px - var(--safe-l) - var(--safe-r)) * 0.2125);
    max-width: calc((min(100vw, 820px) - 84px) * 0.425);
    transform: translate(-50%, 12px);
  }
  #screen-summary.active ~ .toast.show { transform: translate(-50%, 0); }
}
@media (max-height: 560px) and (min-width: 560px) and (max-width: 700px), (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 700px) {
  #screen-summary.active ~ .toast {
    left: calc(10px + var(--safe-l) + (100vw - 80px - var(--safe-l) - var(--safe-r)) * 0.155);
    max-width: calc((100vw - 80px) * 0.31);
    /* A size down: in this 120-190px column a long message took three
       lines at 14px and rose onto the stars at 480x320. */
    font-size: 12px;
    line-height: 1.2;
    padding: 6px 10px;
  }
}


/* ========================= LETTERING IN THE WORLD ========================
   The floor numbers' rule for any words over the game: a light fill in a
   dark outline with a hard drop - never the reverse. Readable over her pink
   sky (the outline) and over floor 1's hell (the fill). */

/* The tutorial line: white in ink (it was periwinkle in white). The outline
   is in em so it scales with the three sizes the line takes.
   Sized to its words, like the story line: placed at left 50%, its box only
   ever got half the screen, so a short line stacked into two or three stubby
   lines (НАЖМИТЕ, / ЧТОБЫ / НЫРНУТЬ) that grew up into the water bar. The
   max-width (94vw, 88vw on a phone) still wraps the long ones. */
.tap-hint {
  width: max-content;
  color: #ffffff;
  -webkit-text-stroke: 0.2em var(--ink);
  filter: drop-shadow(0 0.09em 0 var(--sky-drop));
}
.tap-hint.show.calm { -webkit-text-stroke-width: 0.2em; }                 /* (0,3,0) */
.tap-hint.show.freeze,
.tap-hint.show.calm.freeze { -webkit-text-stroke-width: 0.2em; }
/* A phone on its side: the freeze wording (the long keyboard one on a
   laptop-sized landscape) a size down, clear of the sound column. */
@media (max-height: 500px) {
  .tap-hint.show.freeze,
  .tap-hint.show.calm.freeze { font-size: clamp(20px, 3.6vw, 30px); }
}

/* The story line: her pink (it was a raspberry right next to the hazard
   reds), in ink. */
.story-card {
  color: var(--sky-love);
  -webkit-text-stroke: 0.2em var(--ink);
  filter: drop-shadow(0 0.1em 0 var(--sky-drop));
}
/* It shows while the tutorial line is still up, and has to stay clear
   under it. With the hearts in their own row (the same query as --bar-top
   92px) the tutorial line moved down 36px and the story line only 26px:
   on a 320-360px phone they touched. On a phone on its side the two-line
   Russian story ran into it too. Both now sit lower (34% still wins on the
   taller phones). */
@media (max-width: 559px), (max-width: 599px) and (min-height: 501px) {
  .story-card { top: max(34%, calc(var(--bar-top) + var(--bar-h) + 112px + var(--safe-t))); }
}
@media (max-height: 500px) and (min-width: 560px),
       (max-height: 500px) and (min-aspect-ratio: 13/10) {
  .story-card { top: max(34%, calc(var(--bar-top) + var(--bar-h) + 108px + var(--safe-t))); }
}
/* A story that wraps grows DOWN, away from the tutorial line: the card is
   held by its first line (0.54em = half its 1.08 line-height), so a second
   line no longer pushes up into it (Russian at 960x540 overlapped by 17px).
   One line: no change. The separate translate property adds to the
   keyframes' transform and to the reduced-motion rule. */
.story-card { translate: 0 calc(50% - 0.54em); }

/* TAP TO START: the blurred pink glow goes; the hard drop stays. */
.skip-hint { filter: drop-shadow(0 4px 0 var(--sky-drop)); }


/* ================================= SKIP ==================================
   A button, so the white pill every other button is: ink word (11:1), the
   ink outline and a ledge. Small and quiet in its corner, but it reads on
   the black letterbox, where the navy one all but disappeared (navy plates
   are the game's messages now - the toast). Its padded hit area (::before
   in style.css) is placed inside the outline, so it reaches 1px further
   up and down to stay over 44px tall. */
.skip-btn {
  background: #ffffff;
  color: var(--ink);
  border: 2.5px solid var(--ink);
  box-shadow: 0 3px 0 var(--sky-ledge-dk);
  font-weight: 900;
  padding: 6px 12px 5px;
}
.skip-btn::before { top: -11px; bottom: -11px; }
.skip-btn:hover { background: #fffaf4; color: var(--ink); }
.skip-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--sky-ledge-dk); }
/* While the prologue runs, the bottom letterbox bar belongs to TAP TO START
   (in Russian it wraps to two lines and the white pill hid its first
   letters). SKIP waits in the top bar instead, on the left, away from the
   sound column, centred in the bar's depth (the same sum as .skip-hint
   uses for the bottom one). A size smaller there (22px + a 2px ledge), so
   on a 320px-tall phone it fits inside the 32px bar with 4px to spare: the
   speech bubbles are drawn under the bar, and at 28px the pill's ledge
   touched ДЕРЖИСЬ! and ¡JAJAJAJA! in the close-ups. The invisible hit area
   runs 3px up and 20px down from the pill (it is placed from inside the
   2px outline): 45px, all on screen. The rescue and the finale keep it
   bottom-left: no hint there. */
body.in-prologue .skip-btn {
  top: max(calc(4px + var(--safe-t)), calc(max(10vh, (100vh - 130vw) / 2) / 2 - 12px));
  bottom: auto;
  font-size: 11px;
  border-width: 2px;
  padding: 4px 10px 3px;
  box-shadow: 0 2px 0 var(--sky-ledge-dk);
}
body.in-prologue .skip-btn::before { top: -5px; bottom: -22px; }
body.in-prologue .skip-btn:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--sky-ledge-dk); }


/* ============================ HOME AND LOADING ===========================
   PLAY and the tiles already ARE the look. Two fixes: on a phone the press
   (.pressing, set on pointerdown - touch never shows :active in time) now
   sinks them onto their ledge as the mouse press does, instead of only
   shrinking; and the picture's heart is the one heart pink. */
#screen-menu .play-btn.pressable.pressing {                    /* (1,3,0) */
  transform: translateY(6px) scale(0.98);
  box-shadow: 0 2px 0 var(--yellow-dk);
  filter: none;
}
.tile-btn.pressable.pressing {                                 /* (0,3,0) */
  transform: translateY(4px);
  box-shadow: 0 1px 0 rgba(10,14,40,0.55);
  filter: none;
}
.bs-heart { fill: var(--sky-love); }

/* The loading bar: the dark hard ledge of the title, and a flat pink fill
   with a shine band (it was a pink-to-pale gradient). */
.load-bar  { box-shadow: 0 4px 0 var(--sky-drop); }
.load-fill { background: var(--sky-love); box-shadow: inset 0 2px 0 rgba(255,255,255,0.45); }


/* ================================= FOCUS =================================
   One ring for every control, keyboard only. The pale --blue on the dark
   scrims and the world; the deeper blue inside the pale panels, where the
   pale one measured under 2:1 against the sky. .icon-btn had no ring. */
.btn:focus-visible,
.icon-btn:focus-visible,
.hud-skins:focus-visible,
.skip-btn:focus-visible,
.skin-tile:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }
.play-btn:focus-visible,
.tile-btn:focus-visible  { outline: 4px solid var(--blue); outline-offset: 4px; }
.panel .btn:focus-visible,
.win-side .btn:focus-visible { outline-color: var(--sky-focus-lt); }
/* The rack scrolls (overflow clips) with only 3px of padding on its left and
   6px on top: a ring 3-6px out was cut off entirely on the left column (and
   on top of a lifted, selected card). Flush with the card it fits inside. */
#screen-shop .skin-tile:focus-visible { outline-color: var(--sky-focus-lt); outline-offset: 0; }


/* ============================== DEAD RULES ===============================
   Not used by the game today, neutralised so nothing red can come back. */
.head-fail { background: linear-gradient(180deg, #3b4677 0%, #242b55 100%); }
.head-fail .star { background: rgba(255,255,255,0.2); }


/* ============================ SCREEN SIZES =============================== */
/* A roomy desktop: the pause window a size up, so it does not read as a
   tooltip in the middle of a big screen. */
@media (min-width: 900px) and (min-height: 700px) {
  #screen-pause .panel { width: 420px; }
  #screen-pause .panel .btn-secondary { min-height: 84px; font-size: 13px; }
  #screen-pause .panel .btn-secondary::before { width: 34px; height: 34px; }
}
/* Phones: the panels give back a little padding; the tiles' word a pixel. */
@media (max-width: 460px) {
  .panel { padding-left: 14px; padding-right: 14px; }
  #screen-pause .panel { column-gap: 8px; }
  #screen-pause .panel .btn-secondary { font-size: 11px; letter-spacing: 0.02em; }
  .win-side { padding-left: 12px; padding-right: 12px; }
  /* The outlines and the roof made the end screen 40-60px taller; at 375x812
     it then scrolled 13-16px in Spanish and Russian (it fitted before).
     A little less air under the buttons and between the rows buys it back. */
  .win-side { padding-bottom: calc(var(--sky-roof-h) + 12px); }
  .win-side .score-list { margin-bottom: 8px; }
  .win-best { margin-bottom: 10px; }
}
/* The shop's stage on a narrow phone: its action sits in a strip beside the
   window, about 115-160px wide, and film mark + word + AD stamp did not fit
   in it (WATCH AD overflowed before this file too; РЕКЛАМА is 55px on its
   own). The stamp moves onto the button's top-right corner, stuck on like a
   sticker - still the film mark and the AD stamp, only arranged - and the
   word may take two short lines, which still fit inside BACK's height. */
@media (max-width: 400px) {
  #screen-shop .stage-act.btn-reward { position: relative; padding-left: 8px; padding-right: 8px; }
  #screen-shop .stage-act.btn-reward .ad-badge { position: absolute; top: -11px; right: -4px; }
  #screen-shop .stage-act .rv-text { white-space: normal; line-height: 1; font-size: 12px; }     /* (1,2,0) */
  #screen-shop .stage-act .rv-ico { width: 20px; height: 15px; }
  /* The stamp on the corner sat on the last of eight pips (Apex, Titan) in
     ANUNCIO / РЕКЛАМА: the button steps down a little, as it does on a phone
     on its side. The stage row keeps its height (the window sets it). */
  #screen-shop .stage-act.btn-reward { margin-top: 9px; }
  /* Eight pips were 122px, wider than the 116px column at 320: smaller
     pips, closer (8 = 92px), so they stay inside the card. */
  .stage-pips { gap: 4px; }
  .stage-pips i { width: 8px; height: 8px; }
  /* The no-ad sentence in two tight lines, no taller than BACK. */
  #screen-shop .stage-act.no-ad { padding-top: 4px; padding-bottom: 4px; }
  #screen-shop .stage-act.no-ad .rv-text { font-size: 11px; line-height: 1; letter-spacing: 0; }  /* (1,3,0) */
  /* BUY in the narrow column: COMPRAR + note + 3000 did not fit in one line
     and stuck out of the pill on both sides. The price goes under the word. */
  #screen-shop .stage-act.btn-buy { flex-wrap: wrap; row-gap: 0; column-gap: 5px; line-height: 1.05; padding-top: 5px; padding-bottom: 5px; }
  #screen-shop .stage-act.btn-buy .buy-word { margin-right: 0; }
}
/* The stage's WATCH AD on a tablet (201px, Russian): the tilted РЕКЛАМА
   stamp sat on the right outline. A little less gap and side padding. */
@media (min-width: 401px) and (min-height: 421px) {
  #screen-shop .stage-act.btn-reward { gap: 5px; padding-left: 10px; padding-right: 8px; }     /* (1,2,0) */
}
@media (max-width: 360px) {
  .win-side .btn.btn-reward { font-size: 14px; letter-spacing: 0.03em; }
}
@media (max-width: 340px) {
  .btn .ad-badge { font-size: 9px; padding: 1px 4px; }
  /* style.css's fold fix (.btn-reward 9px sides) loses to .btn's 16px here,
     and the new 4px outline takes 8px more: give the room back. */
  .btn.btn-reward { padding-left: 10px; padding-right: 9px; }
  /* A 280px fold: REINICIAR and ПРОДОЛЖИТЬ ran 2px / 7px into their
     buttons' padding. */
  #screen-pause .panel .btn-secondary { padding-left: 2px; padding-right: 2px; }
  #screen-pause #btnResume { padding-left: 12px; padding-right: 12px; letter-spacing: 0.03em; }
  #screen-pause .panel .btn-secondary { font-size: 10.5px; letter-spacing: 0; }
  #screen-pause .panel .btn-secondary::before { width: 26px; height: 26px; }
}

/* Short screens (a laptop with a toolbar, a phone on its side): everything a
   notch tighter - the tap targets stay 44px or more. */
@media (max-height: 620px) {
  .btn { min-height: 46px; margin-bottom: 9px; font-size: 15px; }
  .btn.btn-big { --sky-lh: 6px; min-height: 52px; }
  .panel { padding-top: 14px; }
  .panel h2 { margin-bottom: 10px; }
  #screen-pause #btnResume { margin-bottom: 12px; }
  #screen-pause .panel .btn-secondary { min-height: 64px; }
  .score-line { padding: 3px 12px; }
  .win-side { padding-top: 10px; }
  .win-banner { padding-top: 6px; }
}
/* 320x568: every button was on screen but the panel's roof ran 19-33px
   past the bottom edge. The same squeeze as a phone on its side, and 4px
   less air above and below (Russian NEXT takes two lines here). */
@media (max-width: 400px) and (max-height: 600px) {
  .win-wrap { padding-top: 10px; padding-bottom: 10px; }
  .score-line { padding: 2px 12px; margin-bottom: 5px; }
  .win-hero { padding: 3px 12px; }
  .screen-win .star { width: 34px; height: 34px; }
  .screen-win .star:nth-child(2) { width: 44px; height: 44px; margin-top: -8px; }
  .win-side { padding-top: 8px; }
  .win-best { margin-bottom: 6px; }
}
/* (style.css's 22px banner sides at 560px tall and under: from 560px wide
   the two-column end screen below sets the banner's padding itself; under
   560px wide the phone's 14px applies.) */
/* An upright screen: the dancing doll takes only the height left over, so
   NEXT, DOUBLE CASH and SKINS stay on screen (at 360x710-390x799 SKINS was
   below the fold). He is drawn to fit his box, never stretched; 56px is his
   floor (only met 480-600px wide; at 64px the two-line Spanish and Russian
   final title pushed the panel's ledge 3px off a 540x722 screen). Up to
   720px tall (320x568 to 360x720) he steps aside, as he already does under
   560px tall. */
@media (max-width: 720px) {
  .win-row { min-height: 0; }
  .win-side { flex-shrink: 0; }
  .win-doll { flex: 0 1 auto; min-height: 56px; object-fit: contain; }
}
@media (max-width: 720px) and (max-height: 720px) {
  .win-row { min-height: auto; }
  .win-doll { display: none; }
}
/* A phone on its side: the tiles lie down - picture beside the word - so the
   whole pause panel is about 230px tall; the city steps aside and only the
   roof stays along the foot. */
@media (max-height: 420px) {
  :root { --sky-city-h: 0px; }
  .panel { padding-bottom: calc(var(--sky-roof-h) + 12px); }
  .panel h2 { font-size: 26px; margin-bottom: 8px; }
  #screen-pause .panel { width: min(100%, 440px); }
  #screen-pause #btnResume { min-height: 48px; margin-bottom: 12px; }
  #screen-pause .panel .btn-secondary {
    flex-direction: row;
    gap: 6px;
    min-height: 44px;
    padding: 4px 6px;
    border-radius: 999px;
  }
  #screen-pause .panel .btn-secondary::before { width: 22px; height: 22px; }
  .win-side { padding-bottom: calc(var(--sky-roof-h) + 12px); }
  /* The end-of-run screen scrolls at this height with or without this file;
     these keep the extra scroll the outlines add to a minimum. */
  .btn { min-height: 44px; margin-bottom: 8px; }
  .btn.btn-big { min-height: 48px; }
  .score-line { padding: 2px 12px; margin-bottom: 5px; }
  .win-hero { padding: 3px 12px; }
  .screen-win .star { width: 34px; height: 34px; }
  .screen-win .star:nth-child(2) { width: 44px; height: 44px; margin-top: -8px; }
  /* The shop has no height to spare here and the stage window is what
     pays for every pixel of frame: a 3px outline and a 7px roof (rim and
     roof, no parapet) inside the 12px foot, 8px more than it had before
     this file rather than 24px. */
  /* WATCH AD in the 150px stage column at 568x320: film mark + word + AD
     stamp did not fit (РЕКЛАМА ran 9px out of the button, the film mark 6px
     out of the other side; it overflowed before this file too). The stamp
     goes on the corner as on a narrow phone; the button sits 5px lower on
     every skin so the stamp clears the pips and the window does not jump. */
  #screen-shop .stage-act { margin-top: 7px; }
  /* The tier word's row went back to the window, which had become smaller
     than a rack card's picture; the tier still shows as the window's ring
     and the card's colour tab. */
  #screen-shop .stage-tier { display: none; }
  #screen-shop .stage-act.btn-reward { position: relative; }
  /* right -2px: here the button is 3px from the card's ring, and at -4px
     the stamp ran onto it. */
  #screen-shop .stage-act.btn-reward .ad-badge { position: absolute; top: -9px; right: -2px; }
  /* 6px: at 4px the window's ink ring covered the card's top border and
     poked 0.5px above it; now it merges 1.5px, as on an upright phone. */
  #screen-shop .shop-stage { padding-top: 6px; }
  #screen-shop .shop-panel {
    border-width: 3px;
    padding-bottom: 12px;
    background:                        /* roof 4 + rim 3, overlapping under the 3px border */
      linear-gradient(var(--sky-roof), var(--sky-roof)) left 0 bottom -3px / 100% 7px no-repeat,
      linear-gradient(var(--sky-rim), var(--sky-rim)) left 0 bottom -3px / 100% 10px no-repeat,
      linear-gradient(180deg, #fff7ed 0%, #ffeedd 100%);
    background-color: #fff7ed;
  }
}
/* Under 568px wide the 440px pause window ran under the sound column (at
   480x320 the home button sat on RESUME). It stops 6px short of the column,
   as the shop panel does, and the tiles close up a little. */
@media (max-height: 420px) and (max-width: 567px) {
  #screen-pause .panel { width: min(440px, calc(100% - 48px - var(--safe-r))); margin-right: calc(48px + var(--safe-r)); }
  #screen-pause .panel .btn-secondary { gap: 4px; letter-spacing: 0.01em; }
}
/* BUY in the 138px stage column of a small phone on its side (568x320):
   COMPRAR + note + price ran past the outline on both sides. The note and
   the price say it on their own - on the green fill, or white when it is
   out of reach. From 700px wide the column has room for the word. */
@media (max-height: 420px) and (max-width: 700px) {
  #screen-shop .stage-act.btn-buy .buy-word { display: none; }
}

/* A phone on its side, up to 900px: the sound column stood over the shop
   panel's right-hand frame (740x360, 568x320). The whole panel now stops
   short of it, so the 60px of padding style.css kept clear inside the panel
   for the column goes back to the rack. */
@media (max-height: 500px) and (max-width: 900px) {
  #screen-shop .shop-panel {                                   /* (1,1,0) */
    width: min(720px, calc(100% - 50px - var(--safe-r)));
    margin-right: calc(50px + var(--safe-r));
    padding-right: 14px;
  }
  #screen-shop .shop-head { padding-right: 0; }
}
/* The same for an upright tablet or a narrow window (601-860 wide): on a
   screen under ~990px tall the panel rose to the sound column and its top
   right corner ran under the music and home buttons (700x900). */
@media (min-width: 601px) and (max-width: 860px) and (min-height: 501px) {
  #screen-shop .shop-panel {                                   /* (1,1,0) */
    width: min(720px, calc(100% - 50px - var(--safe-r)));
    margin-right: calc(50px + var(--safe-r));
    padding-right: 14px;
  }
  #screen-shop .shop-head { padding-right: 0; }
}

/* The home screen on a 568x320 phone: PLAY and its two tiles needed 286px
   in a 231px column, so the language tile ran off the right edge and the
   screen slid sideways. Two equal columns and a tighter row. */
@media (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 640px) {
  #screen-menu.active { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  #screen-menu .menu-actions { gap: 8px; }
  #screen-menu .play-btn { min-width: 0; padding: 0 12px; }
}

/* The end of the run on a phone on its side, and on any landscape screen
   560px tall or less (a 960x540 game frame too - the doll already steps
   aside there): stacked in one column it had to be scrolled 80-250px, and
   NEXT started at the bottom edge with DOUBLE CASH, SKINS and MENU below it
   - the player saw a title and three numbers. Two columns instead: the
   banner and the stars on the left, centred against the panel, and the
   numbers and every button on the right, all on screen. A phone on its
   side under 560px wide (480x320, 520x320) too: stacked, it scrolled
   160-260px. */
@media (max-height: 560px) and (min-width: 560px), (max-height: 500px) and (min-aspect-ratio: 13/10) {
  .win-wrap { display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); grid-template-rows: 1fr auto auto 1fr; column-gap: 12px; row-gap: 0; align-items: start; align-content: center; width: min(100%, 820px); height: 100%; padding: 6px calc(62px + var(--safe-r)) 6px calc(10px + var(--safe-l)); }
  .win-banner { grid-column: 1; grid-row: 2; justify-self: center; margin: 0; max-width: 100%; padding: 4px 12px; }
  .win-banner h2 { font-size: clamp(16px, 3.6vw, 26px); padding-bottom: 4px; }
  .win-bar { height: 40px; width: min(240px, 100%); }
  /* 14px: the big middle star's tip sat on the banner's ledge at 6px. */
  .screen-win .stars { grid-column: 1; grid-row: 3; justify-self: center; margin-top: 14px; }
  .win-row { grid-column: 2; grid-row: 1 / span 4; margin: 0; align-self: center; }
  .win-side { padding-top: 8px; padding-bottom: calc(var(--sky-roof-h) + 6px); }
  .win-side .score-list { display: flex; gap: 6px; margin-bottom: 6px; }
  .win-side .score-line { flex: 1 1 0; min-width: 0; margin: 0; padding: 2px 10px; gap: 6px; }
  .win-side .score-line > span { min-width: 0; font-size: 10px; letter-spacing: 0.04em; line-height: 1.05; }
  /* СЛЕДУЮЩИЙ УРОВЕНЬ took two lines (79px) in the 337px column at 740 wide. */
  /* 11px under NEXT: DOUBLE CASH's hop rose over NEXT's ledge at 6px. */
  .win-side .btn.btn-big { font-size: clamp(17px, 2.6vw, 23px); margin-bottom: 11px; }
  .win-hero { margin-bottom: 4px; }
  .win-best { margin-bottom: 6px; }
  .win-side .btn { margin-bottom: 6px; }
}
@media (max-height: 560px) and (min-width: 560px) and (max-width: 700px), (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 700px) {
  .win-wrap { grid-template-columns: minmax(0, 0.62fr) minmax(0, 1.38fr); column-gap: 8px; }
  .win-banner { padding-left: 8px; padding-right: 8px; }
  .win-banner h2 { font-size: 15px; letter-spacing: 0.02em; }
  .win-side .btn.btn-big { font-size: 17px; letter-spacing: 0.02em; padding-left: 10px; padding-right: 10px; }
  .win-side .btn.btn-reward { font-size: 12px; letter-spacing: 0.01em; padding-left: 10px; padding-right: 8px; gap: 6px; }
  .win-side .btn-row .btn { font-size: 13px; letter-spacing: 0.02em; }
}
/* Under 560px wide the column is narrower still: Russian СЛЕДУЮЩИЙ УРОВЕНЬ
   wrapped (at 17px, and at 15px on a 480px phone) and pushed the panel's
   ledge off the bottom. A size down, and the panel's sides as on a phone. */
@media (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 559px) {
  .win-side { padding-left: 12px; padding-right: 12px; }
  .win-side .btn.btn-big { font-size: 15px; letter-spacing: 0.01em; }
}
/* A laptop window 561-660px tall (1366x768 with the browser's bars, 1280x600)
   and any screen 461px+ wide at that height: stacked, with the outlines and
   the roof, the end screen scrolled 7-61px and the panel's roof or MENU went
   under the bottom edge. The same squeeze as the two-column layout, kept in
   one column: less air, smaller stars, the three numbers side by side. */
@media (min-width: 461px) and (min-height: 561px) and (max-height: 660px) {
  .win-wrap { padding-top: 8px; padding-bottom: 8px; }
  .win-bar { height: 40px; }
  .screen-win .star { width: 36px; height: 36px; }
  .screen-win .star:nth-child(2) { width: 46px; height: 46px; margin-top: -8px; }
  .win-side .score-list { display: flex; gap: 6px; margin-bottom: 8px; }
  .win-side .score-line { flex: 1 1 0; min-width: 0; margin: 0; padding: 2px 10px; gap: 6px; }
  .win-side .score-line > span { min-width: 0; font-size: 10px; letter-spacing: 0.04em; line-height: 1.05; }
}


/* ======================= LAST FITTINGS (round 4) =========================
   Four small fits the final checks found, each measured in EN/ES/RU. */

/* A phone on its side under 560 wide: the two score pills are ~119px each,
   and ИДЕАЛЬНЫЕ ПРИЗЕМЛЕНИЯ ran under its number. Tighter pills, a smaller
   number, and a little more room for the longer label. */
@media (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 559px) {
  .win-side .score-line { padding-left: 7px; padding-right: 7px; gap: 4px; }
  .win-side .score-line b { font-size: 16px; }
  .win-side .score-line:nth-child(2) { flex-grow: 1.3; }
}
/* The home row at 480x320: ИГРАТЬ widens PLAY until the language tile ran
   off the right edge. A tighter row and a size-down label. */
@media (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 519px) {
  #screen-menu .menu-actions { gap: 6px; }
  #screen-menu .play-btn { font-size: 17px; padding: 0 10px; }
}
/* DOUBLE CASH in Russian, where УДВОИТЬ ДЕНЬГИ takes two lines: a wrapped
   label filled the whole pill and pushed the film mark and the AD stamp
   back out to the ends. Sized to its words, the three stay one group. */
@media (max-width: 389px), (max-height: 500px) and (min-aspect-ratio: 13/10) and (max-width: 559px) {
  .win-side .btn.btn-reward .rv-text:lang(ru) { width: min-content; }
}
/* WATCH AD in Russian at 601-760 wide: СМОТРИ + РЕКЛАМА ran past the pill's
   inside edge. A touch tighter; still 46px, the same as BACK. */
@media (min-width: 601px) and (max-width: 760px) and (min-height: 421px) {
  #screen-shop .stage-act.btn-reward { gap: 4px; letter-spacing: 0.03em; padding-left: 8px; padding-right: 8px; }
  #screen-shop .stage-act.btn-reward .rv-text { font-size: 14px; }
}

/* The end screen comes in with a small pop instead of rising 18px from
   below: rising, its bottom stood past the screen's edge for the first
   frames, and where it fills the height it flashed a scrollbar. */
@keyframes skyWinIn {
  from { transform: scale(0.94); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.win-wrap { animation-name: skyWinIn; }

/* ============================ REDUCED MOTION =============================
   Restated here, last, because every animation above is a later rule than
   style.css's own reduced-motion block and would otherwise win. No idles,
   wiggles, nudges or pops; entrances become a short fade (the panel's and
   the title's keyframes start at opacity 0, so they are replaced, not just
   stopped). Also closes three gaps style.css left open: the end-of-run
   entrance, the refusal shake and the loading spin. */
@keyframes skyFade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .panel { animation: skyFade 0.18s ease-out both; }
  #screen-pause.active .panel h2 { animation: none; opacity: 1; }
  #screen-pause.active #btnResume,
  #screen-pause.active #btnResume.pressable.popped,
  .btn.btn-reward.urging,
  .btn.pressable.popped,
  .btn.pressable.denied,
  .pressable.denied,
  .hud-skins,
  .hud-skins-dot.on,
  .shop-stage.burst .stage-cv { animation: none; }
  .win-wrap { animation: skyFade 0.2s ease-out both; }
  .btn-reward.loading .rv-ico { animation: none; opacity: 0.6; }
  #ui .btn:active:not(:disabled),
  #ui .btn.pressable.pressing { transition: none; }
}


/* ============================ PASS 5: THEY TALK ==========================
   His bubble (UI.sayShow 'him'): the same bubble as hers, hung under HIS
   head on the water's front instead of under her badge - one of them calls,
   the other answers. Ink words on white (11.2:1) with the water's blue
   along its bottom edge, so it never reads as hers (berry on white) or the
   monster's (plum). No red, no blur; the ledge is the HUD's dark one.

   The box (.bar-say-box) sits at the top-left of the bubble row under the
   bar - where her bubble's top is - and is slid along it with a transform
   (UI.sayFollow), so the bubble inside keeps its own transform for the pop.
   --tail is his mouth, measured from the bubble's padding edge (UI sets it):
   the tail points up at him and the pop grows out of him. */
.bar-say-box {
  position: absolute;
  left: 0;
  top: calc(100% + 3px);              /* the row her bubble hangs in */
  width: 0;
  height: 0;
  pointer-events: none;
}
.bar-say.him {                                                   /* (0,2,0) */
  top: 0;
  left: 0;
  right: auto;
  color: var(--ink);
  box-shadow: inset 0 -3px 0 #4cb8f5, 0 2px 0 var(--sky-ledge-dk);
  transform-origin: var(--tail, 50%) -6px;
}
.bar-say.him::before { right: auto; left: calc(var(--tail, 50%) - 7px); }   /* (0,2,1) */
.bar-say.him::after  { right: auto; left: calc(var(--tail, 50%) - 5px); }
/* A line laid out unseen, to measure it before it shows (UI.sayMeasure). */
.bar-say.measure { display: block; visibility: hidden; animation: none; }   /* (0,2,0) */

/* ---- pass 5 fixes ----
   461-699px wide the sound column's buttons are 46px (40px up to 460px),
   so the bar's 62px from the right left her bubble - and his, near her
   badge - 4px from Home. 6px more, as on an upright phone (10px); the
   hearts move with the bar's centre (26px -> 29px left of the HUD's). */
@media (min-width: 461px) and (max-width: 699px) {
  .progress { right: calc(68px + var(--safe-r)); }
  .hud-hearts { right: 58px; }
}
/* Him behind a bubble at the top of a bounce (UI.sayUnder): it fades to a
   ghost of itself while he is. filter, not opacity, so it stacks with the
   pop's own opacity; no blur. */
.bar-say { transition: filter 0.12s linear; }
.bar-say.under { filter: opacity(0.3); }                           /* (0,2,0) */
/* The tube over a hanging Drop-In Anvil (UI.anvilUnder, pass 7): see-through,
   so the anvil's ghost and drop line show through it on a landscape phone. */
.progress .fluid-bar { transition: opacity 0.15s linear; }
.progress.anvil-under .fluid-bar { opacity: 0.28; }                /* (0,3,0) */
@media (prefers-reduced-motion: reduce) {
  .progress .fluid-bar { transition: none; }
}
@media (prefers-reduced-motion: reduce) {
  .bar-say { transition: none; }
}
/* An upright tablet (768x1024): with the next-unlock bar gone the rack
   still scrolled under the 660px cap, a third of the screen unused. The
   panel may take the height, 24px clear top and bottom, up to 900px. */
@media (min-width: 601px) and (min-height: 960px) {
  #screen-shop .shop-panel { max-height: min(100% - 48px, 900px); }  /* (1,1,0) */
}

/* The shop on a tall screen: the stage card keeps to its own height at the
   top of its column, and the preview window stops at 340px tall - the rack
   beside it runs the panel's full height now the next-unlock bar is gone,
   and stretched with it the window became a tall strip of empty sky. */
@media (min-width: 601px) and (min-height: 861px) {
  #screen-shop .shop-stage { align-self: start; }
  #screen-shop .stage-cv { max-height: 340px; }
}


/* ============ PASS 6: OUT OF HEARTS - THE MONSTER'S WATCH PARTY ============
   The offer no longer floats over the floor he died on. The game draws the
   monster's party about the player on its own canvas instead (js/revive.js),
   so this screen is see-through: no dim (the picture has its own dusk and
   falls away at the corners) and no broken heart (she is crying, he is
   laughing, and the TV is showing how). The two buttons stand where the
   picture leaves room for them: a column on the right of a wide screen,
   level with the TV he points at and under the sound column; a band along
   the bottom of a tall one. UI.placeRevive measures that room (Revive.zone)
   and hands it over as four paddings and a width; until it has, the old
   centred stack. Both keep their size and their dress - REVIVE violet with
   the film mark and the AD stamp, never the taller of the two - and the
   whole screen is still the free way on. */
#screen-revive.screen-revive {                                 /* (1,1,0) */
  background: none;
  justify-content: var(--rv-jc, center);
  padding: var(--rv-t, 16px) var(--rv-r, 16px) calc(var(--rv-b, 16px) + var(--safe-b)) var(--rv-l, 16px);
}
#screen-revive.screen-revive::before { display: none; }        /* (1,1,1) */
#screen-revive .revive-btn,
#screen-revive .revive-continue {                              /* (1,1,0) */
  width: var(--rv-w, min(280px, 84vw));
  flex: 0 0 auto;
}
/* A narrow side column (a landscape phone, the button under 230px): the
   film mark, the words and the AD stamp pack tighter - in RU and ES they
   stuck out past the violet pill's edges. Set by UI.placeRevive. */
#screen-revive.rv-narrow .revive-btn { padding-left: 8px; padding-right: 8px; gap: 6px; }          /* (1,2,0) */
#screen-revive.rv-narrow .revive-btn .rv-ico { width: 24px; height: 18px; }                        /* (1,3,0) */
#screen-revive.rv-narrow .revive-btn .rv-text { font-size: 14px; letter-spacing: 0.02em; }
#screen-revive.rv-narrow .revive-btn .ad-badge { font-size: 9px; padding: 1px 3px; letter-spacing: 0.03em; }
/* PASS 7: the offer's two buttons get the eye - both of them, and
   neither one bigger: a drop-in (a squash, never a stretch), a shine
   across each in turn, both inside the first second; the heartbeat
   ring and glow round each and the light on them are drawn by
   js/revive.js behind the buttons. */
#screen-revive .revive-btn.entering,
#screen-revive .revive-continue.entering {                     /* (1,2,0) */
  animation: rvDrop 0.42s cubic-bezier(.3,.7,.4,1) both;
  transform-origin: 50% 100%;
}
#screen-revive .revive-continue.entering { animation-delay: 0.12s; }
@keyframes rvDrop {
  0%   { opacity: 0; transform: translateY(-46px); }
  30%  { opacity: 1; }
  60%  { transform: translateY(0) scaleY(0.9); }
  80%  { transform: translateY(-5px) scaleY(1); }
  100% { opacity: 1; transform: none; }
}
#screen-revive .revive-btn,
#screen-revive .revive-continue { position: relative; overflow: hidden; }
#screen-revive .revive-btn::after,
#screen-revive .revive-continue::after {
  content: ""; position: absolute; top: -20%; bottom: -20%; left: -45%; width: 34%;
  background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,0.55) 50%, rgba(255,255,255,0));
  transform: translateX(0) skewX(-20deg);
  animation: rvShine 2.6s ease-in-out 0.62s infinite;
  pointer-events: none;
}
#screen-revive .revive-continue::after {
  background: linear-gradient(90deg, rgba(255,205,90,0), rgba(255,205,90,0.55) 50%, rgba(255,205,90,0));
  animation-delay: 0.8s;
}
@keyframes rvShine {
  0%        { transform: translateX(0) skewX(-20deg); }
  26%, 100% { transform: translateX(470%) skewX(-20deg); }
}
@media (prefers-reduced-motion: reduce) {
  #screen-revive .revive-btn.entering, #screen-revive .revive-continue.entering { animation: none; }
  #screen-revive .revive-btn::after, #screen-revive .revive-continue::after { animation: none; opacity: 0; }
}

/* ============== PASS 8C: THE OFFER RUNS OUT - "CONTINUE" FILLS ==============
   Nothing pressed for eight seconds and the game carries on by itself
   (UI.tickOffer), as if "tap to continue playing" had been pressed. The free
   button says so as it happens: it fills from the left with the gold of the
   fairy lights strung over his party, like a bar loading, and where the gold
   has reached, its words are PLAY's lettering - white in the ink outline, as
   on every gold button - so the label reads the whole way across. A warm
   light rides the gold's front edge and faint bands drift through it while
   it counts; over the last second and a half the button breathes (two slow
   swells, no flash). The gold is clipped to the pill by the button itself
   and passes under its shine. REVIVE is untouched. --rc-p (0..1) is set by
   UI.autoPaint; .rc-run while it counts, .rc-pulse at the end, .rc-drain
   when REVIVE is pressed (the gold goes at once). */
#screen-revive .revive-continue .rc-fill {                       /* (1,2,0) */
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: inherit;
  border-radius: inherit;
  background: linear-gradient(180deg, #ffe58a 0%, #ffd23f 46%, #ffbe2b 100%);
  box-shadow: inset 0 3px 0 rgba(255,255,255,0.5);
  color: #fff;
  -webkit-text-stroke: 0.19em var(--ink);
  paint-order: stroke fill;
  clip-path: inset(0 calc(100% - var(--rc-p, 0) * 100%) 0 0);
  transition: opacity 0.2s ease;
  pointer-events: none;
}
/* the bands: the progress bar's own stripes, faint, drifting to the right */
#screen-revive .revive-continue .rc-fill::before {
  content: ""; position: absolute; inset: 0 0 0 -22px;   /* (pass 14h) one period wider: moved, not repainted */
  background: linear-gradient(135deg,
      rgba(255,255,255,0.2) 25%, rgba(255,255,255,0) 25%, rgba(255,255,255,0) 50%,
      rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.2) 75%, rgba(255,255,255,0) 75%) 0 0 / 22px 22px;
}
/* the light on its front edge: warm white just behind the edge, and the edge
   itself a thin line of the gold's own deep amber, so it stays crisp on the
   white button */
#screen-revive .revive-continue .rc-edge {
  position: absolute; top: 0; bottom: 0;
  left: calc(var(--rc-p, 0) * 100% - 24px);
  width: 24px;
  background: linear-gradient(90deg, rgba(255,250,226,0) 0%, rgba(255,250,226,0.62) 80%,
      rgba(255,250,226,0.8) calc(100% - 2px), #e89b14 calc(100% - 2px));
}
#screen-revive .revive-continue .rc-fill > .rc-text { position: relative; }   /* over the bands */
#screen-revive .revive-continue.rc-run .rc-fill::before { animation: rcBands 0.9s linear infinite; }
#screen-revive .revive-continue.rc-run .rc-edge { animation: rcEdge 1.1s ease-in-out infinite alternate; }
@keyframes rcBands { from { transform: translateX(0); } to { transform: translateX(22px); } }
@keyframes rcEdge { from { opacity: 0.6; } to { opacity: 1; } }
#screen-revive .revive-continue.rc-drain .rc-fill { opacity: 0; }
/* The last second and a half: a gentle swell, twice - never a flash. */
#screen-revive .revive-continue.rc-pulse { animation: rcBreathe 0.75s ease-in-out infinite; }
@keyframes rcBreathe {
  0%, 100% { transform: none; }
  50%      { transform: scale(1.045); }
}
@media (prefers-reduced-motion: reduce) {
  /* a plain fill: no bands, no moving light, no swell */
  #screen-revive .revive-continue .rc-fill::before { display: none; }
  #screen-revive .revive-continue.rc-run .rc-edge { animation: none; }
  #screen-revive .revive-continue.rc-pulse { animation: none; }
}

/* ========================= PASS 14: PORTAL SKINS ==========================
   Inside the portal, every third floor: one rewarded skin on a pedestal in a
   swirling tunnel (the canvas: js/portaloffer.js), and over it, in the room
   the picture leaves (UI.placePortal): the tier, the name in gold, a flavour
   line, GET IT! (the house's violet ad button, gold-rimmed and glowing in the
   skin's colour) and NO THANKS - a white pill that fills with the portal's
   cool blue like a lit fuse over six seconds, then carries on by itself.
   Nothing here says time is short: the fuse and the dimming picture show
   it. Never red: violet for the ad, gold for the name, cyan for the fuse. */
#screen-portal.screen-portal {                                   /* (1,1,0) */
  background: none;
  padding: 0;
  overflow: hidden;
  cursor: default;
  display: none;
}
#screen-portal.screen-portal.active { display: block; }          /* (1,2,0) */
#screen-portal.screen-portal::before { display: none; }
body.in-ad #screen-portal { visibility: hidden; }
#screen-portal .p14-col {
  position: absolute;
  left: var(--p14-l, 56%);
  width: var(--p14-cw, 40%);
  top: var(--p14-t, 22%);
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  pointer-events: none;                    /* only the buttons take taps */
}
#screen-portal .p14-col > * { pointer-events: auto; }
#screen-portal.p14-band .p14-col {
  left: calc(16px + var(--safe-l));
  right: calc(16px + var(--safe-r));
  width: auto;
  top: calc(var(--p14-t, 6%) + var(--safe-t));
  bottom: calc(var(--p14-b, 24px) + var(--safe-b));
}
#screen-portal.p14-band .p14-name { max-width: var(--p14-nw, 100%); }
#screen-portal.p14-band .p14-tag { margin-top: auto; }

/* the tier: a small pill of its colour (never red: epic magenta-violet,
   legendary gold, mythic teal) */
#screen-portal { --p14-tier: #f0b429; --p14-tier-dk: #946100; }
#screen-portal.p14-tier-epic { --p14-tier: #c026d3; --p14-tier-dk: #7a1687; }
#screen-portal.p14-tier-legendary { --p14-tier: #f0b429; --p14-tier-dk: #946100; }
#screen-portal.p14-tier-mythic { --p14-tier: #19b8c9; --p14-tier-dk: #0d6f7a; }
#screen-portal .p14-kicker {
  display: inline-block;
  margin: 0 0 10px;
  padding: 4px 14px 4px;
  border: 3px solid var(--ink);
  border-radius: 999px;
  background: var(--p14-tier);
  box-shadow: inset 0 2px 0 rgba(255,255,255,0.4), 0 3px 0 rgba(10,14,40,0.6), 0 0 18px rgba(255,255,255,0.18);
  color: #fff;
  font-size: 13px; font-weight: 900; letter-spacing: 0.14em; line-height: 1.1;
  -webkit-text-stroke: 0.18em var(--ink);
  paint-order: stroke fill;
  animation: p14Fade 0.3s ease-out 0.45s both;
}
/* the name: big gold letters in the ink outline, each slamming in after the
   last (UI.p14Letters: <span class="p14-w"><i data-c>..</i></span>). The
   outline and the hard drop are the letter itself; the gold is laid over
   it, clipped to the glyph (::after), so the outline never eats the gold. */
#screen-portal .p14-name {
  margin: 0 0 8px;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: clamp(34px, 5.2vw, 64px);
  font-style: italic; font-weight: 900;
  line-height: 1.02;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
  -webkit-text-stroke: 0.16em var(--ink);
  paint-order: stroke fill;
  filter: drop-shadow(0 0.07em 0 var(--sky-drop)) drop-shadow(0 0 0.35em rgba(255,214,90,0.35));
  overflow-wrap: normal;
}
#screen-portal .p14-name .p14-w { display: inline-block; white-space: nowrap; }
#screen-portal .p14-name i {
  position: relative;
  display: inline-block;
  font-style: inherit;
  padding-right: 0.03em;
  animation: p14Slam 0.34s cubic-bezier(.2,1.6,.4,1) both;
  animation-delay: calc(0.55s + var(--i, 0) * 0.04s);
}
#screen-portal .p14-name i::after {
  content: attr(data-c);
  position: absolute; left: 0; top: 0;
  -webkit-text-stroke: 0;
  color: transparent;
  background: linear-gradient(180deg, #fff6c8 0%, #ffe16b 38%, #ffd23f 55%, #f0a020 100%);
  -webkit-background-clip: text;
          background-clip: text;
}
@keyframes p14Slam {
  0%   { opacity: 0; transform: translateY(-0.45em) scale(1.9); }
  55%  { opacity: 1; transform: translateY(0.04em) scale(0.9); }
  100% { opacity: 1; transform: none; }
}
@keyframes p14Fade {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
#screen-portal .p14-tag {
  margin: 0 0 16px;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: clamp(15px, 1.5vw, 20px);
  font-style: italic; font-weight: 700;
  line-height: 1.25;
  color: #ffffff;
  text-shadow: 0 2px 0 var(--ink), 0 0 3px var(--ink), 0 0 12px rgba(10,8,40,0.9);
  animation: p14Fade 0.4s ease-out 1s both;
}

/* ---- GET IT!: the ad button, made to be wanted ---------------------------- */
#screen-portal .p14-get,
#screen-portal .p14-no {                                         /* (1,1,0) */
  width: var(--p14-bw, min(340px, 100%));
  max-width: 100%;
  margin: 0 0 12px;
  flex: 0 0 auto;
}
#screen-portal .p14-get {
  position: relative;
  min-height: 58px;
  font-size: 20px;
  letter-spacing: 0.08em;
  gap: 10px;
  /* a gold rim inside the ink, and a white shine sweeping across (14h: the
     shine is .p14-shine, moved, not a background repainted every frame) */
  box-shadow: inset 0 0 0 2px #ffcb2e, var(--sky-shine), 0 var(--sky-lh) 0 var(--sky-lc);
  background-image: none;
  isolation: isolate;                      /* the shine (z -1) under the words */
}
/* (pass 14h) the shine: a clip the button's shape just inside its gold rim,
   and in it a band 2.6 times its width slid across (transform only) - the
   same sweep the background-position animation made, at the same pace */
#screen-portal .p14-get .p14-shine {
  position: absolute; inset: 2px; z-index: -1;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
}
#screen-portal .p14-get .p14-shine::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 260%;
  background: linear-gradient(105deg, rgba(255,255,255,0) 42%, rgba(255,255,255,0.5) 50%, rgba(255,255,255,0) 58%);
  transform: translateX(-80%);
  animation: p14ShineX 1.6s ease-in-out 1.1s infinite;
}
#screen-portal .p14-get .rv-text { font-size: 1em; }
#screen-portal .p14-get .rv-ico { width: 30px; height: 22px; }
/* its glow, in the skin's colour, breathing (faster over the last 1.5 s) */
#screen-portal .p14-get::before {
  content: "";
  position: absolute; inset: -5px;
  border-radius: 999px;
  /* it grows as the fuse burns (--p14-p): the one light that stays */
  box-shadow: 0 0 calc(18px + 22px * var(--p14-p, 0)) calc(6px + 8px * var(--p14-p, 0)) var(--p14-glow, rgba(255,214,90,0.9));
  opacity: 0.55;
  pointer-events: none;
  animation: p14Breathe 1.2s ease-in-out infinite;
}
#screen-portal .p14-get.p14-hot::before { animation-duration: 0.6s; }
#screen-portal .p14-get.entering {
  animation: p14Drop 0.42s cubic-bezier(.3,.7,.4,1) 0.55s both;
  transform-origin: 50% 100%;
}
#screen-portal .p14-get.loading::before { animation: none; opacity: 0.3; }
@keyframes p14Breathe {
  0%, 100% { opacity: 0.35; transform: scale(0.98); }
  50%      { opacity: 0.95; transform: scale(1.04); }
}
/* (14h) the old background-position 130% -> -30% of a 260% image, as a
   transform of a 260%-wide band: -80% -> +18.46% of its own width */
@keyframes p14ShineX {
  0%        { transform: translateX(-80%); }
  45%, 100% { transform: translateX(18.46%); }
}
@keyframes p14Drop {
  0%   { opacity: 0; transform: translateY(-40px); }
  30%  { opacity: 1; }
  60%  { transform: translateY(0) scaleY(0.9); }
  80%  { transform: translateY(-5px) scaleY(1); }
  100% { opacity: 1; transform: none; }
}

/* ---- NO THANKS: the fuse --------------------------------------------------- */
/* as tall as GET IT!: a rewarded button is never bigger than the standard
   one beside it (the house rule, Poki) - and the fuse is the thing to watch */
#screen-portal .p14-no {
  position: relative;
  overflow: hidden;
  min-height: 58px;
  font-size: 17px;
  color: var(--ink);
}
#screen-portal .p14-no.entering {
  animation: p14Drop 0.42s cubic-bezier(.3,.7,.4,1) 0.67s both;
  transform-origin: 50% 100%;
}
#screen-portal .p14-no .p14-text { position: relative; }
#screen-portal .p14-no .p14-fill {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: inherit;
  border-radius: inherit;
  background: linear-gradient(180deg, #8ae6ff 0%, #34b0ff 55%, #2356c4 100%);
  box-shadow: inset 0 3px 0 rgba(255,255,255,0.45);
  color: #fff;
  -webkit-text-stroke: 0.19em var(--ink);
  paint-order: stroke fill;
  clip-path: inset(0 calc(100% - var(--p14-p, 0) * 100%) 0 0);
  transition: opacity 0.2s ease;
  pointer-events: none;
}
/* the lit end of the fuse: a white-cyan four-point star riding the edge,
   embers flicking back off it (cyan, never orange) */
#screen-portal .p14-no .p14-spark {
  position: absolute; top: 50%;
  left: calc(var(--p14-p, 0) * 100%);
  width: 26px; height: 26px;
  margin: -13px 0 0 -13px;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle, #ffffff 0 14%, rgba(190,245,255,0.95) 26%, rgba(110,220,255,0) 62%);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
#screen-portal .p14-no .p14-spark::before,
#screen-portal .p14-no .p14-spark::after {
  content: ""; position: absolute; top: 11px; left: 8px;
  width: 4px; height: 4px; border-radius: 50%;
  background: #bff4ff;
}
#screen-portal .p14-no.p14-run .p14-spark { opacity: 1; animation: p14Spark 0.24s steps(2, jump-none) infinite alternate; }
#screen-portal .p14-no.p14-run .p14-spark::before { animation: p14Ember 0.5s linear infinite; }
#screen-portal .p14-no.p14-run .p14-spark::after { animation: p14Ember 0.5s linear 0.25s infinite; }
#screen-portal .p14-no .p14-glow-edge { display: none; }
@keyframes p14Spark {
  from { transform: rotate(0deg) scale(0.85); }
  to   { transform: rotate(45deg) scale(1.15); }
}
@keyframes p14Ember {
  0%   { opacity: 1; transform: translate(0, 0); }
  100% { opacity: 0; transform: translate(-16px, 5px); }
}
/* the last second and a half: it breathes, quicker - never a flash */
#screen-portal .p14-no.p14-hot { animation: p14Hot 0.5s ease-in-out infinite; }
@keyframes p14Hot {
  0%, 100% { transform: none; }
  50%      { transform: scale(1.035); }
}
/* GET IT pressed: the fuse goes out at once */
#screen-portal .p14-no.p14-drain .p14-fill,
#screen-portal .p14-no.p14-drain .p14-spark { opacity: 0; }

/* ---- a phone on its side ----------------------------------------------------- */
#screen-portal.p14-short .p14-kicker { font-size: 12px; margin-bottom: 6px; padding: 3px 12px; }
#screen-portal.p14-short .p14-name { font-size: 38px; margin-bottom: 4px; }
#screen-portal.p14-short .p14-tag { font-size: 13px; margin-bottom: 10px; }
#screen-portal.p14-short .p14-get { min-height: 50px; font-size: 17px; margin-bottom: 10px; }
#screen-portal.p14-short .p14-no { min-height: 50px; font-size: 15px; margin-bottom: 0; }
html[lang="ru"] #screen-portal.p14-short .p14-name { font-size: 30px; }
/* ---- a phone upright ------------------------------------------------------- */
#screen-portal.p14-band .p14-name { font-size: clamp(30px, 9vw, 44px); }
#screen-portal.p14-band .p14-tag { font-size: 15px; margin-bottom: 14px; }
#screen-portal.p14-band .p14-no { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  /* one still frame: no slam, no shine, no breathing, no spark; a plain fill
     and a steady gold rim */
  #screen-portal .p14-kicker, #screen-portal .p14-tag, #screen-portal .p14-name i { animation: none; }
  #screen-portal .p14-get, #screen-portal .p14-get.entering, #screen-portal .p14-no.entering,
  #screen-portal .p14-no.p14-hot { animation: none; }
  #screen-portal .p14-get .p14-shine { display: none; }
  #screen-portal .p14-get::before { animation: none; opacity: 0.6; }
  #screen-portal .p14-no.p14-run .p14-spark { animation: none; opacity: 0; }
  #screen-portal .p14-no .p14-fill { transition: none; }
}

/* ---- the shop: FOUND IN PORTALS ---------------------------------------------- */
.rar-mythic { --tier: #19b8c9; --tier-ink: #0d6f7a; --tier-glow: rgba(25,184,201,0.45); }
.skin-tile.mystery { background: #1d2050; }
.skin-tile.mystery .tile-cv {
  background:
    radial-gradient(60% 46% at 50% 58%, rgba(150,110,255,0.42) 0%, rgba(40,30,110,0) 72%),
    linear-gradient(180deg, #151738 0%, #241c58 100%);
}
.skin-tile.mystery .tile-name { color: #d8ccff; letter-spacing: 0.12em; }
.skin-tile.mystery.selected { background: #2c2a7a; }
.skin-tile.mystery.selected .tile-name { color: #ffffff; }
.chip-portal { background: rgba(122,87,232,0.18); box-shadow: 0 0 0 1.5px rgba(150,120,255,0.55) inset; padding: 1px 5px; }
.chip-swirl {
  display: inline-block;
  width: 13px; height: 13px; border-radius: 50%;
  background:
    radial-gradient(circle, #ffffff 0 16%, rgba(255,255,255,0) 22%),
    conic-gradient(#4fd2ff, #7a57e8, #4fd2ff, #7a57e8, #4fd2ff);
}
.shop-stage.mystery .stage-cv {
  background:
    radial-gradient(48% 38% at 50% 60%, rgba(150,110,255,0.5) 0%, rgba(0,0,0,0) 72%),
    radial-gradient(2px 2px at 18% 14%, rgba(255,255,255,0.9), rgba(255,255,255,0)),
    radial-gradient(2px 2px at 74% 9%,  rgba(255,255,255,0.75), rgba(255,255,255,0)),
    linear-gradient(180deg, #121433 0%, #241a52 100%);
}
#screen-shop .stage-act.p14-none { display: none; }               /* (1,2,0) */
.stage-info .p14-hint {
  max-width: 100%;
  margin: 2px 0 4px;
  font-size: 12px; font-weight: 800; font-style: italic; line-height: 1.25;
  color: #5b3fd0;
  text-align: center;
}
.stage-info .p14-hint[hidden] { display: none; }

/* ---- (apex pass) THE FINAL PORTAL --------------------------------------------
   The last portal skin (the seraph since the seraph pass; the apex before)
   is the one to climb for. In the shop its card
   stands alone on the last row (UI.p14FinalCard): (pass 14g) a spotlight
   that hugs it - the card plus a glow margin, centred - a golden aura behind (two fans of rays turning against each other, a
   breathing glow, gold motes drifting up), the card bigger than the rest, in
   an ornate gold frame with a light running round it, a crown on its top
   edge and gold corners. Not his yet: "???" and a dark silhouette with a gold
   rim before its sunburst; his: the skin itself, the frame fully lit. All CSS
   animation (transform and opacity only); under reduced motion one still,
   glowing frame. Never red. In the portal: a gold ribbon over the tier. */
.skin-grid .p14-fin-row {                                        /* (0,2,0) */
  grid-column: 1 / -1;
  position: relative;
  isolation: isolate;
  /* clipped (the aura stays on its band) but never squashed: a clipped grid
     item's automatic minimum is 0, and the rack overflows, so the row would
     shrink to its padding - min-content keeps the card's whole height */
  overflow: hidden;
  min-height: min-content;
  display: flex; justify-content: center; align-items: center;
  justify-self: center;          /* (14g) hugs the card: no full-width band */
  width: max-content;
  max-width: 100%;
  margin: 10px 0 6px;
  padding: 30px 26px 18px;       /* the crown's 24px + room; the glow margin */
  border-radius: 20px;
  background:
    radial-gradient(46% 62% at 50% 54%, rgba(255,196,64,0.30) 0%, rgba(255,196,64,0) 72%),
    radial-gradient(120% 100% at 50% 38%, #2d2163 0%, #181238 55%, #0d0a22 100%);
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 4px rgba(255,210,90,0.30), 0 3px 0 var(--sky-ledge, rgba(43,58,103,0.3));
}
.p14-fin-aura { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.p14-fin-aura i { position: absolute; display: block; }
.p14-fin-glow {
  left: 50%; top: 54%;
  width: 320px; height: 280px; margin: -140px 0 0 -160px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255,214,96,0.62) 0%, rgba(255,200,70,0.22) 55%, rgba(255,200,70,0) 100%);
  animation: p14FinBreathe 3.2s ease-in-out infinite;
}
.p14-fin-rays {
  left: 50%; top: 54%;
  width: 520px; height: 520px; margin: -260px 0 0 -260px;
  border-radius: 50%;
  background: repeating-conic-gradient(from 0deg, rgba(255,214,90,0) 0deg, rgba(255,222,120,0.5) 5deg, rgba(255,214,90,0) 10deg, rgba(255,214,90,0) 22.5deg);
  -webkit-mask-image: radial-gradient(closest-side, #000 12%, rgba(0,0,0,0.6) 42%, rgba(0,0,0,0) 100%);
          mask-image: radial-gradient(closest-side, #000 12%, rgba(0,0,0,0.6) 42%, rgba(0,0,0,0) 100%);
  animation: p14FinSpin 48s linear infinite;
}
.p14-fin-rays.p14-fin-rays2 {
  width: 440px; height: 440px; margin: -220px 0 0 -220px;
  background: repeating-conic-gradient(from 6deg, rgba(255,250,228,0) 0deg, rgba(255,250,228,0.36) 1.6deg, rgba(255,250,228,0) 3.2deg, rgba(255,250,228,0) 15deg);
  animation: p14FinSpin 72s linear infinite reverse;
}
.p14-fin-mote {
  bottom: 6px;
  left: calc(5% + var(--m, 0) * 10%);
  width: 5px; height: 5px; border-radius: 50%;
  background: #fff6d0;
  box-shadow: 0 0 5px 2px rgba(255,206,84,0.85);
  opacity: 0;
  animation: p14FinMote 5.4s linear infinite;
  animation-delay: calc(var(--m, 0) * -0.57s);
}
.p14-fin-mote:nth-child(3n) { width: 3px; height: 3px; animation-duration: 6.6s; }
.p14-fin-mote:nth-child(3n+1) { animation-duration: 4.6s; }
@keyframes p14FinSpin { to { transform: rotate(360deg); } }
@keyframes p14FinBreathe {
  0%, 100% { opacity: 0.6; transform: scale(0.96); }
  50%      { opacity: 1;   transform: scale(1.04); }
}
@keyframes p14FinMote {
  0%   { opacity: 0;    transform: translate(0, 0); }
  15%  { opacity: 1; }
  60%  { opacity: 0.85; transform: translate(4px, -120px); }
  100% { opacity: 0;    transform: translate(-3px, -200px); }
}

/* the card: bigger than the rest, its own frame (no tier tab, no white) */
.skin-grid .skin-tile.p14-final,
.skin-grid .skin-tile.p14-final.mystery,
.skin-grid .skin-tile.p14-final.selected,
.skin-grid .skin-tile.p14-final.equipped:not(.selected) {       /* (0,4,0)+ */
  width: 188px; max-width: 100%;
  padding: 18px 11px 12px;
  gap: 3px;
  border-radius: 16px;
  background: transparent;
  box-shadow: none;
  z-index: 1;
}
.skin-grid .skin-tile.p14-final::before { display: none; }
.skin-grid .skin-tile.p14-final > * { position: relative; z-index: 1; }
.skin-grid .skin-tile.p14-final > .p14-fin-frame { position: absolute; z-index: 0; }
.skin-grid .skin-tile.p14-final > .p14-fin-orn { position: absolute; z-index: 2; }
/* its glow, breathing (opacity only) */
.skin-grid .skin-tile.p14-final::after {
  content: "";
  position: absolute; inset: -7px; z-index: -1;
  border-radius: 22px;
  box-shadow: 0 0 16px 5px rgba(255,204,72,0.8), 0 0 38px 12px rgba(255,190,60,0.32);
  pointer-events: none;
  animation: p14FinGlow 3.2s ease-in-out infinite;
}
@keyframes p14FinGlow { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
/* the frame: gold, an ink line round it, a light running round the gold
   (a turning wedge, seen only in the frame: the dark heart covers the rest) */
.p14-fin-frame {
  inset: 0;
  border-radius: 16px;
  overflow: hidden;
  background: linear-gradient(160deg, #fff6c8 0%, #ffd23f 20%, #c98a12 46%, #ffe58a 68%, #b8780e 100%);
  box-shadow: 0 0 0 2.5px var(--ink);
}
.p14-fin-frame::before {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 400px; height: 400px; margin: -200px 0 0 -200px;
  background: conic-gradient(from 0deg, rgba(255,255,255,0) 0deg, rgba(255,255,255,0) 280deg, rgba(255,255,255,0.95) 335deg, rgba(255,255,255,0) 360deg);
  animation: p14FinSpin 3.4s linear infinite;
}
.p14-fin-frame::after {
  content: "";
  position: absolute; inset: 5px;
  border-radius: 11px;
  background:
    radial-gradient(62% 52% at 50% 46%, rgba(255,196,64,0.36) 0%, rgba(255,196,64,0) 75%),
    radial-gradient(90% 80% at 50% 40%, #33266e 0%, #1a1442 62%, #100c2c 100%);
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 3.5px rgba(255,232,150,0.6);
}
/* the crown on its top edge, the four gold corners */
.p14-fin-orn { inset: 0; pointer-events: none; }
.p14-fin-orn svg { position: absolute; display: block; overflow: visible; }
.p14-fin-crown {
  left: 50%; top: -24px;
  width: 62px; height: 43px; margin-left: -31px;
  filter: drop-shadow(0 0 5px rgba(255,206,84,0.9));
}
.p14-fin-cn { width: 24px; height: 24px; }
.p14-fin-cn.tl { left: -6px; top: -6px; }
.p14-fin-cn.tr { right: -6px; top: -6px; transform: scaleX(-1); }
.p14-fin-cn.bl { left: -6px; bottom: -6px; transform: scaleY(-1); }
.p14-fin-cn.br { right: -6px; bottom: -6px; transform: scale(-1, -1); }
/* its words: THE FINAL PORTAL on a gold pill; the name in gold */
.p14-fin-label {
  margin: 2px 0 1px;
  padding: 3px 10px 3px;
  border-radius: 999px;
  background: linear-gradient(180deg, #fff3c4 0%, #ffd23f 55%, #e8a316 100%);
  box-shadow: 0 0 0 2px var(--ink), 0 0 10px rgba(255,206,84,0.7);
  color: var(--ink);
  font-size: 9.5px; font-weight: 900; letter-spacing: 0.12em; line-height: 1.15;
  white-space: nowrap;
}
.skin-grid .skin-tile.p14-final .tile-cv,
.skin-grid .skin-tile.p14-final.mystery .tile-cv {
  aspect-ratio: 240 / 220;
  background: none;
  border-radius: 10px;
}
.skin-grid .skin-tile.p14-final .tile-name,
.skin-grid .skin-tile.p14-final.mystery .tile-name,
.skin-grid .skin-tile.p14-final.selected .tile-name {
  font-size: 16px; font-weight: 900; letter-spacing: 0.14em;
  color: #ffe58a;
  text-shadow: 0 2px 0 var(--ink), 0 0 10px rgba(255,200,70,0.85);
}
.skin-grid .skin-tile.p14-final.mystery .tile-name { letter-spacing: 0.3em; }
/* (verify pass) the tile's name clips for its ellipsis, which cut the gold
   glow into a visible box round "???": the final card's names are short, so
   let the glow out */
.skin-grid .skin-tile.p14-final .tile-name { overflow: visible; text-overflow: clip; }
.skin-tile.p14-final .chip-portal { background: rgba(255,214,90,0.16); box-shadow: 0 0 0 1.5px rgba(255,214,90,0.75) inset; padding: 1px 6px; }
.skin-tile.p14-final .chip-portal .chip-swirl {
  background:
    radial-gradient(circle, #ffffff 0 16%, rgba(255,255,255,0) 22%),
    conic-gradient(#ffd23f, #fff3c4, #e8a316, #fff3c4, #ffd23f);
}
.skin-tile.p14-final .chip-owned { background: #ffe58a; color: var(--ink); box-shadow: 0 0 0 1.5px var(--ink) inset; }
/* chosen: lifted, a white ring round the gold */
.skin-grid .skin-tile.p14-final.selected { transform: translateY(-3px); }
.skin-tile.p14-final.selected .p14-fin-frame { box-shadow: 0 0 0 2.5px var(--ink), 0 0 0 5.5px #ffffff, 0 0 0 7.5px var(--ink); }
/* his: the frame fully lit - a second light running round, a warmer heart */
.skin-tile.p14-final.owned .p14-fin-frame::before {
  background: conic-gradient(from 0deg, rgba(255,250,220,0) 0deg, rgba(255,250,220,0.9) 25deg, rgba(255,250,220,0) 50deg,
                             rgba(255,250,220,0) 180deg, rgba(255,250,220,0.9) 205deg, rgba(255,250,220,0) 230deg, rgba(255,250,220,0) 360deg);
  animation-duration: 4.2s;
}
.skin-tile.p14-final.owned .p14-fin-frame::after {
  background:
    radial-gradient(62% 52% at 50% 46%, rgba(255,206,84,0.5) 0%, rgba(255,206,84,0) 75%),
    radial-gradient(90% 80% at 50% 40%, #3d2c78 0%, #1f174c 62%, #130e33 100%);
}
.p14-fin-row.owned .p14-fin-glow { background: radial-gradient(closest-side, rgba(255,224,120,0.75) 0%, rgba(255,206,84,0.28) 55%, rgba(255,206,84,0) 100%); }
/* a phone on its side: a little smaller */
@media (max-height: 420px) {
  .skin-grid .p14-fin-row { padding: 28px 22px 12px; }
  .skin-grid .skin-tile.p14-final,
  .skin-grid .skin-tile.p14-final.mystery,
  .skin-grid .skin-tile.p14-final.selected,
  .skin-grid .skin-tile.p14-final.equipped:not(.selected) { width: 150px; padding: 16px 10px 10px; }
}
/* the stage, the final one not his yet: a gold sky (the canvas: its sunburst
   behind the gold-rimmed silhouette) */
.shop-stage.mystery.p14-final .stage-cv {
  background:
    radial-gradient(50% 42% at 50% 56%, rgba(255,200,70,0.42) 0%, rgba(0,0,0,0) 72%),
    radial-gradient(2px 2px at 18% 14%, rgba(255,255,255,0.9), rgba(255,255,255,0)),
    radial-gradient(2px 2px at 74% 9%,  rgba(255,255,255,0.75), rgba(255,255,255,0)),
    linear-gradient(180deg, #120f30 0%, #2a1c54 100%);
  box-shadow: 0 0 0 2px #ffd23f, 0 0 14px rgba(255,206,84,0.55);
}
.shop-stage.p14-final .p14-hint { color: #8a5a00; }

/* ---- the portal: THE FINAL PORTAL, a gold ribbon over the tier ---------------- */
#screen-portal .p14-ribbon {
  position: relative; z-index: 0;
  display: inline-block;
  margin: 0 0 8px;
  padding: 6px 26px 6px;
  background: var(--ink);
  color: var(--ink);
  font-size: 14px; font-weight: 900; letter-spacing: 0.16em; line-height: 1.1;
  white-space: nowrap;
  clip-path: polygon(0 0, 100% 0, calc(100% - 13px) 50%, 100% 100%, 0 100%, 13px 50%);
  animation: p14Fade 0.3s ease-out 0.35s both;
}
#screen-portal .p14-ribbon[hidden] { display: none; }
#screen-portal .p14-ribbon::before,
#screen-portal .p14-ribbon .p14-rib-shine {
  content: "";
  position: absolute; inset: 3px; z-index: -1;
  clip-path: polygon(0 0, 100% 0, calc(100% - 11px) 50%, 100% 100%, 0 100%, 11px 50%);
}
#screen-portal .p14-ribbon::before { background: linear-gradient(180deg, #fff6c8 0%, #ffe16b 40%, #ffc21f 62%, #e99a12 100%); }
/* (14h) its shine: a still clip the gold's own shape (.p14-rib-shine, inside
   the ink edges), and in it a band 2.6 times its width slid across (transform
   only) - the same sweep the background-position animation made, never over
   the ink */
#screen-portal .p14-ribbon .p14-rib-shine { pointer-events: none; }
#screen-portal .p14-ribbon .p14-rib-shine::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 260%;
  background: linear-gradient(105deg, rgba(255,255,255,0) 40%, rgba(255,255,255,0.75) 50%, rgba(255,255,255,0) 60%);
  transform: translateX(-80%);
  animation: p14ShineX 2.2s ease-in-out 0.9s infinite;
}
/* the final one's name glows warmer */
#screen-portal.p14-final .p14-name { filter: drop-shadow(0 0.07em 0 var(--sky-drop)) drop-shadow(0 0 0.45em rgba(255,214,90,0.65)); }
#screen-portal.p14-short .p14-ribbon { font-size: 11px; padding: 4px 20px; margin-bottom: 5px; }
#screen-portal.p14-band .p14-ribbon { font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  /* one still frame: the aura held, the glow steady, the motes scattered */
  .p14-fin-glow, .p14-fin-rays, .p14-fin-frame::before, .skin-grid .skin-tile.p14-final::after { animation: none; }
  .skin-grid .skin-tile.p14-final::after { opacity: 0.8; }
  .p14-fin-frame::before { transform: rotate(30deg); }
  .p14-fin-mote { animation: none; opacity: 0.75; transform: translateY(calc(var(--m, 0) * -13px - 24px)); }
  #screen-portal .p14-ribbon { animation: none; }
  #screen-portal .p14-ribbon .p14-rib-shine { display: none; }
}

/* ===================== (2026-10-01) THE SHOP, FULL-SCREEN =====================
   The user: "dont show the gameplay scene behind the skins panel". The shop is
   a page of its own now. (pass 14g) Its backdrop is the shop theme's own
   (pass 14h: theme B, ROOFTOP AT NIGHT, the one the user picked - appended
   after this sheet under body.shop-theme-b, its layer in #screen-shop): the
   old dusk sky and its nine drifting bokeh lights are gone ("I don't like the
   background of the panel skins"). The dusk gradient below stays only as an
   opaque fallback under no theme class.
   Nothing of the world shows, and the game no longer draws the world under it
   at all (Game.holdBlank, UI.showScreen). No red.
   The panel keeps its design and takes the room: up to 1060 x 880, between a
   back arrow in the top-left corner (#btnShopX: since pass 14h the way out,
   with Esc - the bottom BACK button is gone) and the sound
   column in the top-right. Upright, the two corners make a bar along the top
   (the sound column lies down into a row) and the panel takes the full width
   under it - so nothing stands over the panel anywhere, and the stage card's
   old notch for the column is gone. */
#screen-shop.screen.screen-dim {                                   /* (1,2,0) */
  overflow: hidden;
  padding: calc(14px + var(--safe-t)) calc(66px + var(--safe-r)) calc(18px + var(--safe-b)) calc(66px + var(--safe-l));
  background:
    radial-gradient(46% 50% at 50% 48%, rgba(255,214,184,0.16) 0%, rgba(255,214,184,0) 100%),
    radial-gradient(85% 50% at 50% 106%, rgba(255,200,140,0.55) 0%, rgba(255,200,140,0) 72%),
    linear-gradient(180deg, #14123a 0%, #251f58 24%, #42307b 46%, #70438e 64%, #a45b8f 80%, #eea27c 100%);
  background-color: #2f2565;
}
/* the panel, over the sky, taking the room (style.css's caps, side margins
   and padding for the sound column are all replaced here) */
#screen-shop .shop-panel {                                        /* (1,1,0) */
  position: relative; z-index: 1;
  width: min(100%, 1060px);
  max-height: min(100%, 880px);
  margin: 0;
  padding-right: 14px;
}
#screen-shop .shop-head { padding-right: 0; }

/* the back arrow: the sound column's squircle, in the opposite corner */
#screen-shop .shop-x {                                            /* (1,1,0) */
  position: absolute; z-index: 2;
  top: calc(10px + var(--safe-t));
  left: calc(12px + var(--safe-l));
  padding: 10px;
}
#screen-shop .shop-x .ico-back { fill: none; stroke: currentColor; }
/* (14h) the only way out on screen now: never under 44 px (the squircle
   itself; .icon-btn::after adds 5 px of reach round it) */
@media (max-width: 460px) {
  #screen-shop .shop-x { width: 44px; height: 44px; padding: 9px; }
}

/* upright: a bar along the top - the arrow on the left, the sound column lying
   down into a row on the right - and the panel the full width under it */
@media (orientation: portrait) {
  #screen-shop.screen.screen-dim { padding: calc(70px + var(--safe-t)) calc(12px + var(--safe-r)) calc(18px + var(--safe-b)) calc(12px + var(--safe-l)); }
  body.shop-up .audio-fixed { flex-direction: row; }
}
@media (orientation: portrait) and (max-width: 460px) {
  #screen-shop.screen.screen-dim { padding-top: calc(62px + var(--safe-t)); }
}
/* a short screen (a phone on its side): the screens' own tighter top and
   bottom (style.css, max-height 440px), the ledge just inside the edge */
@media (max-height: 440px) and (orientation: landscape) {
  #screen-shop.screen.screen-dim { padding-top: calc(8px + var(--safe-t)); padding-bottom: calc(10px + var(--safe-b)); }
}
/* the stage card's notch for the column (style.css) is not needed any more */
@media (max-width: 600px) and (min-height: 421px) {
  #screen-shop .shop-stage { margin-right: 0; padding-right: 12px; }
}
/* a wider panel: a larger stage, more and larger cards (a phone on its side
   keeps its own tight layout) */
@media (min-width: 601px) and (min-height: 421px) {
  #screen-shop .shop-body { grid-template-columns: minmax(232px, 28%) 1fr; gap: 14px; }
  #screen-shop .skin-grid { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 7px; }
}

/* ---- (seraph pass, 2026-10-01) EVEN ROWS -------------------------------------
   The portal collection is 18 cards (the rainbow is retired) plus the final
   card on its own row, the cash rack 6: the rack has 6 columns, or 3 where it
   is too narrow for 6 (UI.p14Cols measures the rack and sets --p14-cols), so
   no row is ever left short. Until it has measured, the old auto-fill holds. */
#screen-shop .skin-grid.p14-cols { grid-template-columns: repeat(var(--p14-cols, 6), minmax(0, 1fr)); }
/* ...the bigger cards of a 3-wide rack keep a name that fits them */
#screen-shop .skin-grid.p14-cols .tile-name { font-size: 11px; }
/* (14g) one column grid, flush with the panel's content edges: the
   scrollbar's gutter is always kept (a scrollbar coming or going never
   changes the width) and hangs out into the panel's right padding (the
   measured --p14-gut, UI.p14Cols), so the last column ends on the wallet's
   line. No side padding: the cards are the edges; the focus ring
   is drawn inside the card so the scroller never clips it. */
#screen-shop .skin-grid {                                         /* (1,1,0) */
  scrollbar-gutter: stable;
  padding-left: 0;
  padding-right: 0;
  margin-right: calc(-1 * var(--p14-gut, 0px));
}
#screen-shop .shop-sec { padding-left: 0; padding-right: 0; }     /* (1,1,0) */
#screen-shop .skin-tile:focus-visible { outline-offset: -3px; }   /* (1,2,0) */
/* every card the same height: the worn tick's chip was 2px taller */
#screen-shop .skin-grid .tile-chip { height: 19px; min-height: 19px; }   /* (1,2,0) */

/* The retired portal skin he owns (the rainbow): its own row, the card and,
   beside it to the row's end, a line saying it is his to keep - only for
   him (UI.p14KeptRow). */
.skin-grid .p14-kept-note {
  grid-column: 2 / -1;
  align-self: center;
  margin: 0;
  padding: 10px 12px;
  border-radius: 12px;
  background: rgba(255,214,90,0.14);
  box-shadow: 0 0 0 1.5px rgba(232,163,22,0.45) inset;
  color: var(--ink);
  font-size: 12px; font-weight: 800; line-height: 1.35;
}

/* =============== (pass 14h) NO BACK BUTTON; MOTION THAT DOES NOT REPAINT ===============
   The user picked theme B and: "remove the bottom back button, there is a
   back arrow". The panel is its head and its body now: the rack and the stage
   take BACK's height and its gap, down to the parapet's air; the top-left
   arrow and Esc are the ways out.
   "It's kinda laggy in the skins section": every glow or flash that animated a
   box-shadow is now a still shadow whose OPACITY moves, every sweep a band that
   is MOVED (transform) inside a clip, so the browser composites them instead
   of painting them again on every frame. The final card's aura holds while it
   is scrolled out of the rack (UI.p14FinWatch: .p14-fin-off), and everything on
   the skins screen holds while the tab is away (UI.p14ShopVis: .shop-paused).
   Reduced motion as before. */
#screen-shop .shop-body { margin-bottom: 0; }                    /* (1,1,0) */

/* A skin opens: the window flares in its tier's light and pops. The flare was
   an animated box-shadow on the canvas (a canvas has no ::before); it is a
   ring of that same light laid over the window now (UI.p14Ring places it on
   the canvas), its opacity alone moving, popping with the window. */
#screen-shop .shop-stage.burst .stage-cv {                        /* (1,3,0) */
  animation: stagePop 0.6s cubic-bezier(.2,1.5,.4,1);
}
#screen-shop .stage-ring {
  position: absolute; z-index: 1;
  display: none;
  border-radius: 13px;
  pointer-events: none;
  box-shadow: 0 0 0 5px var(--tier), 0 0 0 7.5px var(--ink), 0 0 0 12px rgba(255,214,184,0.9);
  opacity: 0;
}
#screen-shop .shop-stage.burst .stage-ring {
  display: block;
  animation: stageRing 0.9s ease-out, stagePop 0.6s cubic-bezier(.2,1.5,.4,1);
}
@keyframes stageRing {
  0%, 100% { opacity: 0; }
  25%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  #screen-shop .shop-stage.burst .stage-ring { animation: none; display: none; }
}

/* The final card's aura (rays, glow, motes, the frame's running light), held
   while the card is out of the rack's view */
#screen-shop .p14-fin-row.p14-fin-off *,
#screen-shop .p14-fin-row.p14-fin-off *::before,
#screen-shop .p14-fin-row.p14-fin-off *::after { animation-play-state: paused; }   /* (1,2,0)+ */
/* ...and everything on the skins screen while the tab is away */
#screen-shop#screen-shop.shop-paused *,
#screen-shop#screen-shop.shop-paused *::before,
#screen-shop#screen-shop.shop-paused *::after { animation-play-state: paused; }    /* (2,1,0)+ */

/* ====================== (pass 14g) THE SHOP THEME ======================
   B ROOFTOP AT NIGHT, the user's pick (pass 14h; A PORTAL VAULT and C HERO'S
   LOCKER are gone): the whole look of the skins screen (backdrop, panel,
   header, stage, cards, final spotlight, buttons), scoped under
   body.shop-theme-b (on <body>), paint only (the layout is the shared one
   above). Source: visual/p14g/theme_b.css (brief: visual/p14g/brief.md). */

/* ======================= (pass 14g) THEME B: ROOFTOP AT NIGHT =======================
   GENERATED by theme_b_src\gen_b.js from theme_b_src\theme_b.tpl.css - edit those,
   then `node gen_b.js`. Scoped under body.shop-theme-b; markup: theme_b_markup.html.

   The shop is her rooftop after dark, the game's own place. The screen is the night
   over the city (navy at the top, the revive scene's rose and peach at the horizon),
   stars, the iron-night moon and two rows of skyline with lit windows. The panel is
   that night sky in a frame, with a string of warm fairy lights along its top edge
   and the roof's brick parapet, lit rim on top, along its foot. Every card is a
   window: a lit one (warm pane, amber curtains) for a skin you can see, an unlit
   one (moonlit glass, a violet glow) for a portal skin still to find. The stage is
   a window onto the roof itself: he stands on the parapet's lit coping, the city
   and the ferris wheel behind him. The final card stands in a lit marquee at the
   end of the rack, in front of the city.
   Gold here is warm bulb light (#ffd27a); selected = PLAY's yellow; worn = green;
   no red. Paint only: no layout property on anything the shared fixes own. Cheap:
   static gradients and inline SVG; three things move (a few stars, the bulbs'
   halos, a few windows), opacity only; reduced motion holds them still.
   (pass 14h, 2026-10-01) The user picked B: it is the shop's one look now
   (body.shop-theme-b is on <body> in the build; themes A and C are gone). The
   bottom BACK button is gone too ("there is a back arrow"): the top-left arrow
   (#btnShopX) and Esc are the ways out, the arrow lamp-lit here; the rack
   and the stage take BACK's height, and the city stands on the parapet. */

/* ---- the page: the night over the city -------------------------------------- */
body.shop-theme-b #screen-shop {
  --ink: #14112e;                 /* the outlines, a shade deeper: they still read on the night */
  --sky-ledge: #0a0820;           /* every ledge a drop into the dark, not a pale lip */
}
body.shop-theme-b #screen-shop.screen.screen-dim {
  background:
    radial-gradient(70% 34% at 50% 100%, rgba(255,179,154,0.42) 0%, rgba(255,179,154,0) 72%),
    radial-gradient(46% 42% at 50% 46%, rgba(120,112,230,0.16) 0%, rgba(120,112,230,0) 100%),
    linear-gradient(180deg, #121848 0%, #1c2466 16%, #262560 34%, #3a3278 54%, #5c4892 72%, #8f5a9e 87%, #c27aa2 100%);
  background-color: #262560;
}
body:not(.shop-theme-b) #screen-shop .p14g-b-layer { display: none; }

/* (14h) contain: strict - the layer is the screen's size whatever is in it,
   so a twinkle in it never makes the browser look outside it */
body.shop-theme-b #screen-shop .p14g-b-layer {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
  contain: strict;
}
body.shop-theme-b #screen-shop .p14g-b-layer > i { position: absolute; display: block; }
body.shop-theme-b #screen-shop .p14g-b-stars {
  left: 0; right: 0; top: 0; height: 72%;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='260' viewBox='0 0 360 260'%3E%3Ccircle cx='85.4' cy='143.2' r='1.5' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Cpath d='M80.9 143.2H89.9M85.4 138.7V147.7' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='138.1' cy='224.9' r='1.2' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M134.5 224.9H141.7M138.1 221.3V228.5' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='12.2' cy='196.1' r='0.7' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Ccircle cx='196' cy='192.5' r='1.3' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Cpath d='M192.1 192.5H199.9M196 188.6V196.4' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='60.2' cy='92.9' r='0.8' fill='%23fff6e8' fill-opacity='0.5'/%3E%3Ccircle cx='215.3' cy='59.8' r='1' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M212.3 59.8H218.3M215.3 56.8V62.8' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='131.3' cy='153.6' r='1.4' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Cpath d='M127.1 153.6H135.5M131.3 149.4V157.8' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='317.8' cy='256.2' r='0.6' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Ccircle cx='93.9' cy='51' r='0.7' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Ccircle cx='64.5' cy='166' r='1.2' fill='%23fff6e8' fill-opacity='0.4'/%3E%3Cpath d='M60.9 166H68.1M64.5 162.4V169.6' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='152.2' cy='196.1' r='0.8' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Ccircle cx='321.9' cy='42.1' r='0.8' fill='%23fff6e8' fill-opacity='0.7'/%3E%3Ccircle cx='67.6' cy='150.8' r='1.3' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Cpath d='M63.7 150.8H71.5M67.6 146.9V154.7' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='18.7' cy='206.5' r='1' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Cpath d='M15.7 206.5H21.7M18.7 203.5V209.5' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='113.1' cy='45.7' r='1.6' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M108.3 45.7H117.9M113.1 40.9V50.5' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='37.3' cy='136.6' r='0.7' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Ccircle cx='159.7' cy='65' r='1' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M156.7 65H162.7M159.7 62V68' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='196' cy='37.8' r='1.4' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M191.8 37.8H200.2M196 33.6V42' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='135.5' cy='76.9' r='0.8' fill='%23fff6e8' fill-opacity='0.7'/%3E%3Ccircle cx='124.6' cy='222.7' r='1.5' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M120.1 222.7H129.1M124.6 218.2V227.2' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='57.8' cy='191.4' r='1.4' fill='%23fff6e8' fill-opacity='0.5'/%3E%3Cpath d='M53.6 191.4H62M57.8 187.2V195.6' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='72.3' cy='115.9' r='1' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Cpath d='M69.3 115.9H75.3M72.3 112.9V118.9' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='356.6' cy='162.8' r='0.6' fill='%23fff6e8' fill-opacity='0.4'/%3E%3Ccircle cx='208.2' cy='5.9' r='1' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Cpath d='M205.2 5.9H211.2M208.2 2.9V8.9' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='331.8' cy='239.8' r='0.6' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Ccircle cx='213.8' cy='82.4' r='1.4' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Cpath d='M209.6 82.4H218M213.8 78.2V86.6' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3C/svg%3E") 0 0 / 360px 260px repeat;
  -webkit-mask-image: linear-gradient(180deg, #000 45%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg, #000 45%, rgba(0,0,0,0) 100%);
}
body.shop-theme-b #screen-shop .p14g-b-tw {                    /* the few that twinkle */
  left: 0; right: 0; top: 0; height: 60%;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='220' viewBox='0 0 300 220'%3E%3Ccircle cx='75.6' cy='219.4' r='1.8' fill='%23fff6e8' fill-opacity='0.5'/%3E%3Cpath d='M70.2 219.4H81M75.6 214V224.8' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='212.8' cy='61.1' r='0.5' fill='%23fff6e8' fill-opacity='0.5'/%3E%3Ccircle cx='109.5' cy='206.2' r='1.1' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Ccircle cx='199.5' cy='200.1' r='1.1' fill='%23fff6e8' fill-opacity='0.7'/%3E%3Ccircle cx='296.1' cy='205.7' r='1.4' fill='%23fff6e8' fill-opacity='0.4'/%3E%3Cpath d='M291.9 205.7H300.3M296.1 201.5V209.9' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='257.8' cy='135.9' r='1.8' fill='%23fff6e8' fill-opacity='0.5'/%3E%3Cpath d='M252.4 135.9H263.2M257.8 130.5V141.3' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='247.7' cy='83.5' r='1.3' fill='%23fff6e8' fill-opacity='0.4'/%3E%3Cpath d='M243.8 83.5H251.6M247.7 79.6V87.4' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='197.1' cy='97.5' r='1.2' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Ccircle cx='260' cy='52.6' r='0.9' fill='%23fff6e8' fill-opacity='0.6'/%3E%3C/svg%3E") 70px 40px / 300px 220px repeat;
  -webkit-mask-image: linear-gradient(180deg, #000 40%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg, #000 40%, rgba(0,0,0,0) 100%);
  will-change: opacity;
  animation: p14gBTwinkle 4.6s ease-in-out infinite alternate;
}
/* the moon (iron night's #f6f8ff, glow #a8c0f4): centred in the left margin
   beside the panel on a desktop, in the top bar on an upright phone */
body.shop-theme-b #screen-shop .p14g-b-moon {
  --mz: clamp(40px, 8vmin, 92px);
  width: var(--mz); height: var(--mz);
  left: calc(max(33px, (100vw - 1060px) / 4) - var(--mz) / 2);
  top: 22vh;
  border-radius: 50%;
  background:
    radial-gradient(circle at 64% 30%, rgba(184,196,232,0.55) 0 9%, rgba(184,196,232,0) 10%),
    radial-gradient(circle at 34% 62%, rgba(184,196,232,0.5) 0 12%, rgba(184,196,232,0) 13%),
    radial-gradient(circle at 70% 70%, rgba(184,196,232,0.4) 0 6%, rgba(184,196,232,0) 7%),
    radial-gradient(circle at 36% 32%, #ffffff 0%, #f6f8ff 48%, #dde4f8 100%);
  background-color: #f6f8ff;
  box-shadow: 0 0 0 3px rgba(168,192,244,0.22), 0 0 28px 8px rgba(168,192,244,0.42), 0 0 90px 30px rgba(168,192,244,0.16);
}
body.shop-theme-b #screen-shop .p14g-b-far {
  left: 0; right: 0; bottom: 0; height: min(36vh, 330px);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='180' viewBox='0 0 600 180'%3E%3Cg fill='none' stroke='%233c2a5c'%3E%3Ccircle cx='452' cy='82' r='52' stroke-width='3.3'/%3E%3Ccircle cx='452' cy='82' r='42.6' stroke-width='1.7'/%3E%3Cpath d='M452 82L504 82' stroke-width='1.5'/%3E%3Cpath d='M452 82L488.8 118.8' stroke-width='1.5'/%3E%3Cpath d='M452 82L452 134' stroke-width='1.5'/%3E%3Cpath d='M452 82L415.2 118.8' stroke-width='1.5'/%3E%3Cpath d='M452 82L400 82' stroke-width='1.5'/%3E%3Cpath d='M452 82L415.2 45.2' stroke-width='1.5'/%3E%3Cpath d='M452 82L452 30' stroke-width='1.5'/%3E%3Cpath d='M452 82L488.8 45.2' stroke-width='1.5'/%3E%3Cpath d='M452 82L419.8 155.8M452 82L484.2 155.8' stroke-width='3.7'/%3E%3C/g%3E%3Ccircle cx='501.7' cy='101' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='501.7' cy='97.4' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='483.2' cy='127.2' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='483.2' cy='123.6' r='1.8' fill='%23ffb8e0'/%3E%3Ccircle cx='452.7' cy='137.6' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='452.7' cy='134' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='422' cy='128.1' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='422' cy='124.5' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='402.8' cy='102.4' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='402.8' cy='98.8' r='1.8' fill='%23ffb8e0'/%3E%3Ccircle cx='402.3' cy='70.2' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='402.3' cy='66.6' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='420.8' cy='44' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='420.8' cy='40.4' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='451.3' cy='33.6' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='451.3' cy='30' r='1.8' fill='%23ffb8e0'/%3E%3Ccircle cx='482' cy='43.1' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='482' cy='39.5' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='501.2' cy='68.8' r='4.2' fill='%233c2a5c'/%3E%3Ccircle cx='501.2' cy='65.2' r='1.8' fill='%23ffd27a'/%3E%3Ccircle cx='452' cy='82' r='4.7' fill='%233c2a5c'/%3E%3Cpath fill='%233c2a5c' d='M0 92h34v88h-34zM36 70h22v110h-22zM46.2 56h1.6v14h-1.6zM60 104h40v76h-40zM102 80h26v100h-26zM107.2 72h15.6v8h-15.6zM130 112h46v68h-46zM178 64h20v116h-20zM187.2 50h1.6v14h-1.6zM200 96h38v84h-38zM240 76h30v104h-30zM247.5 64h15v8h-15zM249 72h1.4v4h-1.4zM259.6 72h1.4v4h-1.4zM272 108h52v72h-52zM326 58h24v122h-24zM337.2 44h1.6v14h-1.6zM352 90h36v90h-36zM520 86h30v94h-30zM526 78h18v8h-18zM552 100h48v80h-48z'/%3E%3Cpath fill='%23ffe0a0' fill-opacity='0.55' d='M27 97h2.4v3.4h-2.4zM3 129h2.4v3.4h-2.4zM15 145h2.4v3.4h-2.4zM51 83h2.4v3.4h-2.4zM75 125h2.4v3.4h-2.4zM63 133h2.4v3.4h-2.4zM87 141h2.4v3.4h-2.4zM69 173h2.4v3.4h-2.4zM105 93h2.4v3.4h-2.4zM117 109h2.4v3.4h-2.4zM111 133h2.4v3.4h-2.4zM111 141h2.4v3.4h-2.4zM117 157h2.4v3.4h-2.4zM123 165h2.4v3.4h-2.4zM111 173h2.4v3.4h-2.4zM139 133h2.4v3.4h-2.4zM169 141h2.4v3.4h-2.4zM193 69h2.4v3.4h-2.4zM193 77h2.4v3.4h-2.4zM193 117h2.4v3.4h-2.4zM221 141h2.4v3.4h-2.4zM227 149h2.4v3.4h-2.4zM261 89h2.4v3.4h-2.4zM261 161h2.4v3.4h-2.4zM317 113h2.4v3.4h-2.4zM281 161h2.4v3.4h-2.4zM329 71h2.4v3.4h-2.4zM335 71h2.4v3.4h-2.4zM335 95h2.4v3.4h-2.4zM335 111h2.4v3.4h-2.4zM329 135h2.4v3.4h-2.4zM329 151h2.4v3.4h-2.4zM335 167h2.4v3.4h-2.4zM361 111h2.4v3.4h-2.4zM379 119h2.4v3.4h-2.4zM523 91h2.4v3.4h-2.4zM541 123h2.4v3.4h-2.4zM529 139h2.4v3.4h-2.4zM591 105h2.4v3.4h-2.4zM555 129h2.4v3.4h-2.4zM573 161h2.4v3.4h-2.4zM567 169h2.4v3.4h-2.4zM585 169h2.4v3.4h-2.4z'/%3E%3Cpath fill='%23ffd98a' fill-opacity='0.55' d='M9 105h2.4v3.4h-2.4zM51 139h2.4v3.4h-2.4zM75 157h2.4v3.4h-2.4zM93 157h2.4v3.4h-2.4zM123 125h2.4v3.4h-2.4zM169 117h2.4v3.4h-2.4zM133 141h2.4v3.4h-2.4zM163 173h2.4v3.4h-2.4zM193 101h2.4v3.4h-2.4zM203 109h2.4v3.4h-2.4zM221 109h2.4v3.4h-2.4zM209 125h2.4v3.4h-2.4zM221 133h2.4v3.4h-2.4zM261 153h2.4v3.4h-2.4zM249 161h2.4v3.4h-2.4zM305 129h2.4v3.4h-2.4zM341 127h2.4v3.4h-2.4zM379 95h2.4v3.4h-2.4zM379 143h2.4v3.4h-2.4zM373 151h2.4v3.4h-2.4zM535 115h2.4v3.4h-2.4zM535 155h2.4v3.4h-2.4zM529 163h2.4v3.4h-2.4zM535 163h2.4v3.4h-2.4zM541 171h2.4v3.4h-2.4zM561 105h2.4v3.4h-2.4zM573 105h2.4v3.4h-2.4zM567 129h2.4v3.4h-2.4z'/%3E%3C/svg%3E") left -60px bottom 0 / auto 100% repeat-x;
}
body.shop-theme-b #screen-shop .p14g-b-near,
body.shop-theme-b #screen-shop .p14g-b-wins {
  left: 0; right: 0; bottom: 0; height: min(23vh, 210px);
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='440' height='130' viewBox='0 0 440 130'%3E%3Cpath fill='%232a1a40' d='M0 52h44v78h-44zM11 40h22v8h-22zM13.2 48h1.4v4h-1.4zM29.4 48h1.4v4h-1.4zM46 78h30v52h-30zM78 40h56v90h-56zM89.2 32h33.6v8h-33.6zM136 70h26v60h-26zM148.2 56h1.6v14h-1.6zM164 56h48v74h-48zM214 84h34v46h-34zM250 46h60v84h-60zM265 34h30v8h-30zM268 42h1.4v4h-1.4zM290.6 42h1.4v4h-1.4zM312 66h28v64h-28zM325.2 52h1.6v14h-1.6zM342 58h50v72h-50zM394 74h46v56h-46zM394 74Q417 48.7 440 74z'/%3E%3Cpath fill='%23ffd98a' fill-opacity='0.95' d='M10 57h3v4h-3zM10 66h3v4h-3zM24 66h3v4h-3zM38 111h3v4h-3zM56 110h3v4h-3zM63 119h3v4h-3zM95 45h3v4h-3zM109 63h3v4h-3zM123 72h3v4h-3zM123 90h3v4h-3zM123 117h3v4h-3zM153 75h3v4h-3zM146 102h3v4h-3zM181 61h3v4h-3zM202 61h3v4h-3zM167 70h3v4h-3zM188 70h3v4h-3zM195 79h3v4h-3zM181 88h3v4h-3zM174 97h3v4h-3zM188 97h3v4h-3zM217 89h3v4h-3zM231 89h3v4h-3zM281 69h3v4h-3zM281 78h3v4h-3zM288 87h3v4h-3zM302 96h3v4h-3zM274 105h3v4h-3zM295 105h3v4h-3zM253 114h3v4h-3zM315 107h3v4h-3zM322 107h3v4h-3zM329 107h3v4h-3zM345 72h3v4h-3zM352 99h3v4h-3zM404 88h3v4h-3zM411 88h3v4h-3zM432 97h3v4h-3zM418 106h3v4h-3zM397 115h3v4h-3zM411 115h3v4h-3z'/%3E%3Cpath fill='%23ffe0a0' fill-opacity='0.95' d='M38 75h3v4h-3zM31 93h3v4h-3zM17 111h3v4h-3zM49 119h3v4h-3zM123 81h3v4h-3zM146 93h3v4h-3zM174 115h3v4h-3zM302 51h3v4h-3zM253 105h3v4h-3zM260 123h3v4h-3zM359 117h3v4h-3z'/%3E%3Cpath fill='%23ffe6b0' fill-opacity='0.95' d='M10 93h3v4h-3zM38 93h3v4h-3zM24 111h3v4h-3zM56 83h3v4h-3zM70 83h3v4h-3zM56 92h3v4h-3zM81 72h3v4h-3zM81 90h3v4h-3zM81 108h3v4h-3zM139 84h3v4h-3zM167 97h3v4h-3zM238 89h3v4h-3zM217 107h3v4h-3zM288 51h3v4h-3zM302 69h3v4h-3zM253 96h3v4h-3zM352 63h3v4h-3zM380 72h3v4h-3zM425 79h3v4h-3zM397 97h3v4h-3zM404 97h3v4h-3zM432 106h3v4h-3z'/%3E%3C/svg%3E") left 0 bottom 0 / auto 100% repeat-x;
}
body.shop-theme-b #screen-shop .p14g-b-wins {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='440' height='130' viewBox='0 0 440 130'%3E%3Cpath fill='%23ffe6b0' fill-opacity='1' d='M3 66h3v4h-3zM3 93h3v4h-3zM153 75h3v4h-3zM167 97h3v4h-3zM188 97h3v4h-3zM217 107h3v4h-3zM224 107h3v4h-3zM295 114h3v4h-3zM315 71h3v4h-3zM329 80h3v4h-3zM359 90h3v4h-3zM425 79h3v4h-3z'/%3E%3Cpath fill='%23ffd98a' fill-opacity='1' d='M123 45h3v4h-3zM146 84h3v4h-3zM146 111h3v4h-3zM153 120h3v4h-3zM188 70h3v4h-3zM174 97h3v4h-3zM181 106h3v4h-3zM202 106h3v4h-3zM253 114h3v4h-3zM274 114h3v4h-3zM274 123h3v4h-3zM302 123h3v4h-3zM380 90h3v4h-3z'/%3E%3C/svg%3E") left 0 bottom 0 / auto 100% repeat-x;
  will-change: opacity;
  animation: p14gBWin 9s step-end infinite;
}
body.shop-theme-b #screen-shop .p14g-b-vig {
  inset: 0;
  background: radial-gradient(125% 100% at 50% 44%, rgba(8,6,28,0) 55%, rgba(8,6,28,0.5) 100%);
}
/* upright: in the top bar, midway between the arrow and the sound row */
@media (orientation: portrait) {
  body.shop-theme-b #screen-shop .p14g-b-moon { --mz: 32px; top: calc(16px + var(--safe-t, 0px)); left: calc(50% - 52px - var(--mz) / 2); }
}
@media (orientation: portrait) and (max-width: 460px) {
  body.shop-theme-b #screen-shop .p14g-b-moon { --mz: 30px; top: calc(14px + var(--safe-t, 0px)); left: calc(50% - 46px - var(--mz) / 2); }
}
@media (orientation: portrait) and (max-width: 340px) {
  body.shop-theme-b #screen-shop .p14g-b-moon { --mz: 24px; top: calc(17px + var(--safe-t, 0px)); }
}
@media (max-height: 440px) and (orientation: landscape) {
  body.shop-theme-b #screen-shop .p14g-b-moon { --mz: 36px; top: 30vh; left: calc(25px - 18px); }
}

/* ---- the panel: the night sky in a frame ------------------------------------
   Fairy lights along the top (::before the string, ::after the halos, which
   breathe), a warm wash under them, a few stars, and the roof's parapet at the
   foot: brick face, a dark joint, the lit coping (the house roof bands' place). */
body.shop-theme-b #screen-shop .shop-panel {
  /* (the house's sky header, min-height 421px, and its short-screen paper are
     both outranked here; the short screen only re-sizes the layers, below) */
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='14' viewBox='0 0 28 14'%3E%3Crect width='28' height='14' fill='%233b4677'/%3E%3Cpath d='M0 6.5H28M0 13.5H28M7 0V6.5M21 0V6.5M0 7V13.5M14 7V13.5' stroke='%232a3260' stroke-width='1.3'/%3E%3Cpath d='M1 1.2H6M15 1.2H20M8 8.2H13M22 8.2H27' stroke='%234a5688' stroke-width='0.9'/%3E%3C/svg%3E") left 0 bottom -4px / 28px 15px repeat-x,
    linear-gradient(#14112e, #14112e) left 0 bottom -4px / 100% 16px no-repeat,
    linear-gradient(180deg, #fff0dc 0%, #ffd6b8 100%) left 0 bottom -4px / 100% 20px no-repeat,
    /* the city at the roof's foot, standing on the parapet's lit coping (14h:
       BACK no longer stands in front of it): it fills what the stage column
       leaves on a big screen and frames the final card */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='120' viewBox='0 0 300 120'%3E%3Cpath fill='%232b2152' d='M0 40h30v80h-30zM32 22h20v98h-20zM41.2 8h1.6v14h-1.6zM54 48h44v72h-44zM100 30h26v90h-26zM105.2 22h15.6v8h-15.6zM128 52h40v68h-40zM170 34h24v86h-24zM176 22h12v8h-12zM177.2 30h1.4v4h-1.4zM185.4 30h1.4v4h-1.4zM196 44h36v76h-36zM234 26h22v94h-22zM244.2 12h1.6v14h-1.6zM258 46h42v74h-42z'/%3E%3Cpath fill='%23ffd98a' fill-opacity='0.45' d='M3 53h2.4v3.4h-2.4zM21 69h2.4v3.4h-2.4zM41 107h2.4v3.4h-2.4zM93 61h2.4v3.4h-2.4zM81 85h2.4v3.4h-2.4zM109 67h2.4v3.4h-2.4zM109 107h2.4v3.4h-2.4zM149 65h2.4v3.4h-2.4zM179 79h2.4v3.4h-2.4zM199 105h2.4v3.4h-2.4zM243 39h2.4v3.4h-2.4zM279 75h2.4v3.4h-2.4zM267 99h2.4v3.4h-2.4zM279 107h2.4v3.4h-2.4z'/%3E%3Cpath fill='%231a1336' d='M150 72h30v48h-30zM182 54h20v66h-20zM191.2 40h1.6v14h-1.6zM204 80h44v40h-44zM250 62h26v58h-26zM255.2 54h15.6v8h-15.6zM278 84h40v36h-40zM20 66h24v54h-24zM26 54h12v8h-12zM27.2 62h1.4v4h-1.4zM35.4 62h1.4v4h-1.4zM46 76h36v44h-36zM84 58h22v62h-22zM94.2 44h1.6v14h-1.6zM108 78h42v42h-42z'/%3E%3Cpath fill='%23ffe6b0' fill-opacity='0.85' d='M153 101h2.6v3.4h-2.6zM171 109h2.6v3.4h-2.6zM185 67h2.6v3.4h-2.6zM197 75h2.6v3.4h-2.6zM185 83h2.6v3.4h-2.6zM185 99h2.6v3.4h-2.6zM191 107h2.6v3.4h-2.6zM213 85h2.6v3.4h-2.6zM243 85h2.6v3.4h-2.6zM219 109h2.6v3.4h-2.6zM237 109h2.6v3.4h-2.6zM265 83h2.6v3.4h-2.6zM253 107h2.6v3.4h-2.6zM29 111h2.6v3.4h-2.6zM87 95h2.6v3.4h-2.6zM111 91h2.6v3.4h-2.6z'/%3E%3Cpath fill='%23ffd98a' fill-opacity='0.85' d='M185 75h2.6v3.4h-2.6zM197 99h2.6v3.4h-2.6zM185 107h2.6v3.4h-2.6zM237 85h2.6v3.4h-2.6zM207 93h2.6v3.4h-2.6zM231 93h2.6v3.4h-2.6zM253 67h2.6v3.4h-2.6zM265 107h2.6v3.4h-2.6zM281 89h2.6v3.4h-2.6zM35 71h2.6v3.4h-2.6zM49 89h2.6v3.4h-2.6zM73 113h2.6v3.4h-2.6zM99 111h2.6v3.4h-2.6zM141 83h2.6v3.4h-2.6z'/%3E%3C/svg%3E") left 0 bottom 16px / auto 150px repeat-x,
    radial-gradient(80% 230px at 50% 100%, rgba(255,176,150,0.20) 0%, rgba(255,176,150,0) 100%),
    radial-gradient(1.5px 1.5px at 31% 38px, rgba(255,246,232,0.85), rgba(255,246,232,0)),
    radial-gradient(1.2px 1.2px at 44% 22px, rgba(255,246,232,0.6), rgba(255,246,232,0)),
    radial-gradient(1.6px 1.6px at 57% 44px, rgba(255,246,232,0.75), rgba(255,246,232,0)),
    radial-gradient(1.2px 1.2px at 66% 26px, rgba(255,246,232,0.55), rgba(255,246,232,0)),
    radial-gradient(1.4px 1.4px at 74% 50px, rgba(255,246,232,0.7), rgba(255,246,232,0)),
    linear-gradient(180deg, rgba(255,196,120,0.17) 0, rgba(255,196,120,0) 70px),
    linear-gradient(180deg, #2b2a6e 0%, #221f56 22%, #1c1946 62%, #23173a 100%);
  background-color: #1f1c4a;
  box-shadow: 0 8px 0 rgba(4,4,20,0.62), inset 0 0 0 1.5px rgba(255,214,160,0.10);
}
@media (max-height: 420px) {
  body.shop-theme-b #screen-shop .shop-panel {        /* 3px border, 12px foot */
    background-size: 16px 8px, 100% 9px, 100% 11px, auto 92px, auto, auto, auto, auto, auto, auto, auto, auto;
    background-position: left 0 bottom -3px, left 0 bottom -3px, left 0 bottom -3px, left 0 bottom 8px, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0;
  }
}
body.shop-theme-b #screen-shop .shop-panel::before,
body.shop-theme-b #screen-shop .shop-panel::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0;
  pointer-events: none;
}
body.shop-theme-b #screen-shop .shop-panel::before {           /* the string */
  z-index: -1;
  height: 22px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='22' viewBox='0 0 150 22'%3E%3Cpath d='M0 1 Q37.5 11 75 1 Q112.5 11 150 1' fill='none' stroke='%2314112e' stroke-width='1.7'/%3E%3Crect x='17.1' y='4.2' width='3.4' height='3.4' rx='0.8' fill='%2314112e'/%3E%3Cellipse cx='18.75' cy='10.8' rx='2.9' ry='3.7' fill='%23ffd27a' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='17.8' cy='9.3' r='0.95' fill='%23ffffff' fill-opacity='0.9'/%3E%3Crect x='35.8' y='5.4' width='3.4' height='3.4' rx='0.8' fill='%2314112e'/%3E%3Cellipse cx='37.5' cy='12' rx='2.9' ry='3.7' fill='%23fff3d6' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='36.5' cy='10.6' r='0.95' fill='%23ffffff' fill-opacity='0.9'/%3E%3Crect x='54.5' y='4.2' width='3.4' height='3.4' rx='0.8' fill='%2314112e'/%3E%3Cellipse cx='56.25' cy='10.8' rx='2.9' ry='3.7' fill='%23ffd36b' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='55.3' cy='9.3' r='0.95' fill='%23ffffff' fill-opacity='0.9'/%3E%3Crect x='92' y='4.2' width='3.4' height='3.4' rx='0.8' fill='%2314112e'/%3E%3Cellipse cx='93.75' cy='10.8' rx='2.9' ry='3.7' fill='%23ffb8e0' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='92.8' cy='9.3' r='0.95' fill='%23ffffff' fill-opacity='0.9'/%3E%3Crect x='110.8' y='5.4' width='3.4' height='3.4' rx='0.8' fill='%2314112e'/%3E%3Cellipse cx='112.5' cy='12' rx='2.9' ry='3.7' fill='%23ffd27a' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='111.5' cy='10.6' r='0.95' fill='%23ffffff' fill-opacity='0.9'/%3E%3Crect x='129.6' y='4.2' width='3.4' height='3.4' rx='0.8' fill='%2314112e'/%3E%3Cellipse cx='131.25' cy='10.8' rx='2.9' ry='3.7' fill='%23fff3d6' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='130.3' cy='9.3' r='0.95' fill='%23ffffff' fill-opacity='0.9'/%3E%3C/svg%3E") 16px 0 / 150px 22px repeat-x;
}
body.shop-theme-b #screen-shop .shop-panel::after {            /* the halos, breathing */
  z-index: -2;
  height: 34px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='34' viewBox='0 0 150 34'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23ffd27a' stop-opacity='0.62'/%3E%3Cstop offset='0.45' stop-color='%23ffd27a' stop-opacity='0.22'/%3E%3Cstop offset='1' stop-color='%23ffd27a' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='p'%3E%3Cstop offset='0' stop-color='%23ffb8e0' stop-opacity='0.6'/%3E%3Cstop offset='0.45' stop-color='%23ffb8e0' stop-opacity='0.2'/%3E%3Cstop offset='1' stop-color='%23ffb8e0' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='18.75' cy='10.8' r='11' fill='url%28%23g%29'/%3E%3Ccircle cx='37.5' cy='12' r='11' fill='url%28%23g%29'/%3E%3Ccircle cx='56.25' cy='10.8' r='11' fill='url%28%23g%29'/%3E%3Ccircle cx='93.75' cy='10.8' r='11' fill='url%28%23p%29'/%3E%3Ccircle cx='112.5' cy='12' r='11' fill='url%28%23g%29'/%3E%3Ccircle cx='131.25' cy='10.8' r='11' fill='url%28%23g%29'/%3E%3C/svg%3E") 16px 0 / 150px 34px repeat-x;
  will-change: opacity;
  animation: p14gBBulbs 3.8s ease-in-out infinite alternate;
}
@media (max-height: 420px) {
  body.shop-theme-b #screen-shop .shop-panel::before { height: 16px; background-size: 112px 16.4px; background-position: 12px 0; }
  body.shop-theme-b #screen-shop .shop-panel::after { height: 26px; background-size: 112px 25.4px; background-position: 12px 0; }
}

/* the title: a lit sign - bulb gold in the dark outline, a warm glow round it */
body.shop-theme-b #screen-shop .shop-head h2 {
  color: #ffd66e;
  filter: drop-shadow(0 0.08em 0 rgba(4,4,20,0.8)) drop-shadow(0 0 0.32em rgba(255,190,90,0.55));
}
/* the chrome inside the night: cream, lamp-lit, on a dark ledge */
body.shop-theme-b #screen-shop .shop-wallet .pill { background: #fff4dc; box-shadow: 0 3px 0 var(--sky-ledge); }
/* (14h) the back arrow is the way out now (no BACK at the foot). (pass 31) Its lamp-lit
   cream look - and the sound buttons' cream while the shop is up - is every button's
   now: see "PASS 31: ONE BUTTON SYSTEM" at the end of this file (the blurred halo is gone). */

/* ---- the stage: a window onto the roof -------------------------------------- */
body.shop-theme-b #screen-shop .rar-common    { --p14gb-tl: #e2d9cc; }
body.shop-theme-b #screen-shop .rar-rare      { --p14gb-tl: #9cc0ff; }
body.shop-theme-b #screen-shop .rar-epic      { --p14gb-tl: #ee9ff8; }
body.shop-theme-b #screen-shop .rar-legendary { --p14gb-tl: #ffd36b; }
body.shop-theme-b #screen-shop .rar-mythic    { --p14gb-tl: #79e7f1; }
body.shop-theme-b #screen-shop .shop-stage {
  --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23ffe8c4' stop-opacity='0.42'/%3E%3Cstop offset='0.5' stop-color='%23ffe8c4' stop-opacity='0.2'/%3E%3Cstop offset='1' stop-color='%23ffe8c4' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E");
  background: linear-gradient(180deg, #392e68 0%, #2b2254 100%);
  background-color: #2f255a;
  box-shadow: inset 0 0 0 3px var(--ink), inset 0 0 0 4.5px rgba(255,214,160,0.12), 0 4px 0 var(--sky-ledge);
}
body.shop-theme-b #screen-shop .shop-stage.rar-rare      { --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%236096ff' stop-opacity='0.55'/%3E%3Cstop offset='0.5' stop-color='%236096ff' stop-opacity='0.2'/%3E%3Cstop offset='1' stop-color='%236096ff' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E"); }
body.shop-theme-b #screen-shop .shop-stage.rar-epic      { --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23d36be6' stop-opacity='0.5'/%3E%3Cstop offset='0.5' stop-color='%23d36be6' stop-opacity='0.2'/%3E%3Cstop offset='1' stop-color='%23d36be6' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E"); }
body.shop-theme-b #screen-shop .shop-stage.rar-legendary { --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23ffc654' stop-opacity='0.6'/%3E%3Cstop offset='0.5' stop-color='%23ffc654' stop-opacity='0.3'/%3E%3Cstop offset='1' stop-color='%23ffc654' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E"); }
body.shop-theme-b #screen-shop .shop-stage.rar-mythic    { --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%2319b8c9' stop-opacity='0.55'/%3E%3Cstop offset='0.5' stop-color='%2319b8c9' stop-opacity='0.2'/%3E%3Cstop offset='1' stop-color='%2319b8c9' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E"); }
body.shop-theme-b #screen-shop .shop-stage.mystery       { --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%239a6eff' stop-opacity='0.62'/%3E%3Cstop offset='0.5' stop-color='%239a6eff' stop-opacity='0.3'/%3E%3Cstop offset='1' stop-color='%239a6eff' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E"); }
body.shop-theme-b #screen-shop .shop-stage.mystery.p14-final { --p14gb-glow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23ffc846' stop-opacity='0.6'/%3E%3Cstop offset='0.5' stop-color='%23ffc846' stop-opacity='0.3'/%3E%3Cstop offset='1' stop-color='%23ffc846' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cellipse cx='130' cy='172' rx='118' ry='112' fill='url%28%23g%29'/%3E%3C/svg%3E"); }
/* the scene is drawn in the canvas's own 260 x 300 box, bottom-anchored at
   100% width as the drawing is, so the coping is always under his feet; a
   taller window gets more night sky (and the moon) above it */
body.shop-theme-b #screen-shop .stage-cv {
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='60' viewBox='0 0 260 60'%3E%3Cdefs%3E%3ClinearGradient id='c' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%237d64a6'/%3E%3Cstop offset='1' stop-color='%23b896c4'/%3E%3C/linearGradient%3E%3CradialGradient id='h'%3E%3Cstop offset='0' stop-color='%23ffd27a' stop-opacity='0.55'/%3E%3Cstop offset='1' stop-color='%23ffd27a' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect y='14' width='260' height='46' fill='%2314112e'/%3E%3Crect y='15.5' width='260' height='12' fill='url%28%23c%29'/%3E%3Crect y='27.5' width='260' height='3.5' fill='%23ffd6b8'/%3E%3Crect y='31' width='260' height='29' fill='%233b4677'/%3E%3Cpath d='M0 38.5H260M0 46H260M0 53.5H260' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M9 31V38.5M18 38.5V46M9 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M36 31V38.5M27 38.5V46M36 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M45 31V38.5M54 38.5V46M45 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M72 31V38.5M63 38.5V46M72 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M81 31V38.5M90 38.5V46M81 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M108 31V38.5M99 38.5V46M108 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M117 31V38.5M126 38.5V46M117 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M144 31V38.5M135 38.5V46M144 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M153 31V38.5M162 38.5V46M153 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M180 31V38.5M171 38.5V46M180 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M189 31V38.5M198 38.5V46M189 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M216 31V38.5M207 38.5V46M216 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M225 31V38.5M234 38.5V46M225 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M252 31V38.5M243 38.5V46M252 46V53.5' stroke='%232a3260' stroke-width='1.2'/%3E%3Cpath d='M0 31.5 Q13 35 26 31.5 Q39 35 52 31.5 Q65 35 78 31.5 Q91 35 104 31.5 Q117 35 130 31.5 Q143 35 156 31.5 Q169 35 182 31.5 Q195 35 208 31.5 Q221 35 234 31.5 Q247 35 260 31.5' fill='none' stroke='%2314112e' stroke-width='0.9'/%3E%3Ccircle cx='13' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='13' cy='35.4' r='1.7' fill='%23ffd27a' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='39' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='39' cy='35.4' r='1.7' fill='%23fff3d6' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='65' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='65' cy='35.4' r='1.7' fill='%23ffd36b' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='91' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='91' cy='35.4' r='1.7' fill='%23ffb8e0' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='117' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='117' cy='35.4' r='1.7' fill='%23ffd27a' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='143' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='143' cy='35.4' r='1.7' fill='%23fff3d6' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='169' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='169' cy='35.4' r='1.7' fill='%23ffd36b' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='195' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='195' cy='35.4' r='1.7' fill='%23ffb8e0' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='221' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='221' cy='35.4' r='1.7' fill='%23ffd27a' stroke='%2314112e' stroke-width='0.6'/%3E%3Ccircle cx='247' cy='35' r='6' fill='url%28%23h%29'/%3E%3Ccircle cx='247' cy='35.4' r='1.7' fill='%23fff3d6' stroke='%2314112e' stroke-width='0.6'/%3E%3C/svg%3E") center bottom / 100% auto no-repeat,
    var(--p14gb-glow) center bottom / 100% auto no-repeat,
    radial-gradient(circle 10px at 78% 9%, #f6f8ff 0 90%, rgba(246,248,255,0) 100%),
    radial-gradient(circle 30px at 78% 9%, rgba(168,192,244,0.5) 0%, rgba(168,192,244,0) 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300' viewBox='0 0 260 300'%3E%3Cdefs%3E%3ClinearGradient id='s' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23262560' stop-opacity='0'/%3E%3Cstop offset='0.2' stop-color='%232c2968'/%3E%3Cstop offset='0.36' stop-color='%233d3479'/%3E%3Cstop offset='0.56' stop-color='%236a4c94'/%3E%3Cstop offset='0.70' stop-color='%239b62a0'/%3E%3Cstop offset='0.78' stop-color='%23dc86a4'/%3E%3Cstop offset='0.84' stop-color='%23ffb39a'/%3E%3Cstop offset='1' stop-color='%23ffd6ac'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='260' height='300' fill='url%28%23s%29'/%3E%3Ccircle cx='69.1' cy='31.5' r='1' fill='%23fff6e8' fill-opacity='0.7'/%3E%3Cpath d='M66.1 31.5H72.1M69.1 28.5V34.5' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='182.5' cy='23.5' r='1' fill='%23fff6e8' fill-opacity='0.5'/%3E%3Cpath d='M179.5 23.5H185.5M182.5 20.5V26.5' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='176.5' cy='0.6' r='1.2' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M172.9 0.6H180.1M176.5 -3V4.2' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='216.4' cy='21' r='1' fill='%23fff6e8' fill-opacity='0.6'/%3E%3Cpath d='M213.4 21H219.4M216.4 18V24' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='130.2' cy='57.9' r='0.8' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Cpath d='M127.8 57.9H132.6M130.2 55.5V60.3' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='192' cy='105.5' r='0.5' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Ccircle cx='13.5' cy='34.2' r='0.6' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Ccircle cx='211.1' cy='80' r='1' fill='%23fff6e8' fill-opacity='0.9'/%3E%3Cpath d='M208.1 80H214.1M211.1 77V83' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Ccircle cx='141.9' cy='8.3' r='1' fill='%23fff6e8' fill-opacity='0.8'/%3E%3Cpath d='M138.9 8.3H144.9M141.9 5.3V11.3' stroke='%23fff6e8' stroke-opacity='0.45' stroke-width='0.6'/%3E%3Cg fill='none' stroke='%237762a8'%3E%3Ccircle cx='62' cy='196' r='22' stroke-width='1.4'/%3E%3Ccircle cx='62' cy='196' r='18' stroke-width='0.7'/%3E%3Cpath d='M62 196L84 196' stroke-width='0.6'/%3E%3Cpath d='M62 196L77.6 211.6' stroke-width='0.6'/%3E%3Cpath d='M62 196L62 218' stroke-width='0.6'/%3E%3Cpath d='M62 196L46.4 211.6' stroke-width='0.6'/%3E%3Cpath d='M62 196L40 196' stroke-width='0.6'/%3E%3Cpath d='M62 196L46.4 180.4' stroke-width='0.6'/%3E%3Cpath d='M62 196L62 174' stroke-width='0.6'/%3E%3Cpath d='M62 196L77.6 180.4' stroke-width='0.6'/%3E%3Cpath d='M62 196L48.4 227.2M62 196L75.6 227.2' stroke-width='1.6'/%3E%3C/g%3E%3Ccircle cx='83' cy='204' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='83' cy='202.5' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='75.2' cy='215.1' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='75.2' cy='213.6' r='0.9' fill='%23ffb8e0'/%3E%3Ccircle cx='62.3' cy='219.5' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='62.3' cy='218' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='49.3' cy='215.5' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='49.3' cy='214' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='41.2' cy='204.6' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='41.2' cy='203.1' r='0.9' fill='%23ffb8e0'/%3E%3Ccircle cx='41' cy='191' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='41' cy='189.5' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='48.8' cy='179.9' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='48.8' cy='178.4' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='61.7' cy='175.5' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='61.7' cy='174' r='0.9' fill='%23ffb8e0'/%3E%3Ccircle cx='74.7' cy='179.5' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='74.7' cy='178' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='82.8' cy='190.4' r='1.8' fill='%237762a8'/%3E%3Ccircle cx='82.8' cy='188.9' r='0.9' fill='%23ffd27a'/%3E%3Ccircle cx='62' cy='196' r='2' fill='%237762a8'/%3E%3Cpath fill='%237762a8' d='M0 214h22v48h-22zM24 200h16v62h-16zM31.2 186h1.6v14h-1.6zM92 206h30v56h-30zM124 196h18v66h-18zM127.6 188h10.8v8h-10.8zM144 210h26v52h-26zM172 192h20v70h-20zM181.2 178h1.6v14h-1.6zM194 204h34v58h-34zM230 198h30v64h-30zM237.5 186h15v8h-15zM239 194h1.4v4h-1.4zM249.6 194h1.4v4h-1.4z'/%3E%3Cpath fill='%23ffe6b0' fill-opacity='0.7' d='M13 233h2v2.8h-2zM3 240h2v2.8h-2zM13 254h2v2.8h-2zM115 218h2v2.8h-2zM100 232h2v2.8h-2zM110 253h2v2.8h-2zM132 208h2v2.8h-2zM127 215h2v2.8h-2zM175 197h2v2.8h-2zM175 218h2v2.8h-2zM175 239h2v2.8h-2zM175 253h2v2.8h-2zM207 209h2v2.8h-2zM197 216h2v2.8h-2zM217 223h2v2.8h-2zM197 230h2v2.8h-2zM207 230h2v2.8h-2zM248 245h2v2.8h-2z'/%3E%3Cpath fill='%234b3874' d='M0 226h30v36h-30zM32 236h24v26h-24zM58 222h40v40h-40zM66 214h24v8h-24zM100 232h22v30h-22zM124 228h34v34h-34zM160 238h26v24h-26zM188 224h38v38h-38zM197.5 212h19v8h-19zM199.4 220h1.4v4h-1.4zM213.2 220h1.4v4h-1.4zM228 232h32v30h-32z'/%3E%3Cpath fill='%23ffd98a' fill-opacity='0.95' d='M21 231h2.4v3h-2.4zM21 238h2.4v3h-2.4zM35 248h2.4v3h-2.4zM91 234h2.4v3h-2.4zM61 241h2.4v3h-2.4zM91 248h2.4v3h-2.4zM133 240h2.4v3h-2.4zM145 254h2.4v3h-2.4zM163 243h2.4v3h-2.4zM237 237h2.4v3h-2.4zM255 237h2.4v3h-2.4zM231 244h2.4v3h-2.4zM231 251h2.4v3h-2.4z'/%3E%3Cpath fill='%23ffe0a0' fill-opacity='0.95' d='M41 241h2.4v3h-2.4zM73 227h2.4v3h-2.4zM67 241h2.4v3h-2.4zM151 233h2.4v3h-2.4zM139 240h2.4v3h-2.4zM139 247h2.4v3h-2.4zM127 254h2.4v3h-2.4zM163 250h2.4v3h-2.4zM197 236h2.4v3h-2.4zM209 236h2.4v3h-2.4zM191 250h2.4v3h-2.4zM197 250h2.4v3h-2.4zM249 244h2.4v3h-2.4z'/%3E%3Cpath fill='%23ffe6b0' fill-opacity='0.95' d='M47 248h2.4v3h-2.4zM47 255h2.4v3h-2.4zM61 227h2.4v3h-2.4zM85 234h2.4v3h-2.4zM79 248h2.4v3h-2.4zM127 240h2.4v3h-2.4zM145 247h2.4v3h-2.4zM181 250h2.4v3h-2.4zM197 229h2.4v3h-2.4zM221 229h2.4v3h-2.4zM215 243h2.4v3h-2.4z'/%3E%3C/svg%3E") center bottom / 100% auto no-repeat,
    radial-gradient(1.6px 1.6px at 16% 6%, rgba(255,246,232,0.9), rgba(255,246,232,0)),
    radial-gradient(1.3px 1.3px at 40% 16%, rgba(255,246,232,0.7), rgba(255,246,232,0)),
    radial-gradient(1.4px 1.4px at 60% 4%, rgba(255,246,232,0.75), rgba(255,246,232,0)),
    radial-gradient(1.2px 1.2px at 28% 26%, rgba(255,246,232,0.55), rgba(255,246,232,0)),
    radial-gradient(1.5px 1.5px at 88% 24%, rgba(255,246,232,0.7), rgba(255,246,232,0)),
    radial-gradient(1.2px 1.2px at 9% 34%, rgba(255,246,232,0.5), rgba(255,246,232,0)),
    radial-gradient(1.3px 1.3px at 52% 31%, rgba(255,246,232,0.6), rgba(255,246,232,0)),
    radial-gradient(1.2px 1.2px at 70% 40%, rgba(255,246,232,0.45), rgba(255,246,232,0)),
    /* the sky above the drawing (a tall window): deep at the top, the
       drawing's own #262560 long before its edge, so there is no seam */
    linear-gradient(180deg, #121339 0px, #1c1c4d 90px, #262560 210px);
  background-color: #262560;
}
body.shop-theme-b #screen-shop .stage-tier { color: var(--p14gb-tl, #e2d9cc); }
body.shop-theme-b #screen-shop .stage-name { color: #fff1d6; }
body.shop-theme-b #screen-shop .stage-info .p14-hint { color: #cbb8ff; }
body.shop-theme-b #screen-shop .shop-stage.p14-final .p14-hint { color: #ffd27a; }
body.shop-theme-b #screen-shop .stage-pips i { background: rgba(255,255,255,0.10); box-shadow: 0 0 0 2px rgba(255,236,200,0.38) inset; }
body.shop-theme-b #screen-shop .stage-pips i.on { background: #7a57e8; box-shadow: 0 0 0 2px #c9b6ff inset; }

/* ---- the buttons: (pass 31) the one button system, at the end of this file ---- */

/* ---- the rack: windows ------------------------------------------------------ */
body.shop-theme-b #screen-shop .skin-grid {
  scrollbar-color: rgba(255,214,150,0.42) rgba(255,255,255,0.05);
}
body.shop-theme-b #screen-shop .shop-sec-title { color: #ffe7bf; }
body.shop-theme-b #screen-shop .shop-sec-count { color: #a99fd6; }
body.shop-theme-b #screen-shop .shop-sec-count b { color: #fff1d6; }
body.shop-theme-b #screen-shop .shop-sec-count.full,
body.shop-theme-b #screen-shop .shop-sec-count.full b { color: #ffd27a; }

/* a lit window: the plum frame (the intro's winFrame), the warm pane with
   amber curtains drawn back, the name on the sill */
body.shop-theme-b #screen-shop .skin-tile:not(.p14-final) {
  background: linear-gradient(180deg, #4b3b7a 0%, #3a2c5a 34%, #31254f 100%);
  background-color: #3a2c5a;
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 3.5px rgba(255,214,160,0.12), 0 3px 0 var(--sky-ledge);
}
body.shop-theme-b #screen-shop .skin-tile:not(.p14-final) .tile-cv {
  border-radius: 9px 9px 5px 5px;
  background:
    radial-gradient(80% 62% at 50% 46%, rgba(255,250,232,0.9) 0%, rgba(255,250,232,0) 70%),
    linear-gradient(90deg, rgba(226,150,80,0.34) 0%, rgba(226,150,80,0) 15%, rgba(226,150,80,0) 85%, rgba(226,150,80,0.34) 100%),
    linear-gradient(180deg, #ffe9b8 0%, #ffd98a 74%, #efbd68 100%);
  background-color: #ffd98a;
  box-shadow: inset 0 0 0 1.5px rgba(20,17,46,0.6), inset 0 5px 0 rgba(20,17,46,0.16);
}
body.shop-theme-b #screen-shop .skin-tile:not(.p14-final) .tile-name { color: #fff1d6; }
/* an unlit window: a portal skin not found yet - moonlit glass, a violet glow */
body.shop-theme-b #screen-shop .skin-tile.mystery:not(.p14-final) {
  background: linear-gradient(180deg, #30347a 0%, #252862 38%, #1f2154 100%);
  background-color: #252862;
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 3.5px rgba(150,130,255,0.30), 0 3px 0 var(--sky-ledge);
}
body.shop-theme-b #screen-shop .skin-tile.mystery:not(.p14-final) .tile-cv {
  background:
    radial-gradient(62% 48% at 50% 58%, rgba(150,110,255,0.46) 0%, rgba(40,30,110,0) 72%),
    linear-gradient(128deg, rgba(190,200,255,0) 0%, rgba(190,200,255,0) 16%, rgba(190,200,255,0.07) 16%, rgba(190,200,255,0.07) 27%, rgba(190,200,255,0) 27%),
    linear-gradient(180deg, #1d2050 0%, #2a2468 100%);
  background-color: #222460;
  box-shadow: inset 0 0 0 1.5px rgba(10,8,30,0.7), inset 0 5px 0 rgba(10,8,30,0.25);
}
body.shop-theme-b #screen-shop .skin-tile.mystery:not(.p14-final) .tile-name { color: #d8ccff; }
/* the one he wears: the green ring inside the ink one */
body.shop-theme-b #screen-shop .skin-tile.equipped:not(.selected):not(.p14-final) {
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 4.5px #3fbf6a, 0 3px 0 var(--sky-ledge);
}
/* the one on the stage: the brightest window - PLAY's yellow frame, glowing */
body.shop-theme-b #screen-shop .skin-tile.selected:not(.p14-final) {
  background: linear-gradient(180deg, #ffe27e 0%, #ffc93c 42%, #f4b42a 100%);
  background-color: #ffc93c;
  box-shadow: inset 0 0 0 3px var(--ink), 0 5px 0 var(--sky-ledge), 0 0 18px 3px rgba(255,201,60,0.42);
}
body.shop-theme-b #screen-shop .skin-tile.selected:not(.p14-final) .tile-name { color: var(--ink); }
/* ...its pane turned up (near white at the heart) inside an ink frame, so the
   window and the yellow frame stay two things */
body.shop-theme-b #screen-shop .skin-tile.selected:not(.p14-final):not(.mystery) .tile-cv {
  background:
    radial-gradient(80% 62% at 50% 46%, #ffffff 0%, rgba(255,255,255,0) 72%),
    linear-gradient(90deg, rgba(226,150,80,0.22) 0%, rgba(226,150,80,0) 14%, rgba(226,150,80,0) 86%, rgba(226,150,80,0.22) 100%),
    linear-gradient(180deg, #fff7e0 0%, #ffe8b0 78%, #f9d488 100%);
  background-color: #ffe8b0;
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 5px 0 rgba(20,17,46,0.14);
}
body.shop-theme-b #screen-shop .skin-tile.selected.mystery:not(.p14-final) .tile-cv {
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 5px 0 rgba(10,8,30,0.25);
}
body.shop-theme-b #screen-shop .skin-grid .p14-kept-note {
  background: rgba(255,210,122,0.12);
  box-shadow: 0 0 0 1.5px rgba(255,210,122,0.5) inset;
  color: #fff1d6;
}

/* ---- the final card: a lit marquee at the end of the rack ---------------------
   The shared fix trims the band to the card plus a glow margin; here it is a
   framed spotlight in the final portal's plum and gold, ringed with bulbs. */
body.shop-theme-b #screen-shop .skin-grid .p14-fin-row {
  background:
    radial-gradient(52% 46% at 50% 55%, rgba(255,200,96,0.36) 0%, rgba(255,200,96,0) 72%),
    radial-gradient(120% 100% at 50% 28%, #3b2152 0%, #26153c 52%, #140c26 100%);
  background-color: #1c1030;
  box-shadow: inset 0 0 0 2px var(--ink), inset 0 0 0 4px rgba(255,210,122,0.6), inset 0 0 0 5.5px var(--ink),
              0 3px 0 var(--sky-ledge), 0 0 22px 2px rgba(255,200,110,0.30);
}
body.shop-theme-b #screen-shop .skin-grid .p14-fin-row::before {
  content: "";
  position: absolute; z-index: 0; inset: 8px;
  padding: 13px 0;
  pointer-events: none;
  background-image:
    radial-gradient(circle at 50% 50%, #fff6d8 0 1.7px, rgba(255,214,120,0.7) 2.3px, rgba(255,214,120,0) 4.6px),
    radial-gradient(circle at 50% 50%, #fff6d8 0 1.7px, rgba(255,214,120,0.7) 2.3px, rgba(255,214,120,0) 4.6px),
    radial-gradient(circle at 50% 50%, #fff6d8 0 1.7px, rgba(255,214,120,0.7) 2.3px, rgba(255,214,120,0) 4.6px),
    radial-gradient(circle at 50% 50%, #fff6d8 0 1.7px, rgba(255,214,120,0.7) 2.3px, rgba(255,214,120,0) 4.6px);
  background-position: left top, left bottom, left top, right top;
  background-size: 19px 10px, 19px 10px, 10px 19px, 10px 19px;
  background-repeat: space no-repeat, space no-repeat, no-repeat space, no-repeat space;
  background-origin: padding-box, padding-box, content-box, content-box;
  background-clip: padding-box, padding-box, content-box, content-box;
}

/* ---- motion: three slow things, opacity only ---------------------------------- */
@keyframes p14gBTwinkle { 0% { opacity: 0.25; } 100% { opacity: 1; } }
@keyframes p14gBBulbs   { 0% { opacity: 0.6; } 100% { opacity: 1; } }
@keyframes p14gBWin {
  0%   { opacity: 1; }
  28%  { opacity: 0.15; }
  44%  { opacity: 1; }
  71%  { opacity: 0.45; }
  86%  { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  body.shop-theme-b #screen-shop .p14g-b-tw,
  body.shop-theme-b #screen-shop .p14g-b-wins,
  body.shop-theme-b #screen-shop .shop-panel::after { animation: none; opacity: 0.9; }
}

/* ============ PASS 18: THE FREE BUTTON FIRST (Poki's reward rules) ============
   "standard and reward options appear simultaneously", "standard button
   equal or larger size than reward button", "standard button positioned
   next to or above reward button" (developers.poki.com/guide/requirements-quality)
   Both offers stacked the reward FIRST in a flex column, so the free one sat
   under it on every screen, and it dropped in 0.12 s later. The DOM keeps its
   order (UI and the keys address the buttons by id); only the drawn order and
   the entrance change. Sizes, colours and the fuse are untouched. */

/* the revive offer: "tap to continue playing" above REVIVE (the section holds
   only the two buttons; its heart ::before is display: none) */
#screen-revive .revive-continue { order: -1; }
/* ...and both land together (it waited 0.12 s for REVIVE) */
#screen-revive .revive-continue.entering { animation-delay: 0s; }

/* the portal offer: NO THANKS above GET IT! (the ribbon, tier, name and tag
   keep their places above both) */
#screen-portal .p14-no { order: 1; }
#screen-portal .p14-get { order: 2; }
/* ...and both drop in at 0.55 s (NO THANKS came 0.12 s after GET IT!) */
#screen-portal .p14-no.entering { animation-delay: 0.55s; }
/* the gaps follow the swap: the gap under the upper button, none under the last */
#screen-portal.p14-short .p14-no { margin-bottom: 10px; }
#screen-portal.p14-short .p14-get { margin-bottom: 0; }
#screen-portal.p14-band .p14-no { margin-bottom: 12px; }
#screen-portal.p14-band .p14-get { margin-bottom: 0; }

/* ============ PASS 19: A PLAINER GET IT!, THE FUSE ON NEITHER BUTTON ============
   The user's decision (2026-10-02): "style GET IT the same as NO THANKS (same
   size, shape, weight, font, colour family and motion - no bigger, no extra
   glow/pulse/shine on GET IT)", the free button still above it, both landing
   together (pass 18); the film mark and the AD stamp stay; the skin's
   showcase stays. Poki: "Overselling the rewarded button or delaying the
   alternative erodes trust."
   GET IT! was violet with a gold rim, a sweeping shine and a glow in the
   skin's colour that breathed and grew as the fuse burned, at 20px. Now it is
   NO THANKS's button: white, ink lettering, 17px (15px on a phone on its
   side), the same rim, ledge, hover and drop-in - and nothing else.
   The fuse burned ON NO THANKS (a blue fill with a spark at its edge, the
   button breathing over the last 1.5 s) and GET IT!'s glow grew with it. It
   now burns on a thin bar of its own between the two buttons, the same blue,
   the same spark, at the same pace (--p14-p, which UI.p14Paint also sets on
   the screen); neither button moves or lights up with it. The picture's
   dimming and the ticks are as they were. */

/* GET IT!: NO THANKS's look (.btn.btn-secondary + #screen-portal .p14-no) */
#screen-portal .p14-get {
  --sky-lc: var(--sky-ledge);
  --sky-shine: inset 0 0 0 transparent;
  min-height: 58px;
  font-size: 17px;
  letter-spacing: 0.06em;
  color: var(--ink);
  background-color: #ffffff;
  -webkit-text-stroke: 0;
  box-shadow: var(--sky-shine), 0 var(--sky-lh) 0 var(--sky-lc);
}
#screen-portal .p14-get:hover:not(:disabled) { background-color: #fffaf4; }
#screen-portal .p14-get::before,
#screen-portal .p14-get.p14-hot::before,
#screen-portal .p14-get.loading::before { content: none; animation: none; }
#screen-portal .p14-get .rv-ico { fill: var(--ink); stroke: var(--ink); }
#screen-portal.p14-short .p14-get { min-height: 50px; font-size: 15px; }

/* NO THANKS: no longer breathes over the last 1.5 s (the fill and the spark
   are gone from it: index.html) */
#screen-portal .p14-no.p14-hot:not(.entering) { animation: none; }

/* the fuse: between the two buttons, as far from each (NO THANKS's gap above
   it, the same gap under it) */
#screen-portal .p19-fuse {
  order: 2;
  position: relative;
  flex: 0 0 auto;
  width: var(--p14-bw, min(340px, 100%));
  max-width: 100%;
  height: 10px;
  margin: 0 0 12px;
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  box-sizing: border-box;
  pointer-events: none;
  animation: p14Fade 0.42s ease-out 0.55s both;     /* in with the buttons */
}
#screen-portal .p14-get { order: 3; }
#screen-portal.p14-short .p19-fuse { height: 8px; margin-bottom: 10px; }
#screen-portal.p14-band .p19-fuse { margin-bottom: 12px; }
#screen-portal .p19-fuse-fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  width: calc(var(--p14-p, 0) * 100%);
  border-radius: inherit;
  background: linear-gradient(180deg, #8ae6ff 0%, #34b0ff 55%, #2356c4 100%);
  transition: opacity 0.2s ease;
}
/* the lit end: the same white-cyan star and embers NO THANKS carried */
#screen-portal .p19-fuse-spark {
  position: absolute; top: 50%;
  left: calc(var(--p14-p, 0) * 100%);
  width: 24px; height: 24px;
  margin: -12px 0 0 -12px;
  opacity: 0;
  background: radial-gradient(circle, #ffffff 0 14%, rgba(190,245,255,0.95) 26%, rgba(110,220,255,0) 62%);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
#screen-portal .p19-fuse-spark::before,
#screen-portal .p19-fuse-spark::after {
  content: ""; position: absolute; top: 10px; left: 7px;
  width: 4px; height: 4px; border-radius: 50%;
  background: #bff4ff;
}
/* (the run and drain classes stay on NO THANKS, where UI.tickPortal and
   UI.p14Get put them; the fuse is its next sibling) */
#screen-portal .p14-no.p14-run ~ .p19-fuse .p19-fuse-spark { opacity: 1; animation: p14Spark 0.24s steps(2, jump-none) infinite alternate; }
#screen-portal .p14-no.p14-run ~ .p19-fuse .p19-fuse-spark::before { animation: p14Ember 0.5s linear infinite; }
#screen-portal .p14-no.p14-run ~ .p19-fuse .p19-fuse-spark::after { animation: p14Ember 0.5s linear 0.25s infinite; }
#screen-portal .p14-no.p14-drain ~ .p19-fuse .p19-fuse-fill,
#screen-portal .p14-no.p14-drain ~ .p19-fuse .p19-fuse-spark { opacity: 0; }

/* a short upright phone (375x667, 360x640): the fuse bar made the column
   22px taller, and the column is anchored to the bottom while the skin's
   picture stands at fixed proportions - so the flavour line climbed onto the
   pedestal's rim. The same 22px back: both buttons 6px shorter (still alike,
   still 52px to tap), the fuse 3px closer to each, the line 4px closer. */
@media (max-height: 720px) {
  #screen-portal.p14-band .p14-tag { margin-bottom: 10px; }
  #screen-portal.p14-band .p14-get,
  #screen-portal.p14-band .p14-no { min-height: 52px; }
  #screen-portal.p14-band .p14-no { margin-bottom: 9px; }
  #screen-portal.p14-band .p19-fuse { margin-bottom: 9px; }
}

@media (prefers-reduced-motion: reduce) {
  #screen-portal .p19-fuse { animation: none; }
  #screen-portal .p14-no.p14-run ~ .p19-fuse .p19-fuse-spark { animation: none; opacity: 0; }
  #screen-portal .p19-fuse-fill { transition: none; }
}


/* =============================================================================
   PASS 31 (2026-10-04): ONE BUTTON SYSTEM - family A "ROOFTOP SKY"
   The user's pick: "A, but use C's Play button in main menu!". Every button in
   the game is one of three roles in one of three sizes, painted by ONE rule
   from per-element variables; the main menu's PLAY alone is family C's
   cartoon sticker (at the end of this section). Notes: visual\p31\build\build31.md.

   Why this lives here, last, scoped #ui: twenty-odd passes each painted their
   own buttons, at weights from (0,1,0) to (2,3,1). Every rule below is #ui
   (1,x,0) and later than all of them, so it wins without !important; the three
   one-off rules that out-ranked any sane selector (theme B's back arrow and
   its cream sound buttons, the stage's BUY / too-poor fills, presses and
   hovers) are deleted where they stood and point here. Paint only - fill, ink,
   letters, drop, the size tokens - so every screen's placement and fitting
   rules (the offers' columns, the pause grid, the end screen) stay theirs.

   ROLES  primary  sunset gold, white letters in the ink stroke (RESUME, EQUIP,
                   BUY, the end screen's MENU)
          free     the shop's lamp-lit cream, violet letters (NO THANKS, tap to
                   continue, the pause tiles, SKIP, every round icon)
          reward   lavender, violet letters, the violet film mark and AD tag -
                   never bigger or brighter than the free one beside it (Poki)
   SIZES  L pill 60 | M pill / circle 52 | S pill / circle 44 (tokens below,
          stepping with the screen)
   STATES hover (a mouse) a shade lighter | press: sinks by its drop less 1px
          onto a 1px drop | disabled 50% | EQUIPPED mint, sunk flat
   No red; no filters or blurs on any button.
   ============================================================================= */
:root {
  --b-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --b-ink: #231d4a;              /* night violet: every outline, every letter stroke */
  --b-word: #3a2c86;             /* violet letters and glyphs on cream / lavender (10.6:1 on cream) */
  --b-word-soft: #6b6290;        /* the price on a BUY out of reach (5.2:1 on cream) */
  --b-muted: #8a83ad;            /* a sound switched off */
  --b-cream: #fff4dc;            /* free */
  --b-cream-hi: #fffaee;
  --b-lav: #e2d9fb;              /* reward: a shade under the free cream, so the free one is the brightest choice */
  --b-lav-hi: #ece5ff;
  --b-violet: #7a57e8;           /* the AD tag and the film mark only */
  --b-gold: linear-gradient(180deg, #ffe596 0%, #ffcf4a 52%, #f9b935 100%);       /* primary */
  --b-gold-hi: linear-gradient(180deg, #ffeaa8 0%, #ffd65e 52%, #fbc046 100%);
  --b-mint: #e6f7ec;             /* EQUIPPED */
  --b-mint-word: #1e7a45;
  --b-drop: rgba(20, 17, 46, 0.62);
  --b-in-cream: inset 0 2px 0 #ffffff, inset 0 -4px 0 rgba(122, 87, 232, 0.14);
  --b-in-lav: inset 0 2px 0 #ffffff, inset 0 -4px 0 rgba(122, 87, 232, 0.26);
  --b-in-gold: inset 0 3px 0 rgba(255, 255, 255, 0.65), inset 0 -4px 0 rgba(196, 116, 8, 0.32);
  --b-h-l: 60px;  --b-fs-l: clamp(19px, 5vw, 23px);
  --b-h-m: 52px;  --b-fs-m: 16px;
  --b-h-s: 44px;  --b-fs-s: 13px;
}
@media (min-width: 900px) and (min-height: 700px) { :root { --b-h-l: 64px; --b-h-m: 56px; --b-fs-m: 17px; } }
@media (max-height: 620px) { :root { --b-h-l: 52px; --b-h-m: 46px; --b-fs-m: 15px; } }
@media (max-height: 420px) { :root { --b-h-l: 48px; --b-h-m: 44px; } }
@media (max-width: 460px)  { :root { --b-h-s: 40px; --b-fs-s: 12px; } }

/* ---- every button starts free (cream), outline 4 / drop 4; the S ones 3 / 3 ---- */
#ui .btn,
#ui .icon-btn,
#ui .tile-btn,
#ui .hud-skins,
#ui .skip-btn {
  --b-fill: var(--b-cream);
  --b-fill-hi: var(--b-cream-hi);
  --b-text: var(--b-word);
  --b-stroke: 0px;
  --b-in: var(--b-in-cream);
  --b-line: 4px;
  --b-lh: 4px;
  --b-rim: 0 0 0 0 transparent;  /* a light ring outside the ink, on dark backdrops only */
}
#ui .icon-btn, #ui .hud-skins, #ui .skip-btn { --b-line: 3px; --b-lh: 3px; }

/* ---- the roles: variables only ---- */
#ui .btn.btn-primary,
#ui .btn.btn-buy {
  --b-fill: var(--b-gold);
  --b-fill-hi: var(--b-gold-hi);
  --b-text: #ffffff;
  --b-stroke: 0.19em;
  --b-in: var(--b-in-gold);
}
#ui .btn.btn-reward {
  --b-fill: var(--b-lav);
  --b-fill-hi: var(--b-lav-hi);
  --b-text: var(--b-word);
  --b-stroke: 0px;
  --b-in: var(--b-in-lav);
}
/* BUY out of reach: cream, the price soft, and no raised drop (a 1px one, like
   EQUIPPED) so it reads "not yet" rather than a free action - still pressable (it shakes) */
#ui .btn.btn-buy.too-poor {
  --b-fill: var(--b-cream);
  --b-fill-hi: var(--b-cream-hi);
  --b-text: var(--b-word-soft);
  --b-stroke: 0px;
  --b-in: var(--b-in-cream);
  --b-lh: 1px;
}
/* EQUIPPED: a state, not an action - mint, already pressed flat */
#ui .btn.stage-act.is-worn {
  --b-fill: var(--b-mint);
  --b-text: var(--b-mint-word);
  --b-stroke: 0px;
  --b-in: inset 0 2px 0 rgba(255, 255, 255, 0.7);
  --b-lh: 1px;
  transform: translateY(3px);
  opacity: 1;
}
/* No ad to be had: the reward's lavender, flat */
#ui .btn.stage-act.no-ad { --b-lh: 1px; opacity: 1; }
#ui .audio-fixed .icon-btn.muted { --b-text: var(--b-muted); }
/* the darkest backdrops (the shop's night panel, the prologue's black bar): the
   night-violet ink melts into them, so a faint lamp-light ring outside it */
#ui #screen-shop .stage-act,
#ui #screen-shop .shop-x,
body.shop-up #ui .audio-fixed .icon-btn,
body.in-prologue #ui .skip-btn { --b-rim: 0 0 0 2px rgba(255, 244, 220, 0.3); }

/* ---- the paint: ONE rule (the role / state selectors are listed so it keeps
   their weight over the old id-scoped fills) ---- */
#ui .btn,
#ui .icon-btn,
#ui .tile-btn,
#ui .hud-skins,
#ui .skip-btn,
#ui .btn.btn-primary,
#ui .btn.btn-reward,
#ui .btn.btn-buy,
#ui .btn.btn-buy.too-poor,
#ui .btn.stage-act.is-worn,
#ui .btn.stage-act.no-ad {
  font-family: var(--b-font);
  font-weight: 900;
  font-style: normal;
  color: var(--b-text);
  background: var(--b-fill);
  border: var(--b-line) solid var(--b-ink);
  -webkit-text-stroke: var(--b-stroke) var(--b-ink);
  paint-order: stroke fill;
  text-shadow: none;
  box-shadow: var(--b-in), 0 var(--b-lh) 0 var(--b-drop), var(--b-rim);
  filter: none;
  transition: transform .07s ease, box-shadow .07s ease;
}

/* ---- the sizes ---- */
#ui .btn { min-height: var(--b-h-m); font-size: var(--b-fs-m); letter-spacing: 0.05em; border-radius: 999px; }
#ui .btn.btn-big { min-height: var(--b-h-l); font-size: var(--b-fs-l); }
#ui .icon-btn {
  width: var(--b-h-s); height: var(--b-h-s);
  padding: calc(var(--b-h-s) * 0.22);
  border-radius: 50%;
}
#ui .tile-btn {                                    /* beside PLAY: M circles */
  width: var(--b-h-m); height: var(--b-h-m);
  padding: calc(var(--b-h-m) * 0.22);
  border-radius: 50%;
}
#ui .hud-skins { min-height: var(--b-h-s); font-size: var(--b-fs-s); letter-spacing: 0.06em; border-radius: 999px; }
/* the S pill is 40px on a phone: an invisible ring (placed from inside the 3px
   outline) keeps the tap area 44px+ (6 + 34 + 6 = 46) */
#ui .hud-skins::before { content: ""; position: absolute; inset: -6px -2px; }
#ui .hud .pill-cash { min-height: var(--b-h-s); }  /* the pill beside it stands as tall */
#ui .skip-btn {
  height: var(--b-h-s);
  padding: 0 16px;
  font-size: var(--b-fs-s);
  letter-spacing: 0.08em;
  line-height: calc(var(--b-h-s) - 2 * var(--b-line));
  border-radius: 999px;
}
#ui .skip-btn::before { inset: -6px -6px; }        /* the 40px pill's ring: a 46px tap area (50 on a desktop) */
/* the prologue's SKIP: centred in the top letterbox bar (the sum .skip-hint
   uses for the bottom one); in a bar under 50px (a phone on its side) a 32px
   pill, so it never reaches into the picture */
body.in-prologue #ui .skip-btn {
  top: max(calc(4px + var(--safe-t)), calc(max(10vh, (100vh - 130vw) / 2) / 2 - var(--b-h-s) / 2 - 2px));
}
@media (max-height: 500px) {
  body.in-prologue #ui .skip-btn { --b-h-s: 32px; padding: 0 12px; }
  /* its ring reaches down into the picture, as the old one did: 6 + 26 + 14 = 46px */
  body.in-prologue #ui .skip-btn::before { top: -6px; bottom: -14px; }
}

/* ---- the states ---- */
@media (hover: hover) {
  #ui .btn:hover:not(:disabled),
  #ui .icon-btn:hover,
  #ui .tile-btn:hover,
  #ui .hud-skins:hover,
  #ui .skip-btn:hover { background: var(--b-fill-hi); }
}
#ui .btn:active:not(:disabled),
#ui .btn.pressable.pressing,
#ui .icon-btn:active,
#ui .icon-btn.pressable.pressing,
#ui .tile-btn:active,
#ui .tile-btn.pressable.pressing,
#ui .hud-skins:active,
#ui .skip-btn:active {
  transform: translateY(calc(var(--b-lh) - 1px));
  box-shadow: var(--b-in), 0 1px 0 var(--b-drop), var(--b-rim);
  filter: none;
}
#ui .btn:disabled { opacity: 0.5; cursor: default; }

/* ---- what rides on them ---- */
#ui .btn .rv-ico { fill: var(--b-violet); stroke: var(--b-violet); }
#ui .btn .ad-badge {
  color: #ffffff;
  background: var(--b-violet);
  border: 2px solid var(--b-ink);
  border-radius: 999px;
  box-shadow: none;
  padding: 1px 7px;
  transform: none;
  -webkit-text-stroke: 0;
  font-size: 10px;
  font-weight: 900;
  letter-spacing: 0.08em;
  line-height: 1.2;
}
#ui .hud-skins .ico-shirt,
#ui .tile-btn .ico-shirt { fill: var(--pink); stroke: var(--b-ink); }
#ui .tile-btn .ico-lang { color: var(--b-word); }
#ui .audio-fixed .slash-cut { stroke: var(--b-cream); }
#ui .skip-hint { -webkit-text-stroke-color: var(--b-ink); }   /* TAP TO START: lettering, not a button */
/* the revive offer: "tap to continue playing" is the longest label in the game,
   so the pair is lettered a step down, both alike - the free one is never the
   smaller - and the free one keeps one line on a desktop */
#ui #screen-revive .revive-btn,
#ui #screen-revive .revive-continue { font-size: 15px; letter-spacing: 0.03em; }
#ui #screen-revive .revive-continue { padding-left: 10px; padding-right: 10px; }
/* the shop's stage on a phone (a strip over the rack) or a phone on its side:
   an M pill with the S lettering (13px), so КУПИТЬ + the note + 1400 keep one line */
@media (max-width: 600px), (max-height: 420px) {
  #ui #screen-shop .stage-act { font-size: 13px; letter-spacing: 0.02em; }
  #ui #screen-shop .stage-act .buy-word { margin-right: 5px; }
  #ui #screen-shop .stage-act .cash-ico.small { margin-right: 4px; }
}
@media (max-width: 370px) {                        /* COMPRAR + 1400 at 360 wide */
  #ui #screen-shop .stage-act { letter-spacing: 0; padding-left: 6px; padding-right: 6px; }
}
/* the revive countdown's gold is the primary's gold */
#ui #screen-revive .revive-continue .rc-fill {
  background: var(--b-gold);
  box-shadow: var(--b-in-gold);
  -webkit-text-stroke: 0.19em var(--b-ink);
}
/* the pause tiles: a free card, icon over word (a pill on a phone on its side) */
#ui #screen-pause .panel .btn-secondary { border-radius: 22px; }
@media (max-height: 420px) { #ui #screen-pause .panel .btn-secondary { border-radius: 999px; } }

/* the end screen's stat rows (BOUNCES, PERFECT LANDINGS) are read, not pressed:
   a thinner outline and no drop, so only the buttons under them look pressable */
#ui .score-line,
#ui .score-line:last-child { border-width: 2px; box-shadow: none; }

/* the icons on the word buttons, in the system ink */
#ui #btnResume::before,
#ui #btnNext[data-i18n="summary.next"]::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8.4 4.9 19 11.1a1 1 0 0 1 0 1.8L8.4 19.1A1 1 0 0 1 7 18.2V5.8a1 1 0 0 1 1.4-.9z' fill='%23fff' stroke='%23231d4a' stroke-width='2.6' stroke-linejoin='round'/%3E%3C/svg%3E");
}
#ui #btnPauseShop::before,
#ui #btnSumShop::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 3.5 12 6l3-2.5 5 2.6-1.7 4.2-2.1-.8V20H7.8v-10.5l-2.1.8L4 6.1z' fill='%23ff8fc8' stroke='%23231d4a' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E");
}
#ui #btnPauseRestart::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-2 -2 28 28' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg stroke='%23231d4a' stroke-width='5.4'%3E%3Cpath d='M1.5 3.5v6h6'/%3E%3Cpath d='M3.8 14.8a8.6 8.6 0 1 0 2-9L1.5 9.5'/%3E%3C/g%3E%3Cg stroke='%234cb8f5' stroke-width='2.3'%3E%3Cpath d='M1.5 3.5v6h6'/%3E%3Cpath d='M3.8 14.8a8.6 8.6 0 1 0 2-9L1.5 9.5'/%3E%3C/g%3E%3C/svg%3E");
}
#ui #btnPauseMenu::before,
#ui #btnSumMenu::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='%23231d4a' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M5.6 10.2v9.6h4.6v-5.2h3.6v5.2h4.6v-9.6L12 4.8z' fill='%23ffc93c' stroke-width='2.2'/%3E%3Cpath d='M2.8 11.6 12 3.9l9.2 7.7' fill='none' stroke-width='2.6'/%3E%3C/svg%3E");
}
#ui #btnNext[data-i18n="summary.menu"]::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke='%23231d4a' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M5.6 10.2v9.6h4.6v-5.2h3.6v5.2h4.6v-9.6L12 4.8z' fill='%23fff' stroke-width='2.4'/%3E%3Cpath d='M2.8 11.6 12 3.9l9.2 7.7' fill='none' stroke-width='2.8'/%3E%3C/svg%3E");
}

/* =============================================================================
   PLAY - the one exception: family C's cartoon sticker. Sunshine yellow with a
   faint halftone, the ink outline, a fat white sticker rim, a hard offset drop,
   tilted -2deg and lettered in italic capitals. The tilt is the `rotate`
   property, so the idle squash-hop and the pulse ring (style.css) still run on
   top of it; its size is still the home screen's (#screen-menu .play-btn).
   (2,x,y): over the old PLAY rules, the press included.
   ============================================================================= */
#screen-menu #btnPlay {
  border: 4px solid var(--b-ink);
  border-radius: 999px;
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.42) 0 1.3px, rgba(255, 255, 255, 0) 1.7px) 0 0 / 7px 7px,
    linear-gradient(180deg, #ffe066 0%, #ffcc2e 60%, #ffb81f 100%);
  color: #ffffff;
  font-family: var(--b-font);
  font-weight: 900;
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  -webkit-text-stroke: 0.2em var(--b-ink);
  paint-order: stroke fill;
  text-shadow: 0.07em 0.09em 0 var(--b-ink);
  box-shadow: inset 0 3px 0 rgba(255, 255, 255, 0.55), inset 0 -4px 0 rgba(214, 128, 0, 0.28),
              0 0 0 4px #ffffff, 3px 7px 0 4px rgba(24, 18, 60, 0.55);
  rotate: -2deg;
  transition: transform .07s ease, box-shadow .07s ease, rotate .12s ease, scale .12s ease;
}
#screen-menu #btnPlay .ico-play { fill: #ffffff; stroke: var(--b-ink); }
#screen-menu #btnPlay::after { border-color: #ffffff; }          /* the pulse ring: the sticker's white */
@media (hover: hover) {
  #screen-menu #btnPlay:hover { rotate: 0deg; scale: 1.04; }
}
#screen-menu #btnPlay:active,
#screen-menu #btnPlay.pressable.pressing {
  transform: translateY(4px) scale(0.97);
  rotate: 0deg;
  scale: 1;
  box-shadow: inset 0 3px 0 rgba(255, 255, 255, 0.55), inset 0 -4px 0 rgba(214, 128, 0, 0.28),
              0 0 0 4px #ffffff, 1px 2px 0 4px rgba(24, 18, 60, 0.55);
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  #screen-menu #btnPlay { transition: transform .07s ease, box-shadow .07s ease; }   /* no swing to straight */
  /* and no snap either: a hover leaves the sticker as it sits */
  #screen-menu #btnPlay:hover { rotate: -2deg; scale: 1; }
}


/* (pass 33) The intro paused: RESUME only. There is nothing to restart, no menu to go back to
   and no shop yet - the player has not played. Gameplay's pause panel is unchanged. */
body.in-prologue #screen-pause #btnPauseShop,
body.in-prologue #screen-pause .btn-row { display: none; }

/* (pass 37) THE SWING, PLAYED: its asks on the TAP TO START line (UI.setSkipHint):
   held - breathing, no second fade-in; nudged after a while with no tap - a
   bigger, quicker pulse; a tap that landed - a pop; the dodge - quick, it is a moment. */
body.in-prologue .skip-hint.is-start.p37-on { animation: startBreathe 1.5s ease-in-out 0s infinite; }
body.in-prologue .skip-hint.is-start.p37-nudge { animation: p37Nudge 0.8s ease-in-out 0s infinite; }
body.in-prologue .skip-hint.is-start.p37-pop { animation: p37Pop 0.26s ease-out 0s both; }
body.in-prologue .skip-hint.is-start.p37-urgent { animation: startIn 0.16s ease-out 0s both, p37Urgent 0.45s ease-in-out 0.16s infinite; }
body.in-prologue .skip-hint.is-start.p37-urgent.p37-on { animation: p37Urgent 0.45s ease-in-out 0s infinite; }
body.in-prologue .skip-hint.is-start.p37-urgent.p37-pop { animation: p37Pop 0.26s ease-out 0s both; }
@keyframes p37Nudge { 0%, 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.14); } }
@keyframes p37Pop { 0% { opacity: 1; transform: translate(-50%, -50%) scale(1.2); } 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); } }
@keyframes p37Urgent { 0%, 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); } }
@media (prefers-reduced-motion: reduce) {
  body.in-prologue .skip-hint.is-start.p37-on, body.in-prologue .skip-hint.is-start.p37-nudge,
  body.in-prologue .skip-hint.is-start.p37-pop, body.in-prologue .skip-hint.is-start.p37-urgent { animation: none; opacity: 1; }
}

/* ============ (pass 38) THIRTY PORTAL SKINS: THE RACK'S LAST ROW ============
   29 cards and the final on its own row: one card-sized tip closes the last
   row at 6, 3 and 2 columns (UI.p38Tip) - where they come from, or ALL FOUND!
   A plain dashed card: no tap, no motion. */
#screen-shop .skin-grid .p38-tip {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-width: 0; padding: 8px 6px; box-sizing: border-box;
  border-radius: 12px; border: 2px dashed rgba(155,124,255,0.55);
  background: rgba(155,124,255,0.10);
  color: var(--ink); font-size: 11px; font-weight: 800; line-height: 1.25; text-align: center;
  overflow-wrap: anywhere;
}
#screen-shop .skin-grid .p38-tip i {
  display: block; width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto;
  background: radial-gradient(circle, #ffffff 0 16%, #9b7cff 17% 42%, #4fb6ff 43% 64%, rgba(79,182,255,0) 66%);
  box-shadow: 0 0 0 2px rgba(43,58,103,0.25);
}
#screen-shop .skin-grid .p38-tip.all { border-style: solid; border-color: rgba(232,163,22,0.6); background: rgba(255,214,90,0.14); }
#screen-shop .skin-grid .p38-tip.all i { background: radial-gradient(circle, #fffbe8 0 16%, #ffd36b 17% 42%, #e8a316 43% 64%, rgba(232,163,22,0) 66%); }
#screen-shop .skin-grid .p38-tip.pad { visibility: hidden; }
