/* ═══ Variables ═══════════════════════════════════════════════════════════════ */
:root {
  --u:            calc(min(100vw, 1000px) / 1000);
  --panel-dark:   #643e08;
  --panel-bg:     #d6b067;
  --panel-border: #ab6e15;
  --panel-rim:    #391e01;
  --paper-bg:     #f2e3c6;
  --text:         #2e1b10;
  --text-muted:   rgba(46,27,16,.6);
  --btn-grad:     linear-gradient(180deg, rgba(250,235,200,.9), rgba(214,176,103,.9));
  --btn-border:   rgba(80,46,2,.7);
  --hair:         1px;
  --ui-font:      clamp(7px, 2.8vw, 28px);
}
@media (-webkit-min-device-pixel-ratio:2),(min-resolution:2dppx) { :root { --hair:.5px } }

/* ═══ Base ════════════════════════════════════════════════════════════════════ */
body {
  display:flex; flex-direction:column; min-height:100vh; font-family:Georgia,serif;
  background:url('img/ui/background.webp') center/cover no-repeat;
}
.game-wrapper { display:flex; flex-direction:column; flex:1; width:100%; max-width:1000px; margin:calc(6*var(--u)) auto }
.hidden { display:none !important }

/* ═══ Utilities ═══════════════════════════════════════════════════════════════ */
.text-truncate { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.flex-center   { display:flex; align-items:center; justify-content:center }
.flex-col      { display:flex; flex-direction:column }
.text-muted    { color:var(--text-muted) }
.btn-gold      { background:var(--btn-grad); color:var(--text); font-weight:600; cursor:pointer }

/* ═══ Panel system — two visual shells, everything reuses these ═══════════════ */

/* Dark outer shell — wraps header columns, footer */
.panel-wrap {
  position:relative; min-width:0; background:var(--panel-dark); color:var(--text);
  border:var(--hair) solid var(--panel-border); outline:calc(5*var(--u)) solid var(--panel-rim);
  border-radius:calc(5*var(--u)); padding:calc(8*var(--u));
}
/* Golden parchment — every content box: header grid, footer nav, inventory,
   shop, dialogs, activity panel, game screen, roster cards, auth cards … */
.panel-grid {
  background:var(--panel-bg); color:var(--text); overflow:hidden;
  border:calc(5*var(--u)) solid var(--panel-rim); outline:var(--hair) solid var(--panel-border);
  border-radius:calc(20*var(--u)); box-shadow:inset calc(2*var(--u)) calc(5*var(--u)) var(--hair) var(--hair) rgba(0,0,0,.3);
}
/* Reusable fix for .panel-wrap placed inside an overflow:hidden ancestor
   (its outline would get clipped invisible there) — swap the outline for an
   equivalent border. Any component in that situation can reuse this. */
.panel-wrap--overflow-safe { outline:none; border:calc(5*var(--u)) solid var(--panel-rim) }

/* ═══ Header ══════════════════════════════════════════════════════════════════ */
header { display:flex; flex-direction:column; font-size:var(--ui-font); width:100% }
.header-top  { display:grid; grid-template-columns:minmax(0,4.5fr) 1fr; align-items:start; gap:calc(12*var(--u)) }
.header-left { display:flex; flex-direction:column; gap:calc(6*var(--u)); min-width:0 }
.header-stats-row { display:flex; align-items:stretch; gap:calc(12*var(--u)); min-width:0 }
.header-stats-row > .panel-wrap:first-child { flex:1 1 auto; min-width:0 }
/* Fixed width, stretched to the stats-grid height (align-items:stretch on the row). */
.avatar-wrap { flex:0 0 auto; width:calc(160*var(--u)); align-self:stretch }

/* Grid / cell system — shared by header stats and footer nav */
.stats-grid    { display:grid; grid-template-columns:repeat(3,1fr) }
.resource-grid { display:flex; flex-direction:column }
.resource-grid.grid .cell { border-width:0 0 var(--hair) 0 }
.grid .cell {
  aspect-ratio:5/2; display:flex; align-items:center; justify-content:center; overflow:hidden;
  border:var(--hair) solid var(--panel-border); border-width:0 var(--hair) var(--hair) 0;
}
.grid .cell:nth-child(3n)  { border-right:0 }
.grid .cell:nth-child(n+4) { border-bottom:0 }
.grid .cell img { width:1.5rem; height:1.5rem; border-radius:50% }
.cell__stat { max-width:100%; padding:0 calc(4*var(--u)) }

.avatar {
  position:relative; width:100%; height:100%; background:#39431b; border-radius:calc(24*var(--u)); margin:0;
  box-shadow:
    inset calc(2*var(--u)) calc(5*var(--u)) var(--hair) var(--hair) rgba(0,0,0,.3),
    0 0 0 calc(2*var(--u)) var(--panel-border), 0 0 0 calc(4*var(--u)) #502e02,
    0 0 0 calc(5*var(--u)) #000, 0 0 0 calc(6*var(--u)) var(--panel-border);
}
/* Absolutely positioned so the (large) image never inflates the row height —
   the avatar conforms to the stats-grid height via height:100% + aspect-ratio. */
.avatar img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; border-radius:calc(24*var(--u)); border:var(--hair) solid #000 }

/* ═══ Layout helpers ══════════════════════════════════════════════════════════ */
#main-wrapper { display:flex; flex:1; flex-direction:column; overflow:hidden; position:relative }
/* The map is now the only persistent content between header and footer —
   activity panels and the personal-room screen render into modals instead
   (see #activity-modal / #personal-room-modal, updateUIForRoom in main.js).
   #main stays in the DOM (harmless — nothing populates it anymore) but takes
   no layout space (display:none below), so #room-map-wrap's flex-grow claims
   the full #main-wrapper height with no sibling left to share it with. */
#main         { display:none }
.push-to-bottom { margin:auto }
.route-spacer   { flex:1 1 auto; min-height:0; overflow:auto }

/* ═══ Room map — persistent, sits above the footer on every room screen ══════ */
.room-map-wrap {
  flex:5 1 0; min-height:0; display:flex; align-items:center; justify-content:center;
  /* Without this, the grid child's JS-set explicit width/height (fitMapGrid,
     map.js) could feed back into the wrap's own size (flex-basis + a sized
     child isn't fully decoupled from content in this nested-flex chain),
     which combined with the ResizeObserver watching this element created a
     runaway resize loop that grew the map to fill the viewport. contain:size
     makes the wrap's size strictly independent of its children, so it can
     only ever come from its own flex-basis. */
  contain:size;
  background:transparent;
  /* Viewport onto a grid that's sized/positioned in JS (map.js) to show
     exactly 3 tiles per row, centered on the character's tile — anything
     beyond that is cropped here, reachable by drag/pinch panning. */
  position:relative; overflow:hidden;
  /* All panning/zooming is handled manually via pointer/touch listeners
     (map.js) so it can clamp to the map's bounds and distinguish a drag from
     a tap-to-travel — disable the browser's native touch scroll/zoom so it
     doesn't fight that. */
  touch-action:none; cursor:grab;
}
.room-map-wrap--panning { cursor:grabbing }
.room-map {
  display:grid;
  /* Fixed pixel tracks (map.js sets --tile-size from the wrap's measured
     width so exactly 3 columns fill it), not 1fr — 1fr sizes tracks to fill
     the wrap, which is exactly what must NOT happen here: the grid needs to
     be free to be larger than the viewport so tiles beyond the centered 3x3
     are cropped instead of shrunk. */
  grid-template-columns:repeat(var(--map-cols,3),var(--tile-size,1fr));
  grid-template-rows:repeat(var(--map-rows,3),var(--tile-size,1fr));
  gap:4px;
  /* map.js drives position via transform (pan + pinch-zoom + centering);
     top-left origin keeps that math simple. */
  position:absolute; top:0; left:0; transform-origin:0 0;
  /* Fully transparent — the room/world background shows straight through;
     only the tiles themselves carry a background. */
  background:transparent;
}

.room-map__node {
  position:relative; display:flex; flex-direction:column; align-items:center; justify-content:flex-start;
  padding-top:calc(8*var(--u)); gap:calc(2*var(--u));
  min-width:0; border-radius:calc(10*var(--u));
  /* Neutral fallback for kinds with no dedicated texture below (watch/dungeon). */
  background:rgba(90,72,50,.4);
  /* Pale 1px rim + a 1px soft glow just outside it, both in the same gold
     (#ffd961) — brightened up to full strength on the current tile below. */
  box-shadow:0 0 0 1px rgba(255,217,97,.4), 0 0 1px 1px rgba(255,217,97,.22);
  transition:box-shadow .15s ease, opacity .15s ease;
}
/* Every node renders at exactly one grid cell (map.js pins grid-column/row
   span to 1 regardless of the node's logical footprint), so all tiles read
   as the same size no matter how much of the underlying grid a room
   actually occupies. */

/* Per-kind tile textures — pure CSS, no image assets. Every tile of the
   same kind shares the exact same background (e.g. all four field rooms
   read as identical grass), matching room-map__node--${node.kind} which
   map.js already applies to every node. */
.room-map__node--field {
  /* Flat, no gradient — ripe-wheat yellow-green, same for every field tile. */
  background:#c2ab3e;
}
.room-map__node--city {
  /* CSS Grid paints/orders grid items by their OWN z-index (not a nested
     descendant's) — this tile hosts multiple stacked entrance markers that
     overflow its square box into the row below, so it needs to out-rank
     that sibling tile in paint order or the sibling steals the overflow's
     clicks. */
  z-index:5;
  background:
    repeating-linear-gradient(0deg, rgba(0,0,0,.08) 0 2px, transparent 2px 18px),
    repeating-linear-gradient(90deg, rgba(0,0,0,.08) 0 2px, transparent 2px 18px),
    linear-gradient(160deg, #9c8f7c, #7d7266 60%, #6b6053);
}
.room-map__node--mine {
  background:
    radial-gradient(circle at 30% 25%, rgba(0,0,0,.25) 0 8%, transparent 9%),
    radial-gradient(circle at 65% 55%, rgba(0,0,0,.2) 0 7%, transparent 8%),
    radial-gradient(circle at 80% 20%, rgba(120,160,255,.18) 0 5%, transparent 6%),
    linear-gradient(160deg, #6b6259, #4a443c 60%, #38332c);
}
.room-map__node--cellar {
  background:
    radial-gradient(circle at 25% 35%, rgba(210,60,50,.35) 0 6%, transparent 7%),
    radial-gradient(circle at 60% 65%, rgba(240,225,200,.3) 0 5%, transparent 6%),
    linear-gradient(160deg, #4c5a3a, #33402a 60%, #262f1f);
}
.room-map__node--forest {
  background:
    radial-gradient(circle at 30% 30%, rgba(0,0,0,.2) 0 10%, transparent 11%),
    radial-gradient(circle at 65% 60%, rgba(0,0,0,.18) 0 9%, transparent 10%),
    radial-gradient(circle at 45% 75%, rgba(0,0,0,.15) 0 8%, transparent 9%),
    linear-gradient(160deg, #4a6b3a, #2f4a26 60%, #223519);
}
.room-map__node--home {
  background:
    repeating-linear-gradient(45deg, rgba(255,217,97,.06) 0 6px, transparent 6px 14px),
    linear-gradient(160deg, #8a5a3a, #6b4429 60%, #543420);
}
.room-map__node--watch {
  background:
    radial-gradient(circle at 50% 30%, rgba(255,217,97,.12) 0 12%, transparent 13%),
    linear-gradient(160deg, #4a5568, #333d4d 60%, #262d38);
}

.room-map__node--reserved { opacity:.45; filter:grayscale(.6) }

.room-map__node--reachable, .room-map__node--toggle { cursor:pointer }
.room-map__node--locked { opacity:.55; filter:grayscale(.35) }
.room-map__node--current { box-shadow:0 0 0 1px #ffd961, 0 0 1px 1px rgba(255,217,97,.7) }

/* Focused sub-view — a single "you are here" tile, shown while inside a
   pocket location with no grid tile of its own (mine/watch/forest). Square,
   same as every other tile — no separate rectangular sizing here; the base
   .room-map rule already computes 1/1 from --map-cols/--map-rows (both set
   to 1 by buildFocusedGrid). */
.room-map__marker--exit { background:linear-gradient(180deg,#eed593,#b87333); border-color:#eed593 }

.room-map__node-body {
  display:flex; flex-direction:column; align-items:center; gap:calc(1*var(--u)); pointer-events:none;
  /* Tiles now carry real (often dark) textures instead of transparent —
     override the page's dark-brown body text so icon/label stay legible. */
  color:#f5ead0; text-shadow:0 1px 2px rgba(0,0,0,.6);
}
.room-map__node-icon { font-size:clamp(1.1rem,4.5vw,1.6rem); line-height:1 }
.room-map__node-label { font-size:clamp(.55rem,2.2vw,.8rem); text-align:center; line-height:1.1 }

/* Markers are individually positioned (left/top set inline per-marker by
   map.js's MARKER_SLOTS — fixed, non-overlapping N/E/S/W(/diagonal) anchor
   points; which marker gets which slot is a deterministic per-room shuffle,
   so a tile's icons sit in the same spot every visit). The wrapper just
   needs to span the tile so those percentages resolve correctly and stay
   clickable above the avatar underneath. */
.room-map__markers { position:absolute; inset:0; pointer-events:none; z-index:2 }
.room-map__marker {
  position:absolute; transform:translate(-50%,-50%);
  display:flex; align-items:center; justify-content:center; border-radius:50%;
  /* Fixed (not min-) width/height + border-box + overflow:hidden: without
     these, the box could render slightly non-square (content-driven sizing)
     or grow past its box via the border, so border-radius:50% stopped being
     a true circle and the background gradient showed past the border edge
     at the corners. width/height + a plain 1px border avoids that.
     Kept smaller than a typical .ui-button-ish tap target on purpose — tiles
     render as small as ~85px, so a bigger marker would eat too much of the
     slot spacing MARKER_SLOTS relies on to keep markers from overlapping. */
  width:clamp(1rem,4.2vw,1.2rem); height:clamp(1rem,4.2vw,1.2rem);
  box-sizing:border-box; overflow:hidden;
  background:var(--btn-grad); border:1px solid var(--btn-border); cursor:pointer;
  pointer-events:auto;
  transition:transform .12s ease, box-shadow .12s ease;
}
.room-map__marker:disabled { cursor:default; opacity:.7 }
.room-map__marker:not(:disabled):hover, .room-map__marker:not(:disabled):focus-visible { transform:translate(-50%,-50%) scale(1.12); box-shadow:0 0 0 calc(2*var(--u)) rgba(238,213,147,.9) }
.room-map__marker--current { box-shadow:0 0 0 calc(2*var(--u)) #b87333 }
.room-map__marker-icon { font-size:clamp(.65rem,2.6vw,.85rem) }

.room-map__avatar-slot {
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  pointer-events:none; z-index:1;
}
.room-map__avatar {
  display:block; width:clamp(1.6rem,7vw,2.2rem); height:clamp(1.6rem,7vw,2.2rem);
  border-radius:50%; object-fit:cover; border:calc(1.5*var(--u)) solid #000;
  box-shadow:0 calc(3*var(--u)) calc(6*var(--u)) rgba(0,0,0,.5);
  /* Deliberately no transition — position changes are instant, no movement animation. */
}

/* Traveling marker — sits centered over the bounding box spanning origin +
   destination (see buildTravelMarker) while a real MovementManager trip is
   in progress, i.e. "the avatar in the middle of two rooms" with a
   countdown above it. Unlike .room-map__avatar above, this one IS animated
   — it's showing an in-progress trip, not an instant position jump. */
.room-map__travel-marker {
  position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:calc(2*var(--u)); pointer-events:none; z-index:5;
}
.room-map__travel-countdown {
  padding:calc(1*var(--u)) calc(5*var(--u)); border-radius:calc(8*var(--u));
  background:rgba(20,12,2,.8); color:#eed593; font-weight:700; white-space:nowrap;
  font-size:clamp(.6rem,2.6vw,.85rem); line-height:1.6;
  box-shadow:0 0 0 calc(1*var(--u)) rgba(238,213,147,.55), 0 calc(2*var(--u)) calc(5*var(--u)) rgba(0,0,0,.5);
}
.room-map__travel-avatar-wrap { animation:room-map-travel-bob .9s ease-in-out infinite }
.room-map__travel-avatar-wrap .room-map__avatar {
  width:clamp(1.8rem,7.5vw,2.4rem); height:clamp(1.8rem,7.5vw,2.4rem);
}
@keyframes room-map-travel-bob {
  0%, 100% { transform:translateY(0) }
  50%      { transform:translateY(calc(-4*var(--u))) }
}

/* ═══ Error bar ═══════════════════════════════════════════════════════════════ */
.error-message {
  display:none; margin:.5rem auto; max-width:28rem; text-align:center; padding:.5rem;
  background:var(--paper-bg); color:#7a1b0c; border:var(--hair) solid var(--panel-border); outline:calc(3*var(--u)) solid var(--panel-rim); border-radius:calc(8*var(--u));
}

/* ═══ Buttons ═════════════════════════════════════════════════════════════════ */
.ui-button {
  display:inline-flex; align-items:center; justify-content:center; gap:.35rem; padding:.6rem 1.25rem; border-radius:999px; border:calc(2*var(--u)) solid var(--btn-border); background:var(--btn-grad); color:var(--text); font-weight:600; cursor:pointer;
}
.ui-button:hover    { box-shadow:0 2px 8px rgba(0,0,0,.35) }
.ui-button:disabled { opacity:.5; cursor:not-allowed; transform:none; box-shadow:none }
.ui-button--primary { background:linear-gradient(135deg,#eed593,#b87333); border-color:rgba(107,57,17,.85); color:#2c1606 }
.ui-button--primary:hover { background:linear-gradient(135deg,#f6e3aa,#c9833f) }
.ui-button--accent  { background:linear-gradient(135deg,#fbbf24,#b87333); border-color:rgba(107,57,17,.8); color:#1f1309 }
.ui-button--outline { background:rgba(255,255,255,.3); border-color:rgba(80,46,2,.5) }
.ui-button--ghost   { background:transparent; border-color:rgba(80,46,2,.35) }
.ui-button--sm      { padding:.45rem 1rem; font-size:.9rem }
.ui-button--xs, .ui-button--compact { padding:.35rem .85rem; font-size:.8rem }
.ui-button--block   { width:100% }

/* ═══ Shared lift-on-hover — transition + translateY ═════════════════════════ */
.ui-button,
.footer-nav__item {
  transition:transform .15s ease, box-shadow .2s ease, background .2s ease;
}
.ui-button:hover,
.footer-nav__item:hover { transform:translateY(-1px) }

/* ═══ Modals ══════════════════════════════════════════════════════════════════ */
.ui-modal, .ui-dialog {
  position:fixed; inset:0; display:flex; align-items:center; justify-content:center;
}
.ui-modal { z-index:50; align-items:stretch }
.ui-dialog { z-index:100 }
.ui-modal__backdrop, .ui-dialog__backdrop { position:absolute; inset:0; background:rgba(0,0,0,.55) }
.ui-modal__frame {
  position:relative; z-index:1; display:flex; flex-direction:column; gap:calc(16*var(--u));
  width:100%; height:100%; max-width:1000px; padding:calc(20*var(--u));
  align-items:stretch; justify-content:flex-end; box-sizing:border-box;
}
@media(min-width:768px) { .ui-modal__frame { flex-direction:row; flex-wrap:wrap; align-items:center; justify-content:center; height:auto; margin:auto } }
/* Shared base for both dialog containers */
.ui-dialog__container,
.effects-modal__container { position:relative; z-index:1; padding:calc(20*var(--u)); display:flex; box-sizing:border-box }
.ui-dialog__container    { width:min(100%,28rem); justify-content:center; align-items:center }
.effects-modal__container { width:min(100%,36rem); max-height:85vh; flex-direction:column; margin:auto; overflow-y:auto }

/* Shared dialog shell — panel visual comes from .panel-grid in HTML.
   flex-shrink:0 is required: panel-grid sets overflow:hidden, which per the
   flexbox spec makes this flex item's automatic minimum size 0 instead of
   content-based — without this, tall content (e.g. the personal room hub)
   gets silently squashed/clipped by the flex algorithm instead of properly
   overflowing so .effects-modal__container's own overflow-y:auto can scroll it. */
.dialog-panel { position:relative; display:flex; flex-direction:column; gap:calc(16*var(--u)); padding:calc(24*var(--u)); width:100%; box-sizing:border-box; flex-shrink:0 }
.dialog-title { margin:0; text-align:center; font-size:clamp(1.2rem,2.2vw,1.6rem); font-weight:700; letter-spacing:.02em }
.dialog-close { position:absolute; top:calc(18*var(--u)); right:calc(18*var(--u)) }
/* Item detail dialog actions */
.ui-dialog__actions { display:flex; flex-direction:column; gap:calc(12*var(--u)) }
/* Item detail dialog — icon / description / effects */
.item-modal__icon-wrap { position:relative; margin:0 auto; width:min(40%, 6rem) }
.item-modal__icon { display:block; width:100%; aspect-ratio:1/1; object-fit:contain }
.item-modal__icon--upgrading { animation:item-glow-pulse .7s ease-out }
.item-modal__description { margin:0; text-align:center; color:var(--text-muted); font-size:clamp(.8rem,1.8vw,.95rem) }
.item-modal__effects { display:flex; flex-direction:column; gap:calc(6*var(--u)) }
.item-effect-row {
  display:flex; align-items:flex-start; gap:calc(8*var(--u)); padding:calc(4*var(--u)) calc(8*var(--u));
  border-radius:calc(8*var(--u)); background:rgba(0,0,0,.06);
}
.item-effect-row__icon { flex:0 0 auto; font-size:1rem; line-height:1 }
.item-effect-row__name { flex:1 1 auto; min-width:0; font-size:clamp(.8rem,1.8vw,.9rem); white-space:normal; word-break:break-word }
.item-effect-row__value { flex:0 0 auto }

@keyframes item-glow-pulse {
  0%   { filter:brightness(1) drop-shadow(0 0 0 transparent); transform:scale(1) }
  40%  { filter:brightness(1.6) drop-shadow(0 0 1.2rem var(--panel-border)); transform:scale(1.18) }
  100% { filter:brightness(1) drop-shadow(0 0 0 transparent); transform:scale(1) }
}
.item-fx-spark {
  position:absolute; top:50%; left:50%; width:.35rem; height:.35rem; margin:-.175rem 0 0 -.175rem;
  border-radius:50%; background:var(--panel-border); box-shadow:0 0 .5rem var(--panel-border);
  pointer-events:none; animation:item-spark .7s ease-out forwards;
}
@keyframes item-spark {
  0%   { transform:translate(0,0) scale(1); opacity:1 }
  100% { transform:translate(var(--spark-x), var(--spark-y)) scale(0); opacity:0 }
}
/* Effects modal list */
.effects-modal__list  { display:flex; flex-direction:column; gap:calc(10*var(--u)); max-height:55vh; overflow-y:auto; padding-right:calc(4*var(--u)) }
.effects-modal__empty { text-align:center; font-style:italic; margin:calc(12*var(--u)) 0 }

/* ═══ Panel layouts — visual from .panel-grid ════════════════════════════════ */

/* Inventory / Shop panels */
.item-panel {
  display:flex; flex-direction:column; gap:calc(16*var(--u)); padding:calc(20*var(--u));
  width:min(100%,28rem); flex:1 1 24rem; min-height:clamp(260px,55vh,420px); box-sizing:border-box;
}
.item-panel__header { display:flex; align-items:center; justify-content:space-between; gap:calc(12*var(--u)) }
.item-panel__title  { margin:0; font-size:clamp(1.25rem,2.2vw,1.6rem); letter-spacing:.02em }
.item-panel__close  { align-self:flex-start }
.item-panel__grid {
  display:grid; grid-template-columns:repeat(auto-fit,minmax(80px,1fr)); gap:calc(14*var(--u)); justify-items:center; align-items:start; flex:1 1 auto; min-height:0;
  padding:calc(8*var(--u)); border-radius:calc(14*var(--u)); background:rgba(255,255,255,.22); box-shadow:inset 0 2px 8px rgba(0,0,0,.18); overflow:auto;
}
@media(min-width:768px) { .item-panel__grid { grid-template-columns:repeat(auto-fit,minmax(96px,1fr)) } }
.inventory-item     { position:relative; width:100%; max-width:calc(96*var(--u)); cursor:pointer; display:flex; flex-direction:column; align-items:center; gap:calc(4*var(--u)) }
.inventory-item img { width:100%; aspect-ratio:1/1; object-fit:contain; display:block }
.item-price         { font-size:clamp(.75rem,1.6vw,.9rem); font-weight:600; text-align:center }
.item-level-badge {
  position:absolute; top:-.35rem; right:-.35rem; z-index:1; display:grid; place-items:center;
  min-width:1.2rem; height:1.2rem; padding:0 .2rem; border:1px solid var(--panel-border); border-radius:50%;
  color:var(--paper-bg); background:var(--panel-rim);
  box-shadow:0 0 .5rem var(--panel-border); font-weight:800; font-size:.7rem; line-height:1;
}

/* Activity panel (mining, watch) */
.activity-panel { display:flex; flex-direction:column; gap:calc(14*var(--u)); padding:calc(22*var(--u)) calc(24*var(--u)); align-items:center; text-align:center; width:min(100%,26rem); align-self:center; margin:auto auto calc(16*var(--u)); overflow:visible }
.activity-panel__title       { margin:0; font-size:clamp(1.4rem,2.4vw,1.8rem); font-weight:700 }
.activity-panel__description { margin:0; font-size:clamp(.95rem,2vw,1.05rem) }
.activity-panel__controls    { display:flex; gap:calc(16*var(--u)); justify-content:center; flex-wrap:wrap }
.activity-panel__choices     { display:flex; flex-wrap:wrap; gap:calc(12*var(--u)); justify-content:center }
.activity-panel__status {
  width:100%; padding:calc(12*var(--u)) calc(16*var(--u)); border-radius:calc(18*var(--u)); background:rgba(255,255,255,.65);
  border:var(--hair) solid rgba(80,46,2,.5); outline:calc(3*var(--u)) solid rgba(57,30,1,.8);
  font-weight:600; display:flex; flex-direction:column; gap:calc(8*var(--u)); text-align:center; align-items:center;
}
.activity-countdown__label { font-size:clamp(.9rem,2vw,1rem); letter-spacing:.03em }
.activity-countdown__progress {
  position:relative; width:100%; height:calc(16*var(--u)); border-radius:calc(14*var(--u)); overflow:hidden;
  background:rgba(0,0,0,.28); border:var(--hair) solid rgba(80,46,2,.7); outline:calc(2*var(--u)) solid rgba(57,30,1,.8);
  box-shadow:inset 0 1px 6px rgba(0,0,0,.35);
}
.activity-countdown__progress-fill { position:absolute; inset:0; transform-origin:left center; transform:scaleX(1); background:linear-gradient(90deg,#3a8f5e,#1e6fa8) }
@keyframes countdown-progress { from{transform:scaleX(1)} to{transform:scaleX(0)} }

/* ═══ Footer nav ══════════════════════════════════════════════════════════════ */
.footer-nav { width:100%; display:grid; grid-auto-flow:column; grid-auto-columns:1fr }
.footer-nav .cell { aspect-ratio:unset; border-right:var(--hair) solid var(--panel-border); border-bottom:0 }
.footer-nav .cell:last-child { border-right:0 }
.footer-nav__item {
  gap:calc(6*var(--u)); padding:calc(6*var(--u)); text-transform:uppercase;
  letter-spacing:.04em; text-align:center; outline:none; flex:1 1 0;
}
.footer-nav__item:hover, .footer-nav__item:focus-visible {
  background:linear-gradient(180deg,rgba(255,240,210,.95),rgba(214,176,103,.95));
  box-shadow:inset 0 calc(3*var(--u)) calc(6*var(--u)) rgba(255,255,255,.4);
}
.footer-nav__item--active {
  background:linear-gradient(180deg,rgba(255,239,208,.95),rgba(230,193,122,.95));
  box-shadow:inset 0 calc(5*var(--u)) calc(10*var(--u)) rgba(0,0,0,.2);
}
.footer-nav__icon  { font-size:clamp(1.4rem,5vw,2rem) }
.footer-nav__icon--img { width:clamp(1.4rem,5vw,2rem); height:clamp(1.4rem,5vw,2rem); object-fit:contain }
.footer-nav__label { font-size:clamp(.7rem,2.1vw,.95rem) }

/* ═══ Status effects ══════════════════════════════════════════════════════════ */
#status-panel {
  position:absolute; top:calc(10*var(--u)); right:calc(10*var(--u)); z-index:10; cursor:pointer;
  display:grid; grid-template-columns:1fr; gap:calc(6*var(--u));
  padding:calc(6*var(--u)); border-radius:calc(14*var(--u)); transition:background .2s ease;
  --effect-size:calc(30*var(--u));
}
#status-panel:empty { display:none }
#status-panel:hover { background:rgba(0,0,0,.15) }
/* --emoji-font / --count-font cascade to children; fallbacks = xs/default sizes */
#status-panel.effects--lg { --effect-size:calc(72*var(--u)); --emoji-font:calc(34*var(--u)); --count-font:calc(16*var(--u)) }
#status-panel.effects--md { --effect-size:calc(54*var(--u)); --emoji-font:calc(26*var(--u)); --count-font:calc(13*var(--u)) }
#status-panel.effects--sm { --effect-size:calc(40*var(--u)); --emoji-font:calc(19*var(--u)); --count-font:calc(10*var(--u)); grid-template-columns:1fr 1fr }
#status-panel.effects--xs { --effect-size:calc(30*var(--u)); grid-template-columns:1fr 1fr }
.status-effect { position:relative; width:var(--effect-size); height:var(--effect-size); border-radius:50%; transition:width .3s ease,height .3s ease }
.status-effect__emoji {
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center; border-radius:50%;
  background:rgba(100,62,8,.25); border:calc(2*var(--u)) solid rgba(171,110,21,.6);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5), 0 4px 8px rgba(0,0,0,.3); font-size:var(--emoji-font, calc(14*var(--u)));
}
.status-effect svg { position:absolute; inset:0; width:100%; height:100%; transform:rotate(-90deg); pointer-events:none }
.status-effect .countdown {
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-weight:bold; color:#fff; text-shadow:0 1px 3px rgba(0,0,0,.8); pointer-events:none; font-size:var(--count-font, calc(8*var(--u)));
}
.progress-ring__circle { stroke:rgba(255,255,255,.85); stroke-width:2; fill:transparent; stroke-linecap:round }

/* ═══ Auth screen ═════════════════════════════════════════════════════════════ */
.auth-screen { display:flex; flex-direction:column; justify-content:center; align-items:center; min-height:100vh; padding:2rem }
.auth-wrapper { display:flex; flex-direction:column; gap:1.5rem; align-items:center; max-width:900px; width:100% }
/* auth-note overrides panel-grid bg to lighter paper */
.auth-note { background:var(--paper-bg); padding:1rem 1.5rem; text-align:center; max-width:720px }
.auth-note.snark { font-style:italic }
.auth-forms { display:flex; flex-direction:column; gap:1.5rem; width:100% }
@media(min-width:768px) { .auth-forms { flex-direction:row; justify-content:center } }
/* auth-card visual from .panel-grid, layout here */
.auth-card { padding:1.5rem; flex:1; display:flex; flex-direction:column; gap:.75rem; min-width:240px }
.auth-card h2    { margin:0 0 .5rem; text-align:center }
.auth-card label { font-weight:bold; font-size:.95rem }
.auth-card input { padding:.6rem; border:var(--hair) solid var(--panel-rim); border-radius:8px; font-size:1rem }
/* auth-button extends .ui-button — only overrides that differ */
.auth-button { margin-top:.5rem; padding:.75rem; font-size:1rem; border-radius:10px; border:var(--hair) solid var(--panel-border); width:100% }
.form-message         { min-height:1.2rem; text-align:center; font-size:.9rem }
.form-message.error   { color:#7a1b0c }
.form-message.success { color:#1f5f2e }

/* ═══ Game screen (personal room) ═══════════════════════════════════════════ */
.game-screen { display:flex; flex-direction:column; gap:1.5rem; padding:1.5rem; max-width:42rem; width:min(100%,42rem); margin:1rem auto; box-sizing:border-box }
.game-screen__header { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:.75rem }
.ui-heading          { font-weight:700; margin:0 }
.ui-heading--section { font-size:clamp(1.4rem,2.2vw,2rem) }
.ui-subheading       { font-size:1.1rem; font-weight:600; margin:0 }

/* Inset panel within game-screen — same warm bronze/brown tint used by
   .avatar-preview/.autoplay-step's inset panels, not a cool white card. */
.ui-panel { background:rgba(100,62,8,.14); border:var(--hair) solid rgba(171,110,21,.45); border-radius:12px; padding:1rem; display:flex; flex-direction:column; gap:.75rem }
.ui-panel--stack { gap:.85rem }
.ui-field  { display:flex; flex-direction:column; gap:.5rem }
.ui-label  { font-size:.95rem; font-weight:600 }
.ui-input, .ui-select {
  width:100%; padding:.6rem .75rem; border-radius:10px; font-size:.95rem; box-sizing:border-box;
  border:1px solid rgba(16,42,37,.7); background:rgba(255,255,255,.92); color:#0f172a; transition:box-shadow .15s ease, border-color .15s ease;
}
.ui-input:focus, .ui-select:focus { outline:none; border-color:#fbbf24; box-shadow:0 0 0 2px rgba(251,191,36,.4) }

/* ═══ Character selector ══════════════════════════════════════════════════════ */
.character-selector { display:flex; flex-direction:column; gap:.75rem }
@media(min-width:640px) { .character-selector { flex-direction:row; align-items:center } }
.character-selector .ui-select    { flex:1 }
.character-selector__button       { flex:0 0 auto; white-space:nowrap }
.character-selector__feedback     { min-height:1.25rem; margin:0; font-size:.9rem }
.character-selector__feedback--success { color:#1f5f2e }

/* ═══ Avatar picker ═══════════════════════════════════════════════════════════ */
.avatar-preview { padding:1rem; background:rgba(0,0,0,.12); border-radius:12px }
.avatar-preview img { width:9.5rem; height:9.5rem; object-fit:contain; border-radius:12px; border:3px solid rgba(171,110,21,.85); box-shadow:0 0 0 3px rgba(171,110,21,.25) }
.avatar-grid   { display:grid; grid-template-columns:repeat(auto-fit,minmax(64px,1fr)); gap:.65rem }
.avatar-option {
  padding:.3rem; border-radius:10px; border:2px solid transparent;
  background:rgba(0,0,0,.12); cursor:pointer;
  transition:border-color .2s ease, transform .2s ease, box-shadow .2s ease;
}
.avatar-option:hover { transform:translateY(-1px); box-shadow:0 4px 12px rgba(0,0,0,.2) }
.avatar-option--selected { border-color:rgba(171,110,21,.85); box-shadow:0 0 0 2px rgba(171,110,21,.3) }
.avatar-image  { width:2.75rem; height:2.75rem; object-fit:contain; transition:transform .2s ease }
.avatar-option--selected .avatar-image { transform:scale(1.05) }

/* ═══ Character roster ════════════════════════════════════════════════════════ */
.roster-panel  { display:flex; flex-direction:column; margin-top:calc(6*var(--u)); position:relative }
.roster-panel__heading { margin:0 0 calc(4*var(--u)); font-size:1em; font-weight:600; color:var(--text) }
.roster-panel__toggle {
  position:absolute; top:calc(8*var(--u)); right:calc(8*var(--u)); z-index:2;
  background:rgba(0,0,0,.55); border-color:rgba(200,130,30,.6);
  color:rgba(255,225,140,1); padding:.4rem 1.2rem; font-size:1rem; font-weight:600;
}
.roster-panel__toggle:hover { background:rgba(0,0,0,.75); color:#fff; border-color:rgba(220,160,50,.8) }
.roster {
  display:grid; gap:.65rem; padding:.75rem;
  background:rgba(100,62,8,.15);
  border:calc(5*var(--u)) solid var(--panel-rim);
  box-shadow:inset 0 0 0 var(--hair) var(--panel-border), inset calc(2*var(--u)) calc(5*var(--u)) var(--hair) var(--hair) rgba(0,0,0,.3);
  border-radius:14px; max-height:45vh; overflow-y:auto;
}
.roster[data-collapsed="true"] {
  display:flex; flex-wrap:wrap; gap:.4rem; padding:.4rem .5rem;
  background:transparent; border-color:transparent; box-shadow:none; max-height:none;
}
.roster[data-collapsed="true"] .roster-card {
  min-height:0; padding:0; background:transparent; border:none; box-shadow:none; width:auto;
}
.roster[data-collapsed="true"] .roster-card:hover { transform:translateY(-1px) }
.roster[data-collapsed="true"] .roster-card__body { display:none }
.roster[data-collapsed="true"] .roster-card__avatar { width:2.5rem; height:2.5rem }
.roster[data-collapsed="true"] .roster-card--empty { display:none }
.roster[data-variant="expanded"] { grid-template-columns:repeat(auto-fill,minmax(200px,260px)); max-height:none }
/* Compact: a short list of a few players, not a filled-out grid — flex-column
   hugs content width without grid auto-fill's fit-content circular sizing. */
.roster[data-variant="compact"] { display:flex; flex-direction:column; width:fit-content; max-width:100%; background:rgba(100,62,8,.08) }

/* roster-card visual from .panel-grid, layout here */
.roster-card { display:flex; flex-direction:column; gap:.6rem; padding:.75rem; min-height:8rem; transition:transform .2s ease, box-shadow .2s ease }
.roster-card:hover    { transform:translateY(-2px); box-shadow:0 8px 18px rgba(0,0,0,.22) }
.roster-card--compact { flex-direction:row; align-items:center; min-height:0 }
.roster-card--active  { box-shadow:0 0 0 3px var(--panel-border) }
.roster-card--empty   { background:rgba(100,62,8,.08); border-style:dashed; align-items:center; justify-content:center; text-align:center }

.roster[data-variant="compact"] .roster-card__avatar   { width:2.75rem; height:2.75rem; border-width:1px }
.roster[data-variant="compact"] .roster-card__location { display:none }
.roster[data-variant="compact"] .roster-card__gauges   { flex-direction:row }
.roster[data-variant="compact"] .roster-card__gauge    { flex:1 }
.roster[data-variant="compact"] .roster-card__name     { font-size:.95rem }

.roster-card__avatar  { width:4.5rem; height:4.5rem; border-radius:50%; overflow:hidden; flex-shrink:0; border:2px solid var(--panel-border); background:rgba(0,0,0,.12) }
.roster-card__avatar img { width:100%; height:100%; object-fit:cover }
.roster-card__body    { display:flex; flex-direction:column; gap:.4rem }
.roster-card__name    { font-weight:700 }
.roster-card__status  { display:flex; align-items:center; gap:.5rem; font-size:.9rem }
.roster-card__status-text { flex:1 }
.roster-card__location    { font-size:.85rem; opacity:.75 }
.roster-card__placeholder { font-style:italic; opacity:.75 }
.roster-card__gauges  { display:flex; flex-direction:column; gap:.35rem }
.roster-card__gauge   { display:flex; flex-direction:column; gap:.2rem; font-size:.8rem }
.roster-card__gauge-label { display:flex; justify-content:space-between }
.roster-card__gauge-meter { position:relative; height:.5rem; border-radius:999px; background:rgba(0,0,0,.2); overflow:hidden }
.roster-card__gauge-fill  { position:absolute; inset-block:0; left:0; border-radius:inherit; width:0 }
.roster-card__gauge--health  .roster-card__gauge-fill { background:linear-gradient(90deg,#14532d,#22c55e) }
.roster-card__gauge--stamina .roster-card__gauge-fill { background:linear-gradient(90deg,#1d4ed8,#38bdf8) }

/* ═══ Attribute rows ══════════════════════════════════════════════════════════ */
.attribute-row { display:flex; align-items:center; justify-content:space-between; gap:1rem; padding:.6rem .4rem; border-bottom:var(--hair) solid rgba(46,27,16,.2) }
.attribute-row__info     { display:flex; gap:.75rem; align-items:center }
.attribute-row__label    { font-weight:600 }
.attribute-row__controls { display:flex; gap:.5rem }
/* Extends .ui-button's bronze/gold gradient (kept, not overridden) — only the
   border is tinted so +/- stay visually distinguishable without looking like
   a different button system. */
.attribute-row__button { gap:.3rem; padding:.35rem .9rem }
.attribute-row__button--increase { border-color:rgba(34,197,94,.7) }
.attribute-row__button--decrease { border-color:rgba(220,38,38,.7) }
.attribute-row__button--disabled { opacity:.5; cursor:not-allowed; transform:none }
.attribute-row__cost   { font-size:.8rem }

/* ═══ Effect cards (effects modal) ═══════════════════════════════════════════ */
.effect-card {
  display:flex; align-items:center; gap:calc(14*var(--u)); padding:calc(12*var(--u)) calc(14*var(--u));
  border-radius:calc(12*var(--u)); background:rgba(255,255,255,.4); border:var(--hair) solid rgba(80,46,2,.3);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.6), 0 2px 6px rgba(0,0,0,.1); transition:background .15s ease;
}
.effect-card:hover { background:rgba(255,255,255,.58) }
.effect-card__icon { width:calc(44*var(--u)); height:calc(44*var(--u)); min-width:calc(44*var(--u)); flex-shrink:0; border-radius:50%; font-size:calc(22*var(--u)); background:rgba(100,62,8,.15); border:calc(2*var(--u)) solid rgba(171,110,21,.55) }
.effect-card__body  { flex:1; min-width:0; display:flex; flex-direction:column; gap:calc(3*var(--u)) }
.effect-card__name  { font-weight:700; font-size:clamp(.85rem,2vw,1rem) }
.effect-card__type  { font-size:clamp(.72rem,1.7vw,.85rem) }
.effect-card__meta  { display:flex; flex-direction:column; align-items:flex-end; gap:calc(4*var(--u)); flex-shrink:0 }
.effect-card__value { font-weight:700; font-size:clamp(.85rem,2vw,1rem); white-space:nowrap; padding:calc(2*var(--u)) calc(10*var(--u)); border-radius:999px; background:rgba(100,62,8,.1) }
.effect-card__value--negative { color:#7a1b0c }
.effect-card__value--positive { color:#1f5f2e }
.effect-card__value--neutral  { color:#2c4a7a }
.effect-card__duration { font-size:clamp(.68rem,1.5vw,.78rem); white-space:nowrap }

/* ═══ Resource chips (Inventory panel, under Items) ══════════════════════════ */
.item-panel__resources { display:flex; flex-direction:column; gap:calc(6*var(--u)); flex:0 0 auto }
.item-panel__resources-title { margin:0; font-size:clamp(.85rem,1.8vw,1rem) }
.resource-chip-list {
  display:flex; flex-wrap:wrap; gap:calc(6*var(--u)); padding:calc(8*var(--u));
  border-radius:calc(10*var(--u)); background:rgba(255,255,255,.22); box-shadow:inset 0 2px 8px rgba(0,0,0,.12);
}
.resource-chip {
  display:flex; align-items:center; gap:calc(4*var(--u)); padding:calc(3*var(--u)) calc(8*var(--u));
  border-radius:999px; background:rgba(255,255,255,.4); border:var(--hair) solid rgba(80,46,2,.3);
  font-size:clamp(.68rem,1.5vw,.8rem);
}
.resource-chip__icon-image { width:calc(16*var(--u)); height:calc(16*var(--u)); object-fit:contain; border-radius:50% }
.resource-chip__icon-unicode { font-size:calc(14*var(--u)); line-height:1 }
.resource-chip__name   { color:var(--text-muted) }
.resource-chip__amount { font-weight:700 }

/* ═══ Autoplay ════════════════════════════════════════════════════════════════ */
.autoplay-steps-list { list-style:none; padding:0; margin:0 0 calc(10*var(--u)); display:flex; flex-direction:column; gap:calc(6*var(--u)) }
.autoplay-step { display:flex; align-items:center; gap:calc(8*var(--u)); padding:calc(6*var(--u)) calc(10*var(--u)); background:rgba(100,62,8,.06); border-radius:calc(6*var(--u)); font-size:clamp(.8rem,1.9vw,.9rem) }
.autoplay-step__label { flex:1 }
.autoplay-step__remove { background:none; border:none; cursor:pointer; color:var(--panel-dark); opacity:.6; font-size:1em; padding:0 calc(4*var(--u)) }
.autoplay-step__remove:hover { opacity:1 }
.autoplay-add-row { display:flex; gap:calc(8*var(--u)); align-items:center }
.autoplay-prob-input { width:calc(80*var(--u)); min-width:60px }

/* ═══ Responsive ══════════════════════════════════════════════════════════════ */
@media(max-width:768px) {
  .game-screen { padding:1.25rem; gap:1.25rem }
  .roster[data-variant="expanded"] { grid-template-columns:repeat(auto-fit,minmax(160px,1fr)) }
  .roster-card--compact { flex-direction:column; align-items:flex-start }
}
@media(max-width:640px) {
  .roster-panel__toggle { align-self:stretch }
  .roster[data-variant="compact"] { grid-template-columns:repeat(auto-fit,minmax(140px,1fr)) }
}

/* ═══ Battle ══════════════════════════════════════════════════════════════════ */
.battle-overlay {
  /* ───────────────────────────────────────────────────────────────────────────
     BATTLE THEME — edit these ~15 vars to re-skin the whole battle UI.
     Default = "darkened bronze": dark + warm so the neon threads glow, but
     keeping Furnado's gold/bronze accents + Georgia serif. Colours never live in
     JS — battle.js only sets which GROUP a thread/card belongs to.
     ─────────────────────────────────────────────────────────────────────────── */
  --bt-bg:            #160c02;                 /* overlay / arena backdrop */
  --bt-arena-glow:    rgb(171 110 21 / .16);   /* faint central ritual glow */
  --bt-panel:         rgb(40 26 8 / .82);      /* card / log glass */
  --bt-panel-border:  rgb(171 110 21 / .55);
  --bt-accent:        #eed593;                 /* gold — titles, self, sigils */
  --bt-text:          #f5e3bd;
  --bt-text-muted:    rgb(245 227 189 / .68);
  --bt-thread-ours:   hsl(190 88% 66%);        /* OUR attack lines (cyan) */
  --bt-thread-theirs: hsl(354 86% 62%);        /* THEIR attack lines (red) */
  --bt-glow:          .85rem;                  /* base neon glow radius */
  --bt-card-min:      calc(44*var(--u));       /* min card height when many (1v8); few → cards fill the column */

  position:fixed; inset:0; z-index:60; display:flex; align-items:center; justify-content:center;
  padding:calc(12*var(--u)); color:var(--bt-text);
  background:
    radial-gradient(circle at 50% 46%, var(--bt-arena-glow), transparent 60%),
    color-mix(in srgb, var(--bt-bg), transparent 8%);
}
.battle-overlay__frame { width:100%; max-width:calc(960*var(--u)); max-height:96vh; display:flex }
.battle-screen {
  flex:1; display:flex; flex-direction:column; gap:calc(10*var(--u));
  padding:calc(14*var(--u)); max-height:96vh; overflow:auto;
}
/* Dark-theme the shared panel chrome — scoped so the rest of the game is untouched */
.battle-overlay .panel-wrap,
.battle-overlay .panel-grid { background:var(--bt-panel); border-color:var(--bt-panel-border); color:var(--bt-text) }

.battle-header { display:flex; align-items:center; justify-content:space-between; gap:1rem }
.battle-header__eyebrow { display:block; font-size:.7rem; letter-spacing:.08em; text-transform:uppercase; color:var(--bt-text-muted) }
.battle-header__title { margin:0; font-size:clamp(1.1rem,3vw,1.6rem); color:var(--bt-accent) }

/* Timer — value centred over its track so the lone number reads intentionally */
.battle-timer { display:flex; flex-direction:column; align-items:center; gap:.2rem; min-width:calc(140*var(--u)) }
.battle-timer__value { font-weight:600; text-align:center; font-variant-numeric:tabular-nums; color:var(--bt-accent) }
.battle-timer__track { position:relative; width:100%; height:.5rem; border-radius:999px; background:rgba(0,0,0,.35); overflow:hidden }
.battle-timer__fill { position:absolute; inset-block:0; left:0; width:100%; border-radius:inherit; background:linear-gradient(90deg,var(--bt-accent),#b87333); transition:width .25s linear }

.battlefield {
  position:relative; display:grid; grid-template-columns:1fr 1fr;
  gap:clamp(calc(80*var(--u)),10vw,calc(160*var(--u)));
  align-items:stretch; block-size:min(70vh,calc(560*var(--u)));
}
.battle-side__title { margin:0 0 .5rem; text-align:center; font-size:.9rem; color:var(--bt-text-muted) }
.battle-side { display:flex; flex-direction:column; min-block-size:0 }
/* Each side sets its own --count (its own headcount) from JS, so the two columns
   size independently: few on a side → tall cards, many → short cards. */
.combatant-list {
  flex:1; display:grid; gap:calc(6*var(--u)); min-block-size:0;
  grid-template-rows:repeat(var(--count,1), minmax(var(--bt-card-min), 1fr));
  align-content:stretch;
}

/* Magic threads overlay — soft-curve SVG lines from attacker to target (lab v8).
   Colour comes from the group class below; never set inline by JS. */
.battle-threads {
  position:absolute; inset:0; width:100%; height:100%;
  overflow:visible; pointer-events:none; z-index:1; mix-blend-mode:screen;
}
.svg-thread-group--ours   { --thread-color:var(--bt-thread-ours) }
.svg-thread-group--theirs { --thread-color:var(--bt-thread-theirs) }
.svg-thread { fill:none; stroke-linecap:round; stroke-linejoin:round; vector-effect:non-scaling-stroke }
.svg-thread--aura {
  stroke:var(--thread-color); stroke-width:10; stroke-opacity:.18;
  filter: blur(4px)
    drop-shadow(0 0 calc(.55*var(--bt-glow)) var(--thread-color))
    drop-shadow(0 0 calc(1.2*var(--bt-glow)) color-mix(in srgb, var(--thread-color), transparent 35%));
}
.svg-thread--body {
  stroke:var(--thread-color); stroke-width:3; stroke-opacity:.62;
  filter: drop-shadow(0 0 calc(.4*var(--bt-glow)) var(--thread-color))
    drop-shadow(0 0 var(--bt-glow) color-mix(in srgb, var(--thread-color), transparent 42%));
}
.svg-thread--core {
  stroke:color-mix(in srgb, var(--thread-color), white 28%); stroke-width:1.2; stroke-opacity:.96;
  filter: drop-shadow(0 0 .18rem var(--thread-color));
}

/* Cards: each fills its grid cell (block-size:100%) so both sides match height.
   3 columns — portrait | copy | marks. container-type lets dense battles drop
   secondary text as cards shrink. */
.combatant-card {
  position:relative; z-index:2; block-size:100%; min-block-size:0;
  border-width:calc(3*var(--u));
  background:var(--bt-panel); border-color:var(--bt-panel-border); color:var(--bt-text);
  container:bt-card / size; overflow:hidden;
}
/* Card body holds the layout so the card itself can act as the query container:
   a container query can't restyle its own container, so the body flips between
   the compact row (default) and the tall vertical column (few combatants). */
/* Pinned to the card box (not block-size:100%) so its height is always definite:
   the percentage-height chain through the flex/grid ancestors doesn't resolve,
   which let the body size to content and overflow the card (clipping the intent). */
.combatant-card__body {
  position:absolute; inset:0; display:grid; grid-template-columns:auto minmax(0,1fr);
  align-items:center; gap:.6rem; padding:calc(6*var(--u)) calc(8*var(--u));
}
.combatant-card--enemy { cursor:pointer }
.combatant-card--enemy:hover { filter:brightness(1.12) }
/* Self: same card as everyone else, just a subtle gold rim to find yourself.
   Doubled class beats `.battle-overlay .panel-grid` which sets the base border. */
.combatant-card.combatant-card--self { border-color:var(--bt-accent) }
.combatant-card--targeted { border-color:var(--bt-accent); box-shadow:0 0 var(--bt-glow) color-mix(in srgb, var(--bt-accent), transparent 55%) }
.combatant-card--attacking { box-shadow:0 0 calc(10*var(--u)) color-mix(in srgb, var(--bt-thread-ours), transparent 60%) }
.combatant-card--down { opacity:.45; filter:grayscale(.6) }
.combatant-card__portrait {
  position:relative; aspect-ratio:1; block-size:min(80%, calc(112*var(--u)));
  min-block-size:1.8rem; border-radius:50%; overflow:visible; flex-shrink:0;
  border:2px solid var(--bt-panel-border); background:rgba(0,0,0,.25); justify-self:center;
}
.combatant-card__portrait img { width:100%; height:100%; border-radius:50%; object-fit:cover }
.combatant-card__sigil {
  position:absolute; top:-.35rem; right:-.35rem; display:grid; place-items:center;
  width:1.2rem; height:1.2rem; border:1px solid currentColor; border-radius:50%;
  color:var(--bt-accent); background:rgba(5,4,10,.84);
  box-shadow:0 0 .7rem currentColor; font-weight:800; font-size:.72rem; line-height:1;
}
.combatant-card__main { min-width:0; display:flex; flex-direction:column; justify-content:center; gap:.25rem }
.combatant-card__name { font-weight:600; font-size:.85rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
/* Marks overlay a corner (out of grid flow) so attack marks popping in/out at
   round start never squeeze the main column and shift the avatar/name/HP. */
.combatant-card__marks { position:absolute; top:calc(6*var(--u)); right:calc(6*var(--u)); z-index:1; display:flex; flex-wrap:wrap; gap:.16rem; min-height:0; justify-content:flex-end; align-content:center; max-width:3.4rem }
.combatant-card__marks:empty { display:none }
.combatant-card__mark {
  display:inline-grid; place-items:center; width:1.05rem; height:1.05rem;
  border:1px solid currentColor; border-radius:50%; background:rgba(5,4,10,.75);
  color:var(--bt-thread-ours); font-size:.7rem; line-height:1;
  text-shadow:0 0 .7rem currentColor; box-shadow:0 0 .5rem currentColor;
}
/* Intent line always reserves its row (even when empty) so picking an action
   never reflows the card or shifts the portrait. */
.combatant-card__intent { font-size:.72rem; color:var(--bt-text-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-height:1.2em; flex-shrink:0 }
/* Few combatants (or a tall desktop column) → portrait card: big rounded-rect
   avatar on top, name → HP num/num → bar stacked beneath. Card height tracks
   count (column ÷ count) and screen size, so this flips off on its own as the
   field fills up or the viewport shrinks — no JS. */
@container bt-card (min-height: 140px) {
  .combatant-card__body { display:flex; flex-direction:column; align-items:center; text-align:center; gap:calc(4*var(--u)) }
  .combatant-card__portrait { flex:1 1 auto; width:100%; block-size:auto; aspect-ratio:auto; min-block-size:0; border-radius:calc(10*var(--u)) }
  .combatant-card__portrait img { border-radius:inherit }
  /* main keeps its full height (name → HP → intent); the portrait yields, so the
     intent line never gets pushed past the card edge */
  .combatant-card__main { align-items:center; flex-shrink:0 }
  /* attack-marks pinned to a corner so the portrait stays dominant */
  .combatant-card__marks { position:absolute; top:calc(6*var(--u)); left:calc(6*var(--u)); right:auto; max-width:none; justify-content:flex-start }
}

/* Shrink gracefully: tight cards drop the intent line, very tight also the name */
@container bt-card (max-height: 64px) { .combatant-card__intent { display:none } }
@container bt-card (max-height: 50px) { .combatant-card__name { font-size:.78rem } }

.battle-command { display:flex; flex-direction:column; gap:.5rem; align-items:center }
.battle-actions { display:flex; flex-wrap:wrap; gap:.5rem; justify-content:center }
.battle-action.is-selected { background:linear-gradient(135deg,var(--bt-accent),#b87333); box-shadow:0 0 0 2px var(--bt-accent); color:#160c02 }
.battle-command__hint { margin:0; font-size:.85rem; color:var(--bt-text-muted); text-align:center; min-height:1.2em }
.battle-command__buttons { display:flex; gap:.6rem; flex-wrap:wrap; justify-content:center }

.battle-log { padding:calc(8*var(--u)); max-height:calc(160*var(--u)); overflow:auto }
.battle-log__title { margin:0 0 .4rem; font-size:.85rem; color:var(--bt-text-muted) }
.battle-log__list { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.2rem; font-size:.8rem }
.battle-log__entry { display:flex; gap:.4rem }
.battle-log__round { color:var(--bt-text-muted); flex-shrink:0; font-variant-numeric:tabular-nums }

@media(max-width:480px) {
  .combatant-card__name { font-size:.78rem }
  .battle-screen { padding:calc(10*var(--u)) }
}
