/* EMBERWALL — in-run chrome: the HUD topbar and bottom bar, the shop/upgrade side panel, tooltips
   and toasts. The blocking first-run tutorial spans screens and gameplay in styles-screens.css.
   Sibling of styles.css (tokens + shared chrome) — load styles.css first, this second, per the
   four-stylesheet split recorded in index.html and docs/decisions.md. */

/* -------------------------------------------------------------------------- topbar -- */

.ui-topbar {
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: 0 14px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(28, 24, 30, 0.96), rgba(28, 24, 30, 0.74));
  backdrop-filter: blur(6px);
}

.ui-stat, .ui-wave {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
}

.ui-stat-icon { font-size: 13px; line-height: 1; align-self: center; }
.ui-stat-value, .ui-wave-value { font-family: var(--mono); font-size: 18px; font-variant-numeric: tabular-nums; }
.ui-stat-sub, .ui-wave-label, .ui-preview-label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}
.ui-stat-gold .ui-stat-icon, .ui-stat-gold .ui-stat-value { color: var(--gold); }
.ui-stat-embers .ui-stat-icon, .ui-stat-embers .ui-stat-value { color: var(--ember); }
.ui-stat-embers.is-low { border-color: rgba(255, 194, 74, 0.5); }
.ui-stat-embers.is-critical { border-color: rgba(255, 93, 108, 0.65); animation: pulse-bad 1.1s ease-in-out infinite; }
.ui-stat-embers.is-critical .ui-stat-value { color: var(--bad); }

.ui-twist { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.ui-twist-icon { font-size: 15px; }
.ui-twist-name { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; }
.ui-twist-status { font-family: var(--mono); font-size: 12px; color: var(--accent); }
.ui-twist.is-none { opacity: 0.55; }
.ui-menu-btn { min-width: var(--tap); }

/* ---------------------------------------------------------------------- bottom bar -- */

.ui-bottombar {
  grid-row: 3;
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: 0 14px;
  box-sizing: border-box;
  border-top: 1px solid var(--line);
  background: linear-gradient(0deg, rgba(28, 24, 30, 0.96), rgba(28, 24, 30, 0.72));
  backdrop-filter: blur(6px);
}

.ui-start {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 168px;
  padding: 8px 18px;
  border-color: var(--line-strong);
}

.ui-start-label { font-size: 15px; font-weight: 600; letter-spacing: 0.04em; }
.ui-start-sub { font-family: var(--mono); font-size: 11px; color: var(--faint); }
/* "Release the wave" is the most important call to action in the game, so when it is armed it
   becomes a solid bar of the world's own accent rather than a tinted ghost (VISUAL-DESIGN §2.2). */
.ui-start.is-ready {
  border-color: transparent;
  background: var(--accent);
  box-shadow: 0 0 22px var(--glow);
}
.ui-start.is-ready .ui-start-label { color: var(--ink-on-accent); font-weight: 700; }
.ui-start.is-ready .ui-start-sub { color: color-mix(in srgb, var(--ink-on-accent) 70%, transparent); }
.ui-start.is-ready:hover:not(:disabled) { border-color: transparent; background: var(--accent); filter: brightness(1.06); }
.ui-pause { min-width: var(--tap); font-size: 16px; }

.ui-speeds {
  display: flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
}

.ui-speed {
  min-width: 46px;
  min-height: 36px;
  padding: 4px 10px;
  border-color: transparent;
  background: none;
  font-family: var(--mono);
  font-size: 13px;
}

.ui-speed.is-active, .ui-pause.is-active, .ui-speed-option.is-active, .ui-diff.is-active, .ui-toggle.is-on {
  border-color: var(--line-hot);
  background: var(--tint);
  color: var(--accent);
}

.ui-preview { display: flex; align-items: center; gap: 10px; margin-left: auto; min-width: 0; overflow: hidden; }
.ui-preview-list { display: flex; align-items: center; gap: 6px; padding: 2px; overflow-x: auto; scrollbar-width: none; }
.ui-preview-list::-webkit-scrollbar { display: none; }

.ui-preview-item {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--sunken);
  white-space: nowrap;
  cursor: pointer;
}

.ui-preview-item.is-air { border-color: rgba(122, 228, 255, 0.4); }
.ui-preview-icon { display: block; }
.ui-preview-count { font-family: var(--mono); font-size: 12px; color: var(--dim); }
.ui-preview-more, .ui-preview-empty { font-size: 12px; color: var(--faint); }

/* ---------------------------------------------------------------------- side panel -- */

.ui-side {
  grid-column: 2;
  grid-row: 2;
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-left: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(30, 26, 33, 0.92), rgba(28, 24, 30, 0.96));
  backdrop-filter: blur(6px);
}

.ui-shop, .ui-shop-list, .ui-upgrade { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

.ui-shop-head, .ui-up-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px 8px;
  border-bottom: 1px solid var(--line);
}

.ui-shop-head { flex-wrap: wrap; }
.ui-shop-title { font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim); }
.ui-shop-hint { flex: 1 1 100%; font-size: 12px; color: var(--faint); }

.ui-cards, .ui-up-cols {
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 10px;
  min-height: 0;
  flex: 1 1 auto;
}

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

/* D61: one heading per damage type, in DAMAGE_TYPES order. Deliberately quiet — it is a divider a
   player reads once to find the type they want, not a label competing with the cards under it.
   The first section sits flush with the top of the list rather than pushed down by its own margin. */
.ui-cards-section {
  margin: 10px 0 -2px;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}
.ui-cards-section:first-child { margin-top: 0; }

/* tower cards — each card carries its damage-type colour as --accent */

/* `flex-shrink` defaults to 1, and .ui-cards is a column flex box with a definite height: ten
   cards whose content is taller than the list were squeezed to a fraction of their content
   height, so every blurb and damage-type label spilled onto the card below. Cards never shrink;
   the list scrolls instead. `height: auto` keeps the button from adopting a fixed line box. */
.ui-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 10px;
  flex: 0 0 auto;
  height: auto;
  min-height: calc(44px + 20px);
  padding: 12px;
  border-radius: var(--radius);
  background: var(--panel);
  text-align: left;
  overflow: hidden;
}

/* Same replaced-element rule as #game: without an explicit box these canvases render at their
   intrinsic 300x150 and blow the card layout apart. */
.ui-card-icon {
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border-radius: var(--radius-sm);
  background: var(--sunken);
}
.ui-card-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ui-card-head { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.ui-card-name { font-size: 14px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-card-price { flex: 0 0 auto; font-family: var(--mono); font-size: 14px; color: var(--gold); margin-left: auto; }

/* D51: four lines, not two. The clamp was written when a blurb was one short sentence of flavour;
   a blurb now says what the tower actually does and what it cannot do, which is the point of it, and
   at two lines the second half was being cut off mid-word in the build list. Four still bounds the
   card — the longest blurb in the game is three lines at the narrowest panel width — so no card can
   grow tall enough to push the rest of the list off screen, which is what the clamp is for. */
.ui-card-blurb {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  overflow: hidden;
  font-size: 12px;
  line-height: 1.35;
  color: var(--dim);
  overflow-wrap: anywhere;
}

.ui-card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 11px; color: var(--faint); }
.ui-card-type { letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); }
.ui-card-note, .ui-card-limit { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.ui-card-note { display: none; }
.ui-card.is-open .ui-card-note { display: block; }
.ui-card-info { align-self: center; min-width: 34px; min-height: 34px; padding: 4px 10px; border-radius: 999px; font-family: var(--mono); }
.ui-card.is-armed { border-color: var(--line-hot); background: var(--tint); box-shadow: 0 0 20px var(--glow); }
.ui-card.is-poor { opacity: 0.55; }
.ui-card.is-maxed { opacity: 0.45; }

/* upgrade panel — two columns, four tiers each (SPEC 11) */

.ui-up-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.ui-up-name { font-size: 15px; font-weight: 600; margin: 0; }
.ui-up-type, .ui-up-tierbadge {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--dim);
}
.ui-up-close { margin-left: auto; min-width: 36px; min-height: 36px; padding: 4px 10px; }
.ui-up-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ui-up-cols.is-single { grid-template-columns: 1fr; }
.ui-up-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ui-up-col-head { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint); margin: 0; }
.ui-up-tiers { display: flex; flex-direction: column; gap: 6px; }

.ui-tier {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  height: auto;
  gap: 3px;
  padding: 8px;
  border-radius: var(--radius-sm);
  background: var(--panel);
  text-align: left;
}

.ui-tier-head { display: flex; align-items: center; gap: 6px; }
.ui-tier-name { font-size: 13px; font-weight: 600; min-width: 0; }
.ui-tier-mark { margin-left: auto; font-size: 12px; color: var(--faint); }
.ui-tier-lock { color: var(--warn); }
.ui-tier-desc { font-size: 11px; line-height: 1.35; color: var(--dim); }
.ui-tier-cost { font-family: var(--mono); font-size: 12px; color: var(--gold); }
.ui-tier-note { font-size: 11px; color: var(--faint); }
.ui-tier.is-bought { border-color: rgba(158, 222, 122, 0.45); background: rgba(158, 222, 122, 0.08); }
.ui-tier.is-bought .ui-tier-mark { color: var(--good); }
.ui-tier.is-buyable { border-color: var(--line-hot); box-shadow: 0 0 16px var(--glow); }
.ui-tier.is-costly { opacity: 0.6; }
.ui-tier.is-costly .ui-tier-cost { color: var(--bad); }
.ui-tier.is-future { opacity: 0.5; cursor: not-allowed; }
.ui-tier.is-future .ui-tier-cost { color: var(--bad); }
.ui-tier.is-future .ui-tier-name { color: var(--dim); }
.ui-tier.is-locked { opacity: 0.5; border-style: dashed; cursor: not-allowed; }
/* Forge-gated (conflict #12): visually distinct from both `.is-future` (grey — buy the earlier
   tiers first) and `.is-locked` (crosspath commitment) — gold dashes read "purchasable elsewhere",
   not "not yet" or "denied". */
.ui-tier.is-forge { opacity: 0.75; border-style: dashed; border-color: color-mix(in srgb, var(--gold) 45%, transparent); }
.ui-tier.is-forge .ui-tier-mark { color: var(--spark); }
.ui-tier.is-forge .ui-tier-cost, .ui-tier.is-forge .ui-tier-note { color: var(--gold); }

.ui-up-stats { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border-top: 1px solid var(--line); background: var(--sunken); }
.ui-up-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(78px, 1fr)); gap: 6px; padding: 8px 12px; background: var(--sunken); }
.ui-up-metric { display: flex; flex-direction: column; gap: 2px; }
.ui-up-metric-label { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); }
.ui-up-metric-value { font-family: var(--mono); font-size: 13px; }
.ui-up-status { display: none; padding: 0 12px; font-size: 12px; color: var(--warn); }
.ui-up-status.is-on { display: block; }
.ui-up-chips { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 12px 8px; }

/* Tithe Shrine income clarity: hidden by JS on any tower with no economic stats at all. */
.ui-income { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-top: 1px solid var(--line); background: var(--sunken); }
.ui-income-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; font-size: 12px; }
.ui-income-label { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); }
.ui-income-value { font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--gold); }
.ui-income-breakdown { font-size: 11px; color: var(--dim); }
/* Hovering this row is what draws the gold bounty ring on the board (render/towerStatus.js). */
.ui-income-bounty { cursor: help; padding-top: 4px; border-top: 1px dashed var(--line); }
.ui-income-bounty:hover .ui-income-label, .ui-income-bounty:hover .ui-income-value { color: var(--gold); }

.ui-stat-row { display: flex; align-items: baseline; gap: 8px; font-size: 12px; }
.ui-stat-name { flex: 1 1 auto; color: var(--faint); }
.ui-stat-num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.ui-stat-delta { font-family: var(--mono); font-size: 11px; color: var(--good); opacity: 0; transition: opacity var(--ui-fade) var(--ease); }
.ui-stat-row.has-delta .ui-stat-delta { opacity: 1; }

.ui-prio { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-top: 1px solid var(--line); }
.ui-prio-label { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); }
.ui-prio-value { flex: 1 1 auto; font-family: var(--mono); font-size: 13px; color: var(--accent); }
.ui-prio-step { min-width: 40px; min-height: 40px; padding: 4px 10px; }
.ui-prio-help { padding: 0 12px 8px; font-size: 11px; color: var(--faint); }
.ui-sell { margin: 8px 12px calc(12px + var(--safe-b)); border-color: rgba(255, 93, 108, 0.35); color: #ffb3ba; }
.ui-sell:hover:not(:disabled) { border-color: var(--bad); background: rgba(255, 93, 108, 0.12); }

/* ---------------------------------------------------------- chips, tags, tooltips -- */

.ui-chips { display: inline-flex; flex-wrap: wrap; gap: 4px; }

.ui-chip, .ui-tag, .badge, .pill {
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

.ui-chip-immune { color: var(--chip-color, var(--text)); border-color: var(--chip-color, var(--line-strong)); }
.ui-chip-none { opacity: 0.6; }
.ui-chip-target { color: var(--accent); border-color: var(--line-hot); }
.ui-tag-camo { color: #b9a8ff; border-color: rgba(176, 122, 255, 0.45); }
.ui-tag-boss { color: var(--bad); border-color: rgba(255, 93, 108, 0.5); }

.ui-tooltip {
  min-width: 180px;
  max-width: 280px;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: rgba(28, 24, 30, 0.95);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--ui-fade) var(--ease), visibility var(--ui-fade) var(--ease);
  z-index: 6;
}

.ui-tooltip.is-visible { opacity: 1; visibility: visible; }
.ui-enemy-tip { left: 0; top: 0; will-change: transform; }
.ui-twist-tip { position: absolute; top: calc(var(--topbar-h) + 6px); right: 14px; }
.ui-tip-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.ui-tip-name { font-size: 14px; font-weight: 600; }
.ui-tip-tags { display: inline-flex; gap: 4px; margin-left: auto; }
.ui-tip-body { font-size: 12px; color: var(--dim); }
.ui-tip-hp { margin: 4px 0 6px; font-family: var(--mono); font-size: 11px; color: var(--dim); }
.ui-tip-row { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; }
.ui-tip-row.is-empty { display: none; }
.ui-tip-label { flex: 0 0 56px; font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); }
.ui-hpbar { height: 4px; border-radius: 999px; background: rgba(255, 255, 255, 0.1); overflow: hidden; }
.ui-hpbar-fill { height: 100%; width: 100%; background: linear-gradient(90deg, var(--bad), var(--warn)); transition: width var(--ui-fade) linear; }

/* -------------------------------------------------------------- toasts & banners -- */

.ui-toasts {
  left: 50%;
  top: calc(var(--topbar-h) + var(--safe-t) + 14px);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: min(92vw, 460px);
  pointer-events: none;
  z-index: 7;
}

.ui-toast {
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: rgba(28, 24, 30, 0.92);
  font-size: 13px;
  text-align: center;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity var(--ui-fade) var(--ease), transform var(--ui-fade) var(--ease);
}

.ui-toast.is-visible { opacity: 1; transform: translateY(0); }
.ui-toast-good { border-color: rgba(158, 222, 122, 0.55); color: var(--good); }
.ui-toast-bad { border-color: rgba(255, 93, 108, 0.6); color: #ffb3ba; }
.ui-toast-warn { border-color: rgba(255, 194, 74, 0.55); color: var(--warn); }
.ui-toast-wave { border-color: var(--line-hot); color: var(--accent); }

.ui-banner {
  position: absolute;
  top: calc(var(--topbar-h) + 8px);
  left: 14px;
  padding: 5px 12px;
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ui-fade) var(--ease), visibility var(--ui-fade) var(--ease);
}

.ui-banner.is-visible { opacity: 1; visibility: visible; }
.ui-banner-dev { border: 1px dashed rgba(255, 194, 74, 0.6); background: rgba(60, 42, 6, 0.7); color: var(--warn); }

/* Boss bar (D45). Sits under the toast stack rather than beside it: a toast is transient and a boss
   fight is not, so the bar takes the anchored position and toasts pass over it. Pointer events are
   off throughout — it is a readout, and the board underneath it must stay clickable. */
.ui-boss-bar {
  position: absolute;
  top: calc(var(--topbar-h) + var(--safe-t) + 56px);
  left: 50%;
  transform: translate(-50%, -6px);
  width: min(88vw, 420px);
  padding: 7px 12px 8px;
  border: 1px solid rgba(255, 93, 108, 0.42);
  border-radius: 10px;
  background: color-mix(in srgb, var(--base) 14%, rgba(28, 18, 22, 0.86));
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ui-fade) var(--ease), visibility var(--ui-fade) var(--ease),
              transform var(--ui-fade) var(--ease), border-color var(--ui-fade) var(--ease);
  z-index: 6;
}

.ui-boss-bar.is-on { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.ui-boss-bar.is-enraged { border-color: rgba(255, 93, 108, 0.9); }

.ui-boss-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 5px; }
.ui-boss-name {
  flex: 1 1 auto;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: #ffd9dd;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ui-boss-state {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bad);
}

.ui-boss-track {
  position: relative;
  height: 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  overflow: hidden;
}
.ui-boss-fill {
  height: 100%;
  width: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #ff5d6c, #ff9d6a);
  transition: width 120ms linear;
}
.ui-boss-bar.is-low .ui-boss-fill { background: linear-gradient(90deg, #b3121f, #ff5d6c); }

/* Ticks are their own layer so rewriting the fill width never touches them. */
.ui-boss-ticks { position: absolute; inset: 0; }
.ui-boss-tick {
  position: absolute;
  top: 0;
  width: 1px;
  height: 100%;
  background: rgba(0, 0, 0, 0.75);
}

.ui-boss-type {
  margin-top: 5px;
  min-height: 12px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  .ui-boss-bar, .ui-boss-fill { transition: none; }
}

/* ------------------------------------------------------ phones: shop as a sheet --- */

.sheet-toggle {
  display: none;
  position: absolute;
  z-index: 5;
  right: calc(12px + var(--safe-r));
  bottom: calc(var(--bottombar-h) + var(--safe-b) + 10px);
  align-items: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 8px 16px;
  border: 1px solid var(--line-hot);
  border-radius: 999px;
  background: rgba(28, 24, 30, 0.92);
  color: var(--accent);
  font: 13px/1 var(--font);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  touch-action: manipulation;
}

.sheet-toggle-caret { transition: transform var(--ui-fade) var(--ease); }
body.sheet-open .sheet-toggle-caret { transform: rotate(180deg); }

@media (max-width: 860px), (orientation: portrait) and (max-width: 1024px) {
  :root { --topbar-h: 52px; --bottombar-h: 76px; --side-w: 0px; --gap: 8px; }

  /* The toggle is a sibling of #ui, so the gameplay and modal state must be expressed on #ui
     itself. Showing it unconditionally at this breakpoint put a floating "SHOP" button over the
     main menu, settings, leaderboards, map editor and first-sighting dialogs. */
  #ui.is-ingame:not(.has-modal-screen) + .sheet-toggle { display: inline-flex; }
  .ui-topbar, .ui-bottombar { padding: 0 10px; gap: 8px; }
  .ui-stat, .ui-wave { padding: 4px 9px; }
  .ui-stat-value, .ui-wave-value { font-size: 16px; }
  .ui-twist-name, .ui-preview { display: none; }
  .ui-start { min-width: 124px; padding: 6px 12px; }

  /* The side panel slides up over the map and is dismissed the same way. */
  .ui-side {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    align-self: end;
    z-index: 4;
    margin-bottom: var(--bottombar-h);
    box-sizing: border-box;
    max-height: min(56vh, 460px);
    max-height: min(56vh, 460px, calc(100dvh - var(--topbar-h) - var(--bottombar-h) - var(--safe-t) - var(--safe-b) - var(--guest-banner-reserve, 0px)));
    border-left: 0;
    border-top: 1px solid var(--line-hot);
    border-radius: var(--radius) var(--radius) 0 0;
    box-shadow: 0 -18px 46px rgba(0, 0, 0, 0.6);
    transform: translateY(105%);
    transition: transform 180ms var(--ease);
  }

  body.sheet-open .ui-side { transform: translateY(0); }
  /* One bounded scroll surface keeps the final tower, upgrade controls and Sell reachable. */
  body:not(.is-versus-match).sheet-open .ui-side { padding-bottom: calc(var(--tap) + 20px); }
  body:not(.is-versus-match) .ui-side {
    overflow-y: auto;
    overscroll-behavior: contain;
    scroll-padding-block: 8px calc(var(--tap) + 20px);
    transition: transform 180ms var(--ease), visibility 0s linear 180ms;
  }
  body:not(.is-versus-match):not(.sheet-open) .ui-side { visibility: hidden; }
  body:not(.is-versus-match).sheet-open .ui-side { visibility: visible; transition-delay: 0s; }
  body:not(.is-versus-match) .ui-side :is(.ui-shop, .ui-shop-list, .ui-upgrade) { flex: 0 0 auto; }
  body:not(.is-versus-match) .ui-side :is(.ui-cards, .ui-up-cols) { flex: 0 0 auto; overflow: visible; }
  body:not(.sheet-open) .ui-side { pointer-events: none; }

  .ui-side::before {
    content: '';
    flex: 0 0 auto;
    width: 42px;
    height: 4px;
    margin: 8px auto 2px;
    border-radius: 999px;
    background: var(--line-strong);
  }

  .ui-tooltip { max-width: min(84vw, 300px); }
  .ui-screen { padding: 12px; }
  .ui-worlds { max-height: 46vh; }
}

@media (max-width: 420px) {
  body:not(.is-versus-match) .ui-topbar { gap: 5px; padding-inline: 6px; }
  body:not(.is-versus-match) .ui-topbar :is(.ui-stat, .ui-wave) { min-width: 0; gap: 4px; padding-inline: 5px; }
  body:not(.is-versus-match) .ui-topbar .ui-wave { flex: 1 1 0; padding-inline: 2px; }
  /* Visually hide the label, not the count or its accessible name. */
  body:not(.is-versus-match) .ui-topbar .ui-wave-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  body:not(.is-versus-match) .ui-topbar .ui-wave-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  body:not(.is-versus-match) .ui-topbar .ui-twist { flex: 0 1 44px; min-width: 0; gap: 4px; padding-inline: 6px; }
  body:not(.is-versus-match) .ui-topbar .ui-twist-status { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  body:not(.is-versus-match) .ui-topbar .ui-menu-btn { flex-shrink: 0; }
  .ui-up-cols { grid-template-columns: 1fr; }
  .ui-start-sub { display: none; }
  body:not(.is-versus-match) .ui-start { min-width: 0; flex: 1 1 0; padding-inline: 8px; align-items: center; }
  .ui-menu-btn { padding: 8px 10px; }
  .ui-levels { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 860px) and (max-height: 500px) {
  body:not(.is-versus-match) .ui-shop-hint { display: none; }
}

/* --------------------------------------------------------------------- motion ----- */

@keyframes pulse-bad {
  0%, 100% { box-shadow: 0 0 0 rgba(255, 93, 108, 0); }
  50% { box-shadow: 0 0 18px rgba(255, 93, 108, 0.45); }
}
