:root {
  /* Palette drawn from the Gems & Bones logo: near-black gunmetal, aged gold
     lettering, and the three gems — ruby / sapphire / emerald. */
  --bg: #100e12;            /* near-black, faint warm */
  --panel: #1a1720;
  --panel-2: #241f2c;
  --panel-3: #2e2838;
  --text: #efe7d6;          /* warm bone */
  --muted: #a89e88;         /* aged parchment */
  --faint: #726a58;
  --accent: #c9a24b;        /* aged gold — the logo lettering */
  --accent-hover: #ddb968;
  --border: #342e3c;

  --discord: #5865f2;       /* keep the login button on-brand */

  /* card types — coolest (acts first) to hottest (acts last) */
  --t-specialBlock: #a978e6;
  --t-special: #6e8bff;
  --t-bonus: #35b06c;       /* emerald gem */
  --t-defensive: #3bb0c4;
  --t-elementalAttack: #e8963a;
  --t-physicalAttack: #d23b38; /* ruby gem */

  /* elements — one colour wherever the element appears */
  --el-fire: #ef8a3c;
  --el-water: #5a8bf0;      /* sapphire gem */
  --el-electric: #f0c04a;
  --el-physical: #d23b38;
  --el-poison: #9ecb3f;

  --mana: #5a8bf0;
  --hp: #d24f5c;
  --good: #35b06c;

  /* item rarity, decided by bonus count (1/2/3) */
  --rarity-common: #9a938a;  /* grey */
  --rarity-rare:   #3fae6b;  /* green */
  --rarity-mythic: #d24f5c;  /* red */

  --font-display: "Cinzel", Georgia, "Times New Roman", serif;
}

/* Rarity colour, pulled from data-rarity on an item container. */
[data-rarity='common'] { --rarity: var(--rarity-common); }
[data-rarity='rare']   { --rarity: var(--rarity-rare); }
[data-rarity='mythic'] { --rarity: var(--rarity-mythic); }
.rarity-tag {
  align-self: flex-start;
  font-size: 0.56rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--rarity, var(--faint));
}

* { box-sizing: border-box; }

/* line-art icon: takes size from width/height attrs, colour from currentColor */
.ico {
  display: inline-block;
  vertical-align: -0.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  line-height: 1.5;
  /* Crypt photo, heavily darkened with a --bg-tinted overlay so it reads as
     a faint backdrop and never competes with panel/text contrast. Fixed so
     it doesn't stretch to the page's full scrollable height. */
  background-color: var(--bg);
  background-image:
    linear-gradient(rgba(16, 14, 18, 0.86), rgba(16, 14, 18, 0.9)),
    url(img/crypt-bg.jpg);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

/* Fill the whole viewport rather than a fixed 720px column. */
.wrap {
  width: 100%;
  min-height: 100vh;
  margin: 0;
  padding: 20px 24px 40px;
}

h1 {
  font-size: 1.8rem;
  margin: 0 0 20px;
}
/* Plain text mark — no banner/logo image for now (both pulled; see the
   .brand markup in index.html). Sized per context same as the old image was. */
.brand {
  margin: 0 0 16px;
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.03em;
  font-size: clamp(1.3rem, 3vw, 1.8rem);
}
/* Big and centered on the login screen... */
.wrap:has(#login-view:not([hidden])) .brand {
  text-align: center;
  margin: 4vh 0 22px;
  font-size: clamp(2rem, 6vw, 3.2rem);
}
#login-view { text-align: center; max-width: 460px; margin: 0 auto; }

/* ...a medium mark on the class-pick screen... */
.wrap:has(#setup-view:not([hidden])) .brand {
  margin-bottom: 14px;
  font-size: clamp(1.6rem, 4.5vw, 2.3rem);
}

/* ...small above the toolbar on the duel screen (the app view instead folds
   it into .topbar itself — see .app-brand — so it's hidden here). */
.wrap:has(#duel-view:not([hidden])) .brand {
  margin-bottom: 8px;
  font-size: clamp(1.1rem, 2.2vw, 1.4rem);
}
.wrap:has(#app-view:not([hidden])) .brand { display: none; }

/* The game name as it sits inside .topbar, flush left (the topbar's own
   .spacer pushes the online count and avatar button right). */
.app-brand {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.03em;
  font-size: clamp(1rem, 2vw, 1.3rem);
  white-space: nowrap;
}

/* Same gold frame as .sheet, scaled down for chrome-sized elements
   (buttons/inputs/tabs). border-image can't recolor on :hover/:focus like a
   plain border can, so those states lean on background/box-shadow/filter
   instead — see the per-element rules below. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--panel-2);
  color: var(--text);
  padding: 10px 16px;
  font-size: 0.95rem;
  cursor: pointer;
  text-decoration: none;
  border-style: solid;
  border-width: 8px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 8px round;
}
.btn:hover { background: var(--panel-3); }
.btn.discord {
  background: var(--discord);
  color: #fff;
  border-image: none;
  border: 1px solid var(--discord);
  border-radius: 8px;
}
.btn.discord:hover { background: #4752c4; }
.btn.primary {
  background: var(--accent);
  color: #211a08;
  font-weight: 700;
  border-image: none;
  border: 1px solid color-mix(in srgb, var(--accent) 70%, #000);
  border-radius: 8px;
}
.btn.primary:hover { background: var(--accent-hover); }
.btn.ghost { background: transparent; }
/* Disabled (e.g. Search / Travel without enough stamina): faded and
   desaturated, with no hover change, so it clearly reads as unavailable. */
.btn:disabled {
  opacity: 0.45;
  filter: grayscale(0.85);
  cursor: not-allowed;
}
.btn:disabled:hover { background: var(--panel-2); }
.btn.ghost:disabled:hover { background: transparent; }
.btn.discord:disabled:hover { background: var(--discord); }
.btn.primary:disabled:hover { background: var(--accent); }

.topbar {
  position: relative;
  z-index: 40; /* above the sticky .tabs (30) so the account popup overlaps them */
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: var(--panel);
  border-style: solid;
  border-width: 14px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 14px round;
}
.spacer { flex: 1; }
.profile { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.profile img { border-radius: 50%; }

/* The topbar keeps just the game name, the online count and a round avatar
   button; profile, language and logout live in a popup under the avatar. */
.profile-toggle {
  display: block;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 2px solid var(--accent);
  border-radius: 50%;
  background: var(--panel-2);
  overflow: hidden;
  cursor: pointer;
}
.profile-toggle:hover { filter: brightness(1.15); }
.profile-toggle img { display: block; width: 100%; height: 100%; object-fit: cover; }
.topbar-menu {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: -14px; /* line up with the topbar's outer edge (its 14px frame) */
  min-width: 230px;
  max-width: min(300px, calc(100vw - 32px));
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  padding: 12px;
  background: var(--panel);
  border-style: solid;
  border-width: 14px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 14px round;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.topbar.menu-open .topbar-menu { display: flex; }
/* Dev tools sit at the bottom of the account popup, below a divider. Hidden
   entirely when all its children are (regular players). */
.topbar-menu .dev-bar {
  padding-top: 12px;
  border-top: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.topbar-menu .dev-bar:not(:has(> :not([hidden]))) { display: none; }
.topbar-controls { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.online-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  color: var(--text);
}
.online-ico { display: block; color: var(--accent); }
/* Header "Return to duel" (a duel still running after a refresh / failed join). */
.duel-return {
  flex-shrink: 0;
  gap: 6px;
  padding: 6px 12px;
  font-size: 0.85rem;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .duel-return { padding: 5px 8px; font-size: 0.78rem; }
}
/* Visually hidden; screen readers still announce "Players online:". */
.online-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

@media (max-width: 600px) {
  .topbar { gap: 8px; }
  /* Phones: half the usual padding inside the framed panels (see also
     .sheet below), so content gets the space; the frame keeps its width. */
  .topbar { padding: 6px; }
  .topbar-menu { padding: 6px; }
  /* Very narrow phones: let the title wrap rather than push the bar wider. */
  .topbar .app-brand { white-space: normal; min-width: 0; line-height: 1.15; }
}

.pill {
  font-size: 0.85rem;
  color: var(--muted);
  background: var(--panel-2);
  padding: 4px 10px;
  border-radius: 999px;
}

.hint { color: var(--muted); font-size: 0.9rem; }

.messages {
  list-style: none;
  margin: 16px 0;
  padding: 12px;
  height: 340px;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.messages li { padding: 4px 0; }
.messages li.system { color: var(--muted); font-style: italic; }
.messages img { vertical-align: middle; border-radius: 50%; }

.class-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
.class-card {
  text-align: left;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  font: inherit;
  transition: border-color 0.15s, background 0.15s;
}
.class-card:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--panel-2);
}
.class-card:disabled { opacity: 0.5; cursor: default; }
.class-card h3 { margin: 0 0 6px; font-size: 1.05rem; }
.class-card p { margin: 0; color: var(--muted); font-size: 0.9rem; }

/* Compact panel pinned to the top of the window; full-bleed over .wrap padding. */
.tabs {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  justify-content: center;
  gap: 4px;
  margin: 16px -24px 0;
  padding: 6px 24px;
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--accent);
  border-bottom: 1px solid var(--accent);
}
.tab {
  display: flex;
  align-items: center;
  gap: 7px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  padding: 7px 14px;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  border-radius: 7px;
}
.tab:hover { color: var(--text); }
.tab-ico { color: var(--muted); }
.tab:hover .tab-ico { color: var(--text); }
.tab.is-active .tab-ico { color: var(--accent); }
.tab.is-active {
  color: var(--text);
  border-style: solid;
  border-width: 6px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 6px round;
}
.tab-panel { margin-top: 0; }

/* Phones: the tabs become a bottom navigation bar, fixed to the bottom edge
   of the screen, icons only (five labelled tabs overflow the width), spread
   evenly. The label stays in the DOM (visually hidden) so screen readers
   still announce it. */
@media (max-width: 600px) {
  .tabs {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    justify-content: space-around;
    gap: 0;
    margin: 0;
    padding: 4px 8px;
    border-top: 1px solid var(--accent);
    border-bottom: 0;
  }
  /* Room under the last panel so the fixed bar never covers it. */
  #app-view { padding-bottom: 64px; }
  .tab { position: relative; padding: 8px 10px; }
  .tab-ico { width: 24px; height: 24px; }
  .tab > span {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* Ornate corner-bracket frame, from Kenney's "Fantasy UI Borders" pack (CC0),
   recolored to --accent gold. border-image needs an explicit border-style/
   -width/-color (kept transparent as a fallback if the image fails to load)
   alongside it — border-radius doesn't apply, the art has its own square
   bracket corners. */
.sheet {
  margin: 16px 0;
  padding: 16px;
  background: var(--panel);
  border-style: solid;
  border-width: 16px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 16px round;
}
/* Phones: half the padding (after .sheet so it wins). */
@media (max-width: 600px) {
  .sheet { padding: 8px; }
}
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.sheet-head h2 { margin: 0; font-size: 1.1rem; display: flex; align-items: center; gap: 8px; }
.sheet-avatar { border-radius: 50%; flex-shrink: 0; }

/* Click to reveal a hint paragraph that's hidden by default, so the panel's
   fixed chrome stays minimal and the card list gets the space instead. */
.info-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  vertical-align: middle;
  margin-left: 2px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  color: var(--faint);
  font-size: 0.68rem;
  line-height: 1;
  cursor: pointer;
}
.info-toggle:hover { color: var(--accent); border-color: var(--accent); }
.level-control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.9rem;
  color: var(--muted);
}
.level-control input {
  width: 64px;
  padding: 6px 8px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  border-style: solid;
  border-width: 6px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 6px round;
}
.level-note { margin: 6px 0 0; }

/* Level + XP progress, under the sheet title. */
.xp-bar { margin: 10px 0 2px; }
.xp-meta {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.72rem;
  margin-bottom: 4px;
  font-variant-numeric: tabular-nums;
}
.xp-meta .xp-lvl { color: var(--text); font-weight: 700; }
.xp-meta .xp-num { color: var(--faint); }
.xp-track {
  height: 6px;
  background: var(--panel-3);
  border-radius: 999px;
  overflow: hidden;
}
.xp-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--t-special), var(--accent));
  transition: width 0.4s ease;
}

/* Stamina reuses the XP-bar layout with its own colour. */
.stamina-bar { margin-top: 4px; }
.stamina-bar .xp-fill {
  background: linear-gradient(90deg, var(--t-elementalAttack, #3aa), var(--t-bonus, #6c6));
}
.stamina-bar.is-low .xp-fill {
  background: linear-gradient(90deg, #b5462f, #d98324);
}
.stamina-bar .xp-short { display: none; }

/* Language picker (topbar + login screen). */
.lang-select {
  appearance: none;
  background: var(--panel-2);
  color: var(--text);
  padding: 8px 10px;
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  border-style: solid;
  border-width: 6px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 6px round;
}
.lang-select:hover { filter: brightness(1.2); }
.lang-row { margin-top: 16px; display: flex; justify-content: center; }

.dev-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dev-ui-toggle { padding: 6px 12px; font-size: 0.85rem; }
.dev-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dev-tools .btn { padding: 6px 12px; font-size: 0.85rem; }
/* `hidden` alone can't beat a class that sets its own `display` (author CSS
   always wins over the UA [hidden] rule) — spell it out for every dev-only
   element that does, so hiding them for non-dev users actually works. */
.dev-tools[hidden],
.btn[hidden] { display: none; }
.dev-tag {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--t-special);
  border: 1px solid color-mix(in srgb, var(--t-special) 40%, var(--border));
  border-radius: 999px;
  padding: 2px 8px;
}
.class-card.is-current {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
#setup-cancel { margin-top: 16px; }

/* Compact vertical stat list, mirroring the duel player sheet (.dp-group):
   no cards, just tiny uppercase headers over hairline-separated rows. */
.stat-groups {
  display: flex;
  flex-direction: column;
  margin-top: 10px;
}
.stat-group {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.stat-group:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.stat-group h3 {
  margin: 0 0 6px;
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
}
.stat-group dl {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr auto;
  row-gap: 3px;
  column-gap: 8px;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}
.stat-group dt {
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 6px;
}
.stat-group dt .ico { width: 12px; height: 12px; }
/* Shared with the duel player panel's .dp-group (see renderPlayerPanel/statRow)
   so a stat's colour always matches between the character sheet and battle. */
.stat-group dt[data-stat='strength'],     .dp-group dt[data-stat='strength']     { color: var(--el-physical); }
.stat-group dt[data-stat='vitality'],     .dp-group dt[data-stat='vitality']     { color: var(--good); }
.stat-group dt[data-stat='intelligence'], .dp-group dt[data-stat='intelligence'] { color: var(--el-water); }
.stat-group dt[data-stat='dexterity'],    .dp-group dt[data-stat='dexterity']    { color: var(--el-electric); }
.stat-group dt[data-stat='attackMin'],    .dp-group dt[data-stat='attackMin']    { color: var(--t-physicalAttack); }
.stat-group dt[data-stat='defense'],      .dp-group dt[data-stat='defense']      { color: var(--t-defensive); }
.stat-group dt[data-stat='health'],       .dp-group dt[data-stat='health']       { color: var(--hp); }
.stat-group dt[data-stat='mana'],         .dp-group dt[data-stat='mana']         { color: var(--mana); }
.stat-group [data-el='fire'] .ico     { color: var(--el-fire); }
.stat-group [data-el='water'] .ico    { color: var(--el-water); }
.stat-group [data-el='electric'] .ico { color: var(--el-electric); }
.stat-group dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.stat-group dd[data-el='fire']     { color: var(--el-fire); }
.stat-group dd[data-el='water']    { color: var(--el-water); }
.stat-group dd[data-el='electric'] { color: var(--el-electric); }
.stat-group dd.geared { color: var(--good); }
.stat-group dd.nerfed { color: var(--hp); }

/* A 4-stat group (general / duel basics) reads as two label/value pairs per
   row instead of one tall column: dt,dd,dt,dd fill a 4-column grid, so the
   list order fills left-to-right, top-to-bottom — item 1 top-left, item 3
   bottom-left, etc. — putting items 1+3 in the left column, 2+4 in the right. */
.stat-group--pairs dl {
  grid-template-columns: auto auto auto auto;
  column-gap: 6px;
  row-gap: 5px;
}
.stat-group--pairs dt:nth-child(4n+3) { margin-left: 12px; }

/* Elemental attack + defense as one name/attack/defense table instead of two
   separate stacked lists. */
.elemental-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}
.elemental-table th {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--faint);
  text-align: right;
  white-space: nowrap;
  padding-bottom: 4px;
  padding-left: 10px;
}
.elemental-table th .ico { width: 11px; height: 11px; vertical-align: -1px; }
.elemental-table th:first-child { width: 100%; }
.elemental-table td { padding: 3px 0; }
.elemental-table td.el-name {
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.elemental-table td:not(.el-name) { text-align: right; padding-left: 10px; }

/* Stat-point allocation editor (sits above the read-only stat list). */
.stat-alloc {
  margin-top: 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
}
.sa-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 10px;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  text-align: left;
  font: inherit;
}
.stat-alloc.is-open .sa-head { border-bottom: 1px solid var(--border); }
.sa-chevron {
  color: var(--faint);
  font-size: 0.7rem;
  transition: transform 0.12s ease;
}
.stat-alloc.is-open .sa-chevron { transform: rotate(90deg); }
.sa-title {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
  display: flex;
  align-items: center;
  gap: 5px;
}
.sa-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
.sa-points {
  margin-left: auto;
  font-size: 0.8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.sa-points.has { color: var(--accent); }
.sa-points.over { color: var(--hp); }
.sa-legend {
  margin: 8px 10px 0;
  font-size: 0.68rem;
  line-height: 1.35;
}
.sa-list {
  margin: 8px 10px 0;
  display: flex;
  flex-direction: column;
  row-gap: 4px;
}
.sa-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 8px;
  font-size: 0.8rem;
}
.sa-label {
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.sa-label .ico { width: 13px; height: 13px; flex: none; }
.sa-cost {
  font-size: 0.66rem;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.sa-cost.short { color: var(--hp); opacity: 0.8; }
.sa-ctr { display: flex; align-items: center; gap: 6px; }
.sa-val {
  min-width: 1.6em;
  text-align: center;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.sa-actions {
  margin: 10px 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.sa-actions .btn { padding: 6px 12px; font-size: 0.85rem; }
.sa-reset { margin-left: auto; }

/* Read-only stat list, in "preview an unsaved build" mode. */
.stat-groups.is-preview {
  border-left: 2px solid var(--el-water);
  padding-left: 8px;
  margin-left: -10px;
}
.preview-banner {
  margin: 0 0 4px;
  font-size: 0.68rem;
  line-height: 1.35;
  color: var(--el-water);
}
.stat-group dd.preview { color: var(--el-water); }
.stat-group dd .pv-mark { font-size: 0.6rem; opacity: 0.9; }

/* App body: a permanent stats panel on the left, the tabbed content on the
   right. Switching tabs only changes the right side; the stats stay put.
   Stacks (stats first, then content) on narrow screens. */
.app-layout { margin-top: 16px; }
.stat-sidebar > .sheet { margin: 0; }

@media (min-width: 900px) {
  .app-layout {
    display: grid;
    grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
    gap: 20px;
    align-items: start;
  }
  .stat-sidebar {
    position: sticky;
    top: 44px; /* clears the sticky tab bar */
    align-self: start;
  }
  .app-main { min-width: 0; }
}

/* Hide/show button in the character panel's header. The chevron points the
   way the panel will move: left/up to hide, right/down to show. */
.sidebar-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  margin-left: auto;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 50%;
  background: var(--panel-2);
  color: var(--accent);
  cursor: pointer;
}
.sidebar-toggle:hover { background: var(--panel-3); }
/* Keep the button on the title's row: the title wraps instead. */
.stat-sidebar #sheet-title { flex: 1 1 0; min-width: 0; }
.sidebar-toggle .ico { transition: transform 0.15s ease; }

/* Collapsed: only the header row stays (avatar, name, class, level). */
.sidebar-collapsed .stat-sidebar .sheet > :not(.sheet-head) { display: none !important; }
/* ...plus the stamina bar, so you can tell whether you can search the map
   without opening the panel. */
.sidebar-collapsed .stat-sidebar .sheet > .stamina-bar:not([hidden]) { display: block !important; }

@media (max-width: 899px) {
  .stat-sidebar { margin-bottom: 16px; }
  .sidebar-toggle .ico { transform: rotate(90deg); }          /* ^ */
  .sidebar-collapsed .sidebar-toggle .ico { transform: rotate(-90deg); } /* v */
  .sidebar-collapsed .stat-sidebar .sheet { padding: 4px 8px; }
  .sidebar-collapsed .stat-sidebar .sheet-head { flex-wrap: nowrap; }
  .sidebar-collapsed #sheet-title {
    min-width: 0;
    font-size: 1rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
  }
  .sidebar-collapsed #sheet-title .sheet-avatar { vertical-align: middle; margin-right: 8px; }
  .sidebar-collapsed .stamina-bar { margin: 2px 0 4px; }
}

@media (min-width: 900px) {
  /* Desktop: the panel folds into a thin strip on the left (avatar + button)
     and the tab content takes the freed width. */
  .app-layout.sidebar-collapsed { grid-template-columns: auto minmax(0, 1fr); }
  .sidebar-collapsed .sidebar-toggle .ico { transform: rotate(180deg); }
  .sidebar-collapsed .stat-sidebar .sheet { padding: 8px 4px; }
  .sidebar-collapsed .stat-sidebar .sheet-head { flex-direction: column; gap: 10px; }
  .sidebar-collapsed #sheet-title { font-size: 0; gap: 0; }
  .sidebar-collapsed .sidebar-toggle { margin-left: 0; }
  /* The strip is too narrow for a sideways bar: a vertical one filling
     bottom-up, with the current number under it (full text on hover). */
  .sidebar-collapsed .stat-sidebar .sheet > .stamina-bar:not([hidden]) {
    display: flex !important;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    margin: 12px 0 0;
  }
  .sidebar-collapsed .stamina-bar .xp-meta { display: none; }
  .sidebar-collapsed .stamina-bar .xp-track {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    width: 8px;
    height: 72px;
  }
  .sidebar-collapsed .stamina-bar .xp-fill {
    width: 100% !important;
    height: var(--pct, 0%);
    transition: height 0.4s ease;
    background: linear-gradient(0deg, var(--t-elementalAttack, #3aa), var(--t-bonus, #6c6));
  }
  .sidebar-collapsed .stamina-bar.is-low .xp-fill {
    background: linear-gradient(0deg, #b5462f, #d98324);
  }
  .sidebar-collapsed .stamina-bar .xp-short {
    display: block;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
  }
}

/* top of each tab's content lines up with the top of the stats panel */
.app-main > .tab-panel > :first-child { margin-top: 0; }

/* ---- Equipment: a "paper doll" — helmet top, weapon / armor / ring across
   the middle, boots bottom. Tiles are square art in a rarity frame; the full
   item shows in a hover (or tap-pinned) popup — see equipSlotTile(). ---- */
.equip-slots {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 96px));
  grid-template-areas:
    ".      helmet ."
    "weapon armor  ring"
    ".      boots  .";
  justify-content: center;
  gap: 10px;
  margin-top: 14px;
}
/* Character tab: the paper doll only needs its own width, so the bag sits
   beside it once the tab has room (sized by the tab, not the window, since
   the character panel on the left may be open or hidden). */
#tab-character,
#tab-deck,
#tab-noxgard { container-type: inline-size; }
.char-layout {
  display: grid;
  gap: 16px;
  align-items: start;
}
.char-layout > .sheet { margin: 0; }
@container (min-width: 700px) {
  .char-layout { grid-template-columns: auto minmax(0, 1fr); gap: 20px; }
}
/* Side by side (desktop): the bag panel is capped at the window height (less
   the sticky tab bar) and only its item list scrolls — so the paper doll
   stays in view while browsing the bag. Stacked (phones) scrolls normally. */
@container (min-width: 700px) {
  .char-layout > .bag-sheet {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 60px);
  }
  .bag-sheet .bag-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 4px; /* room for the item cards' hover glow */
    margin-right: -4px;
    scrollbar-gutter: stable;
    /* trimmed to whole rows by fitBagToWholeRows(); scroll a row at a time */
    position: relative;
    scroll-snap-type: y mandatory;
    scroll-padding: 4px;
  }
  .bag-sheet .bag-list > * { scroll-snap-align: start; }
}

.pd-slot[data-slot='weapon'] { grid-area: weapon; }
.pd-slot[data-slot='helmet'] { grid-area: helmet; }
.pd-slot[data-slot='armor']  { grid-area: armor; }
.pd-slot[data-slot='boots']  { grid-area: boots; }
.pd-slot[data-slot='ring']   { grid-area: ring; }
.pd-slot {
  /* square art on top + a 20px strip below for the durability bar
     (empty slots keep the same size, see .pd-empty-art) */
  padding: 0 0 20px;
  display: grid;
  place-items: center;
  overflow: hidden;
  position: relative;
  background: color-mix(in srgb, var(--faint) 10%, var(--panel-3));
  border: 2px solid var(--rarity, var(--border));
  border-radius: 8px;
  box-shadow: 0 0 8px color-mix(in srgb, var(--rarity, transparent) 35%, transparent);
  color: var(--faint);
  cursor: pointer;
}
.pd-slot:not(.empty):hover,
.pd-slot:not(.empty):focus-visible {
  box-shadow: 0 0 14px color-mix(in srgb, var(--rarity) 70%, transparent);
  outline: none;
}
.pd-slot.empty { border-style: dashed; opacity: 0.55; cursor: default; }
.pd-slot .ic-art,
.pd-slot .pd-empty-art { width: 100%; aspect-ratio: 1 / 1; border: 0; border-radius: 0; }
.pd-slot .pd-empty-art { display: grid; place-items: center; }
/* Durability in the strip under the art (see slotDurabilityBar()). */
.pd-slot .pd-dura {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 7px;
  height: 6px;
  background: rgba(0, 0, 0, 0.6);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}
.pd-slot.broken .ic-art { filter: grayscale(1) brightness(0.6); }

.bag-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 7px;
  margin-top: 12px;
}
/* Phones: two narrower columns instead of one card per row. */
@container (max-width: 480px) {
  .bag-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Bag filter bar — slot buttons + a "usable only" toggle. */
.bag-filter {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin: 12px 0 2px;
}
.bag-filter .filter-ctl {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  font-size: 0.78rem;
  color: var(--muted);
}
.bag-filter .filter-ctl > span { margin-right: 2px; }
.bag-filter button {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  border-radius: 6px;
  padding: 3px 10px;
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
}
.bag-filter button:hover { color: var(--text); border-color: var(--accent); }
.bag-filter button.is-active {
  color: var(--text);
  background: var(--panel-2);
  border-color: var(--accent);
}
.item-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.item-card.is-equipped {
  /* a gold ring, layered over the rarity border */
  box-shadow: 0 0 0 2px var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--panel-2));
}

/* ---- Item rarity on every representation ---- */
.item-card[data-rarity] { border-color: var(--rarity); }
.item-card[data-rarity] .ic-name { color: var(--rarity); }
.dp-gear-tile[data-rarity] {
  border-color: var(--rarity);
  box-shadow: 0 0 0 1px var(--rarity) inset;
}
.dp-gear-tile[data-rarity]:not(.empty):hover,
.dp-gear-tile[data-rarity]:not(.empty):focus-visible {
  border-color: var(--rarity);
  box-shadow: 0 0 0 1px var(--rarity) inset, 0 0 6px color-mix(in srgb, var(--rarity) 60%, transparent);
}
.item-pop[data-rarity] {
  border-color: var(--rarity);
}
.item-pop[data-rarity] .ip-head { color: var(--rarity); }
.item-pop .ip-rarity {
  font-size: 0.58rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--rarity, var(--faint));
  margin: -3px 0 7px;
}
.ic-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 6px; }
.ic-name { font-family: var(--font-display); font-weight: 700; font-size: 0.8rem; }
/* Row 2 on a bag card: type + rarity together (see bagCard()). */
.ic-meta { display: flex; align-items: center; gap: 6px; }
.ic-slot {
  font-size: 0.56rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--faint);
  flex: none;
}

/* Real per-item art (public/img/items/<templateId>.jpg) when it exists;
   falls back to the generic per-slot glyph otherwise -- see itemArt(). A
   full 1:1 square (not a short strip) so diagonal weapon art isn't cropped. */
.ic-art {
  align-self: stretch;
  width: 100%;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: 6px;
  background: color-mix(in srgb, var(--faint) 10%, var(--panel-3));
  border: 1px solid var(--border);
  overflow: hidden;
}
.ic-art .ico { width: 38%; height: 38%; color: var(--faint); }
.ic-art-img { width: 100%; height: 100%; object-fit: cover; }

.ic-req { display: flex; flex-wrap: wrap; gap: 6px; font-size: 0.68rem; color: var(--muted); }
.ic-req .unmet { color: var(--t-elementalAttack); font-weight: 700; }
/* actions sit on the card's bottom edge regardless of how much sits above */
.ic-actions { display: flex; flex-wrap: wrap; gap: 5px; margin-top: auto; padding-top: 3px; }
.item-card .mini-btn { padding: 2px 7px; font-size: 0.74rem; }

/* vertical stat lines: one per stat / bonus */
.istats { display: flex; flex-direction: column; gap: 2px; }
.istat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 0.68rem;
  color: var(--muted);
  min-width: 0;
}
.istat-l { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.istat-l .ico { flex: none; }
.istat-v { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--text); flex: none; }
.istat--bonus .istat-l { color: var(--good); }
.istat--bonus .istat-v { color: var(--good); }

/* Comparison mode (hovering a bag item against what's equipped in its slot).
   The --bonus rule above is more specific, so restate both here to win. */
.istat-v--better, .istat--bonus .istat-v--better { color: var(--good); }
.istat-v--worse, .istat--bonus .istat-v--worse { color: var(--hp); }

/* element glyph inside a shield outline — elemental DEFENSE */
.sh-ico { position: relative; display: inline-flex; flex: none; }
.sh-ico .sh-inner {
  position: absolute;
  left: 50%;
  top: 56%;
  transform: translate(-50%, -50%);
}

.dura { display: flex; align-items: center; gap: 6px; cursor: help; }
.dura .ico { width: 11px; height: 11px; color: var(--faint); flex: none; }
.dura.low .ico { color: var(--t-elementalAttack); }
.dura.broken .ico { color: var(--hp); }
.dura-label {
  font-size: 0.62rem;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
  min-width: 44px;
}
.dura.broken .dura-label { color: var(--hp); }
.dura-track {
  flex: 1;
  height: 4px;
  background: var(--panel-3);
  border-radius: 999px;
  overflow: hidden;
}
.dura-fill { height: 100%; background: var(--good); }
.dura-fill.low { background: var(--t-elementalAttack); }
.dura-fill.broken { background: var(--hp); }

/* ---- Deck ---- */

/* My deck on top, card pool below — same plain vertical stack as the
   Character tab's panels, page scrolls normally. */
.deck-layout {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.deck-layout > .sheet { margin: 0; }

/* The two buttons in a .sheet-head next to the h2 (e.g. "Battle plans" +
   "Reset to starter") grouped together at the right. */
.sheet-head-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
#bp-open .pill { margin-left: 2px; }

/* Battle plans: saved, ordered card lists for duels. Opened on demand (see
   #bp-open in .sheet-head-actions) as a modal instead of sitting inline. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.6);
}
/* Setting our own `display` here would otherwise beat the UA [hidden]
   rule at equal specificity and stay visible — see memory
   css-hidden-display-gotcha. */
.modal-backdrop[hidden] { display: none; }
.modal {
  width: 100%;
  max-width: 560px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  margin: 0;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.modal-head h2 { margin: 0; font-size: 1.1rem; }
.modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
}
.modal-close:hover { color: var(--text); border-color: var(--accent); }

/* Loot popup after a world-map win: the drops as Bag-style item cards. */
.loot-modal { max-width: 520px; text-align: center; }
.loot-modal .modal-head h2 { color: var(--accent); font-family: var(--font-display); }
.loot-modal .hint { margin: 8px 0 12px; }
.loot-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  text-align: left;
}
/* Two cards side by side even on a phone (half the row each, up to 160px). */
.loot-cell { width: min(160px, calc(50% - 5px)); display: flex; flex-direction: column; gap: 4px; }
.loot-cell.lost .item-card { opacity: 0.45; filter: grayscale(0.8); }
.loot-lost { margin: 0; font-size: 0.74rem; color: var(--hp); text-align: center; }
.loot-ok { margin-top: 14px; }

#bp-body { padding-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.bp-new { align-self: flex-start; }
.bp-list { display: flex; flex-direction: column; gap: 6px; }
.bp-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.02);
}
.bp-name { font-weight: 600; }
.bp-cards { margin-left: auto; }
.bp-star {
  background: none;
  border: 0;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--faint);
  padding: 0;
}
.bp-star.on { color: var(--accent); }

.bp-editor { display: flex; flex-direction: column; gap: 12px; }
.bp-name-row input {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
}
.bp-slots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 6px;
}
.bp-slot {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font: inherit;
}
.bp-slot.empty { color: var(--faint); cursor: default; border-style: dashed; }
.bp-slot.burn { border-color: var(--hp); }
.bp-slot-r { font-size: 0.62rem; color: var(--faint); letter-spacing: 0.05em; }
.bp-slot-n { display: flex; align-items: center; gap: 4px; font-size: 0.8rem; }
.bp-slot-n .ico { width: 11px; height: 11px; color: var(--c); }
.bp-slot-mana {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 0.64rem;
  color: var(--mana);
  font-variant-numeric: tabular-nums;
}
.bp-slot-mana .ico { width: 9px; height: 9px; }
.bp-slot.burn .bp-slot-mana { color: var(--hp); }

.bp-mana-note { display: flex; align-items: center; margin: 0; font-size: 0.72rem; }
.bp-mana-note .ico { width: 12px; height: 12px; color: var(--mana); }
.bp-mana-note.warn strong { color: var(--hp); }

.bp-burn {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 0.66rem;
  color: var(--hp);
}
.bp-burn .ico { width: 10px; height: 10px; }
.bp-slot[data-type='specialBlock']    { --c: var(--t-specialBlock); }
.bp-slot[data-type='special']         { --c: var(--t-special); }
.bp-slot[data-type='bonus']           { --c: var(--t-bonus); }
.bp-slot[data-type='defensive']       { --c: var(--t-defensive); }
.bp-slot[data-type='elementalAttack'] { --c: var(--t-elementalAttack); }
.bp-slot[data-type='physicalAttack']  { --c: var(--t-physicalAttack); }
/* The battle plan editor needs room for the 15 slots + the deck's cards. */
#bp-modal .modal { max-width: 1000px; }
.bp-sort { margin-bottom: -4px; }
.bp-src {
  max-height: max(360px, calc(100vh - 460px));
  overflow-y: auto;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  margin-right: -6px;
  padding-right: 6px;
}
.bp-actions { display: flex; gap: 8px; }

/* Duel: opening pre-fill line, centered like the rest of the action area's
   rows. ("Following plan" + Play manually now live on the picked card's
   overlay instead of their own banner — see .gcard-overlay.) */

/* per-type distribution strip at the top of "My deck" */
.deck-dist {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 12px 0 4px;
}
.deck-dist .dchip {
  --c: var(--faint);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px 3px 7px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--c) 34%, var(--border));
  background: color-mix(in srgb, var(--c) 12%, var(--panel-2));
  color: var(--c);
  font-size: 0.74rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.deck-dist .dchip.empty {
  --c: var(--faint);
  opacity: 0.5;
  font-weight: 400;
}
.deck-dist .dchip .ico { width: 12px; height: 12px; }

/* sort control */
.sort-ctl {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--muted);
}
.sort-ctl > span { margin-right: 2px; }
.sort-ctl button {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  border-radius: 6px;
  padding: 3px 10px;
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
}
.sort-ctl button:hover { color: var(--text); border-color: var(--accent); }
.sort-ctl button.is-active {
  color: var(--text);
  background: var(--panel-2);
  border-color: var(--accent);
}

/* card lists: one flat grid each, no more per-type header rows — the card's
   own colour strip + type label already carry that grouping. */
.card-groups { display: flex; flex-direction: column; margin-top: 14px; }
.card-groups > .empty-note { color: var(--muted); font-size: 0.9rem; margin: 4px 0 0; }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
  align-content: start;
}
/* Phones: the Deck tab's deck + pool grids use two columns, like the bag
   (#tab-deck is a size container; the duel hand is not affected). */
@container (max-width: 480px) {
  #tab-deck .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
}

/* ---- Card component: deck editor, card pool, duel hand ---- */
.gcard {
  --c: var(--accent);
  position: relative;
  container-type: inline-size; /* labels adapt to the card's own width, below */
  display: flex;
  flex-direction: column;
  background: var(--panel-2);
  border-style: solid;
  border-width: 8px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 8px round;
  overflow: hidden;
  color: var(--text);
  font: inherit;
  text-align: left;
}
button.gcard { cursor: pointer; }
/* The frame's baked-in gold can't swap to the card's type colour on
   hover/select like a plain border could, so that feedback moves to a glow. */
button.gcard:hover:not(:disabled) { box-shadow: 0 0 0 2px var(--c); }
.gcard:disabled { opacity: 0.4; cursor: default; }
.gcard.is-selected { box-shadow: 0 0 0 2px var(--c), 0 0 10px 1px color-mix(in srgb, var(--c) 55%, transparent); }

/* Pool card your class cannot use: visible for reference, not addable. */
.gcard.is-locked { opacity: 0.5; }
.gcard.is-locked .gcard__meta { color: var(--t-elementalAttack); }

.gcard[data-type='specialBlock']    { --c: var(--t-specialBlock); }
.gcard[data-type='special']         { --c: var(--t-special); }
.gcard[data-type='bonus']           { --c: var(--t-bonus); }
.gcard[data-type='defensive']       { --c: var(--t-defensive); }
.gcard[data-type='elementalAttack'] { --c: var(--t-elementalAttack); }
.gcard[data-type='physicalAttack']  { --c: var(--t-physicalAttack); }

.gcard__strip { height: 4px; background: var(--c); flex: none; }

.gcard__mana {
  position: absolute;
  top: 8px;
  right: 8px;
  min-width: 30px;
  height: 30px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--mana);
  border: 2px solid var(--panel-2);
  color: #08203c;
  font-weight: 700;
  font-size: 0.98rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
  z-index: 2;
}
.gcard__mana.free {
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.gcard__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 10px 10px;
  flex: 1;
}
.gcard__type {
  display: flex;
  align-items: center;
  gap: 5px;
  padding-right: 30px; /* clear the mana disc */
  min-height: 16px;
  color: var(--c);
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1.25;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.gcard__type .ico { width: 12px; height: 12px; }
/* The "Free" pill is much wider than a number disc: keep the label clear
   of it. */
.gcard:has(.gcard__mana.free) .gcard__type { padding-right: 72px; }
/* Narrow cards (phone deck columns, the duel hand, tight desktop grids) leave
   long Polish type labels too little room beside the mana disc ("Wzmocnienie"
   needs ~95px). Drop the small type icon (the art box below shows the same
   glyph), shrink the disc a little and tighten the letter spacing; on "Free"
   cards the label drops just below the pill instead. */
@container (max-width: 190px) {
  .gcard__type { padding-right: 24px; letter-spacing: 0.04em; }
  .gcard__type .ico { display: none; }
  .gcard__mana { min-width: 24px; height: 24px; }
  .gcard__mana:not(.free) { font-size: 0.8rem; }
  .gcard:has(.gcard__mana.free) .gcard__type { padding: 22px 0 0; }
}
.gcard__art {
  height: 64px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--c) 15%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--c) 26%, var(--border));
}
.gcard__art .ico {
  width: 28px;
  height: 28px;
  color: color-mix(in srgb, var(--c) 60%, var(--faint));
}
.gcard__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.86rem;
  line-height: 1.15;
}
.gcard__meta { font-size: 0.63rem; color: var(--faint); letter-spacing: 0.02em; }
/* Hidden by default (only basic info shown) everywhere a card renders — My
   Deck, Card Pool, duel hand. The "Show descriptions" toggle in the Card
   Pool header flips this one class on <body>. */
.gcard__rule { display: none; }
body.show-card-text .gcard__rule {
  display: block;
  margin: 0;
  font-size: 0.71rem;
  line-height: 1.36;
  color: var(--muted);
  flex: 1;
}
.gcard__rule .eltag {
  color: var(--e, var(--muted));
  font-weight: 600;
  white-space: nowrap;
}
.gcard__rule .eltag .ico { width: 11px; height: 11px; }

.gcard__preview { display: flex; flex-wrap: wrap; gap: 4px; }
.pchip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 7px 1px 5px;
  font-size: 0.66rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--c, var(--faint)) 16%, var(--panel-2));
  color: var(--c, var(--muted));
  /* Long effects (e.g. "Odporność na ogień -4") wrap rather than spill past
     the card edge; 9px keeps a one-line chip pill-shaped. */
  max-width: 100%;
  border-radius: 9px;
}
.pchip .ico { width: 10px; height: 10px; }

.gcard__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-top: 2px;
  padding-top: 7px;
  border-top: 1px solid var(--border);
  font-size: 0.63rem;
  color: var(--faint);
  letter-spacing: 0.02em;
}
.gcard__foot .count { color: var(--muted); font-variant-numeric: tabular-nums; }
.gcard__controls { display: flex; align-items: center; gap: 6px; }

.mini-btn {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: 6px;
  padding: 3px 9px;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.mini-btn:hover:not(:disabled) { border-color: var(--accent); }
.mini-btn:disabled { opacity: 0.4; cursor: default; }
.mini-btn.is-active { color: var(--text); background: var(--panel-2); border-color: var(--accent); }
.pill.warn { color: var(--t-elementalAttack); }

/* ---- Duel ---- */
.duel-cta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.duel-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0 12px;
}
.duel-head h2 { margin: 0; font-size: 1.2rem; font-family: var(--font-display); }
/* Phone-only pieces of the duel header (see the phone block at the end). */
.duel-brand,
.leave-short { display: none; }
.duel-clock { display: contents; }

/* Info panel: you | center (log) | opponent */
.duel-info { display: grid; gap: 16px; }
.duel-center { min-width: 0; position: relative; }

/* Overlay for the played-card flourish (see .fx-chip) — sits above the log,
   never intercepts clicks. */
.duel-fx {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 5;
}

/* Round-by-round plays: resolved history (both sides) plus your own queued
   picks for the rounds ahead — replaces the old separate "last played"
   snapshot and the player panel's queued-plays list. */
.duel-last-played {
  max-height: 160px;
  overflow-y: auto;
  margin-bottom: 8px;
  padding: 4px 2px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.duel-last-played:empty { display: none; }
.dlp-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.dlp-row {
  display: grid;
  grid-template-columns: 46px 1fr 1fr;
  align-items: center;
  gap: 6px;
  padding: 2px 6px;
  font-size: 0.72rem;
}
.dlp-row.now { background: color-mix(in srgb, var(--accent) 16%, transparent); border-radius: 6px; }
.dlp-r {
  display: flex;
  flex-direction: column;
  gap: 1px;
  color: var(--faint);
  font-size: 0.62rem;
  font-variant-numeric: tabular-nums;
}
/* Mana left after that round, moved here (next to its round label) from
   the mana-plan strip that used to sit above the hand. */
.dlp-mana { display: inline-flex; align-items: center; gap: 2px; color: var(--mana); font-weight: 700; }
.dlp-mana .ico { width: 9px; height: 9px; }
.dlp-mana.burn { color: var(--hp); }
.dlp-side {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
.dlp-side .ico { flex: none; width: 12px; height: 12px; }
.dlp-side.from-you .ico { color: var(--accent); }
.dlp-side.from-oppo {
  flex-direction: row-reverse;
  justify-content: flex-end;
  text-align: right;
}
.dlp-side.from-oppo .ico { color: var(--el-physical); }
.dlp-side.empty { color: var(--faint); font-weight: 400; }
.fx-chip {
  position: absolute;
  top: 50%;
  left: 50%;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--panel-2);
  border: 1px solid var(--accent);
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45);
  animation: fx-pop-you 0.9s ease both;
}
.fx-chip .ico { width: 14px; height: 14px; color: var(--accent); }
.fx-chip.from-oppo { animation-name: fx-pop-oppo; border-color: var(--el-physical); }
.fx-chip.from-oppo .ico { color: var(--el-physical); }
@keyframes fx-pop-you {
  0% { transform: translate(-50%, -50%) translate(-140px, 70px) scale(0.7); opacity: 0; }
  25% { opacity: 1; }
  55% { transform: translate(-50%, -50%) scale(1.08); opacity: 1; }
  100% { transform: translate(-50%, -50%) translateY(-12px) scale(0.92); opacity: 0; }
}
@keyframes fx-pop-oppo {
  0% { transform: translate(-50%, -50%) translate(140px, -70px) scale(0.7); opacity: 0; }
  25% { opacity: 1; }
  55% { transform: translate(-50%, -50%) scale(1.08); opacity: 1; }
  100% { transform: translate(-50%, -50%) translateY(12px) scale(0.92); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .fx-chip { animation: none; opacity: 1; transform: translate(-50%, -50%); }
}
@media (min-width: 900px) {
  /* Spans the screen (a small gutter via vw, no max-width cap) rather than
     capping out at a fixed column width on wide monitors. */
  .duel-info {
    position: relative;
    left: 50%;
    transform: translateX(-50%);
    width: 98vw;
    grid-template-columns: minmax(210px, 260px) minmax(0, 1fr) minmax(210px, 260px);
    grid-template-areas: 'you center oppo';
    align-items: start;
  }
  #duel-you { grid-area: you; }
  .duel-center { grid-area: center; }
  #duel-oppo { grid-area: oppo; }
  #duel-you,
  #duel-oppo {
    position: sticky;
    top: 12px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
  }
}

.duel-player {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 11px;
  background: var(--panel);
}
.duel-player.oppo { border-color: color-mix(in srgb, var(--el-physical) 40%, transparent); }
.duel-player.you { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.dp-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 3px;
}
.dp-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
}
.dp-avatar { border-radius: 50%; vertical-align: middle; margin-right: 5px; }
.dp-title .dp-sub {
  display: block;
  font-family: "Barlow", system-ui, sans-serif;
  font-weight: 400;
  font-size: 0.76rem;
  color: var(--muted);
  margin-top: 1px;
}

/* Shrink toggle: collapsed by default at every width (like the stat
   allocation panel's chevron) so the resting panel is closer to an Equipment
   tile's footprint — bars + status only, not the full stat breakdown. */
.dp-toggle {
  flex: none;
  background: none;
  border: 0;
  color: var(--faint);
  cursor: pointer;
  font-size: 0.7rem;
  padding: 4px;
  margin: -4px -4px 0 0;
  transition: transform 0.12s ease;
}
.duel-player.is-open .dp-toggle { transform: rotate(90deg); }
.duel-player:not(.is-open) .dp-detail { display: none; }

.dp-bars { display: flex; flex-direction: column; gap: 6px; margin: 9px 0 4px; }
/* HP / mana bars carry their value inside (see resourceBar in client.js). */
.dp-bar .track { position: relative; height: 17px; }
.dp-bar .bar-text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 0.72rem;
  font-weight: 700;
  color: #fff;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 2px #000, 0 0 3px #000;
  white-space: nowrap;
  pointer-events: none;
}
.dp-bar .bar-text .ico { width: 11px; height: 11px; filter: drop-shadow(0 1px 1px #000); }
.track { height: 9px; background: var(--panel-3); border-radius: 999px; overflow: hidden; }
.track > .fill { height: 100%; border-radius: 999px; transition: width 0.4s ease; }
.fill.hp { background: linear-gradient(90deg, var(--t-defensive), var(--good)); }
.fill.hp.wounded { background: linear-gradient(90deg, var(--el-fire), var(--el-electric)); }
.fill.hp.critical { background: var(--hp); animation: hp-pulse 1.1s ease-in-out infinite; }
.fill.mana { background: linear-gradient(90deg, #2f6fd0, var(--mana)); }
@keyframes hp-pulse { 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) { .fill.hp.critical { animation: none; } }

.dp-status { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 9px 2px 6px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  background: color-mix(in srgb, var(--e, var(--faint)) 16%, var(--panel-2));
  color: var(--e, var(--muted));
}
.chip .ico { width: 11px; height: 11px; }

.dp-ready { color: var(--good); font-weight: 600; white-space: nowrap; } /* inside .dp-sub */
.dp-ready.waiting { color: var(--faint); font-weight: 400; }

/* Stat block — as compact as the Equipment tab's item stat rows (.istat):
   small type, tight row/group gaps, no room wasted on a spacious dl grid. */
.dp-group {
  margin-top: 7px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}
.dp-group > h5 {
  margin: 0 0 4px;
  font-family: "Barlow", system-ui, sans-serif;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--faint);
}
.dp-group dl {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr auto;
  row-gap: 2px;
  column-gap: 8px;
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
}
.dp-group dt {
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dp-group dt .ico { width: 12px; height: 12px; }
.dp-group dt.fire .ico { color: var(--el-fire); }
.dp-group dt.water .ico { color: var(--el-water); }
.dp-group dt.elec .ico { color: var(--el-electric); }

/* General Basic's 4 stats as a 2x2 grid instead of one tall column —
   mirrors .stat-group--pairs on the character sheet. */
.dp-group--pairs dl {
  grid-template-columns: auto auto auto auto;
  column-gap: 6px;
  row-gap: 2px;
}
.dp-group--pairs dt:nth-child(4n+3) { margin-left: 10px; }

/* Elemental attack + defense combined table, sized down to the same density
   as the rest of these compact duel stat groups. */
.dp-group .elemental-table { font-size: 0.7rem; }
.dp-group .elemental-table th { font-size: 0.58rem; padding-bottom: 3px; }
.dp-group .elemental-table td { padding: 2px 0; }
.dp-group dd { margin: 0; font-weight: 600; }
/* Same element colours as the character sheet's elemental table
   (.stat-group [data-el]/dd[data-el]) — dpElementalTable sets the same
   data-el attribute, just on plain <td>s instead of a nested <dd>. */
.dp-group .elemental-table [data-el='fire']     .ico { color: var(--el-fire); }
.dp-group .elemental-table [data-el='water']    .ico { color: var(--el-water); }
.dp-group .elemental-table [data-el='electric'] .ico { color: var(--el-electric); }
.dp-group .elemental-table td[data-el='fire']     { color: var(--el-fire); }
.dp-group .elemental-table td[data-el='water']    { color: var(--el-water); }
.dp-group .elemental-table td[data-el='electric'] { color: var(--el-electric); }
/* No `dd` tag qualifier -- dpElementalTable's buffed/nerfed cells are plain
   <td>s, not <dd>s. */
.dp-group .buffed { color: var(--good); }
.dp-group .nerfed { color: var(--hp); }
/* The td[data-el] rules above match td.buffed/.nerfed too (same tag) at
   equal specificity, so a gear-buffed/nerfed defense value needs its own
   equally-specific rule here (declared last) to still win over the tint. */
.dp-group .elemental-table td.buffed { color: var(--good); }
.dp-group .elemental-table td.nerfed { color: var(--hp); }

/* Equipment the fighter brought in: a slot grid of icon tiles. */
.dp-gear-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.dp-gear-tile {
  width: 30px;
  height: 30px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  display: grid;
  place-items: center;
  cursor: help;
}
.dp-gear-tile .ico { width: 15px; height: 15px; color: var(--muted); }
.dp-gear-tile.empty { opacity: 0.3; cursor: default; }
.dp-gear-tile:not(.empty):hover,
.dp-gear-tile:not(.empty):focus-visible {
  border-color: var(--accent);
  outline: none;
}

/* Hover popup — fixed so the panel's overflow can't clip it. */
.item-pop {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 9px 11px;
  min-width: 150px;
  max-width: 230px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
}
.item-pop-float { position: fixed; z-index: 200; pointer-events: none; }
/* Pinned by a click/tap on an equipment tile: clickable (Unequip). */
.item-pop-float.pinned { pointer-events: auto; }
.item-pop .dura { margin-top: 6px; }
.item-pop .ip-actions { display: flex; margin-top: 8px; }
.item-pop .mini-btn { padding: 2px 7px; font-size: 0.74rem; }
.item-pop .ip-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  font-size: 0.82rem;
  margin-bottom: 7px;
}
.item-pop .ip-head .ico { width: 13px; height: 13px; color: var(--faint); flex: none; }
.item-pop .ip-slot {
  margin-left: auto;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
  font-weight: 700;
}

.dp-meta {
  margin-top: 10px;
  font-size: 0.74rem;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
  display: flex;
  gap: 12px;
}

/* mana projection strip in the action area */
.mana-plan {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin: 4px 0 12px;
}
.mana-plan .mp {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 5px 9px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-2);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  min-width: 58px;
}
.mana-plan .mp.pick { border-color: var(--accent); }
.mana-plan .mp.burn { border-color: var(--hp); }
.mana-plan .mp .mp-r { color: var(--faint); font-size: 0.66rem; letter-spacing: 0.03em; }
.mana-plan .mp .mp-v { color: var(--mana); font-weight: 700; display: inline-flex; align-items: center; gap: 3px; }
.mana-plan .mp .mp-v .ico { width: 11px; height: 11px; }
.mana-plan .mp .mp-c { color: var(--muted); font-size: 0.66rem; }
.mana-plan .mp.burn .mp-c { color: var(--hp); }

#duel-log {
  height: auto;
  min-height: 0;
  max-height: 100px; /* content is already capped to LOG_VISIBLE_ROUNDS rounds; this is a safety net for long rounds */
  margin: 10px 0;
  padding: 8px 10px;
  font-size: 0.82rem;
}
#duel-log:empty { display: none; }
#duel-log li { padding: 2px 0; }

/* New entries (this round only — see duelLogRoundsShown) fade/slide in
   instead of just appearing, staggered by their position in the round. */
#duel-log li.log-enter {
  animation: log-line-in 0.32s ease both;
  animation-delay: calc(var(--i, 0) * 45ms);
}
@keyframes log-line-in {
  from { opacity: 0; transform: translateY(5px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  #duel-log li.log-enter { animation: none; }
}
#duel-log li.turn {
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
  font-style: normal;
  margin-top: 6px;
}
#duel-log li.dmg { color: #f2a2a2; }
#duel-log li.dot { color: var(--el-poison); }
#duel-log li.heal { color: var(--good); }
#duel-log li.buff { color: var(--t-bonus); }
#duel-log li.debuff { color: #e5a06b; }
#duel-log li.block { color: var(--t-specialBlock); }
#duel-log li.dim { color: var(--faint); }
#duel-log .mi {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-weight: 700;
  white-space: nowrap;
}
#duel-log .mi .ico { width: 12px; height: 12px; }
#duel-log .mi.fire { color: var(--el-fire); }
#duel-log .mi.water { color: var(--el-water); }
#duel-log .mi.electric { color: var(--el-electric); }
#duel-log .mi.physical { color: var(--el-physical); }
#duel-log .mi.poison { color: var(--el-poison); }
#duel-log .mi.heal { color: var(--good); }
#duel-log .mi.hp { color: var(--hp); }

/* Card picking + mana planning, in the center column under the log — a
   plain divider is enough since it already sits inside .duel-center. */
.duel-action {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.duel-action h3 { margin: 0 0 10px; font-family: var(--font-display); font-weight: 600; }
.duel-reward { margin: 0 0 8px; font-weight: 700; color: var(--t-special); font-variant-numeric: tabular-nums; }
.duel-reward.levelup { color: var(--good); }
.duel-reward.loot { color: var(--rarity, var(--accent)); }

/* Hand type filter — same idea as .bag-filter's slot buttons, narrower set. */
.hand-filter {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.hand-filter button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  border-radius: 6px;
  padding: 3px 9px;
  font: inherit;
  font-size: 0.74rem;
  cursor: pointer;
}
.hand-filter button:hover { color: var(--text); border-color: var(--accent); }
.hand-filter button.is-active { color: var(--text); background: var(--panel-2); border-color: var(--accent); }
.hand-filter button .ico { width: 11px; height: 11px; }
.hand-filter .hf-count::before { content: '('; }
.hand-filter .hf-count::after { content: ')'; }

/* A hand-of-cards fan: each card's resting rotation/lift is set inline by
   fanHand() (--rot/--lift), CSS just applies them and handles the hover
   pop-forward. No scrollbar, ever — a big hand overlaps tighter instead
   (--overlap, also set by fanHand()), and the type filter (.hand-filter) is
   what actually keeps the shown count low. Centered, so a small/filtered
   hand doesn't hug the left edge. Padding is a fallback safe area — left/
   right for a fanned card's rotation (its corners swing out past its own
   unrotated width), top for the hovered/focused card's upward pop
   (translateY + scale growth from its bottom pivot) — fanHand() overrides
   all three inline with the exact numbers computed from the real rendered
   card once it's on screen. overflow:hidden clips anything that still
   doesn't fit instead of scrolling or pushing the page wider. */
.hand {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 0;
  overflow: hidden;
  margin: 10px 0;
  padding: 46px 44px 6px; /* fanHand() overrides all three once it measures the real card */
}
.hand .gcard {
  width: 152px;
  flex: none;
  margin-left: var(--overlap, -46px);
  position: relative;
  transform-origin: bottom center;
  transform: rotate(var(--rot, 0deg)) translateY(var(--lift, 0px));
  z-index: calc(var(--i, 0) + 1);
  transition: transform 0.15s ease, box-shadow 0.15s ease, z-index 0.15s, margin-left 0.15s ease;
}
.hand .gcard:first-child { margin-left: 0; }
.hand button.gcard:hover,
.hand .gcard:focus-visible,
.hand .gcard.is-selected {
  transform: translateY(-18px) scale(1.07);
  z-index: 60;
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.5);
}
@media (prefers-reduced-motion: reduce) {
  .hand .gcard { transition: none; }
}

/* Accept/Cancel for the card you just picked, right on top of it in the
   fan — no more swapping the hand out for a separate preview, which used
   to jump the whole layout around when picking or changing your mind. */
.gcard-overlay {
  position: absolute;
  inset: 0;
  z-index: 1; /* above the card's own content (art, mana badge, etc.) */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 8px;
  padding: 12px;
  background: color-mix(in srgb, var(--panel) 90%, transparent);
  border-radius: 8px;
}
.gcard-overlay .btn { justify-content: center; padding: 8px 10px; font-size: 0.82rem; }
.gcard-overlay .mini-btn { text-align: center; font-size: 0.76rem; }

/* Opening plan builder (rounds 1-5): intro text, slots and the accept
   button all centered, matching the hand/filter/mana-plan below them. */
.plan-builder .hint { text-align: center; font-size: 0.78rem; line-height: 1.35; margin: 4px 0; }
.plan-builder .plan-filled { color: var(--faint); }
.plan-actions { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 4px 0 6px; }
.plan-slots {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin: 10px 0;
}
.plan-slot {
  --c: var(--border);
  min-width: 150px;
  border: 1px solid var(--border);
  border-top: 3px solid var(--c);
  border-radius: 10px;
  padding: 7px 10px 9px;
  background: var(--panel-2);
  color: var(--muted);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.plan-slot:hover { border-color: var(--accent); }
.plan-slot .ps-r {
  font-size: 0.66rem;
  color: var(--faint);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.plan-slot .ps-name {
  display: flex;
  align-items: center;
  gap: 5px;
  font-weight: 600;
  font-size: 0.85rem;
  margin-top: 2px;
}
.plan-slot .ps-name .ico { width: 12px; height: 12px; color: var(--c); }
.plan-slot[data-type] { color: var(--text); }
.plan-slot[data-type='specialBlock']    { --c: var(--t-specialBlock); }
.plan-slot[data-type='special']         { --c: var(--t-special); }
.plan-slot[data-type='bonus']           { --c: var(--t-bonus); }
.plan-slot[data-type='defensive']       { --c: var(--t-defensive); }
.plan-slot[data-type='elementalAttack'] { --c: var(--t-elementalAttack); }
.plan-slot[data-type='physicalAttack']  { --c: var(--t-physicalAttack); }

/* ---- City: inner tabs (Blacksmith / Trader) ---- */
/* Daily chest: icon | heading + status | Open, on one row (wraps on phones). */
.daily-chest {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.daily-chest .chest-ico { flex: none; width: 30px; height: 30px; color: var(--accent); }
.daily-chest .chest-text { flex: 1 1 150px; min-width: 0; }
.daily-chest h2 { margin: 0; font-size: 1.1rem; }
.daily-chest .chest-text .hint { margin: 2px 0 0; font-size: 0.82rem; line-height: 1.35; }
.daily-chest .chest-hint { flex-basis: 100%; margin: 0; color: var(--hp); }
/* Nav dot on the City tab while a chest is waiting. */
.tab.has-dot { position: relative; }
.tab.has-dot::after {
  content: '';
  position: absolute;
  top: 5px;
  right: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--panel);
}
.city-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.city-tab {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  border-radius: 8px;
  padding: 8px 16px;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.city-tab:hover { color: var(--text); border-color: var(--accent); }
.city-tab.is-active { color: var(--text); border-color: var(--accent); background: var(--panel-2); }
.smith-fuel-slots.trade-offer { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 360px; }
.trade-slot { margin-top: 0; }
#trade-btn { margin-top: 4px; }

/* ---- Blacksmith (City tab) ---- */
.smith-sub-h {
  margin: 14px 0 6px;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--faint);
  font-weight: 700;
}
.smith-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 7px;
}
/* Phones: two columns, like the bag (#tab-noxgard is a size container). */
@container (max-width: 480px) {
  .smith-picker { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* The picker's heading / empty note span the whole row, not one card cell. */
.smith-picker > :not(.item-card) { grid-column: 1 / -1; }
button.smith-item {
  font: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
}
button.smith-item:hover { border-color: var(--accent); }
.smith-target {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 10px;
  max-width: 220px;
}
.smith-target .item-card { flex: 1; }
/* Both set their own `display`, which would otherwise beat the UA [hidden]
   rule (see the .btn[hidden] / .modal-backdrop[hidden] rules above). */
.smith-picker[hidden],
.smith-target[hidden] { display: none; }
.smith-fuel-slots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(115px, 1fr));
  gap: 7px;
  margin: 8px 0;
}
.smith-fuel-slot {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 0.8rem;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.smith-fuel-slot:hover { border-color: var(--accent); }
.smith-fuel-slot.empty { opacity: 0.7; border-style: dashed; cursor: default; }
.smith-fuel-slot[data-rarity] { border-color: var(--rarity); }
.smith-fuel-empty { color: var(--faint); font-size: 0.76rem; }

/* ---- World map ---- */
/* Top-of-tab bar while in a location: "You are in X" + Search. */
.map-quick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
/* Sets its own display, so spell out [hidden] (see .smith-picker[hidden]). */
.map-quick[hidden] { display: none; }
.map-quick-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.map-quick-actions .hint { margin: 0; }
.location-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 12px;
  margin-top: 14px;
}
.location-card {
  padding: 14px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.location-card.is-here {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.location-card h3 {
  margin: 0 0 6px;
  font-size: 1.05rem;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.location-card p { margin: 0 0 12px; color: var(--muted); font-size: 0.9rem; }
.location-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.location-note:empty { display: none; }
.location-note { margin: 10px 0 0; }

.chat-form { display: flex; gap: 8px; }
.chat-form input {
  flex: 1;
  padding: 10px 12px;
  background: var(--panel-2);
  color: var(--text);
  font-size: 0.95rem;
  border-style: solid;
  border-width: 6px;
  border-color: transparent;
  border-image: url(ui/panel-frame.png) 32 / 6px round;
}

/* Phones: half the space outside the framed panels — page gutter and the
   gaps between panels (kept last so it wins over the base rules above). */
@media (max-width: 600px) {
  .wrap { padding: 10px 12px 20px; }
  .sheet { margin: 8px 0; }
  .app-layout { margin-top: 8px; }
  .stat-sidebar { margin-bottom: 8px; }
  .char-layout { gap: 8px; }
  .deck-layout { gap: 10px; }
}

/* Phone duel screen (kept last so it wins over the base duel rules):
   - the header is one framed bar like the app's .topbar — game name, round,
     timer and a short "Leave" — replacing the page .brand + "Duel" title;
   - your panel comes first, then the enemy's, then the log / cards;
   - the shrunk player panels are compact: tighter padding and bars; the
     class/level/ready line stays under the name, mana under HP.
   The column is capped at the screen width (minmax(0, 1fr)) so a long name
   can't stretch the whole screen sideways. */
@media (max-width: 600px) {
  .wrap:has(#duel-view:not([hidden])) .brand { display: none; }
  .duel-head {
    flex-wrap: nowrap;
    gap: 6px;
    margin: 0 0 8px;
    padding: 6px;
    background: var(--panel);
    border-style: solid;
    border-width: 14px;
    border-color: transparent;
    border-image: url(ui/panel-frame.png) 32 / 14px round;
  }
  .duel-head h2,
  .leave-long { display: none; }
  .duel-brand { display: block; white-space: normal; line-height: 1.15; }
  /* Round over timer, so a long "Time's up…" never pushes into the name. */
  .duel-clock {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 3px;
    margin-left: auto;
  }
  .leave-short { display: inline; }
  .duel-head .pill { font-size: 0.76rem; padding: 2px 8px; white-space: nowrap; }
  #duel-leave { flex-shrink: 0; padding: 4px 10px; font-size: 0.82rem; border-width: 6px; }

  .duel-info {
    gap: 8px;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'you' 'oppo' 'center';
  }
  #duel-oppo { grid-area: oppo; }
  #duel-you { grid-area: you; }
  .duel-center { grid-area: center; }

  .duel-player { padding: 6px 8px; }
  .dp-head { margin-bottom: 0; }
  .dp-title { font-size: 0.85rem; min-width: 0; overflow-wrap: anywhere; }
  .dp-title .dp-sub { font-size: 0.72rem; }
  .dp-avatar { width: 18px; height: 18px; }
  .dp-bars { gap: 4px; margin: 5px 0 0; }
  .dp-bar .track { height: 15px; }
  .dp-status { margin-top: 5px; }
}

/* Phone duel screen, part 2: the log and the round-plays list are 30% less
   tall than on desktop (100 -> 70px, 160 -> 112px), and the hand filter is
   icon + count only so all its buttons fit on one line. */
@media (max-width: 600px) {
  #duel-log { max-height: 70px; }
  .duel-last-played { max-height: 112px; }
  .hand-filter { flex-wrap: nowrap; align-items: stretch; gap: 4px; }
  .hand-filter button { gap: 3px; padding: 4px 7px; }
  .hand-filter .hf-count::before,
  .hand-filter .hf-count::after { content: none; }
  .hand-filter button .ico { width: 14px; height: 14px; }
  .hand-filter .hf-label { display: none; }
}

/* Phone opening plan: the 5 slots in two rows (3 + 2) instead of three. */
@media (max-width: 600px) {
  .plan-slots { gap: 6px; margin: 6px 0; }
  .plan-slot {
    flex: 0 0 calc((100% - 12px) / 3);
    min-width: 0;
    padding: 4px 6px 6px;
  }
  .plan-slot .ps-r { font-size: 0.6rem; }
  .plan-slot .ps-name { font-size: 0.78rem; overflow-wrap: anywhere; }
  .plan-slot .ps-name .ico { width: 11px; height: 11px; }
}

/* ---- Introduction missions (Missions tab) ---- */
.mission-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: grid;
  gap: 8px;
}
.mission {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.mission-mark {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--border);
  color: var(--muted);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.mission-title { font-weight: 700; }
.mission-desc { margin: 3px 0 0; font-size: 0.85rem; line-height: 1.4; }
.mission-side { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.mission.is-active { border-color: var(--accent); box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 30%, transparent); }
.mission.is-active .mission-mark { border-color: var(--accent); color: var(--accent); }
.mission.is-done { border-color: var(--good); }
.mission.is-done .mission-mark,
.mission.is-claimed .mission-mark { border-color: var(--good); color: var(--good); }
.mission.is-claimed { opacity: 0.6; }
.mission.is-locked { opacity: 0.45; }
.mission.is-locked .mission-title { font-weight: 400; }
.mission-all-done {
  padding: 12px;
  text-align: center;
  color: var(--good);
  font-weight: 700;
}
/* Phones: the buttons drop under the text. */
@container (max-width: 480px) {
  .mission { grid-template-columns: 30px minmax(0, 1fr); }
  .mission-side { grid-column: 2; justify-content: flex-start; }
}
#tab-missions { container-type: inline-size; }

/* Where a mission's Go button points: a short gold pulse. */
.mission-spot { animation: mission-spot 0.8s ease-in-out 3; border-radius: 6px; }
@keyframes mission-spot {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50% { box-shadow: 0 0 0 3px var(--accent), 0 0 18px var(--accent); }
}

/* "Mission complete" note in the corner. */
.mission-toast {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(360px, calc(100vw - 32px));
  padding: 10px 14px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
}
.mission-toast .ico { flex: none; color: var(--accent); }
.mission-toast span { flex: 1; min-width: 0; }
/* own `display` would beat the UA [hidden] rule — css-hidden-display-gotcha */
.mission-toast[hidden] { display: none; }
@media (max-width: 600px) {
  .mission-toast { bottom: 72px; } /* above the bottom tab bar */
}
