/* In-game HUD: level, wave plaque, currencies, rails, action bar, core.
 *
 * The chrome is the generated sprite kit in assets/ui/ — hexagon level crest,
 * angular EXP plate, coin/gem pills, scrollwork banner, skull-capped core bar
 * and the square button faces. Sprites carry the art only; every value, fill
 * and state lives in DOM on top, so nothing is baked.
 *
 * Three things to know before editing:
 *   * A sprite is a `background-image` on its container, so child elements
 *     paint above it. Fills are children inset to the frame's inner rect, and
 *     those percentages are measured off the source art — the comments give the
 *     pixel rect each one came from. Fill widths are a fraction of the whole
 *     sprite, so HUD.js scales its 0-100% into the track (see CORE_TRACK).
 *   * Rail buttons use the button face as a `border-image` rather than a
 *     background. The sprite has an icon baked into its middle; slicing takes
 *     only the frame, which drops that icon and lets the button be any size.
 *   * The kit has no glows — deliberately, they were composited over a
 *     checkerboard in the source. Glow is `filter: drop-shadow` here, so it can
 *     pulse and follow state. See `.boss` and `.danger`.
 */
/* Every HUD cluster is absolutely positioned, and an absolutely positioned box
   anchors to its containing block's PADDING box — so padding here insets all of
   them at once. That is the whole safe-area fix: inside the CrazyGames app the
   game is fullscreen, and without this the level crest sat under an iPhone's
   notch and the core bar under the home indicator. Zero on every other device. */
#hud {
  position: fixed; inset: 0; z-index: 10; pointer-events: none;
  padding: var(--sa-t) var(--sa-r) var(--sa-b) var(--sa-l);
}
#hud > * { pointer-events: auto; }

/* --------------------------------------------------------- top left */
.hud-topleft { position: absolute; left: 12px; top: 8px; display: flex; align-items: flex-start; gap: 0; }

/* Hexagon crest, 240×264. The disc holds a tower emblem and the waist a
   segmented arc, so the number goes in a chip on the lower point rather than
   fighting the emblem for the middle. */
.level-badge {
  position: relative; width: 100px; height: 110px; flex: none; z-index: 2;
  background: url('../assets/ui/badge-level.webp') center/contain no-repeat;
  display: flex; align-items: flex-end; justify-content: center;
  filter: drop-shadow(0 4px 6px rgba(0,0,0,.55));
}
.lb-word { display: none; }               /* the crest already reads as "level" */
.lb-num {
  position: relative; bottom: 4px;
  min-width: 30px; padding: 0 7px 1px;
  font-size: 17px; font-weight: 900; line-height: 1.25; text-align: center; color: #fff5cf;
  background: linear-gradient(180deg, #4d3468, #26173a);
  border: 2px solid #150c1e; border-radius: 8px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.22), 0 2px 0 rgba(0,0,0,.5);
  text-shadow: 0 2px 0 rgba(0,0,0,.85);
}

/* ---------------------------------------------------------- exp bar
 * The CSS capsule that replaced the old angled `bar-xp.webp` plate is now art
 * again — but as the three separate pieces the shape actually needs, not one
 * baked plate: `bar-xp-frame` is the empty gold capsule, `bar-xp-fill` is a
 * seamless green band that tiles into it, and the count stays live DOM on top.
 *
 * The channel is not centred in the frame — the sprite's lower gold face is
 * thicker than its upper rim, the way an extruded shape reads. So the fill's
 * inset is quoted from the sprite rather than assumed symmetric, and it lives
 * in these four variables because the groove, the fill and the tick marks all
 * have to agree with it. Percentages, so the whole bar can be resized in one
 * place (--xp-w) without re-deriving anything. */
/* Clear of the crest, not tucked under it: the crest has z-index 2 and would
   otherwise eat the left end of the heading. */
.xp-block {
  position: relative; margin: 16px 0 0 4px; width: var(--xp-w);
  /* Measured off bar-xp-frame.webp (640×127): the channel runs x 47..592,
     y 25..83. Anything that has to sit in the groove uses these. */
  --xp-w: 244px;
  --xp-cap: 7.34%;   /* rounded end cap, left and right */
  --xp-top: 19.7%;
  --xp-bot: 33.9%;
}
.xp-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 0 3px 3px; line-height: 1;
}
.xp-word {
  font-size: 9.5px; font-weight: 900; letter-spacing: .2em; color: #b49ae0;
  text-shadow: 0 2px 0 rgba(0,0,0,.8);
}
.xp-next {
  font-size: 9.5px; font-weight: 900; letter-spacing: .1em; color: var(--gold);
  text-shadow: 0 2px 0 rgba(0,0,0,.8);
}
.xp-bar {
  position: relative; width: 100%; aspect-ratio: 640 / 127;
  background: url('../assets/ui/bar-xp-frame.webp') center / 100% 100% no-repeat;
  /* The drop shadow is cast here rather than painted into the sprite, so it
     follows the alpha of the capsule instead of boxing it. */
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.55));
}
/* The sprite's channel is already dark and already recessed. This only deepens
   it, so an empty bar does not read as a flat hole. */
.xp-groove {
  position: absolute;
  left: var(--xp-cap); right: var(--xp-cap); top: var(--xp-top); bottom: var(--xp-bot);
  border-radius: 999px;
  background: #1a0f2c; box-shadow: inset 0 2px 5px rgba(0,0,0,.9);
}
/* `--xp` is the 0..1 progress, set by HUD.js. Doing the arithmetic here keeps
   the channel geometry in one place — the script does not need to know the
   sprite has end caps at all. */
.xp-fill {
  position: absolute;
  left: var(--xp-cap); top: var(--xp-top); bottom: var(--xp-bot);
  width: calc((100% - var(--xp-cap) * 2) * var(--xp, 0));
  border-radius: 999px; overflow: hidden;
  /* `auto 100%` keeps the band at its own aspect and repeats it along the bar,
     which is what makes it a fill and not a stretched blob. */
  background: url('../assets/ui/bar-xp-fill.webp') left center / auto 100% repeat-x;
  box-shadow: 0 0 10px rgba(110,227,106,.5);
  transition: width .3s ease-out;
}
/* A highlight that travels the fill, so a bar that is filling reads as alive
   rather than as a static block of green. */
.xp-shine {
  position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 35%, rgba(255,255,255,.45) 50%, transparent 65%);
  background-size: 200% 100%;
  animation: xp-sweep 2.6s linear infinite;
}
@keyframes xp-sweep { to { background-position: -200% 0; } }
/* Quarter marks. Cheap to read, and they turn "6.83K/13K" into a picture. */
.xp-ticks {
  position: absolute;
  left: var(--xp-cap); right: var(--xp-cap); top: var(--xp-top); bottom: var(--xp-bot);
  border-radius: 999px; pointer-events: none;
  background: repeating-linear-gradient(90deg,
    transparent 0, transparent calc(25% - 1px),
    rgba(0,0,0,.55) calc(25% - 1px), rgba(0,0,0,.55) 25%);
}
.xp-text {
  position: absolute;
  left: var(--xp-cap); right: var(--xp-cap); top: var(--xp-top); bottom: var(--xp-bot);
  display: flex; align-items: center; justify-content: center;
  font-size: 11.5px; font-weight: 900; letter-spacing: .04em; color: #fff;
  text-shadow: 0 2px 0 rgba(0,0,0,.95), 0 0 6px rgba(0,0,0,.9);
}

/* ------------------------------------------------------ wave plaque */
/* The plaque is a 9-slice: the gold skull bosses live in the corner regions and
   must not stretch, so the slice is taken past them (800×323 art, 54/33/58/31)
   and the border widths hold them at the same proportion on screen. Only the
   narrow decorative strips along the edges get stretched, which is what they
   were drawn to do. */
/* The frame widths live in `--pl` because the border-image has to repeat them:
   `border-width` sets the ring, and the `/ ... /` slot in border-image sets how
   much of the sprite fills it. They must agree or the skull bosses stretch, so a
   breakpoint that resizes the plaque changes one variable instead of two lists.

   Sized down from its original 320px minimum / 26px numerals: the plaque sits
   over the top of the arena, and at that size it covered the gate end of the
   hero road on a 16:9 monitor and most of the board on anything smaller. It only
   ever carries three short lines, so it does not need the width. */
.wave-plaque {
  --pl: 17px 11px 18px 10px;
  position: absolute; left: 50%; top: 10px; transform: translateX(-50%);
  min-width: 218px; padding: 3px 16px 5px; text-align: center;
  background: transparent;
  border: 17px solid transparent;
  border-width: var(--pl);
  /* `fill` keeps the sprite's own violet middle — without it the frame is a
     ring around a transparent hole. */
  border-image: url('../assets/ui/plaque-wave.webp') 54 33 58 31 fill / var(--pl) / 0 stretch;
  border-radius: 0;
  box-shadow: none;
  filter: drop-shadow(0 5px 5px rgba(0,0,0,.55));
}
.wave-chapter {
  font-size: 9px; font-weight: 900; letter-spacing: .14em; text-transform: uppercase;
  color: #c9a6ff; text-shadow: 0 2px 0 rgba(0,0,0,.8);
  margin-bottom: 0;
}
.wave-plaque.boss .wave-chapter { color: #ffb0c2; }
.wave-text {
  font-size: 19px; font-weight: 900; letter-spacing: .04em; color: var(--gold);
  text-shadow: 0 3px 0 #000, 3px 0 0 #000, -3px 0 0 #000, 0 -3px 0 #000, 0 5px 0 rgba(0,0,0,.5);
}
.wave-sub { font-size: 9px; font-weight: 800; letter-spacing: .1em; color: var(--dim); text-transform: uppercase; }
/* Boss waves glow rather than recolour: the plaque is one sprite now, so the
   state has to come from a filter that follows its alpha. */
.wave-plaque.boss {
  box-shadow: none;
  filter: drop-shadow(0 5px 5px rgba(0,0,0,.55)) drop-shadow(0 0 12px rgba(255,60,90,.85));
}
.wave-plaque.boss .wave-text { color: #ff6a86; }

/* ----------------------------------------------------- top right */
/* Cleared past the right rail (86px wide at right:12px) — the rail is later in
   the DOM, so anything that reaches under it is silently drawn over. */
.hud-topright {
  position: absolute; right: 108px; top: 12px;
  display: flex; align-items: flex-start; gap: 14px;
}
.wallet { display: flex; flex-direction: column; gap: 6px; }

/* Wallet capsules.
 *
 * The first pill art was two sprites — one per currency — with the medallion
 * baked in, and they were different heights, so the coin row and the gem row
 * never shared a baseline. `pill-wallet` is the fix asked for in the art brief:
 * ONE empty capsule used by both rows, with a recessed socket at the left end
 * and nothing printed inside. The medallion seats into that socket as the same
 * standalone icon file the egg button and the shop prices use.
 *
 * The box keeps the sprite's own 640×213 aspect. Stretching it to an arbitrary
 * height would turn the socket into an ellipse, and a squashed circle is the
 * one distortion the eye catches instantly. */
.coin-pill, .gem-pill {
  position: relative; flex: none;
  display: flex; align-items: center; justify-content: flex-end;
  width: 170px; aspect-ratio: 640 / 213; padding: 0 18px 2px 0;
  font-size: 17px; font-weight: 900; color: #fff;
  background: url('../assets/ui/pill-wallet.webp') center / 100% 100% no-repeat;
  border: 0; border-radius: 0;
  box-shadow: none;
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.5));
  text-shadow: 0 2px 0 rgba(0,0,0,.9), 0 0 7px rgba(0,0,0,.55);
}
/* One sprite for both rows, so the gem row is told apart by its medallion and a
   cool cast rather than by a different capsule. */
.gem-pill { filter: drop-shadow(0 3px 4px rgba(0,0,0,.5)) hue-rotate(-16deg) saturate(1.15); }
/* Seated in the socket the art already draws: its centre sits at 15.2% of the
   sprite's width, and the medallion is a shade wider than the recess so it
   reads as set into the plate rather than painted on it. */
.coin-pill .pill-icon, .gem-pill .pill-icon {
  position: absolute; left: 15.2%; top: 50%; transform: translate(-50%, -50%);
  width: 21%; aspect-ratio: 1; display: block;
  background: center/contain no-repeat;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,.7));
}
.coin-pill .pill-icon { background-image: url('../assets/ui/icon-coin.webp'); }
.gem-pill .pill-icon  { background-image: url('../assets/ui/icon-gem.webp'); }

/* ------------------------------------------------------ speed switch
 * The permanent speed control, wearing the kit's segmented capsule: the art
 * (bub-switch) carries the pill, the three numbers and the lit 3x hint, so
 * the container's old plate is gone and the segments are transparent hit
 * zones over it. State is an overlay — a warm glow on the live segment, a
 * dark veil and the play badge on the locked 3x. The bare text underneath
 * runs the boost countdown. */
.speed-switch {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.5));
}
.ss-word { display: none; }              /* the capsule art speaks for itself */
.ss-steps {
  display: flex; width: 132px; height: 63px;
  background: url('../assets/ui/bub-switch.webp') center / 100% 100% no-repeat;
}
.ss-step {
  flex: 1; padding: 0; border: 0; cursor: pointer;
  background: none; font-size: 0;        /* the numbers are baked into the art */
  transition: filter .12s;
}
.ss-step:hover { filter: brightness(1.18); }
.ss-step:active { transform: translateY(2px); }
/* The live speed: a warm glow over its segment. */
.ss-step.on { background: radial-gradient(ellipse at center, rgba(255,214,90,.5) 0%, rgba(255,214,90,0) 72%); }
/* Locked 3x: veiled, with the same play badge the rewarded rail buttons wear. */
.ss-step.locked { position: relative; background: rgba(18,10,28,.55); border-radius: 40%; }
.ss-step.locked::after {
  content: '▶'; position: absolute; right: -4px; top: -5px;
  width: 15px; height: 15px; line-height: 14px; font-size: 7px;
  background: #b8332f; border: 2px solid #150c1e; border-radius: 50%;
  color: #ffe9b0; text-shadow: none;
}
.ss-time {
  font-size: 9px; font-weight: 900; letter-spacing: .06em;
  color: #9ab8d8; text-shadow: 0 2px 0 rgba(0,0,0,.9);
}
.speed-switch.live .ss-time { color: #ffe9b0; }

/* Inline currency glyph beside a price, cut from the same sheet as the pills so
   the coin on a button is the same coin as the one in the wallet. */
.cost-ico {
  display: inline-block; flex: none; width: 1.15em; height: 1.15em;
  vertical-align: -0.22em;
  background-position: center; background-size: contain; background-repeat: no-repeat;
}
.cost-ico.coin { background-image: url('../assets/ui/icon-coin.webp'); }
.cost-ico.gem  { background-image: url('../assets/ui/icon-gem.webp'); }

/* ------------------------------------------------------------ rails */
.rail { position: absolute; display: flex; flex-direction: column; gap: 19px; }
.rail-right { right: 12px; top: 12px; }

/* Icon bubbles. Every rail and left-column button is the same chocolate-ringed
   sphere: glossy violet plastic, a fat cocoa border, and nothing on the face
   but the icon — labels and pills are gone, the title tooltip carries the
   name, and the only text allowed on a bubble is live state (a countdown,
   the speed multiplier). The state styles below (.on, .boosted, .ready)
   repaint the fill; the ring stays. */
.rail-btn, .lc-btn {
  position: relative; width: 64px; height: 64px; min-height: 0; padding: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0;
  font-family: var(--font); font-size: 10px; font-weight: 900; line-height: 1.15;
  letter-spacing: .03em; color: #efe4ff; cursor: pointer;
  background: radial-gradient(circle at 34% 28%, #65478c 0%, #3a2456 58%, #241338 100%);
  border: 4px solid #5a3216;            /* the chocolate ring */
  border-radius: 50%;
  box-shadow:
    inset 0 4px 7px rgba(255,255,255,.22),
    inset 0 -7px 10px rgba(0,0,0,.5),
    0 4px 8px rgba(0,0,0,.55),
    0 0 0 2px #2e1a0c;                  /* dark seam so the ring reads on dark ground */
  text-shadow: 0 2px 0 rgba(0,0,0,.85);
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.5));
  transition: transform .08s, filter .12s;
}
.rail-btn:hover, .lc-btn:hover { filter: drop-shadow(0 3px 4px rgba(0,0,0,.5)) brightness(1.18); }
.rail-btn:active, .lc-btn:active { transform: translateY(3px); }
/* Rail icons. The emoji in the markup stay as the fallback and the accessible
   label; `font-size: 0` hides the glyph without hiding the element, so a button
   whose art fails to load still shows something rather than an empty frame.
   The speed button is deliberately excluded — its face is the live multiplier,
   and it re-declares font-size below. */
/* 34px inside a 64px bubble. Every icon in the kit is drawn inside a violet
   flame aura, and below about 28px the aura is all that survives the
   downscale — the anvil, the awning and the spellbook all collapse into the
   same purple smudge. */
.rb-ico {
  width: 34px; height: 34px; font-size: 24px; line-height: 1;
  background: center/contain no-repeat;
}
#btn-forge     .rb-ico { background-image: url('../assets/ui/ico-forge.webp'); }
#btn-shop      .rb-ico { background-image: url('../assets/ui/ico-shop.webp'); }
#btn-codex     .rb-ico { background-image: url('../assets/ui/ico-codex.webp'); }
#btn-automerge .rb-ico { background-image: url('../assets/ui/ico-automerge.webp'); }
#btn-rebirth   .rb-ico { background-image: url('../assets/ui/ico-rebirth.webp'); }
/* The bubbly kit (docs/BUBBLY-UI-PROMPTS.md): the whole face is one piece of
   art — chocolate ring, gloss and baked-in name included — so the CSS bubble
   behind it steps aside entirely: no border, no fill, just the picture and a
   shadow. State styles below now work through halo and dimming; they can no
   longer repaint a fill that is not there. */
#btn-forge, #btn-shop, #btn-codex, #btn-automerge, #btn-speed, #btn-rebirth,
#btn-spin, #btn-gift {
  background: center/contain no-repeat;
  border: 0; border-radius: 0; box-shadow: none;
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55));
}
#btn-forge     { background-image: url('../assets/ui/bub-forge.webp'); }
#btn-shop      { background-image: url('../assets/ui/bub-shop.webp'); }
#btn-codex     { background-image: url('../assets/ui/bub-codex.webp'); }
#btn-automerge { background-image: url('../assets/ui/bub-automerge.webp'); }
#btn-speed     { background-image: url('../assets/ui/bub-speed.webp'); }
#btn-rebirth   { background-image: url('../assets/ui/bub-rebirth.webp'); }
#btn-spin      { background-image: url('../assets/ui/bub-spin.webp'); }
#btn-gift      { background-image: url('../assets/ui/bub-gift.webp'); }
/* The art carries its own icon, so the icon span retires on the kit buttons.
   The legacy webp set above stays as the first paint before CSS catches up,
   and #speed-mult is exempt — it is live state, not an icon. */
#btn-forge .rb-ico, #btn-shop .rb-ico, #btn-codex .rb-ico,
#btn-automerge .rb-ico, #btn-rebirth .rb-ico,
#btn-spin .lc-ico, #btn-gift .lc-ico { display: none; }
/* Live state rides just under the bubble — bare text, no pill, no chrome. */
.rb-state, .lc-state {
  position: absolute; left: 50%; bottom: -13px; transform: translateX(-50%);
  font-size: 9.5px; font-weight: 900; white-space: nowrap;
  color: #cfe0ff; text-shadow: 0 2px 0 rgba(0,0,0,.9), 0 0 5px rgba(0,0,0,.8);
}
/* Lit state: a gold halo — the art face cannot be repainted. */
.rail-btn.on {
  color: #ffe9b0;
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 9px rgba(255,196,84,.85));
}
.rail-btn.on .rb-state { color: #ffe9b0; }

/* Rewarded-video slots carry a small play badge, so the cost of pressing them
   is legible before the press rather than after. Hidden while the reward is
   already running. */
.rail-btn.locked::after {
  content: '▶'; position: absolute; right: -3px; top: -3px;
  width: 17px; height: 17px; line-height: 16px; font-size: 8px;
  background: #b8332f; border: 2px solid #150c1e; border-radius: 50%;
  color: #ffe9b0; text-shadow: none;
}
.rail-btn.on.locked::after { display: none; }

/* The Dark Forge, top of the rail. It is the deepest screen in the game, so it
   gets the one warm button on an otherwise violet stack. */
.forge-btn { color: #ffe2ab; }
.forge-btn:hover { filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) brightness(1.25); }

/* Speed. The multiplier is the one piece of live text the kit cannot bake —
   it rides on the art's centre, and the countdown sits under the bubble. */
#speed-mult.rb-ico {
  display: block; position: absolute; left: 50%; top: 36%; transform: translate(-50%, -50%);
  width: auto; height: auto; background: none;
  font-size: 19px; font-weight: 900; letter-spacing: -.02em;
  color: #fff3c8; text-shadow: 0 2px 0 rgba(0,0,0,.9), 0 0 7px rgba(0,0,0,.75);
}
.speed-btn.boosted {
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 11px rgba(255,196,84,.9));
}
.speed-btn.boosted #speed-mult { animation: speed-throb 1.1s ease-in-out infinite; }
@keyframes speed-throb { 0%, 100% { transform: translate(-50%, -50%) scale(1); } 50% { transform: translate(-50%, -50%) scale(1.14); } }

/* Bloodline Rebirth. Dim and quiet until the level gate opens, then the pink
   halo and pulse take over — the art face itself never changes. */
.rebirth-btn { opacity: .66; filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) grayscale(.45); }
.rebirth-btn.ready {
  opacity: 1;
  color: #ffd9e6;
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 9px rgba(255,90,140,.75));
  animation: rebirth-pulse 2.2s ease-in-out infinite;
}
@keyframes rebirth-pulse {
  0%, 100% { filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 7px rgba(255,90,140,.55)); }
  50%      { filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 15px rgba(255,90,140,.95)); }
}

/* ------------------------------------------------ auto-merge offer */
/* Slides in above the action bar every couple of minutes. Deliberately in the
   bottom-right gutter rather than centre screen: it is an offer, not a modal,
   and it must never sit over the grid the player is dragging on. */
.auto-offer {
  position: absolute; right: 12px; bottom: 156px;
  display: flex; align-items: center; gap: 9px;
  max-width: 320px; padding: 9px 40px 9px 11px;
  background: linear-gradient(180deg, #4a2f66, #2c1a44);
  border: 3px solid #150c1e; border-radius: 14px;
  box-shadow: inset 0 0 0 2px rgba(165,97,255,.45), 0 6px 0 rgba(0,0,0,.45),
              0 0 22px rgba(140,80,255,.35);
  animation: offer-in .32s cubic-bezier(.2,1.4,.5,1) both;
}
.auto-offer.hidden { display: none; }
@keyframes offer-in { from { transform: translateX(26px); opacity: 0; } }
.ao-ico { font-size: 26px; animation: ao-spin 3.4s linear infinite; }
@keyframes ao-spin { to { transform: rotate(360deg); } }
.ao-body { flex: 1; min-width: 0; }
.ao-title { font-size: 12px; font-weight: 900; letter-spacing: .05em; color: #ffe9b0; }
.ao-sub { font-size: 11px; font-weight: 700; line-height: 1.3; color: var(--dim); }
.ao-sub b { color: #b6ff5a; }
.ao-yes {
  flex: none; padding: 7px 12px; font-family: var(--font); font-size: 11px; font-weight: 900;
  letter-spacing: .06em; color: #22160a; cursor: pointer;
  background: linear-gradient(180deg, #ffd76a, #d99a22);
  border: 2px solid #150c1e; border-radius: 9px; box-shadow: 0 3px 0 #7a5210;
}
.ao-yes:active { transform: translateY(2px); box-shadow: 0 1px 0 #7a5210; }
.ao-no {
  position: absolute; right: 7px; top: 7px; width: 20px; height: 20px;
  font-size: 11px; font-weight: 900; color: var(--dim); cursor: pointer;
  background: #1d1229; border: 2px solid #150c1e; border-radius: 6px;
}
.ao-no:hover { color: #fff; }

/* ------------------------------------------------------- action bar */
.action-bar { position: absolute; right: 12px; bottom: 12px; display: flex; gap: 10px; align-items: flex-end; }

.action-btn {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 2px;
  font-family: var(--font); font-weight: 900; letter-spacing: .04em; cursor: pointer;
  border: 0; border-radius: 0; background: none;
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55));
  transition: transform .08s, filter .12s;
}
.action-btn:hover { filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) brightness(1.1); }
.action-btn:active { transform: translateY(4px); }

/* 305×343 — gold disc with a price plate across its foot. The sprite supplies
   the egg and the plate, so only the live cost is drawn, into the plate. */
.egg-btn {
  width: 118px; height: 133px; padding: 0 0 8px; color: #ffdf9a;
  background: url('../assets/ui/btn-egg.webp') center/contain no-repeat;
  justify-content: flex-end;
}
.egg-btn .ab-ico, .egg-btn .ab-label { display: none; }
/* The sprite has "$100" painted into its price plate, so the live cost is not
   drawn beside it — it is drawn *over* it, on an opaque chip cut to the plate's
   rect (px 55,245 → 250,310 of 305×343) and styled to match. */
.ab-cost {
  position: absolute; left: 18%; top: 71%; width: 64%; height: 19%;
  display: flex; align-items: center; justify-content: center; gap: 4px;
  font-size: 15px; color: #ffdf9a; text-shadow: 0 2px 0 rgba(0,0,0,.9);
  background: linear-gradient(180deg, #6b3f1c, #46270f);
  border: 2px solid #2a1607; border-radius: 999px;
  box-shadow: inset 0 0 0 2px rgba(255,196,84,.6), inset 0 2px 0 rgba(255,255,255,.12);
}
.ab-cost .cost-ico { width: 16px; height: 16px; vertical-align: 0; }
.ab-tier {
  position: absolute; left: 50%; top: -4px; transform: translateX(-50%);
  padding: 2px 10px; font-size: 11px; color: #fff; white-space: nowrap;
  background: linear-gradient(180deg, #6a4cc0, #3b2470);
  border: 2px solid #150c1e; border-radius: 9px; text-shadow: 0 2px 0 #000;
}
.egg-btn:disabled { filter: grayscale(.6) brightness(.75); cursor: not-allowed; }

/* -------------------------------------------------------- core panel */
.core-panel { position: absolute; left: 12px; bottom: 10px; width: 330px; }
.core-title {
  margin-left: 6px; font-size: 14px; font-weight: 900; letter-spacing: .06em; color: #ff9de8;
  text-shadow: 0 2px 0 #000, 2px 0 0 #000, -2px 0 0 #000, 0 -2px 0 #000;
}

/* 743×130, skull-capped. The source art has a part-filled track painted in, so
   `::before` lays down an empty track over it and `.core-fill` draws the live
   amount on top. Track px 119,26 → 624,72. */
.core-bar {
  position: relative; width: 100%; height: 58px; margin-top: 1px;
  background: url('../assets/ui/bar-core.webp') center/contain no-repeat;
  border: 0; border-radius: 0; overflow: visible;
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.55));
}
.core-bar::before {
  content: ''; position: absolute;
  left: 16%; top: 20%; width: 68%; height: 36.2%;
  background: #221333; border-radius: 3px;
  box-shadow: inset 0 2px 5px rgba(0,0,0,.75);
}
.core-fill {
  position: absolute; left: 16%; top: 20%; height: 36.2%; width: 68%;
  max-width: 68%; border-radius: 3px;
  background:
    repeating-linear-gradient(90deg, rgba(0,0,0,.5) 0 2px, transparent 2px 22px),
    linear-gradient(180deg, #ff9de8, #ff2fd0 45%, #a01a8a);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.4);
  transition: width .25s;
}
.core-num {
  position: absolute; left: 16%; top: 20%; width: 68%; height: 36.2%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 900; color: #fff; text-shadow: 0 2px 0 rgba(0,0,0,.95);
}
/* The warning halo the kit does not carry, lit only when the core is actually
   in trouble. */
.core-panel.danger .core-bar {
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.55)) drop-shadow(0 0 10px rgba(255,40,60,.85));
  animation: corePulse 1.1s ease-in-out infinite;
}
@keyframes corePulse {
  50% { filter: drop-shadow(0 3px 4px rgba(0,0,0,.55)) drop-shadow(0 0 19px rgba(255,40,60,1)); }
}

.core-stats { display: flex; justify-content: space-between; margin: 1px 8px 0; font-size: 12px; font-weight: 800; color: var(--dim); }
.core-stats span { text-shadow: 0 2px 0 rgba(0,0,0,.7); }
#stat-dps { color: var(--gold); }

.bond-list { margin: 4px 0 0 6px; display: flex; flex-wrap: wrap; gap: 4px; }
.bond {
  font-size: 10px; font-weight: 900; letter-spacing: .03em;
  padding: 2px 7px; border-radius: 7px;
  background: rgba(100, 216, 255, .16); border: 2px solid rgba(100, 216, 255, .5); color: #b9edff;
}

/* -------------------------------------------------------- info card */
.info-card {
  position: absolute; left: 12px; bottom: 172px; width: 288px; padding: 10px 12px 12px;
  background: linear-gradient(180deg, #4a4162, #2a2338 45%, #191324);
  border: 3px solid #150c1e; border-radius: 14px;
  box-shadow: inset 0 0 0 2px rgba(255,196,84,.35), inset 0 3px 0 rgba(255,255,255,.14), 0 6px 0 rgba(0,0,0,.5);
}
.info-close { position: absolute; right: 6px; top: 5px; width: 24px; height: 24px;
  background: #191324; color: var(--dim); border: 2px solid var(--edge); border-radius: 7px;
  cursor: pointer; font-weight: 900; font-family: var(--font); }
.info-head { display: flex; align-items: center; gap: 8px; }
.info-tier {
  padding: 3px 8px; font-size: 13px; font-weight: 900; color: #1c1226;
  background: linear-gradient(180deg, #ffe9a0, var(--gold)); border: 2px solid var(--edge); border-radius: 8px;
}
.info-name { font-size: 15px; font-weight: 900; text-shadow: 0 2px 0 rgba(0,0,0,.8); }
.info-el { margin-top: 3px; font-size: 11px; font-weight: 900; letter-spacing: .14em; }
.info-rows { margin-top: 7px; display: grid; grid-template-columns: 1fr 1fr; gap: 3px 10px; font-size: 12px; }
.info-rows span { color: var(--dim); font-weight: 700; }
.info-rows b { color: #fff; float: right; }
.info-ability { margin-top: 8px; font-size: 11.5px; line-height: 1.45; color: #d9cfff;
  border-top: 2px solid rgba(255,255,255,.08); padding-top: 6px; }
.info-ability b { color: var(--cyan); display: block; margin-bottom: 2px; }

/* ----------------------------------------------------------- toasts */
/* 124px, not 96px: the plaque bottoms out at 103px, and `toastIn` starts the
   toast 14px above its resting place — at 96 the entry frame clipped the plaque's
   lower rim on any viewport where the two are both centred. */
.toast-wrap { position: absolute; left: 50%; top: 124px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 6px; pointer-events: none; }
.toast {
  padding: 7px 18px; font-size: 14px; font-weight: 900; letter-spacing: .04em; color: #fff;
  background: linear-gradient(180deg, #4a4162, #241d33);
  border: 3px solid var(--edge); border-radius: 11px;
  box-shadow: 0 5px 0 rgba(0,0,0,.45);
  text-shadow: 0 2px 0 rgba(0,0,0,.8);
  animation: toastIn .25s ease-out, toastOut .4s ease-in 2.1s forwards;
}
.toast.good { border-color: #2f9e46; color: #d6ffe6; }
.toast.bad  { border-color: #8a2338; color: #ffd6de; }
@keyframes toastIn { from { opacity: 0; transform: translateY(-14px) scale(.9); } }
@keyframes toastOut { to { opacity: 0; transform: translateY(-10px); } }

/* --------------------------------------------------------- left column */
/* The Lucky Spin, the new-player gift and the how-to, stacked under the level
   crest. Same chocolate bubble as the rail (the shared rule lives up in the
   rails section) so the column reads as part of the kit, not a floating ad. */
.hud-leftcol {
  position: absolute; left: 12px; top: 126px;
  display: flex; flex-direction: column; gap: 19px;
}
.lc-ico { font-size: 28px; line-height: 1; }
.lc-q { font-weight: 900; }
/* Ready to spin: gold halo and a pulse — the art face stays put. */
.spin-btn.ready {
  color: #ffe9b0;
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 9px rgba(255,196,84,.85));
  animation: spin-ready-pulse 2s ease-in-out infinite;
}
.spin-btn.ready .lc-state { color: #ffe9b0; }
@keyframes spin-ready-pulse {
  0%, 100% { filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 6px rgba(255,196,84,.55)); }
  50% { filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 13px rgba(255,196,84,.95)); }
}
/* Rate-limited: greyed and quiet, the bare text runs the cooldown clock. */
.spin-btn.cooling { opacity: .66; filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) grayscale(.5); }
.spin-btn.cooling .lc-state { color: #9ab8d8; }
/* The gift glows gold while claimable, so the eye goes straight to it; the
   rest of the hour it sits greyed with the countdown underneath. */
.gift-btn.ready {
  color: #fff3c8;
  filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) drop-shadow(0 0 10px rgba(255,214,90,.85));
  animation: spin-ready-pulse 1.6s ease-in-out infinite;
}
.gift-btn.cooling { opacity: .66; filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) grayscale(.5); }
.gift-btn.cooling .lc-state { color: #9ab8d8; }


/* ---------------------------------------------------------- tutorial */
/* The first-run guide. The dim is one radial-gradient with a transparent
   hole over the target — no geometry, and pointer-events stay off so the
   game plays straight through it. The hand is an emoji stand-in until the
   generated pointer lands (docs/TUTORIAL-ART-PROMPTS.md); the bubble is
   pure CSS in the same toy-plastic language as the kit. */
/* The `#hud > *` catch-all hands every direct child pointer-events back, so
   this needs the id in the selector to keep the overlay click-through. */
#hud > .tutor { position: fixed; inset: 0; z-index: 30; pointer-events: none; }
.tutor-dim { position: absolute; inset: 0; transition: background .25s; }
.tutor-hand {
  position: absolute; width: 54px; height: 54px; margin: -10px 0 0 -10px;
  font-size: 46px; line-height: 1;
  filter: drop-shadow(0 3px 3px rgba(0,0,0,.6));
  transform: rotate(-20deg);
  animation: tutor-point .9s ease-in-out infinite;
}
@keyframes tutor-point {
  0%, 100% { transform: rotate(-20deg) translate(0, 0); }
  50% { transform: rotate(-20deg) translate(-9px, -9px); }
}
.tutor-bubble {
  /* 280px is wider than a 320px screen minus the gutters Tutorial.js places it
     in, so the width yields on the smallest phones. */
  position: absolute; width: min(280px, calc(100vw - 36px)); padding: 12px 14px 10px;
  background: linear-gradient(180deg, #fff6e6, #ffe9c8);
  border: 3px solid #5a3216; border-radius: 16px;
  box-shadow: 0 6px 0 rgba(0,0,0,.35), 0 10px 22px rgba(0,0,0,.5);
  font-size: 13px; font-weight: 800; line-height: 1.45; color: #2b1a44;
  pointer-events: auto;
}
.tutor-bubble::after {
  content: ''; position: absolute; left: 50%; bottom: -13px; transform: translateX(-50%);
  border: 8px solid transparent; border-top: 11px solid #5a3216;
}
/* Placed below its target instead of above: the tail flips to the top. */
.tutor-bubble--below::after { bottom: auto; top: -13px; border-top: 0; border-bottom: 11px solid #5a3216; }
.tutor-skip {
  display: block; margin: 8px auto 0; padding: 3px 12px;
  font-family: var(--font); font-size: 9px; font-weight: 900; letter-spacing: .1em;
  color: #8a6a4a; background: none; border: 2px solid #c9a97e; border-radius: 8px; cursor: pointer;
}
.tutor-skip:hover { color: #5a3216; border-color: #5a3216; }

/* Sound bubble: greyed and quiet while muted. */
.sound-btn.muted { opacity: .66; filter: drop-shadow(0 4px 5px rgba(0,0,0,.55)) grayscale(.6); }

/* ========================== responsive HUD ==========================
 * Three shapes, in order of how much they rearrange:
 *
 *   1. <= 1180px wide — everything shrinks; the layout itself holds.
 *   2. <= 1000px wide — the wallet stacks over the speed switch, because at this
 *      width the three top clusters stop fitting side by side. This is the tier
 *      a 1024x768 tablet and a 960px-wide portal iframe land in, and skipping it
 *      is what had the wave plaque running under the coin pills there.
 *   3. <= 620px wide (phone portrait) — the top band cannot hold two clusters
 *      plus a centred plaque at any size, so it becomes three stacked rows: the
 *      plaque, then crest + wallet, then the switch.
 *   4. <= 520px tall (phone landscape) — the right rail is taller than the
 *      screen, so it folds into two columns and the hatchery shrinks out of it.
 *
 * 3 and 4 both apply on a square-ish small screen, and that is intended: the
 * portrait rules restack the top band and the landscape rules fold the rails.
 */
@media (max-width: 1180px) {
  .level-badge { width: 76px; height: 84px; }
  /* The bar and the wallet pills carry their sprite's aspect ratio, so they are
     resized by width alone — pinning a height here would squash the capsule's
     end caps and turn the pill's round socket into an ellipse. */
  .xp-block { --xp-w: 194px; margin-top: 10px; }
  .xp-text { font-size: 10.5px; }
  .hud-topright { right: 92px; gap: 9px; }
  .coin-pill, .gem-pill { width: 138px; font-size: 14px; }
  .ss-steps { width: 108px; height: 51px; }
  .core-panel { width: 244px; }
  .core-bar { height: 43px; }
  .core-num { font-size: 10px; }
  .wave-plaque { --pl: 15px 10px 16px 9px; min-width: 186px; padding: 2px 12px 4px; }
  .wave-text { font-size: 17px; }
  .rail-btn, .lc-btn { width: 54px; height: 54px; border-width: 3px; }
  .rb-ico { width: 28px; height: 28px; }
  .lc-ico { font-size: 24px; }
  .speed-btn .rb-ico { font-size: 19px; }
  .egg-btn { width: 96px; height: 108px; }
}

/* ------------------------------------- 2. wallet over the switch */
/* The top band is three clusters wide, and below about 1000px the centred plaque
   reaches the right-hand one. Stacking the wallet and the speed switch into one
   column halves that cluster's width and buys back ~120px, which is more than
   the collision ever needs — and it costs nothing, because the switch is touched
   once a run while the wallet is read constantly. */
@media (max-width: 1000px) {
  .hud-topright { flex-direction: column; align-items: flex-end; gap: 9px; }
  .wallet { order: -1; }
  /* Same reason the phone tiers do it: a viewport-centred toast is wide enough to
     reach the crest row on a 768px tablet. Bounded by this tier's clusters — the
     crest row ends at 286px, the stacked wallet column owns the last 230px. */
  .toast-wrap { left: 296px; right: 240px; top: 100px; transform: none; }
  .toast { max-width: 100%; }
}

/* Six rail bubbles need more vertical room than four plates did — and each
   needs a clear strip under it for its countdown. On a short viewport the
   rail tightens rather than running off-screen. */
@media (max-height: 760px) {
  .rail { gap: 13px; }
  .hud-leftcol { gap: 13px; }
  .rail-btn, .lc-btn { width: 50px; height: 50px; border-width: 3px; }
  /* 26px is the floor for these icons — below it the flame aura they are all
     drawn inside is the only thing that survives the downscale. */
  .rb-ico { width: 26px; height: 26px; font-size: 18px; }
}

/* ---------------------------------------------- 2. phone portrait */
@media (max-width: 620px) {
  /* Row 1: the plaque owns the top strip on its own. It is the one thing that
     has to be read mid-wave, and centring it is only possible if nothing else
     shares the row. */
  .wave-plaque {
    --pl: 12px 8px 13px 7px;
    top: 6px; min-width: 0; width: min(168px, 46vw); padding: 2px 8px 3px;
  }
  .wave-chapter { font-size: 7.5px; letter-spacing: .1em; }
  .wave-text { font-size: 15px; }
  .wave-sub { font-size: 7.5px; letter-spacing: .06em; }

  /* Row 2: crest + experience on the left, wallet on the right. Every offset
     below is measured off the row above it rather than estimated — the plaque
     resolves to 62px tall at this size, the crest row to 55px, the wallet plus
     switch to 134px. Guessing them is what had the wave counter buried behind
     the coin pills in the first place. */
  .hud-topleft { left: 8px; top: 72px; }
  .level-badge { width: 50px; height: 55px; }
  .lb-num { font-size: 12px; min-width: 22px; padding: 0 5px 1px; bottom: 3px; }
  .xp-block { --xp-w: 132px; margin-top: 7px; }
  .xp-word, .xp-next { font-size: 7.5px; letter-spacing: .1em; }
  .xp-text { font-size: 9px; }

  /* The wallet leads and the speed switch tucks under it: on a phone the top
     right corner is where the eye goes for currency, and speed is a setting
     touched once a run. `order` does the swap without moving the markup. */
  .hud-topright {
    right: 8px; top: 72px;
    flex-direction: column; align-items: flex-end; gap: 7px;
  }
  .wallet { order: -1; gap: 4px; }
  .coin-pill, .gem-pill { width: 104px; font-size: 12px; padding: 0 12px 2px 0; }
  .ss-steps { width: 88px; height: 42px; }
  .ss-time { font-size: 8px; }

  /* Rails start below the whole top band: 72 + 134 for the wallet-and-switch
     column, 72 + 55 for the crest row. The 10px gap keeps six bubbles plus their
     countdown strips inside a 640px-tall screen, where the hatchery starts at
     546px — a 13px clearance that a 13px gap would have eaten. */
  .rail-right { right: 8px; top: 212px; gap: 10px; }
  .hud-leftcol { left: 8px; top: 136px; gap: 11px; }
  .rail-btn, .lc-btn { width: 44px; height: 44px; border-width: 3px; }
  .rb-ico { width: 24px; height: 24px; font-size: 16px; }
  .lc-ico { font-size: 21px; }
  .speed-btn .rb-ico, #speed-mult.rb-ico { font-size: 15px; }
  .rb-state, .lc-state { font-size: 8.5px; bottom: -11px; }
  .rail-btn.locked::after, .ss-step.locked::after { width: 13px; height: 13px; line-height: 12px; font-size: 6px; }

  /* Bottom row. The hatchery is the most-pressed button in the game, so it keeps
     a comfortable target while losing the width that was pushing its tier chip
     across the core bar and up into the rail. */
  .egg-btn { width: 76px; height: 86px; padding: 0 0 5px; }
  .ab-cost { font-size: 11px; }
  .ab-cost .cost-ico { width: 12px; height: 12px; }
  .ab-tier { font-size: 8.5px; padding: 1px 6px; top: -3px; max-width: 108px; overflow: hidden; text-overflow: ellipsis; }
  .action-bar { right: 8px; bottom: 8px; }

  .core-panel { left: 8px; bottom: 6px; width: min(196px, 52vw); }
  .core-title { font-size: 10px; }
  .core-bar { height: 34px; }
  .core-num { font-size: 8.5px; }
  .core-stats { font-size: 9.5px; margin: 0 6px 0; }
  .bond { font-size: 8px; padding: 1px 5px; }
  .bond-list { gap: 3px; margin: 3px 0 0 4px; }

  /* Toasts clear the restacked top band instead of landing on the wallet. They
     also stop centring on the viewport and centre inside the gap between the two
     rails instead — on a 320px screen a centred toast is wider than that gap. */
  /* `right: 116px` clears the wallet column's own width, so the band never shares
     an x range with it — which matters because `toastIn` starts the toast 14px
     high, and 6px of vertical clearance was not enough to survive that. */
  .toast-wrap { left: 58px; right: 116px; top: 224px; transform: none; }
  .toast { font-size: 11px; padding: 5px 12px; max-width: 100%; }

  /* The info card and the auto-merge offer both sat at fixed heights tuned to a
     desktop bottom bar. On a phone they ride just above the shorter one. */
  .info-card { left: 8px; right: 8px; width: auto; bottom: 110px; }
  .info-rows { font-size: 11px; }
  .auto-offer { right: 8px; left: 8px; max-width: none; bottom: 104px; }
}

/* --------------------------------- 3b. short portrait (320x568) */
/* A single column of six bubbles is 314px tall. That fits an 844px phone with
   300px to spare and a 640px one with 20px, but on a 568px screen it runs
   straight into the hatchery. Below 700px of height the portrait rail folds the
   same way the landscape one does. */
@media (max-width: 620px) and (max-height: 700px) {
  .rail-right {
    top: 212px;
    display: grid; grid-template-columns: repeat(2, auto); gap: 13px 8px;
  }
  /* The folded rail is 96px wide instead of 44px, so the toast band gives it
     back the difference. */
  .toast-wrap { right: 104px; }
}

/* ------------------------------------------- 4. phone landscape */
@media (max-height: 520px) {
  /* A single column of six bubbles needs 377px; a landscape phone has 360. Two
     columns of three need 150px, which also leaves the hatchery its corner back
     — that collision is what had the egg sitting on top of the speed and
     rebirth buttons. */
  .rail-right {
    right: 8px; top: 8px;
    display: grid; grid-template-columns: repeat(2, auto);
    gap: 14px 8px;
  }
  .hud-leftcol {
    left: 8px; top: 76px;
    display: grid; grid-template-columns: repeat(2, auto);
    gap: 14px 8px;
  }
  .rail-btn, .lc-btn { width: 46px; height: 46px; }
  .rb-ico { width: 25px; height: 25px; font-size: 17px; }
  .lc-ico { font-size: 22px; }

  /* Clear of the two-column rail (110px) rather than the old one-column 92px. */
  .hud-topright {
    right: 118px; top: 8px;
    flex-direction: column; align-items: flex-end; gap: 6px;
  }
  .wallet { order: -1; gap: 4px; }
  .coin-pill, .gem-pill { width: 116px; font-size: 13px; }
  .ss-steps { width: 96px; height: 46px; }

  .hud-topleft { left: 8px; top: 6px; }
  .level-badge { width: 54px; height: 59px; }
  .lb-num { font-size: 13px; min-width: 24px; padding: 0 5px 1px; }
  .xp-block { --xp-w: 148px; margin-top: 8px; }
  .xp-text { font-size: 9.5px; }

  .wave-plaque { --pl: 12px 8px 13px 7px; min-width: 0; width: 168px; padding: 2px 8px 3px; top: 6px; }
  .wave-chapter { font-size: 7.5px; }
  .wave-text { font-size: 15px; }
  .wave-sub { font-size: 7.5px; }

  .egg-btn { width: 74px; height: 83px; padding: 0 0 5px; }
  .ab-cost { font-size: 10.5px; }
  .ab-cost .cost-ico { width: 12px; height: 12px; }
  .ab-tier { font-size: 8px; padding: 1px 5px; max-width: 104px; overflow: hidden; text-overflow: ellipsis; }
  .action-bar { right: 8px; bottom: 6px; }

  .core-panel { left: 8px; bottom: 4px; width: 212px; }
  .core-title { font-size: 10px; }
  .core-bar { height: 34px; }
  .core-num { font-size: 8.5px; }
  .core-stats { font-size: 9.5px; }
  .bond { font-size: 8px; padding: 1px 5px; }

  /* The plaque resolves to 66px tall here and both are centred, so the band starts
     below it rather than 8px inside it — and it is bounded by the two top
     clusters instead of centred on the viewport, because a viewport-centred
     toast is wider than the gap between them. The offsets are the clusters'
     own: the crest row ends at 214px, the wallet column occupies the last 234px
     (118px offset + 116px wide). */
  .toast-wrap { left: 222px; right: 242px; top: 80px; transform: none; }
  .toast { font-size: 11px; padding: 5px 12px; max-width: 100%; }
  .info-card { left: 8px; width: 250px; bottom: 96px; }
  .auto-offer { right: 8px; bottom: 96px; max-width: 250px; }
}

/* ------------------------------- 5. narrow landscape (568x320) */
/* The hardest viewport the game has to hold: an older phone turned sideways.
   568px of width has to carry the crest row, a centred plaque and the wallet
   column side by side, and 320px of height leaves no room to stack them. So the
   plaque gives up its two supporting lines and becomes the wave number alone —
   the chapter and the countdown are the first things a player stops reading when
   the screen is this small, and the number is the one that matters mid-wave.

   Widths here are solved, not guessed: crest row 8..178, plaque 226..342
   centred, wallet column from 350. */
@media (max-height: 520px) and (max-width: 620px) {
  .wave-chapter, .wave-sub { display: none; }
  .wave-plaque {
    --pl: 10px 7px 11px 6px;
    top: 5px; min-width: 0; width: auto; padding: 1px 9px 2px;
  }
  .wave-text { font-size: 14px; }

  .xp-block { --xp-w: 112px; margin-top: 7px; }
  .xp-word, .xp-next { font-size: 7px; letter-spacing: .08em; }
  .xp-text { font-size: 8.5px; }
  .level-badge { width: 48px; height: 53px; }
  .lb-num { font-size: 11px; min-width: 20px; padding: 0 4px 1px; bottom: 2px; }

  .coin-pill, .gem-pill { width: 100px; font-size: 11.5px; padding: 0 11px 2px 0; }
  .ss-steps { width: 84px; height: 40px; }
  .ss-time { font-size: 7.5px; }

  .rail-btn, .lc-btn { width: 42px; height: 42px; }
  .rb-ico { width: 23px; height: 23px; font-size: 15px; }
  .lc-ico { font-size: 19px; }
  .rail-right { right: 6px; }
  .hud-topright { right: 104px; }
  .hud-leftcol { top: 68px; }

  .core-panel { width: 176px; }
  .core-title { font-size: 9px; }
  .core-bar { height: 29px; }
  .egg-btn { width: 66px; height: 74px; }

  /* Only the wave number is left above, so the band moves up to meet it. Same
     bounded-by-the-clusters trick, at this tier's narrower offsets: crest row
     ends at 178px, wallet column owns the last 204px. */
  .toast-wrap { left: 186px; right: 212px; top: 58px; transform: none; }
  .info-card { width: 208px; bottom: 86px; }
  .auto-offer { max-width: 208px; bottom: 86px; }
}
