/* ============================================================================
   THE LONG DESCENT — styles
   Mobile-first. The whole palette shifts by zone via --bg-top/-bottom/-accent.
   ============================================================================ */

:root {
  --bg-top: #3ea9c9;
  --bg-bottom: #0d5a86;
  --accent: #7fe3ff;

  --ink: #eaf6ff;
  --ink-dim: #9fb8cc;
  --ink-faint: #6f89a0;
  --panel: rgba(6, 20, 34, 0.55);
  --panel-solid: #0a1a2b;
  --line: rgba(255, 255, 255, 0.09);
  --good: #58e0a8;
  --bad: #ff5a6a;

  --radius: 16px;
  --radius-sm: 11px;
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', 'Roboto Mono', Menlo, Consolas, monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* The [hidden] attribute must always win over class-based display rules,
   otherwise hidden overlays/panels keep covering the screen and eat input. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  background: #02060d;
  color: var(--ink);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

body {
  display: flex;
  justify-content: center;
  /* On desktop, center the "phone". */
  padding: 0;
}

/* ------------------------------ App shell ------------------------------ */
#app {
  position: relative;
  width: 100%;
  max-width: 480px;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: linear-gradient(180deg, var(--bg-top) 0%, var(--bg-bottom) 62%, #01040a 100%);
  transition: background 1.4s ease;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
  box-shadow: 0 0 60px rgba(0, 0, 0, 0.6);
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

@media (min-width: 520px) {
  body { padding: 18px 0; align-items: center; }
  #app {
    min-height: 0;
    height: calc(100dvh - 36px);
    max-height: 900px;
    border-radius: 28px;
    border: 1px solid rgba(255, 255, 255, 0.08);
  }
}

/* ------------------------------ Header ------------------------------ */
.hud {
  display: flex;
  /* NOWRAP, deliberately. This was `wrap`, from when .hud-currencies claimed its
     own row with `flex: 1 0 100%`. Nothing needs a second row any more, and wrap
     is how the store/menu buttons kept getting shoved down one — first by the
     hull banner (#5), then by wide currency numbers once the chips moved onto the
     depth line. With nowrap that is structurally impossible: the header is always
     exactly three text lines tall, so the scene below it never shifts either. */
  flex-wrap: nowrap;
  /* CENTER, not flex-start: the currency band and the buttons centre against the
     whole height of the left text stack. */
  align-items: center;
  justify-content: space-between;
  /* 12px sides, not 14. Measured: at 440pt the longest zone name (167px) + three
     max-magnitude chips (161px) + the buttons (88px) came to 416px against 412px of
     inner width — 4px short, which is what those 2px per side buy back. Vertically
     tight at both ends so the ship sits close under the bar. */
  padding: 10px 12px 4px;
  z-index: 3;
}
/* No negative margins — this band centres itself against the left stack via
   .hud's align-items: center, so it never needs to hang across anything. */
.hud-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }
/* THREE BANDS.
   left   .hud-zone      natural width, shrinks and ellipsises last
   middle .hud-mid       takes the slack and centres the chips inside it
   right  .hud-actions   natural width, never shrinks

   The chips themselves never shrink — .hud-mid does. So when the header is tight
   the zone name and layer line give up characters (both already ellipsise) and the
   sums stay whole and legible, which is the point of showing them.

   Width budget at 375pt: 347px usable, zone name up to 167px, chips ~150px at this
   size, buttons 88px. It is close, so the chip digits step down a point below
   390px — see the media query lower down. */
/* The left stack is the ONLY band that shrinks. The chips and the buttons both keep
   their natural width, and .hud's `justify-content: space-between` puts equal air on
   either side of the middle band — which is what centres it, without it having to
   claim a width and fight the other two for it.
   The budget genuinely does not close in the worst case: the longest zone name is
   167px, three chips at maximum magnitude are ~190px, the buttons are 88px, and a
   440pt phone has 412px. Something has to give, and it is a few characters off the
   end of the zone name rather than the sums, which are the numbers a player is
   actually reading. Letting the chips absorb it instead crushed all three counts to
   one or two glyphs. */
.hud-zone { flex: 0 1 auto; min-width: 0; }
.hud-mid { flex: 0 0 auto; display: flex; align-items: center; }
.hud-currencies {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 2px;
}
/* Compact, uniform height. A 13px <img> pearl, a 13px <svg> jar and a text diamond
   otherwise give three chips three slightly different heights. */
.hud-currencies .hud-pearls,
.hud-currencies .hud-vault,
.hud-currencies .hud-cores {
  font-size: 11px; padding: 0 5px; gap: 3px;
  height: 20px; box-sizing: border-box;
  display: inline-flex; align-items: center;
  min-width: 0; overflow: hidden;
}
.hud-currencies .pearl-ic { width: 13px; height: 13px; }
.hud-currencies .jar-ic { width: 13px; height: 13px; }
/* Narrower than the 440pt class: there is no row for the chips to wrap to in a
   three-band header, so the digits step down a point and the band stays one line.
   This buys ~15px, which clears 428pt outright.
   At 390pt and below the budget still does not close in the worst case — the longest
   procedurally-generated zone name plus three max-magnitude chips plus the buttons
   needs ~401px against 366px — so a few characters come off the END OF THE ZONE NAME
   there. That is the deliberate choice: those deep-zone names are generated flavour,
   the sums are numbers the player is reading. */
@media (max-width: 430px) {
  .hud-currencies .hud-pearls,
  .hud-currencies .hud-vault,
  .hud-currencies .hud-cores { font-size: 10px; padding: 0 4px; height: 19px; }
}
.hud-zone-name {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.3px;
}
.hud-zone-sub {
  font-size: 11px;
  color: var(--ink-dim);
  letter-spacing: 0.4px;
  margin-top: 1px;
}
/* Compact so three currency chips + two buttons never crush the zone name
   (tester #13). The zone identity keeps priority via .hud-zone min-width:0. */
/* (.hud-right was split into .hud-actions + .hud-currencies) */
.hud-cores {
  display: flex; align-items: center; gap: 3px;
  font-weight: 700; font-size: 12.5px;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.core-gem { color: #9ad2ff; text-shadow: 0 0 8px #6fb6ff; }
.core-gem.big { font-size: 30px; }
.mute-btn {
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--line);
  color: var(--ink);
  width: 40px; height: 40px;
  border-radius: 50%;
  font-size: 15px;
  cursor: pointer;
}

/* ------------------------------ Energy readout (overlaid on the scene) --------- */
.scene-energy {
  position: absolute;
  /* SYMMETRIC padding, not a left-only inset. `left: 80px; right: 8px` kept the
     block clear of the mini-map but shifted its centre 36px right, so the energy
     figure and its rate no longer sat over the probe. Equal padding does both jobs:
     the content box is still centred on the scene, and its edges stop 76px in —
     clear of the rail, which ends at 72px. */
  top: 16px; left: 0; right: 0;
  padding: 0 76px;
  box-sizing: border-box;
  /* A COLUMN. .buff-pill is inline-block and .energy-value is inline-flex, so as
     plain inline siblings they shared one line and the pill sat beside the number
     instead of above it. */
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  pointer-events: none; /* taps fall through to the scene */
  z-index: 3;
}
.energy-value {
  font-size: clamp(28px, 8.5vw, 40px);
  font-weight: 800;
  letter-spacing: -0.5px;
  line-height: 1;
  display: inline-flex; align-items: center; gap: 6px;
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.55);
}
.bolt {
  width: 0.62em; height: 0.62em;
  color: var(--accent);
  filter: drop-shadow(0 0 8px var(--accent));
}
.energy-rate {
  margin-top: 4px;
  font-size: 14px;
  color: var(--accent);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.5);
}
.buff-pill {
  display: inline-block;
  margin: 0 0 5px;   /* it sits ABOVE the energy figure now */
  max-width: 100%;
  font-size: 11px;
  font-weight: 700;
  color: #ffe9a8;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(255, 200, 60, 0.14);
  border: 1px solid rgba(255, 200, 60, 0.35);
}

/* ------------------------------ Scene ------------------------------ */
.scene {
  position: relative;
  /* 1 0 auto — does NOT shrink. The module panel below is scrollable and takes
     the squeeze instead, which makes this box a constant height in every state
     and is what lets the mini-map be bottom-anchored without resizing. */
  flex: 1 0 auto;
  min-height: 300px;
  margin: 4px 12px 0;   /* 8px read as a dead band under the header */
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  touch-action: manipulation;
  background:
    radial-gradient(130% 70% at 50% -8%, rgba(150, 220, 255, 0.12), transparent 55%),
    radial-gradient(90% 60% at 50% 118%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 60%),
    linear-gradient(180deg, rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0.42));
  border: 1px solid var(--line);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), inset 0 -40px 80px rgba(0, 0, 0, 0.3);
}

.scene-fx {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* "tap to harvest" — a little thought bubble drifting off the ship's side,
   so the painted hull never covers it. Dots trail down-left toward the probe. */
.tap-hint {
  position: absolute;
  left: calc(50% + 52px);
  top: calc(50% - 74px);
  padding: 7px 11px;
  font-size: 10px;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  white-space: nowrap;
  color: rgba(220, 243, 255, 0.85);
  background: rgba(4, 18, 30, 0.66);
  border: 1px solid rgba(127, 227, 255, 0.3);
  border-radius: 14px;
  pointer-events: none;
  animation: hintBob 3.6s ease-in-out infinite;
}
.tap-hint::before, .tap-hint::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  background: rgba(4, 18, 30, 0.66);
  border: 1px solid rgba(127, 227, 255, 0.28);
}
.tap-hint::before { width: 7px; height: 7px; left: -11px; bottom: -7px; }
.tap-hint::after { width: 4px; height: 4px; left: -20px; bottom: -16px; }
@keyframes hintBob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}
/* Painted inline icons inside coach text. */
.coach-ic { width: 14px; height: 14px; vertical-align: -2px; display: inline-block; }

/* Probe */
.probe {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  text-align: center;
}
.probe-body {
  line-height: 0;
  /* ALL probe glows compose here, on the element that holds only the sprite,
     so drop-shadow traces the hull's alpha. Putting any glow on .probe (which
     also contains the rectangular lamp div) cast the shadow from the
     container's composite instead — the "boxy double glow" testers saw. */
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.5)) var(--tier-glow, ) var(--supporter-glow, );
  animation: bob 3.6s ease-in-out infinite;
}
/* Painted hull sprite (falls back to the inline SVG if it fails to load).
   Cap BOTH axes: hull art aspect ratios vary a lot (the torpedo is tall and
   slim), so a fixed width alone let tall hulls dominate the scene.
   --hull-hue drifts the lamp's cyan toward the current zone accent. */
.probe-img {
  max-width: 84px; max-height: 112px; width: auto; height: auto;
  display: block; filter: hue-rotate(var(--hull-hue, 0deg));
}
.probe-svg { width: 92px; height: auto; display: block; }
.p-accent-stroke { stroke: var(--accent); }
.p-accent-fill { fill: var(--accent); }
.p-eye { filter: drop-shadow(0 0 5px var(--accent)); }
.p-eye-ring { fill: color-mix(in srgb, var(--accent) 22%, transparent); }
.p-blink { animation: pblink 2.6s ease-in-out infinite; }
@keyframes pblink { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
.probe-lamp {
  position: absolute;
  top: 44px; left: 50%;
  width: 90px; height: 150px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 40%, transparent), transparent 72%);
  clip-path: polygon(38% 0, 62% 0, 100% 100%, 0 100%);
  opacity: 0.55;
  pointer-events: none;
}
.probe.pulse .probe-body { animation: tapPulse 0.32s ease; }
@keyframes bob {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-8px) rotate(2deg); }
}
@keyframes tapPulse {
  0% { transform: scale(1); }
  40% { transform: scale(1.22); }
  100% { transform: scale(1); }
}

/* Ship evolution: orbiting module parts + hull-tier glow */
.probe { z-index: 2; }
.probe-attachments {
  position: absolute;
  top: 50%; left: 50%;
  width: 0; height: 0;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 1;
}
.attach {
  position: absolute;
  left: 0; top: 0;
  width: 0; height: 0;
  transform-origin: 0 0;
  transition: transform 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
.attach-i {
  position: absolute;
  left: 0; top: 0;
  width: 9px; height: 9px;
  transform: translate(-50%, -50%) rotate(45deg);
  border-radius: 2px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 92%, #fff), var(--accent));
  box-shadow: 0 0 7px color-mix(in srgb, var(--accent) 70%, transparent);
  animation: attachBob 3s ease-in-out infinite;
}
@keyframes attachBob { 0%, 100% { opacity: 0.7; } 50% { opacity: 1; } }

/* The more modules you own, the more the hull glows. (filter only — bob owns transform) */
/* Hull-mark prestige glow, layered into .probe-body's filter chain above.
   Tiers 3-4 stay translucent — at full strength they read as a slab. */
.probe[data-tier="1"] { --tier-glow: drop-shadow(0 0 6px color-mix(in srgb, var(--accent) 45%, transparent)); }
.probe[data-tier="2"] { --tier-glow: drop-shadow(0 0 10px color-mix(in srgb, var(--accent) 60%, transparent)); }
.probe[data-tier="3"] { --tier-glow: drop-shadow(0 0 10px color-mix(in srgb, var(--accent) 55%, transparent)); }
.probe[data-tier="4"] { --tier-glow: drop-shadow(0 0 13px color-mix(in srgb, var(--accent) 65%, transparent)); }

/* Bubbles */
.bubbles { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.bubbles span {
  position: absolute;
  bottom: -20px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  animation: rise linear infinite;
}
.bubbles span:nth-child(1) { left: 12%; width: 5px; height: 5px; animation-duration: 7s; animation-delay: 0s; }
.bubbles span:nth-child(2) { left: 28%; width: 9px; height: 9px; animation-duration: 9s; animation-delay: 1.5s; }
.bubbles span:nth-child(3) { left: 41%; width: 4px; height: 4px; animation-duration: 6s; animation-delay: 0.8s; }
.bubbles span:nth-child(4) { left: 57%; width: 7px; height: 7px; animation-duration: 8.5s; animation-delay: 2.2s; }
.bubbles span:nth-child(5) { left: 68%; width: 5px; height: 5px; animation-duration: 7.5s; animation-delay: 3s; }
.bubbles span:nth-child(6) { left: 78%; width: 10px; height: 10px; animation-duration: 10s; animation-delay: 0.4s; }
.bubbles span:nth-child(7) { left: 88%; width: 4px; height: 4px; animation-duration: 6.5s; animation-delay: 1.9s; }
.bubbles span:nth-child(8) { left: 49%; width: 6px; height: 6px; animation-duration: 8s; animation-delay: 4s; }
@keyframes rise {
  0% { transform: translateY(0); opacity: 0; }
  10% { opacity: 0.7; }
  90% { opacity: 0.5; }
  100% { transform: translateY(-320px); opacity: 0; }
}

/* Floating tap numbers */
.floatnum {
  position: absolute;
  transform: translate(-50%, -50%);
  font-weight: 800;
  font-size: 23px;
  color: #fff;
  text-shadow: 0 0 10px var(--accent), 0 1px 2px rgba(0,0,0,0.6);
  pointer-events: none;
  animation: floatup 0.9s ease-out forwards;
  z-index: 5;
}
/* Objective navigation: briefly ring the control the goal points at. */
.nav-flash { animation: navFlash 0.85s ease-out 2; }
@keyframes navFlash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(127, 227, 255, 0); }
  35% { box-shadow: 0 0 0 3px rgba(127, 227, 255, 0.8), 0 0 18px rgba(127, 227, 255, 0.45); }
}
@media (prefers-reduced-motion: reduce) {
  .nav-flash { animation: none; outline: 2px solid var(--accent); }
}

@keyframes floatup {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.7); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -190%) scale(1.1); }
}

/* Depth gauge */
/* Depth now lives in the header beside the zone name (#7/#12: the in-scene
   DEPTH bar duplicated the mini-map, and both testers asked for it gone). That
   also removed a layout collision: .depth-gauge sat at bottom:74px and
   .scene-actions at bottom:12px, both absolutely positioned, so the moment the
   cooldown chips appeared the wrapping action row grew past 62px and rendered
   straight through the bar and the "next:" line (#17, five builds running). */
.hud-depth {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 12px; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hud-depth.blocked { color: #ffb4a2; font-weight: 700; }

/* Scene action buttons */
/* Bottom-anchored and now EXACTLY one row tall, always. The cooldown pills used
   to be a second row inside this column, so the container's height changed with
   them; they float over the descend button instead (see .descend-stack), which is
   what lets the mini-map run all the way down to this row. */
.scene-actions {
  position: absolute;
  left: 12px; right: 12px; bottom: 12px;
}
/* The invariant: this row is identical in every state — same two children, same
   widths, same place. Previously the hull-gated button took flex:1 1 100% and the
   cooldown chips joined the same wrapping row, so the pair moved and resized
   constantly (#2/#3). */
.scene-actions-main { display: flex; gap: 8px; align-items: stretch; }

/* The descend button plus its floating options. Absolutely positioned so the
   pills never add height and never reach left of this column. */
.descend-stack { position: relative; flex: 1 1 auto; display: flex; min-width: 0; }
/* Cooldown options: one thin line each, side by side, hovering over the descend
   button only. */
.dive-opts {
  position: absolute; left: 0; right: 0; bottom: 100%; margin-bottom: 6px;
  display: flex; gap: 6px;
}
.dive-btn {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 74px;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(4, 16, 28, 0.7);
  color: var(--ink);
  padding: 6px 4px;
  cursor: pointer;
  backdrop-filter: blur(6px);
}
.dive-btn-icon { display: inline-flex; color: var(--accent); }
.dive-btn-icon .ic { width: 20px; height: 20px; }
.dive-btn-label { font-size: 11px; font-weight: 700; margin-top: 2px; font-variant-numeric: tabular-nums; }
.dive-btn.ready { border-color: color-mix(in srgb, var(--accent) 60%, transparent); box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 30%, transparent); }
.dive-btn:disabled { opacity: 0.55; cursor: default; }

.descend-btn {
  flex: 1 1 auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  border-radius: var(--radius-sm);
  border: none;
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 85%, #fff), var(--accent));
  color: #04121e;
  padding: 7px 10px;
  cursor: pointer;
  font-weight: 800;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4), 0 0 20px color-mix(in srgb, var(--accent) 40%, transparent);
}
.descend-top { font-size: 13px; letter-spacing: 0.5px; }
.descend-top span { font-weight: 700; opacity: 0.8; }
/* One line, always. When hull-gated this carries "cost → destination zone", and
   a long generated zone name wrapped it to two lines on a 320px phone, growing
   the button by 7px — the exact resize this row is supposed to never do. */
.descend-cost {
  font-size: 11px; font-weight: 700; opacity: 0.85; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.descend-btn:disabled {
  background: rgba(255, 255, 255, 0.08);
  color: var(--ink-faint);
  box-shadow: none;
  cursor: default;
}
/* Parked at the hull's rated limit — the button becomes the reinforce purchase. */
.descend-btn.hull-gated {
  background: linear-gradient(180deg, #ffcf7d, #e89b3c);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4), 0 0 20px rgba(232, 155, 60, 0.45);
}
.descend-btn.hull-gated:disabled {
  background: rgba(255, 190, 100, 0.14);
  color: #d9b98c;
  box-shadow: none;
}
.descend-btn:active:not(:disabled) { transform: translateY(1px); }

/* ------------------------------ Objective tracker ------------------------------ */
.objective {
  /* Thinner (#3): it is a persistent strip, not a card to read. */
  margin: 6px 12px 0;
  padding: 5px 12px 6px;
  background: rgba(8, 22, 38, 0.55);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.objective { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.objective:active { transform: scale(0.99); }
.objective-go { color: var(--ink-faint); font-size: 12px; margin-left: 7px; }
.objective-head { display: flex; justify-content: space-between; align-items: center; }
.objective-head .objective-reward { margin-left: auto; }
.objective-kicker { font-size: 9px; letter-spacing: 2px; color: var(--ink-dim); }
.objective-reward { font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; gap: 3px; opacity: 0.92; }
/* Label and count share a line and the bar is the last thing in the card — one
   row shorter than the count sitting under the bar. */
.objective-label-row {
  display: flex; align-items: baseline; gap: 8px;
  margin: 3px 0 6px;
}
.objective-label {
  font-size: 13px; font-weight: 600;
  flex: 1 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.objective-bar {
  height: 5px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.4);
  overflow: hidden; border: 1px solid var(--line);
}
.objective-fill {
  height: 100%; width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #fff));
  transition: width 0.4s ease;
}
.objective-count {
  font-size: 10px; color: var(--ink-faint);
  flex: 0 0 auto; font-variant-numeric: tabular-nums;
}
.objective.flash { animation: objFlash 0.7s ease; }
@keyframes objFlash {
  0% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
  35% { box-shadow: 0 0 18px color-mix(in srgb, var(--accent) 55%, transparent); border-color: var(--accent); }
  100% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
}

/* ------------------------------ Tabs ------------------------------ */
.tabs {
  display: flex;
  gap: 3px;
  padding: 10px 12px 4px;
  z-index: 3;
}
.tabs button {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--ink-dim);
  font-size: 12px;
  font-weight: 700;
  padding: 8px 0;
  border-radius: 10px;
  cursor: pointer;
  position: relative;
}
.tabs button.active { color: var(--ink); background: rgba(255, 255, 255, 0.07); }

/* ------------------------------ Panels ------------------------------ */
/* Fixed-width holder for the two jump arrows. Its size never changes, so the tab
   labels never move — see the note in index.html. */
.pj-slot {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 2px;
}
.pj-btn {
  flex: 0 0 auto; align-self: center;
  width: 30px; height: 30px; margin-left: 2px;
  border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: rgba(255, 255, 255, 0.06); color: var(--ink-dim);
  font-size: 10px; line-height: 1;
}
.pj-btn:active { transform: scale(0.92); }

.panel-wrap {
  flex: 0 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Reaches the bottom edge. It only has to clear the home indicator now that
     the jump control moved into the tab row — the old 58px was reserved for that
     button and showed up as a dark band under the last row. */
  padding: 4px 12px calc(10px + env(safe-area-inset-bottom));
  max-height: 42vh;
}
@media (min-width: 520px) { .panel-wrap { max-height: none; } }

.panel { display: flex; flex-direction: column; gap: 8px; }

/* Manual-harvest (tap) card — sits at the top of the Modules panel */
.tap-card {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 11px;
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(0,0,0,0.18));
  border: 1px solid var(--line);
}
.tap-card-info { display: flex; flex-direction: column; min-width: 0; flex: 1; }
/* Both lines are single-line by construction, so these two cards are a constant
   50px tall whatever the width, the magnitude of the numbers, or how wide the
   upgrade button's cost has grown. The copy was shortened to fit; this makes the
   height an invariant rather than a hope — on a 320px phone a big cost label
   squeezes this column enough to wrap even 25 characters. */
.tap-card-title,
.tap-card-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tap-card-title { font-size: 13px; font-weight: 700; }
.tap-card-lv { font-size: 11px; font-weight: 600; color: var(--ink-dim); }
.tap-card-sub { font-size: 11px; color: var(--ink-dim); margin-top: 1px; }
.tap-card-sub b { color: var(--ink); }
.tap-upgrade-btn {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--ink);
  font-size: 12px; font-weight: 700;
  cursor: pointer;
}
.tap-upgrade-btn .wide-cost { font-variant-numeric: tabular-nums; color: var(--accent); }
.tap-upgrade-btn:disabled { opacity: 0.5; cursor: default; }

/* Buy-amount selector */
.buy-amount {
  display: flex; align-items: center; gap: 4px;
  margin-bottom: 2px;
}
.buy-amount-label { font-size: 11px; color: var(--ink-dim); margin-right: 4px; }
.buy-amount button {
  flex: 1;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--line);
  color: var(--ink-dim);
  padding: 5px 0;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.buy-amount button.active {
  color: #04121e;
  background: var(--accent);
  border-color: var(--accent);
}

/* Module rows */
.module-list { display: flex; flex-direction: column; gap: 7px; }
.module {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  width: 100%;
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--ink);
  cursor: pointer;
  backdrop-filter: blur(4px);
}
.module:active:not(:disabled) { transform: scale(0.99); }
.module:disabled { cursor: default; }
.module.locked { opacity: 0.5; }
.module.far { display: none; }
.module:disabled:not(.locked) .module-cost { color: var(--bad); }

.module-icon {
  width: 38px; height: 38px; flex: 0 0 38px;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
}
.module.locked .module-icon { opacity: 0.5; }

/* Stylized SVG sprite icons (modules + fish) */
.spr { width: 100%; height: 100%; display: block; object-fit: contain; }
.s-accf { fill: var(--accent); }
.s-accs { stroke: var(--accent); }
.s-glow { filter: drop-shadow(0 0 2.5px var(--accent)); }
.module-main { flex: 1; min-width: 0; }
.module-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.module-name { font-size: 14px; font-weight: 700; }
.module-owned {
  font-size: 15px; font-weight: 800;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.module-desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 11px;
  color: var(--ink-faint);
  margin: 1px 0 3px;
}
.module-meta { display: flex; gap: 8px; font-size: 11px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.module-milestone { color: #ffd98a; }
.module-buy { text-align: right; flex: 0 0 auto; min-width: 74px; }
.module-cost { display: block; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.module-buyqty { font-size: 10px; color: var(--ink-faint); }

/* Cards */
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px;
  backdrop-filter: blur(4px);
}
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.card-head h3 { font-size: 15px; }
.card-sub { font-size: 12px; color: var(--ink-dim); }
.card-note { font-size: 12px; color: var(--ink-dim); line-height: 1.5; margin-top: 4px; }
.card-note.surge { color: #ffe9a8; margin-top: 8px; font-weight: 600; }

.wide-btn {
  margin-top: 12px;
  width: 100%;
  display: flex; align-items: center; justify-content: space-between;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  color: var(--ink);
  padding: 11px 13px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.wide-btn:disabled { opacity: 0.5; cursor: default; }
.wide-btn:not(:disabled):active { transform: translateY(1px); }
.wide-cost { font-variant-numeric: tabular-nums; color: var(--accent); }

/* Refit card */
.refit-card { text-align: left; }
.refit-cores { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.refit-cores-num { font-size: 30px; font-weight: 800; line-height: 1; }
.refit-cores-label { font-size: 12px; color: var(--ink-dim); margin-top: 2px; }
.refit-btn { background: rgba(120, 190, 255, 0.14); border-color: rgba(120, 190, 255, 0.4); }

/* Log */
.log-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.log-entry {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
}
.log-zone { font-size: 13px; font-weight: 700; margin-bottom: 5px; }
.log-zone span { font-size: 11px; color: var(--ink-faint); font-weight: 400; }
.log-text { font-size: 12px; line-height: 1.6; color: var(--ink-dim); font-style: italic; }
.log-entry.frag {
  border-left-width: 2px;
  border-left-color: color-mix(in srgb, var(--accent) 40%, transparent);
  background: rgba(6, 20, 34, 0.32);
  padding: 9px 13px;
}
.log-frag-depth {
  font-size: 10px; letter-spacing: 1.5px;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  margin-bottom: 3px;
}

.stats-card h3 { font-size: 14px; margin-bottom: 10px; }
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.stat-grid > div { display: flex; flex-direction: column; }
.stat-k { font-size: 11px; color: var(--ink-faint); }
.stat-v { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ------------------------------ Bestiary ------------------------------ */
.bestiary-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.bestiary-head h3 { font-size: 15px; }
.bestiary-count { font-size: 10.5px; color: var(--ink-dim); text-align: right; }
.bestiary-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.fish-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 10px;
  text-align: center;
  backdrop-filter: blur(4px);
}
.fish-card.locked { opacity: 0.45; }
.fish-icon { width: 46px; height: 40px; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)); }
.fish-icon.silhouette { filter: brightness(0.32) saturate(0.25); opacity: 0.5; }
.fish-name { font-size: 13px; font-weight: 700; margin-top: 6px; }
.fish-bonus { font-size: 11px; color: var(--accent); font-weight: 600; margin-top: 2px; }
.fish-card.locked .fish-bonus { color: var(--ink-faint); font-weight: 400; }
.fish-note { font-size: 10.5px; color: var(--ink-faint); font-style: italic; line-height: 1.4; margin-top: 5px; }
.fish-card { position: relative; }
.fish-card.edition {
  border-color: var(--ed);
  box-shadow: 0 0 12px color-mix(in srgb, var(--ed) 45%, transparent), inset 0 0 22px color-mix(in srgb, var(--ed) 12%, transparent);
}
.fish-ed { font-size: 9.5px; font-weight: 800; letter-spacing: 0.5px; margin-bottom: 3px; text-shadow: 0 0 6px currentColor; }

/* Bestiary showcase — tap a discovered fish to see the painting big. */
.fish-card:not(.locked) { cursor: pointer; }
.fish-card:not(.locked):active { transform: scale(0.97); }
.showcase-art { width: min(60vw, 232px); height: min(60vw, 232px); margin: 4px auto 0; }
.showcase-art .spr { filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.55)); }
.showcase-ed { display: inline-block; font-size: 12px; margin: 6px 0 0; }
.showcase-bonus { font-size: 13px; color: var(--accent); font-weight: 700; margin-top: 2px; }

/* v-next: fish inventory — copy-count badge on cards + sell-from-hold panel */
.fish-count {
  position: absolute; top: 6px; right: 6px;
  font-size: 11px; font-weight: 800;
  color: #04121e; background: var(--accent);
  border-radius: 999px; padding: 1px 6px;
  box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 50%, transparent);
}
.fish-hold {
  margin: 12px 0 4px;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.03);
  text-align: left;
}
.hold-title { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--ink-faint); margin-bottom: 4px; }
.hold-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 4px 0; }
.hold-row + .hold-row { border-top: 1px solid rgba(255, 255, 255, 0.06); }
.hold-label { font-size: 13px; font-weight: 600; }
.hold-sell {
  min-height: 44px; min-width: 92px; padding: 6px 14px;
  border: 1px solid rgba(127, 227, 255, 0.5); border-radius: 999px;
  background: rgba(127, 227, 255, 0.06); color: var(--accent);
  font-weight: 800; font-size: 12px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px; white-space: nowrap;
}
.hold-sell:active { transform: scale(0.96); }
.hold-keep { font-size: 11px; color: var(--ink-faint); font-style: italic; }
.showcase-close { justify-content: center; }

/* Painted zone backdrop bleeding out of the story modal's top edge. */
.story-banner {
  display: block;
  width: calc(100% + 44px);
  height: 150px;
  margin: -22px -22px 14px;
  border-radius: 20px 20px 0 0;
  object-fit: cover;
}
/* Offline "welcome back" cameo — the deepest catalogued fish. */
.offline-mate { display: flex; flex-direction: column; align-items: center; gap: 4px; margin: 4px 0 2px; }
.offline-mate .spr { width: 92px; height: 92px; }
.offline-mate span { font-size: 11.5px; color: var(--ink-dim); font-style: italic; }

/* Native-store affordances. */
.store-restore {
  width: 100%; margin-top: 14px; padding: 10px 0;
  background: transparent; border: 1px solid var(--line); border-radius: 10px;
  color: var(--ink-dim); font-size: 12.5px; font-weight: 600; cursor: pointer;
}

/* Painted store & currency icons (no more emoji in the shop). */
.pearl-ic { width: 15px; height: 15px; vertical-align: -3px; display: inline-block; }
.store-ic { width: 34px; height: 34px; object-fit: contain; display: block; }
.tab-ic { width: 14px; height: 14px; vertical-align: -2px; display: inline-block; }
.store-offer-ic { width: 22px; height: 22px; flex: 0 0 auto; display: inline-block; }
.skin-art { width: 54px; height: 54px; object-fit: contain; display: block; margin: 0 auto; }
.store-buy .pearl-ic, .bundle .pearl-ic { width: 13px; height: 13px; vertical-align: -2px; }

/* ------------------------------ Modals ------------------------------ */
.modal-root {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(6px);
  z-index: 40;
  padding: 24px;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.modal-root.open { opacity: 1; }
/* While fading out (not yet fully hidden) don't intercept taps meant for the UI. */
.modal-root:not(.open) { pointer-events: none; }
.modal-card {
  width: 100%;
  max-width: 340px;
  /* Never taller than the viewport — long content scrolls INSIDE the card.
     (A post-dive card once outgrew the screen with no way to scroll it.) */
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: linear-gradient(180deg, #0b2136, #050f1c);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 20px;
  padding: 22px;
  text-align: center;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
  transform: translateY(10px) scale(0.98);
  transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
.modal-root.open .modal-card { transform: translateY(0) scale(1); }
.modal-kicker { font-size: 11px; letter-spacing: 2px; text-transform: uppercase; color: var(--ink-dim); }
.modal-title { font-size: 22px; margin: 4px 0; }
.modal-sub { font-size: 12px; color: var(--ink-faint); margin-bottom: 12px; }
.modal-story {
  font-size: 13px; line-height: 1.65; color: var(--ink-dim);
  font-style: italic; margin: 10px 0 18px;
}
.modal-energy { color: var(--accent); font-style: normal; }
.offline-stats {
  display: flex; flex-direction: column; gap: 6px;
  margin: 0 0 16px;
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.offline-stats > div { display: flex; justify-content: space-between; align-items: baseline; }
.os-k { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: var(--ink-dim); }
.os-v { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.modal-fine { font-size: 10.5px; color: var(--ink-faint); text-align: center; margin-top: 8px; }
.modal-btn {
  width: 100%;
  min-height: 44px;
  background: var(--accent);
  color: #04121e;
  border: none;
  padding: 12px;
  border-radius: 12px;
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
}
.modal-btn.ghost { background: rgba(255, 255, 255, 0.08); color: var(--ink); }
.modal-actions { display: flex; gap: 8px; }

/* ------------------------------ Toasts ------------------------------ */
.toasts {
  position: absolute;
  left: 0; right: 0; bottom: 16px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  pointer-events: none;
  z-index: 50;
  padding: 0 16px;
}
.toast {
  background: rgba(6, 20, 34, 0.92);
  border: 1px solid var(--line);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 600;
  padding: 9px 14px;
  border-radius: 999px;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  max-width: 100%;
  text-align: center;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* Warning toasts wear the same amber as the REINFORCE HULL button they are about,
   so the message and the button read as one thing. */
.toast.warn {
  background: linear-gradient(180deg, #ffcf7d, #e89b3c);
  border-color: rgba(0, 0, 0, 0.25);
  color: #1a1204;
  font-weight: 700;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5), 0 0 18px rgba(232, 155, 60, 0.35);
}
.toast.warn b { color: #0d0a02; }
/* NOT a flex container. As `inline-flex` every text run and every <b> in a message
   became its own flex item, so a sentence with an emphasised zone name in the middle
   laid out as three shrink-to-fit columns, each wrapping internally:
     "Hull not rated  |  The Pressure  |  — reinforce to"
     "for             |  Cathedral     |  descend."
   As a plain inline-block the text just flows. The fish sprite that flex was there
   for aligns inline instead. */
.toast { display: inline-block; }
.toast-fish {
  display: inline-block; width: 22px; height: 18px;
  vertical-align: -4px; margin-right: 4px;
}
.toast-fish .spr { width: 100%; height: 100%; }

/* ------------------------------ Store / header ------------------------------ */
/* The left band's flex behaviour is set with the other two bands further up — it must
   NOT grow. A leftover `flex: 1 1 auto` here (from when the chips were on the depth
   line and the stack had to stretch) was winning on cascade order and swallowing all
   the free space, which pinned the currency band against the buttons and left the
   gap on its other side. */
.hud-zone-name, .hud-zone-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hud-actions { flex-shrink: 0; }
/* Small inline symbol sizing for the custom (non-emoji) glyphs. */
.ic-inline { width: 12px; height: 12px; vertical-align: -2px; }
.ed-ic { width: 12px; height: 12px; vertical-align: -1.5px; filter: drop-shadow(0 0 4px currentColor); }
.jar-ic { width: 14px; height: 14px; vertical-align: -2px; color: #ffd94a; }
.tv-ic { width: 15px; height: 15px; vertical-align: -3px; }
.btn-ic { width: 14px; height: 14px; vertical-align: -2px; margin-right: 2px; }
.coach-gear { width: 13px; height: 13px; vertical-align: -2px; }
.fish-zone { font-size: 9.5px; color: var(--ink-faint); letter-spacing: 0.4px; margin-top: 1px; }
/* Tappable vault balance — answers "what is that jar" permanently. */
.hud-vault {
  display: inline-flex; align-items: center; gap: 3px;
  background: rgba(255, 217, 74, 0.09); color: #ffd94a;
  border: 1px solid rgba(255, 217, 74, 0.35); border-radius: 999px;
  padding: 3px 8px; font-size: 12.5px; font-weight: 800; cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.hud-pearls {
  display: flex; align-items: center; gap: 3px;
  font-weight: 700; font-size: 12.5px;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.pearl-gem { display: inline-flex; color: #bfe9ff; filter: drop-shadow(0 0 4px #bfe9ff); }
.pearl-gem .ic { width: 15px; height: 15px; }
.store-btn {
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid var(--line);
  color: var(--ink);
  width: 40px; height: 40px;
  border-radius: 50%;
  cursor: pointer;
}
.store-btn .ic, .mute-btn .ic { width: 17px; height: 17px; }

/* Inline vector icons (paths declare their own fill/stroke; currentColor = the
   element's `color`, so themed icons just set color) */
.ic { width: 1em; height: 1em; display: inline-block; vertical-align: -0.14em; }
.ic-e { color: var(--accent); }

.store-root {
  position: absolute; inset: 0;
  z-index: 45;
  display: flex; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.store-root.open { opacity: 1; }
.store-card {
  width: 100%; max-width: 480px;
  background: linear-gradient(180deg, #0a1c30, #050e1a);
  display: flex; flex-direction: column;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: calc(14px + env(safe-area-inset-top)) 14px calc(20px + env(safe-area-inset-bottom));
}
/* Pinned: .store-card is the scroller, so the count stays readable while you
   scroll down to what you want to spend it on (#1). The negative margins and
   matching padding let the sticky band span the card's full width rather than
   leaving a transparent gutter its content shows through. */
.store-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  position: sticky; top: calc(-1px - env(safe-area-inset-top)); z-index: 5;
  margin: calc(-14px - env(safe-area-inset-top)) -14px 0;
  padding: calc(14px + env(safe-area-inset-top)) 14px 8px;
  background: linear-gradient(180deg, #0a1c30 82%, rgba(10, 28, 48, 0));
}
/* Title and count on ONE line (#4). The count shrinks rather than wrapping —
   it is a chip beside the heading, not a second heading. */
.store-headline { display: flex; align-items: center; gap: 10px; min-width: 0; }
.store-title { font-size: 20px; font-weight: 800; white-space: nowrap; }
.store-pearls {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 1 auto; min-width: 0;
  font-size: 14px; font-weight: 800; color: #dff4ff; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden;
  background: rgba(75, 212, 255, 0.12); border: 1px solid rgba(75, 212, 255, 0.45);
  border-radius: 999px; padding: 6px 13px;
}
.store-pearls .pearl-ic { width: 16px; height: 16px; }
.store-close {
  background: rgba(255, 255, 255, 0.1); border: 1px solid var(--line); color: var(--ink);
  width: 44px; height: 44px; border-radius: 50%; font-size: 14px; cursor: pointer; flex: 0 0 auto;
}
.store-tagline { font-size: 12px; color: var(--ink-dim); line-height: 1.5; margin: 12px 0; }
.store-section-title { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: var(--ink-faint); margin: 16px 0 8px; }
.store-items { display: flex; flex-direction: column; gap: 8px; }
.store-item {
  position: relative; display: flex; align-items: center; gap: 10px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 11px;
}
.store-item-icon { font-size: 25px; width: 32px; text-align: center; }
.store-item-main { flex: 1; min-width: 0; }
.store-item-name { font-size: 14px; font-weight: 700; display: block; }
.store-item-blurb { font-size: 11px; color: var(--ink-dim); display: block; margin-top: 1px; line-height: 1.35; }
.store-buy {
  flex: 0 0 auto; border: none; border-radius: 10px; padding: 9px 12px; cursor: pointer; min-height: 44px;
  font-size: 13px; font-weight: 800; background: var(--accent); color: #04121e; white-space: nowrap;
}
.store-buy.cant { opacity: 0.45; }
.store-buy s { opacity: 0.7; font-weight: 500; }
.store-owned { flex: 0 0 auto; font-size: 12px; font-weight: 800; color: var(--good); padding: 9px 4px; }
.store-badge {
  position: absolute; top: -7px; left: 10px;
  font-size: 9px; font-weight: 800; letter-spacing: 0.4px;
  background: var(--accent); color: #04121e; padding: 2px 7px; border-radius: 999px;
}
.store-skins { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.skin-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 8px; text-align: center;
}
.skin-card.eq { border-color: var(--accent); box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 30%, transparent); }
.skin-glyph { font-size: 30px; }
.skin-name { display: block; font-size: 12px; font-weight: 700; margin: 4px 0 8px; }
.skin-eq { font-size: 11px; font-weight: 800; color: var(--good); }
.store-bundles { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
.bundle {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 12px 6px; cursor: pointer; color: var(--ink);
}
.bundle-label { font-size: 9.5px; font-weight: 700; color: var(--ink-dim); text-align: center; min-height: 22px; display: flex; align-items: center; }
.bundle-amt { font-size: 13px; font-weight: 800; }
.bundle-price { font-size: 12px; font-weight: 700; color: var(--accent); }
.bundle-bonus { font-size: 9px; font-weight: 800; color: var(--good); }
.store-foot { font-size: 10.5px; color: var(--ink-faint); line-height: 1.5; margin-top: 16px; text-align: center; }

/* Premium storefront. Colour has ROLES (art-director): gold = costs money
   (prices + real-money CTAs), cyan = spend soft-currency (Pearls), purple =
   subscription, red is RESERVED for --bad only. No urgency animation on paid
   buttons — pulsing is reserved for earned/free rewards. */
.store-card.mode-max { background: linear-gradient(180deg, #2a0e12, #10060a); }
.store-card.mode-max .store-title { color: #ffd94a; text-shadow: 0 0 12px rgba(255, 180, 40, 0.5); }
/* Pearl (soft-currency) buys = cyan; real-money .store-buy.money stays gold. */
.store-card.mode-max .store-buy { background: linear-gradient(180deg, #8fe9ff, #4bd4ff); color: #04121e; box-shadow: 0 4px 14px rgba(75, 212, 255, 0.3); }
.store-card.mode-max .store-badge { background: #ffd21f; color: #3a1500; }
.store-card.mode-max .store-tagline { color: var(--ink-dim); font-weight: 600; }
.store-card.mode-max .bundle-price { color: #ffd94a; }
/* Weekly offer: a calm bordered card, not a full-gold billboard, so real
   products win the eye. Only GRAB IT stays gold. */
.store-offer {
  display: flex; align-items: center; gap: 9px; text-align: left;
  background: none; color: var(--ink-dim);
  border-radius: 10px; padding: 8px 10px; font-size: 11.5px; font-weight: 600; line-height: 1.35;
  border: 1px solid color-mix(in srgb, #ffd21f 26%, transparent);
}
.store-offer b { color: #ffd94a; }
.store-offer .offer-text { flex: 1; min-width: 0; }
.offer-buy {
  flex: 0 0 auto; margin-left: auto; min-height: 38px; background: none;
  color: #ffd94a; border: 1px solid color-mix(in srgb, #f2a326 65%, transparent);
  border-radius: 9px; padding: 8px 14px; font-weight: 800; font-size: 12px; cursor: pointer;
}
@keyframes offerPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
.bundle.hot { border-color: #ffd21f; box-shadow: 0 0 14px rgba(255, 200, 30, 0.3); }

/* ── Themes (palette-only; the painted world stays painted) ─────────────── */
body[data-theme="biolume"]   { --bg-top: #6a48d8; --bg-bottom: #240b52; --accent: #c39bff; }
body[data-theme="blooddeep"] { --bg-top: #b8404f; --bg-bottom: #47080f; --accent: #ff8f7a; }
body[data-theme="daylight"]  { --bg-top: #63c2e4; --bg-bottom: #14688f; --accent: #ffe08a; }

.theme-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.theme-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 9px 12px;
}
.theme-dot { width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 8px currentColor; }
.theme-name { flex: 1; font-weight: 700; font-size: 14px; text-align: left; }

/* Save codes */
.save-code {
  width: 100%; min-height: 110px; margin-top: 10px; resize: vertical;
  background: rgba(0, 0, 0, 0.35); color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px; padding: 10px;
  font: 11px/1.45 ui-monospace, Menlo, Consolas, monospace; word-break: break-all;
}

/* Supporter glow (tip-jar gratitude) */
/* Supporter glow: a VARIABLE consumed by .probe-body's filter chain — never a
   filter on .probe itself, which would shadow the lamp's rectangle too. */
.probe.supporter { --supporter-glow: drop-shadow(0 0 13px rgba(255, 205, 90, 0.5)); }

/* Vault-fill float (manual progress feedback) */
.vault-float {
  position: fixed; top: 62px; right: 16px; z-index: 60; pointer-events: none;
  font-size: 13px; font-weight: 800; color: #ffd94a;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
  animation: vaultFloat 1.25s ease-out forwards;
}
@keyframes vaultFloat {
  from { opacity: 0; transform: translateY(8px); }
  20% { opacity: 1; }
  to { opacity: 0; transform: translateY(-26px); }
}
@media (prefers-reduced-motion: reduce) { .vault-float { animation: none; opacity: 0; } }

/* Dive recharge chip (pearl sink) */
.dive-recharge {
  /* One line of text, not a mystery bubble. The round chips sat over the probe's
     lamp and needed a toast to explain what they were; "Skip for 4 pearls" needs
     no explaining (#3). */
  flex: 1 1 0; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 30px; padding: 4px 8px;
  background: rgba(4, 14, 22, 0.62); color: rgba(143, 233, 255, 0.9);
  border: 1px solid rgba(75, 212, 255, 0.22); border-radius: 999px;
  font-size: 11px; font-weight: 700; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dive-recharge:disabled { opacity: 0.6; cursor: default; }
.dive-recharge .pearl-ic { width: 12px; height: 12px; }

/* ------------------------------ Core upgrades ------------------------------ */
.core-upgrades { display: flex; flex-direction: column; gap: 7px; }
.cu-item {
  display: flex; align-items: center; gap: 10px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 10px 11px;
}
.cu-icon { font-size: 22px; width: 30px; text-align: center; }
.cu-main { flex: 1; min-width: 0; }
.cu-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
/* The name never wraps — "Auto-Descend" was breaking across two lines because the
   "Lv 1 - every 4.0s" meta beside it was allowed to squeeze it. The meta yields
   instead; it is the less important half. */
.cu-name { font-size: 14px; font-weight: 700; white-space: nowrap; flex: 0 0 auto; }
.cu-lv {
  font-size: 12px; color: var(--ink-dim); font-variant-numeric: tabular-nums;
  flex: 0 1 auto; min-width: 0; text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cu-max-lbl { color: var(--ink-faint); }
.cu-desc { display: block; font-size: 11px; color: var(--ink-dim); margin-top: 1px; }
.cu-buy {
  flex: 0 0 auto; border-radius: 10px; padding: 9px 12px; cursor: pointer;
  font-size: 13px; font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums;
  background: rgba(120, 190, 255, 0.16); color: #cfe6ff; border: 1px solid rgba(120, 190, 255, 0.4);
}
.cu-buy.cant { opacity: 0.45; }
.cu-pause {
  flex: 0 0 auto; min-width: 36px; min-height: 36px; border-radius: 10px;
  background: rgba(255, 255, 255, 0.05); color: var(--ink-dim);
  border: 1px solid var(--line); font-size: 13px; cursor: pointer;
}
.cu-pause.paused { color: var(--good); border-color: var(--good); }
.cu-max { flex: 0 0 auto; font-size: 12px; font-weight: 800; color: var(--good); padding: 9px 6px; }

/* Reset */
.reset-btn {
  margin-top: 12px; width: 100%;
  background: transparent; border: 1px solid rgba(255, 90, 106, 0.4); color: #ff8a95;
  padding: 9px; border-radius: 10px; font-size: 12px; font-weight: 700; cursor: pointer;
}
.reset-btn:active { background: rgba(255, 90, 106, 0.1); }

/* ------------------------------ Coach / onboarding ------------------------------ */
.coach {
  position: absolute;
  left: 10px; right: 10px; bottom: calc(10px + env(safe-area-inset-bottom));
  z-index: 44;
  display: flex; align-items: center; gap: 10px;
  background: linear-gradient(180deg, #0e2c46, #071a2b);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  border-radius: 14px;
  padding: 12px 14px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5), 0 0 18px color-mix(in srgb, var(--accent) 25%, transparent);
  animation: coachIn 0.3s ease;
}
@keyframes coachIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
.coach-text { flex: 1; font-size: 12.5px; line-height: 1.45; font-weight: 600; }
.coach-x { flex: 0 0 auto; background: var(--accent); color: #04121e; border: none; border-radius: 9px; padding: 8px 14px; font-size: 12px; font-weight: 800; cursor: pointer; }
.coach-skip { flex: 0 0 auto; background: transparent; color: var(--ink-faint); border: none; font-size: 11px; cursor: pointer; text-decoration: underline; }

.coach-hl {
  position: relative;
  z-index: 4;
  animation: coachPulse 1.3s ease-in-out infinite;
  border-radius: 14px;
}
@keyframes coachPulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); }
  50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 28%, transparent); }
}

/* ============================================================================
   DIVE RUN overlay (built by dive.js)
   ============================================================================ */
/* Trench progress map — thin left rail over the scene (regular play). */
.trench {
  position: absolute; left: 8px;
  /* Fills the rail's whole column: 10px below the scene's top down to 6px above
     the action row (12px inset + 48px row = 60, +6 clearance = 66).
     This is relative to .scene again, which is what caused the resizing in three
     earlier attempts — but the reason it is safe NOW is measured, not assumed:
     .scene is a constant height per viewport in every state (spread 0 across
     fresh / objective shown / all modules owned / each tab / dock shown), because
     the header is a fixed 76px, the action row is a fixed 48px, and .scene no
     longer shrinks (flex: 1 0 auto) so the module panel absorbs every change
     instead. Before those three things it moved by 40-212px during ordinary play.
     A flat literal height was an earlier fix, and it worked, but it had to be
     sized for the SMALLEST possible scene (223px) which left a large dead gap
     above the buttons on any tall phone. If .scene ever starts varying again,
     this goes back to a constant — re-measure with scene-range.mjs first.

     66px is not an arbitrary clearance — it is the exact line the cooldown pills'
     bottom edge sits on (12px scene inset + 48px action row + 6px pill gap), so the
     rail ends flush with the Skip / Watch row.
     NOTE the consequence: that line is only 6px above the Dive Run button, so with
     the pills hidden the rail sits close to it. That same clearance was called
     "crammed" once, back when the rail was also using only 67% of its box for the
     progress line; the alignment is deliberate now and the track fills 79%. If it
     reads tight again, align to the pills' TOP (bottom: 96px) rather than going back
     to a free-floating number. */
  top: 8px; bottom: 66px; height: auto; width: 64px;
  z-index: 3; pointer-events: none;
  border-radius: 10px; overflow: hidden;
  border: 1px solid rgba(140, 180, 210, 0.18);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}
.trench-canvas { width: 100%; height: 100%; display: block; }

.dive-overlay {
  position: absolute; inset: 0;
  z-index: 60;
  background: #01060f;
  animation: diveIn 0.3s ease;
}
.dive-overlay.dive-closing { animation: diveOut 0.26s ease forwards; }
@keyframes diveIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes diveOut { from { opacity: 1; } to { opacity: 0; } }
.dive-canvas { position: absolute; inset: 0; touch-action: none; }
.dive-hud {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: 14px 16px;
  padding-top: calc(14px + env(safe-area-inset-top));
  font-weight: 800;
  pointer-events: none;
}
.dive-hud-left { color: var(--accent); font-size: 18px; flex: 1; }
.dive-hud-mid { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 3px; }
.dive-hud-right { flex: 1; display: flex; align-items: flex-start; justify-content: flex-end; gap: 10px; }
.dive-time {
  font-size: 34px; font-weight: 900; color: #fff;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 12px rgba(0, 0, 0, 0.55);
}
.dive-time.urgent { color: #ff5a6a; animation: diveUrgent 0.7s ease-in-out infinite; }
@keyframes diveUrgent {
  0%, 100% { transform: scale(1); text-shadow: 0 0 10px rgba(255, 90, 106, 0.5); }
  50% { transform: scale(1.16); text-shadow: 0 0 22px rgba(255, 90, 106, 0.95); }
}
.dive-lives {
  font-size: 22px; color: #ff5a6a; letter-spacing: 2px;
  text-shadow: 0 0 8px rgba(255, 90, 106, 0.45);
}
/* Spent hull pips recede (muted, no glow) so lost lives read at a glance. */
.dive-lives .lv-empty { color: #6f8394; text-shadow: none; }
.dive-quit {
  pointer-events: auto;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid var(--line);
  color: #fff;
  width: 44px; height: 44px;
  border-radius: 50%;
  font-size: 14px; font-weight: 700;
  cursor: pointer;
}
.dive-hint {
  margin-top: 3px;
  max-width: 250px;
  text-align: center;
  font-size: 12px;
  line-height: 1.35;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  pointer-events: none;
  transition: opacity 0.5s ease;
  animation: hintPulse 2s ease-in-out infinite;
}
@keyframes hintPulse { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.75; } }

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .probe-body, .bubbles span, .dive-hint, .dive-time.urgent { animation: none !important; }
}

/* Readable over the unlit canvas — the hint used to drown in the black
   outside the light cone. A quiet backing pill keeps it legible everywhere. */
.dive-hint {
  color: #d6ecf7;
  background: rgba(2, 10, 18, 0.62);
  padding: 5px 12px;
  border-radius: 999px;
}
@media (prefers-reduced-motion: reduce) {
  .dive-hint { transition: none; }
  #app { transition: none; }
}

/* ============================ Stats tab (Phase B) ============================ */

/* Achievements — a browsable trophy case. */
.ach-progress {
  height: 8px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 10px;
}
.ach-progress-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #fff));
  box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 45%, transparent);
  transition: width 0.4s ease;
}
.ach-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 8px;
}
.ach-card {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 11px 12px;
  backdrop-filter: blur(4px);
}
.ach-card.locked { opacity: 0.5; }
.ach-card.unlocked {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: inset 0 0 20px color-mix(in srgb, var(--accent) 8%, transparent);
  cursor: pointer;
}
.ach-card.unlocked:active { transform: scale(0.98); }
.ach-icon { font-size: 21px; line-height: 1.1; width: 26px; text-align: center; flex: 0 0 auto; }
.ach-card.locked .ach-icon { filter: grayscale(1) brightness(0.75); }
.ach-body { flex: 1; min-width: 0; }
.ach-name { font-size: 12.5px; font-weight: 700; }
.ach-card.locked .ach-name { color: var(--ink-dim); }
.ach-desc {
  font-size: 10.5px; color: var(--ink-faint); line-height: 1.35; margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ach-mini { margin-top: 7px; height: 4px; background: rgba(255, 255, 255, 0.08); border-radius: 999px; overflow: hidden; }
.ach-mini-fill { height: 100%; background: var(--ink-faint); border-radius: 999px; }
.ach-unlocked-tag { margin-top: 6px; font-size: 9.5px; letter-spacing: 0.5px; font-weight: 700; color: var(--accent); }

/* Achievement showcase (tap an unlocked one). */
.ach-show-icon { font-size: 68px; text-align: center; margin: 6px 0 2px; filter: drop-shadow(0 8px 20px rgba(0, 0, 0, 0.5)); }

/* Statistics graphs. */
.stats-graphs-title {
  font-size: 11px; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--ink-dim); margin: 16px 2px 2px; font-weight: 700;
}
.stat-graph { padding: 12px 12px 10px; }
.stat-graph .card-head { margin-bottom: 8px; }
.stat-graph .card-head h3 { font-size: 13px; }
.stat-chart { width: 100%; height: 88px; display: block; }

/* Income-by-module bars. */
.income-bars { display: flex; flex-direction: column; gap: 7px; }
/* Each row is label | track | % — the track column MUST be sized by the grid,
   or every bar collapses to a stub (tester #16). Names get the bigger share of
   the row now and the bar is slimmer (tester #11: bars too wide, names cut). */
.income-row { display: grid; grid-template-columns: minmax(0, 46%) 1fr 34px; align-items: center; gap: 10px; margin-bottom: 7px; }
.income-label { font-size: 11.5px; color: var(--ink-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.income-track { display: block; height: 6px; background: rgba(255, 255, 255, 0.06); border-radius: 999px; overflow: hidden; }
.income-fill {
  display: block;
  height: 100%;
  min-width: 2px; /* a 1% module still shows a nub */
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #fff));
  border-radius: 999px;
}
.income-pct { font-size: 11px; color: var(--ink-dim); text-align: right; font-variant-numeric: tabular-nums; }
.income-empty { font-size: 12px; color: var(--ink-faint); padding: 4px 0; }

/* Anonymous-analytics toggle (Log tab) — visible only when keys are configured. */
.settings-btn {
  margin-top: 8px; width: 100%;
  background: transparent; border: 1px solid var(--line); color: var(--ink-dim);
  padding: 9px; border-radius: 10px; font-size: 12px; font-weight: 700; cursor: pointer;
}
.settings-btn:active { background: rgba(255, 255, 255, 0.06); }
.settings-btn .on { color: var(--good); }

/* Attention pip on the Stats tab — new achievements since the tab was last open. */
.tabs button { position: relative; }
.tab-pip {
  position: absolute; top: 5px; right: 10px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
}
.ed-count { color: var(--accent); font-weight: 600; }

/* Single-action modals dismiss on a tap anywhere; say so, quietly. */
.modal-tap-hint {
  margin-top: 10px;
  font-size: 10px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
}

/* Zone-crossing pressure wave — the boundary should feel like breaking through. */
.zone-wave { position: absolute; inset: 0; pointer-events: none; z-index: 6; overflow: hidden; }
.zone-wave::before {
  content: '';
  position: absolute; left: 50%; top: 42%;
  width: 24px; height: 24px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  border: 3px solid var(--accent);
  box-shadow: 0 0 34px var(--accent), inset 0 0 18px color-mix(in srgb, var(--accent) 55%, transparent);
  opacity: 0.95;
  animation: zoneWave 0.9s cubic-bezier(0.16, 0.84, 0.44, 1) forwards;
}
@keyframes zoneWave {
  to { width: 175vmax; height: 175vmax; opacity: 0; border-width: 1px; }
}
.app-shake { animation: appShake 0.55s ease-out; }
@keyframes appShake {
  0%, 100% { transform: none; }
  20% { transform: translateY(5px); }
  40% { transform: translateY(-4px); }
  60% { transform: translateY(2px); }
  80% { transform: translateY(-1px); }
}
@media (prefers-reduced-motion: reduce) {
  .zone-wave::before, .app-shake { animation: none !important; }
}

/* Echoes — procedural deep species reuse an authored painting, ghost-tinted. */
.fish-card.echo .fish-icon,
.echo-art {
  filter: hue-rotate(150deg) saturate(0.65) brightness(1.12);
  opacity: 0.92;
}
.fish-card.echo .fish-name { font-style: italic; }
.fish-card.echo {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 30%, var(--line));
}

/* Game Center leaderboard button (Stats tab, native shell only). */
.gc-btn { margin-top: 0; }

/* ============================ Menu (⚙) ============================ */
.menu-card { gap: 4px; }
.menu-close {
  appearance: none; border: 1px solid var(--line); background: rgba(255,255,255,0.05);
  color: var(--ink); width: 34px; height: 34px; border-radius: 10px;
  font-size: 16px; cursor: pointer; flex: 0 0 auto;
}
.menu-page { display: flex; flex-direction: column; gap: 2px; padding-top: 6px; }
.menu-card .store-section-title { margin: 14px 4px 7px; font-size: 10px; letter-spacing: 2.2px; }
/* Grouped-card menu: one quiet panel per section, hairline dividers inside —
   rows read as a set instead of a stack of loose buttons. */
.menu-group {
  display: flex; flex-direction: column;
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--line);
  border-radius: 16px;
  overflow: hidden;
}
.menu-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; text-align: left;
  background: transparent;
  border: none;
  padding: 11px 12px;
  color: var(--ink);
  font-size: 13.5px; font-weight: 600;
  cursor: pointer;
}
.menu-group .menu-row + .menu-row { border-top: 1px solid rgba(127, 227, 255, 0.07); }
.menu-group .menu-row[hidden] + .menu-row { border-top: none; }
.menu-row:not(.disabled):active { background: rgba(127, 227, 255, 0.07); }
.menu-row.disabled { opacity: 0.45; cursor: default; }
/* Icon tile: a soft accent-tinted square, so every row leads with the same
   calm glyph treatment instead of mixed-color emoji. */
.menu-row-icon {
  width: 30px; height: 30px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9px;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  color: var(--accent);
}
.menu-row-icon .ic { width: 16px; height: 16px; }
.menu-row-label { flex: 1; }
.menu-row-right { color: var(--ink-faint); font-size: 14px; display: inline-flex; align-items: center; }
.menu-pill {
  font-size: 11px; font-weight: 700;
  border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 10px; color: var(--ink-faint);
}
.menu-pill.on { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); background: color-mix(in srgb, var(--good) 10%, transparent); }
.menu-soon {
  font-size: 9.5px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase;
  border: 1px solid var(--line); border-radius: 999px; padding: 3px 8px; color: var(--ink-faint);
}
.menu-ver { font-size: 11px; color: var(--accent); font-weight: 700; }
.menu-footer {
  text-align: center; font-size: 11px; color: var(--ink-faint);
  padding: 16px 0 4px; letter-spacing: 0.4px;
}
.menu-prose { font-size: 13.5px; line-height: 1.65; color: var(--ink-dim); display: flex; flex-direction: column; gap: 10px; padding: 4px 2px; }
.menu-prose b { color: var(--ink); }
.menu-release { margin-bottom: 12px; }
.menu-release-v { font-weight: 800; color: var(--accent); margin-bottom: 4px; }
.menu-release ul { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: var(--ink-dim); }
.menu-stat-grid { padding: 4px 2px; }
.menu-stat-head {
  grid-column: 1 / -1;
  font-size: 11px; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--accent); font-weight: 700; margin-top: 8px;
}

/* ==========================================================================
   DEEP ECONOMY (2.0 monetization overhaul) — vault, subscription, packs,
   season, daily calendar, home ad-dock, demo-ad overlay. Matches the ocean
   palette; the storefront inherits the gold/red .store-card.mode-max tone.
   ========================================================================== */

/* The store & season are a fixed "place": pin --accent to surface-cyan so the
   storefront stays color-stable even at deep zones (where the game's live
   --accent has drifted to violet/coral). The daily calendar deliberately keeps
   the live zone accent (it lives in the modal chrome and reads as native). */
.store-root, .season-root { --accent: #7fe3ff; }

/* Quick-links + first-purchase banner */
.store-quicklinks { display: flex; gap: 8px; margin-top: 12px; }
.store-quick {
  position: relative; flex: 1; min-height: 44px; border-radius: 11px; cursor: pointer;
  border: 1px solid var(--line); background: rgba(255, 255, 255, 0.06); color: var(--ink);
  font-size: 13px; font-weight: 700;
}
.q-pip { position: absolute; top: 7px; right: 9px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--good); box-shadow: 0 0 6px var(--good); }
.store-firstbuy {
  margin-top: 10px; padding: 10px 12px; border-radius: 11px; font-size: 12px; line-height: 1.45;
  background: linear-gradient(180deg, rgba(127, 227, 255, 0.14), rgba(127, 227, 255, 0.05));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); color: var(--ink);
}

/* Real-money buttons — gold, distinct from pearl (red) buys under mode-max */
.store-buy.money { background: linear-gradient(180deg, #ffd873, #f2a326) !important;
  color: #3a1f00 !important; box-shadow: 0 4px 14px rgba(240, 170, 40, 0.35) !important; }

/* Value packs + bundle ribbons */
.store-packs { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.pack {
  display: flex; align-items: center; gap: 10px; padding: 11px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
}
.pack.owned { opacity: 0.6; }
.pack-label { font-size: 13px; font-weight: 800; flex: 0 0 auto; }
.pack-contents { flex: 1; font-size: 11px; color: var(--ink-dim); line-height: 1.35; }
.bundle-ribbon {
  position: absolute; top: -8px; left: 50%; transform: translateX(-50%);
  font-size: 9.5px; font-weight: 800; letter-spacing: 0.3px; white-space: nowrap;
  background: #ffd21f; color: #3a1500; padding: 2px 8px; border-radius: 999px;
}
.bundle-ribbon.anchor { background: #b98cff; color: #1a0730; }
.bundle.anchor { opacity: 0.9; }

/* Pressure Vault card */
.vault-card {
  margin-top: 14px; padding: 13px; border-radius: 14px;
  background: linear-gradient(180deg, rgba(127, 227, 255, 0.12), rgba(10, 26, 43, 0.6));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
.vault-head { display: flex; align-items: center; gap: 10px; }
.vault-jar { font-size: 30px; }
.vault-title { font-size: 15px; font-weight: 800; }
.vault-sub { font-size: 11px; color: var(--ink-dim); line-height: 1.35; }
.vault-bar {
  position: relative; height: 26px; margin: 11px 0 4px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.35); overflow: hidden; border: 1px solid var(--line);
}
.vault-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), #cdf3ff);
  transition: width 0.4s ease; min-width: 2px; }
/* Legible over BOTH the dark empty track and the bright fill (art-director). */
.vault-amt { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 800; color: #eaf6ff; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85); }
.vault-boost { display: block; font-size: 10.5px; font-weight: 800; color: #ffd94a; margin: 4px 0; text-align: center; }
.vault-crack {
  width: 100%; min-height: 46px; margin-top: 8px; border: none; border-radius: 11px; cursor: pointer;
  font-size: 14px; font-weight: 800; color: #3a1f00;
  background: linear-gradient(180deg, #ffd873, #f2a326); box-shadow: 0 4px 14px rgba(240, 170, 40, 0.35);
}
.vault-crack.cant { background: rgba(255, 255, 255, 0.1); color: var(--ink-faint); box-shadow: none; cursor: default; }

/* Subscription card */
.sub-card {
  margin-top: 14px; padding: 13px; border-radius: 14px;
  background: linear-gradient(180deg, rgba(185, 140, 255, 0.16), rgba(10, 26, 43, 0.5));
  border: 1px solid color-mix(in srgb, #b98cff 40%, transparent);
}
.sub-card.owned { border-color: var(--good); }
.sub-head { display: flex; align-items: flex-start; gap: 10px; }
.sub-title { font-size: 15px; font-weight: 800; display: flex; align-items: center; gap: 7px; }
.sub-badge { font-size: 9.5px; font-weight: 800; background: #b98cff; color: #1a0730; padding: 2px 7px; border-radius: 999px; }
.sub-blurb { font-size: 11px; color: var(--ink-dim); line-height: 1.4; margin-top: 3px; }
.sub-buy {
  width: 100%; min-height: 46px; margin-top: 10px; border: none; border-radius: 11px; cursor: pointer;
  font-size: 14px; font-weight: 800; color: #fff;
  /* Deepened so white text clears WCAG AA (≥4.5:1) across the whole gradient. */
  background: linear-gradient(180deg, #6f3cbe, #4e1f96); box-shadow: 0 4px 14px rgba(110, 60, 190, 0.45);
}

/* Season sheet */
.season-card { background: linear-gradient(180deg, #071f31, #04101d); }
.season-meta { font-size: 12px; color: var(--accent); margin-top: 3px; font-weight: 700; }
.season-xpbar {
  position: relative; height: 22px; margin: 12px 0; border-radius: 999px;
  background: rgba(0, 0, 0, 0.35); overflow: hidden; border: 1px solid var(--line);
}
.season-xpfill { height: 100%; background: linear-gradient(90deg, var(--accent), #b98cff); transition: width 0.4s ease; }
.season-xptext { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 800; color: var(--ink); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85); }
.season-premium.owned { text-align: center; color: var(--good); font-weight: 800; font-size: 13px; padding: 8px; }
.season-unlock {
  width: 100%; min-height: 46px; border: none; border-radius: 11px; cursor: pointer; margin-bottom: 6px;
  font-size: 14px; font-weight: 800; color: #3a1f00;
  background: linear-gradient(180deg, #ffd873, #f2a326); box-shadow: 0 4px 14px rgba(240, 170, 40, 0.35);
}
.season-tracks-head, .season-tier { display: grid; grid-template-columns: 34px 1fr 1fr; gap: 6px; align-items: center; }
.season-tracks-head { font-size: 10px; letter-spacing: 1px; text-transform: uppercase; color: var(--ink-faint); margin: 10px 0 6px; padding: 0 2px; }
.season-tiers { display: flex; flex-direction: column; gap: 6px; }
.season-tier { padding: 5px; border-radius: 10px; background: rgba(255, 255, 255, 0.03); border: 1px solid transparent; }
.season-tier.reached { border-color: color-mix(in srgb, var(--accent) 25%, transparent); background: rgba(127, 227, 255, 0.06); }
.st-num { font-size: 13px; font-weight: 800; text-align: center; color: var(--ink-dim); }
.sc { font-size: 11px; padding: 8px 6px; border-radius: 8px; text-align: center; display: flex; align-items: center; justify-content: center; min-height: 44px; }
.sc.empty { color: var(--ink-faint); opacity: 0.4; }
.sc.pending { color: var(--ink-dim); background: rgba(255, 255, 255, 0.04); }
.sc.locked { color: var(--ink-faint); background: rgba(255, 255, 255, 0.03); }
.sc.done { color: var(--good); font-weight: 700; background: rgba(88, 224, 168, 0.1); }
/* claimable-but-not-next: solid, no pulse. Only the single next claim pulses. */
.sc.ready, .sc.claim { border: none; cursor: pointer; font-weight: 800; color: #04121e;
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #1c6f8c)); }
.sc.claim { box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 45%, transparent); animation: offerPulse 1.4s ease-in-out infinite; }

/* Daily calendar grid */
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; margin: 12px 0; }
.cal-cell {
  aspect-ratio: 1; border-radius: 9px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; background: rgba(255, 255, 255, 0.05); border: 1px solid var(--line); font-size: 9px;
}
.cal-cell.past { opacity: 0.4; }
.cal-cell.claimed { opacity: 0.55; }
.cal-cell.today { border-color: var(--accent); box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 40%, transparent);
  background: rgba(127, 227, 255, 0.14); }
.cal-cell.milestone { background: rgba(255, 210, 31, 0.12); border-color: rgba(255, 210, 31, 0.4); }
.cal-day { font-size: 10px; font-weight: 800; color: var(--ink-dim); }
.cal-rw {
  font-size: 9px; font-weight: 700; min-width: 0;
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center; column-gap: 2px;
}
.cal-rw .pearl-ic { width: 9px; height: 9px; vertical-align: -1px; }
/* Grid items refuse to shrink below content by default — the milestone
   column (…17 ◆1) overflowed the card's right edge on phones without this. */
.cal-cell { min-width: 0; padding: 2px 1px; }

/* Home rewarded-ad dock */
.reward-dock {
  /* Locked, not scrollable (#5, asked for since build 5). Three chips always
     fit at 320px+ once they can shrink, so scrolling only ever hid them.
     flex:0 0 auto is load-bearing: #app is a flex column, and without it the
     dock is the first thing squeezed — it collapsed to 14px and clipped the
     pills behind the tab bar. */
  flex: 0 0 auto;
  display: flex; gap: 6px; padding: 5px 10px;
  background: rgba(2, 8, 15, 0.5);
}
.rd-btn {
  /* Shrink to fit rather than overflow; 36px still clears the tap-target floor
     with the dock's padding included. */
  flex: 1 1 0; min-width: 0; min-height: 36px; padding: 0 8px;
  border-radius: 999px; cursor: pointer; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  border: 1px solid var(--line); background: rgba(255, 255, 255, 0.07); color: var(--ink);
  font-size: 12px; font-weight: 800;
}
.rd-btn .pearl-ic { width: 12px; height: 12px; vertical-align: -1px; }
.rd-boost { border-color: color-mix(in srgb, #ffd21f 45%, transparent); color: #ffe08a; }
.rd-boost.on { background: linear-gradient(180deg, rgba(255, 210, 31, 0.28), rgba(255, 160, 40, 0.16));
  color: #ffe9a8; animation: offerPulse 2s ease-in-out infinite; }
.skin-locked { font-size: 11px; font-weight: 700; color: var(--ink-faint); }

/* Demo rewarded-ad overlay (from ads.js) */
.ad-demo { position: fixed; inset: 0; z-index: 999; display: flex; align-items: center; justify-content: center;
  background: rgba(2, 6, 13, 0.92); opacity: 0; transition: opacity 0.2s ease; }
.ad-demo.open { opacity: 1; }
.ad-demo-card { text-align: center; color: var(--ink); font-family: var(--font); padding: 28px; max-width: 320px; }
.ad-demo-badge { display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: 1.5px;
  background: rgba(255, 255, 255, 0.12); color: var(--ink-dim); padding: 4px 12px; border-radius: 999px; margin-bottom: 18px; }
.ad-demo-spinner { width: 44px; height: 44px; margin: 0 auto 16px; border-radius: 50%;
  border: 3px solid rgba(127, 227, 255, 0.25); border-top-color: var(--accent); animation: adSpin 0.8s linear infinite; }
@keyframes adSpin { to { transform: rotate(360deg); } }
.ad-demo-title { font-size: 15px; font-weight: 700; }
.ad-demo-count { font-size: 30px; font-weight: 800; color: var(--accent); margin: 8px 0; }
.ad-demo-fine { font-size: 10.5px; color: var(--ink-faint); line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
  .ad-demo-spinner, .rd-boost.on, .sc.claim, .offer-buy { animation: none; }
}
