/* Exiva XP — page components layered on the shell primitives. */

/* ---------- home ---------- */

.home-dashboard-head { display: grid; gap: var(--s2); }
.home-dashboard-head h1 { width: fit-content; }
.home-dashboard-head p { margin: 0; color: rgb(var(--ink-2)); }
.home-metric-grid, .metric-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s6); margin-top: var(--s6); }
/* below the full sidebar a KPI row can't hold 4-5 cards, and auto-fit left a
   single card stranded (Home 3+2 / 4+1, four-card rows 3+1): two per row,
   and an odd last card takes the whole row instead of half of it */
@media (max-width: 1000px) {
  .home-metric-grid, .metric-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  :is(.home-metric-grid, .metric-row) > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* KPI card — shell.js metric(), the one card every page's headline numbers
   use: eyebrow label, value, optional .fine.dim detail line and sparkline.
   Chrome (surface/border/radius) comes from .panel. */
.metric { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; padding: var(--s4); }
.metric > b, .metric-value-row > b { font-size: var(--fs-28); font-weight: 700; line-height: 1; letter-spacing: -.3px; white-space: nowrap; }
.metric b em { margin-left: 2px; color: rgb(var(--ink-2)); font-size: var(--fs-14); font-style: normal; font-weight: 600; }
/* compact tier (Character's point-stat cards); doubled class so it outranks
   the ≤700px .metric step below */
.metric.metric-sm { gap: var(--s1); padding: var(--s4); }
.metric.metric-sm > b { font-size: var(--fs-18); line-height: 24px; letter-spacing: normal; }
.home-next-hunt, .home-attention { display: flex; flex-direction: column; gap: var(--s4); margin-top: var(--s6); padding: var(--s6); }
.home-card-kicker { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.home-next-hunt > p { margin: 0; }
.home-next-hunt h3 { font-size: var(--fs-18); line-height: 24px; }
.home-next-hunt > p { max-width: 70ch; font-size: var(--fs-14); line-height: 18px; }
.home-hunt-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4); }
.home-hunt-stats span { display: grid; gap: 2px; }
.home-hunt-stats b { font-size: var(--fs-18); line-height: 24px; }
.home-hunt-stats small { color: rgb(var(--ink-2)); font-size: var(--fs-12); }
.home-card-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }
.home-attention-list { display: grid; }
.home-attention-list > div { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); min-height: 48px; border-top: 1px solid rgb(var(--line)); }
.home-attention-list > div:first-child { border-top: 0; }
.home-attention-list a { color: rgb(var(--ink-info)); font-weight: 600; white-space: nowrap; }
.home-attention-list a:hover { text-decoration: underline; }
.home-shortcuts-section { display: grid; gap: var(--s3); margin-top: var(--s6); }
.home-shortcuts { display: flex; flex-wrap: wrap; gap: var(--s3); }
.home-shortcuts a { display: inline-flex; align-items: center; gap: var(--s2); min-height: 36px; padding: var(--s2) var(--s4); border: 1px solid rgb(var(--line)); border-radius: var(--r-pill); font-size: var(--fs-14); font-weight: 600; }
.home-shortcuts a:hover { background: rgba(var(--overlay), var(--overlay-a)); }
.home-shortcuts svg { width: 18px; height: 18px; }

/* ---------- explore grid + filter bar ---------- */

/* Deliberately NOT sticky, at any width. On Planner and Codex this bar
   carries eight or nine fields and stands 346-362px tall — 43-45% of an
   800px viewport — so pinning it parked a wall of filters over the results
   and clipped the top of every row that scrolled under it, titles included.
   iOS Safari had its own version of the same failure: `position: sticky`
   plus `backdrop-filter` left a stretched "ghost" copy of the bar behind
   mid-scroll, overlapping the content below. Neither is worth
   adjust-filters-while-browsing on a bar this tall — it sits at the top of
   the page and the filters are one scroll away. A short bar (Tools' single
   Tier picker) loses nothing either. */
.filter-bar {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s3); align-items: flex-start;
  margin: var(--s5) 0 var(--s4); padding: var(--s5);
  background: rgb(var(--surface-raised));
  border: 1px solid rgb(var(--line)); border-radius: var(--r-input);
}

.segmented { display: inline-flex; max-width: 100%; padding: 2px; overflow-x: auto; background: rgb(var(--surface-2)); border-radius: var(--r-input); scrollbar-width: none; }
.segmented::-webkit-scrollbar { display: none; }
.segmented button { min-height: 30px; padding: 0 var(--s3); border: 0; border-radius: calc(var(--r-input) - 2px); background: transparent; color: rgb(var(--ink-2)); font: inherit; font-size: var(--fs-12); font-weight: 600; white-space: nowrap; cursor: pointer; }
/* --shadow was referenced here but never defined anywhere in the codebase —
   rgba(var(--shadow), .12) silently resolved to an invalid, ignored value,
   so the pressed segment's shadow never actually rendered */
.segmented button:not([aria-pressed="true"]):hover { color: rgb(var(--ink)); }
/* controls inside overflow scrollers (.segmented, the admin rule strip, the
   next-hunts tray) draw their focus ring inside — the default 2px offset was
   clipped away by the scroller */
.segmented button:focus-visible, .admin-rule-filter:focus-visible, .next-hunts-item:focus-visible { outline-offset: -2px; }
.segmented button[aria-pressed="true"] { background: rgb(var(--surface-selected)); color: rgb(var(--ink)); box-shadow: 0 1px 3px rgba(0, 0, 0, .12); }
.filter-compact { display: grid; gap: var(--s4); }
.filter-compact-head { display: flex; align-items: end; justify-content: space-between; gap: var(--s4); }
.filter-compact-groups { display: flex; align-items: end; flex-wrap: wrap; gap: var(--s4); padding-top: var(--s4); border-top: 1px solid rgb(var(--line)); }
.filter-segment { display: grid; gap: var(--s2); min-width: 0; }
.advanced-filters { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s4); padding-top: var(--s4); border-top: 1px solid rgb(var(--line)); }
.advanced-filters .filter-segment { min-width: 0; }
.advanced-filters .filter-segment .segmented { width: 100%; }
.advanced-filters .lbl { min-width: 0; gap: var(--s2); }
.advanced-filters .lbl > input, .advanced-filters .lbl > select, .advanced-filters .lbl > .sort-menu { min-width: 0; width: 100%; }

@media (max-width: 1000px) {
  .advanced-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.tile-rank { position: absolute; top: -9px; right: var(--s3); }
.planner-card { position: relative; display: grid; gap: var(--s3); }
.planner-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); }
.planner-card-creatures { min-height: 34px; }

@media (max-width: 700px) {
  /* Reserve room beside the floating GitHub/appearance cluster (.rail-foot,
     ~50px tall) only for the lines that sit next to it: a float on block
     page heads, the first rows of Home's grid head. Reserving the whole
     header squeezed every lede into a ~210px column. (The Character hero
     needs none: its ring, not its text, sits beside the cluster.) */
  .page-head::before { content: ''; float: right; width: calc(var(--tap) * 2 + var(--s4)); height: calc(var(--tap) + var(--s2)); }
  .home-dashboard-head > :not(:last-child) { padding-right: calc(var(--tap) * 2 + var(--s4)); }
  /* Stacked and tighter here; the bar is already static and opaque for every
     width (see the base rule above for why it isn't sticky anywhere). */
  .filter-bar {
    flex-direction: column; align-items: stretch; gap: var(--s2); margin: var(--s4) 0; padding: var(--s3);
  }
  .filter-compact-head { align-items: stretch; flex-direction: column; }
  .advanced-filters { grid-template-columns: minmax(0, 1fr); }
}

.count-line { margin: 0 0 var(--s3); }

.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--s4); }
.codex-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.planner-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tile { display: flex; flex-direction: column; gap: var(--s3); padding: var(--s4); transition: box-shadow var(--t-hover) var(--e-standard); }
.tile:hover { box-shadow: var(--shadow-card); }
.tile-top { display: flex; align-items: center; gap: var(--s3); }
.tile-top .ring { width: 48px; height: 48px; }
.tile-top .ring > .ring-core { width: 41px; height: 41px; font-size: var(--fs-15); }
.tile-top .name { font-weight: 700; font-size: var(--fs-15); line-height: 19px; }
.tile-stats { display: flex; gap: var(--s5); }
.tile-stats .stat b { display: block; font-size: var(--fs-16); }
.tile-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.name + .tile-tags { margin-top: 6px; }

/* Badge — the source Badge component verbatim (the "LIVE" pattern): solid
   tone fill, white 11px/700 uppercase, 4px radius. Reserved for genuine
   STATUS indicators only: basis (Curated/Logged/Blended), admin's
   rules-check (Faulted/Flagged/Clean), evidence provenance. The neutral
   fill (--neutral-fixed) and the tone fills never theme-swap. */
.badge {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 3px 7px; border-radius: var(--r-sm);
  font-size: var(--fs-11); line-height: 13px; font-weight: 700;
  letter-spacing: .5px; text-transform: uppercase;
  color: rgb(var(--white-fixed)); background: rgb(var(--neutral-fixed));
  white-space: nowrap;
}
.badge-success { background: rgb(var(--green)); }
.badge-info { background: rgb(var(--blue)); }
.badge-error { background: rgb(var(--red)); }
/* the source Badge has no caution tone — the brand gradient's orange stop
   is the sanctioned extension (AGENTS.md §5); never the --c-fire data token */
.badge-warning { background: rgb(var(--brand-orange)); }
/* third gradient-as-data-colour exception (see AGENTS.md §5, matching the
   reference mockup): the single top-ranked item in a list, never a status */
.badge-highlight { background: var(--grad-hero); }

/* Pill — Exiva's quiet data/label chip (not a source component): soft wash
   fill, ink text. Categorical metadata (vocations, family/tier/rarity,
   loot, habitats) and the element readouts (pillEl) live here — solid
   uppercase badges on every label would out-shout the content. The source's
   Tag is NOT this either: that's the interactive filter chip from the brand
   type-tester, and this app currently has no such control. */
.pill {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 2px 10px; border-radius: var(--r-pill);
  font-size: var(--fs-12); line-height: 16px; font-weight: 600;
  background: rgb(var(--wash)); color: rgb(var(--ink));
  white-space: nowrap;
}
/* element pills carry their element as a tone, exactly like the semantic
   tones below tint theirs: a soft wash of the element colour. Text stays
   ink for contrast (holy yellow and ice blue are unreadable as text
   colours in light mode). No dot — the source Badge has no such part. */
.pill[class*="el-"] { background: rgba(var(--el), .14); }
/* semantic tones, not colour names — matches the source Badge component's
   tone system (a fixed vocabulary of meanings, not literal hues) */
.pill-info { background: rgba(var(--blue), .12); color: rgb(var(--ink-info)); }
/* a pill that navigates (Codex habitats → ground) carries a link cue the
   same-looking static labels don't */
a.pill { text-decoration: underline; text-decoration-color: rgba(var(--ink-info), .4); text-underline-offset: 2px; }
a.pill:hover { background: rgba(var(--blue), .2); text-decoration-color: currentColor; }
.pill-success { background: rgba(var(--green), .14); color: rgb(var(--ink-success)); }
.pill-error { background: rgba(var(--red), .12); color: rgb(var(--ink-error)); }
.pill-warning { background: rgba(var(--brand-orange), .14); color: rgb(var(--ink-warning)); }

.el-physical { --el: var(--c-physical); }
.el-earth { --el: var(--c-earth); }
.el-fire { --el: var(--c-fire); }
.el-energy { --el: var(--c-energy); }
.el-ice { --el: var(--c-ice); }
.el-holy { --el: var(--c-holy); }
.el-death { --el: var(--c-death); }

/* creature artwork (official Tibia library GIFs) */
.critter {
  width: 48px; height: 48px; object-fit: contain;
  image-rendering: pixelated; flex-shrink: 0;
}
.critter-lg { width: 84px; height: 84px; }
.critter-sm { width: 28px; height: 28px; }
.imb-icon {
  width: 32px; height: 32px; object-fit: contain;
  image-rendering: pixelated; flex-shrink: 0;
}
.imb-icon-sm { width: 20px; height: 20px; }
.art-disc {
  display: grid; place-items: center; flex-shrink: 0;
  background: rgb(var(--surface-2)); border-radius: var(--r-lg);
}
.tile-top .art-disc { width: 56px; height: 56px; }
.masthead .art-disc { width: 108px; height: 108px; }

/* trust meter (confidence) */
.trust { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-12); color: rgb(var(--ink-2)); }
.trust .seg { display: inline-flex; gap: 2px; }
.trust .seg i { width: 10px; height: 4px; border-radius: var(--r-xs); background: rgb(var(--track)); }
.trust .seg i.on { background: rgb(var(--green)); }

/* ---------- data table ---------- */

/* scroll shadows: an edge darkens only on the side with more columns to
   reach (the `local` covers hide each shadow once scrolled to that end) */
.sheet {
  overflow-x: auto;
  background:
    linear-gradient(to right, rgb(var(--surface-raised)) 40%, rgba(var(--surface-raised), 0)) left / 32px 100% no-repeat local,
    linear-gradient(to left, rgb(var(--surface-raised)) 40%, rgba(var(--surface-raised), 0)) right / 32px 100% no-repeat local,
    linear-gradient(to right, rgba(var(--ink), .12), rgba(var(--ink), 0)) left / 12px 100% no-repeat scroll,
    linear-gradient(to left, rgba(var(--ink), .12), rgba(var(--ink), 0)) right / 12px 100% no-repeat scroll,
    rgb(var(--surface-raised));
}
table.grid { width: 100%; border-collapse: collapse; font-size: var(--fs-14); }
table.grid th, table.grid td { padding: 10px var(--s4); text-align: left; white-space: nowrap; } /* 10px: established cozy half-step, matches .fact/.tool-kpis/inputs */
table.grid th {
  font-size: var(--fs-11); letter-spacing: 1px; text-transform: uppercase; font-weight: 600;
  color: rgb(var(--ink-2)); border-bottom: 1px solid rgb(var(--line));
  position: sticky; top: 0;
  background: rgb(var(--surface-raised));
}
table.grid th.on { color: rgb(var(--ink)); }
table.grid td { border-bottom: 1px solid rgb(var(--line)); }
table.grid tr:last-child td { border-bottom: 0; }
table.grid tbody tr:hover, table.grid tr.is-current { background: rgba(var(--overlay), var(--overlay-a)); }
table.grid .num { text-align: right; }
table.grid a { color: rgb(var(--ink-info)); font-weight: 600; }
table.grid a:hover { text-decoration: underline; }
.sheet + .count-line { margin-top: var(--s2); }
.admin-transfer-actions { display: flex; flex-wrap: wrap; gap: var(--s3); }
.admin-undo { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s3); }
.admin-rule-filters { display: flex; gap: var(--s5); overflow-x: auto; margin-bottom: var(--s4); border-bottom: 1px solid rgb(var(--line)); scrollbar-width: none; }
.admin-rule-filters::-webkit-scrollbar { display: none; }
.admin-rule-filter { min-height: 40px; padding: 0 0 var(--s2); border: 0; border-bottom: 2px solid transparent; background: transparent; color: rgb(var(--ink-2)); font: inherit; font-size: var(--fs-14); font-weight: 600; cursor: pointer; white-space: nowrap; }
.admin-rule-filter:not([aria-pressed="true"]):hover { color: rgb(var(--ink)); }
.admin-rule-filter[aria-pressed="true"] { color: rgb(var(--ink)); border-bottom-color: rgb(var(--blue)); }

/* ---------- detail pages ---------- */

.masthead { display: flex; align-items: center; gap: var(--s5); padding: var(--s6) 0 var(--s5); flex-wrap: wrap; }
.masthead .ring { width: 88px; height: 88px; }
.masthead .ring > .ring-core { width: 78px; height: 78px; font-size: var(--fs-26); }
.masthead .sub { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }

.duo { display: grid; gap: var(--s4); grid-template-columns: 1fr; }
@media (min-width: 880px) { .duo { grid-template-columns: 1fr 1fr; } }

/* at most four facts per row, so the Codex dossier's eight read 4+4, not 6+2 */
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(max(140px, calc((100% - 3 * var(--s2)) / 4)), 1fr)); gap: var(--s2); }
.fact { background: rgb(var(--surface-2)); border-radius: var(--r-md); padding: 10px var(--s3); }
.fact b, .fact > span { display: block; }
.fact b { font-size: var(--fs-15); }
.facts-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* element meter */
.meter { display: grid; grid-template-columns: 68px 1fr 44px; gap: var(--s2); align-items: center; font-size: var(--fs-13); }
.meter + .meter { margin-top: var(--s2); }
.meter .lane { height: 7px; border-radius: var(--r-pill); background: rgb(var(--track)); position: relative; overflow: visible; }
.meter .lane > .fill { position: absolute; inset: 0 auto 0 0; border-radius: var(--r-pill); background: rgb(var(--el, var(--ink-2))); }
.meter .lane > .baseline { position: absolute; top: -3px; bottom: -3px; width: 1px; background: rgb(var(--line-strong)); }
.meter .val { text-align: right; color: rgb(var(--ink-2)); }

/* running descriptive text (Codex lore, behaviour): one measure and leading */
.prose { max-width: 72ch; margin: 0; font-size: var(--fs-15); line-height: 22px; }
.prose-lede { margin-bottom: var(--s5); }

.tips { margin: 0; padding-left: 18px; display: grid; gap: 6px; font-size: var(--fs-14); line-height: 18px; }

/* Shared dossier micro-patterns (creatures.js / grounds.js / charms.js /
   submit.js detail views repeat these identically — one class each instead
   of hand-typed margins that drifted between files, e.g. 8px vs 10px for
   the same "lede under an eyebrow" role). */
.eyebrow-lede { margin: 0 0 var(--s3); }
.eyebrow-lede-sub { margin: var(--s4) 0 var(--s2); }
.pill-row { display: flex; gap: var(--s2); flex-wrap: wrap; }
/* deliberately tighter than .pill-row — the established gap for a smaller
   secondary pill row (damage ranking / incoming damage), not a token gap */
.pill-row-tight { display: flex; gap: 6px; flex-wrap: wrap; }
.pill-row-top { display: flex; gap: var(--s2); margin-top: var(--s4); flex-wrap: wrap; }
.dossier-note { margin: var(--s3) 0 0; }
.battle-note { margin: calc(var(--s2) * -1) 0 var(--s4); }
.show-more { text-align: center; margin-top: var(--s5); }
.tile-top-gap { margin-bottom: var(--s3); }

/* ---------- submit flow ---------- */

.steps { display: grid; gap: var(--s5); }
.step { position: relative; }
.step > .step-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); }
.step > .step-head h2 { font-size: var(--fs-18); }

/* step progress indicator — numbered, lined, shows only the active step's content */
.stepper { margin: 0 0 var(--s5); }
.stepper-list { display: flex; align-items: center; list-style: none; margin: 0; padding: 0; }
.stepper-item { display: flex; align-items: center; flex: 1 1 0; }
.stepper-item:last-child { flex: 0 0 auto; }
.stepper-item::after { content: ''; flex: 1 1 auto; height: 1px; background: rgb(var(--line-strong)); margin: 0 var(--s2); }
.stepper-item:last-child::after { display: none; }
.stepper-item.is-done::after { background: rgb(var(--ink)); }
.stepper-btn { display: flex; align-items: center; gap: var(--s2); min-height: var(--tap); background: none; border: 0; padding: var(--s1) 0; font: inherit; color: rgb(var(--ink-2)); cursor: pointer; }
/* upcoming steps stay legible in ink-2 on their own circle — at reduced
   opacity they all but vanished in dark mode */
.stepper-btn[disabled] { cursor: default; }
.stepper-num { display: grid; place-items: center; width: 26px; height: 26px; border-radius: var(--r-circle); background: rgb(var(--surface-2)); color: rgb(var(--ink-2)); font-size: var(--fs-13); font-weight: 700; flex-shrink: 0; }
.stepper-item.is-done .stepper-num { background: rgb(var(--ink)); color: rgb(var(--surface)); }
.stepper-item.is-active .stepper-num { background: rgb(var(--blue)); color: rgb(var(--white-fixed)); }
.stepper-item.is-active .stepper-btn { color: rgb(var(--ink)); }
.stepper-label { font-size: var(--fs-13); font-weight: 600; white-space: nowrap; }
@media (max-width: 520px) { .stepper-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); } }
/* keeps the last step's circle clear of the floating GitHub/appearance
   cluster (.rail-foot, fixed top-right) — focusing a later step's heading
   can scroll a short flow right under it; same reserve as the page heads. */
@media (max-width: 700px) { .stepper-list { padding-right: calc(var(--tap) * 2 + var(--s4)); } }

.step-nav { display: flex; justify-content: space-between; gap: var(--s3); margin-top: var(--s4); }

.guess-list { display: grid; gap: var(--s2); }
.guess {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; min-height: var(--tap); text-align: left; color: rgb(var(--ink)); font: inherit;
  padding: var(--s3) var(--s4); cursor: pointer;
  border: 1.5px solid rgb(var(--stroke)); border-radius: var(--r-input);
  background: rgb(var(--surface-raised));
  transition: border-color var(--t-hover) var(--e-standard);
}
.guess:hover { background: rgba(var(--overlay), var(--overlay-a)); }
.guess.picked { border-color: rgb(var(--blue)); }
.guess-copy { display: grid; gap: 2px; }
.guess .pct { margin-left: auto; font-weight: 700; font-size: var(--fs-15); }

.note { border-radius: var(--r-md); padding: var(--s3) var(--s4); font-size: var(--fs-14); margin: var(--s2) 0; }
.note-error { background: rgba(var(--red), .08); color: rgb(var(--ink-error)); }
.note-warning { background: rgba(var(--brand-orange), .1); color: rgb(var(--ink-warning)); }
.note-success { background: rgba(var(--green), .1); color: rgb(var(--ink-success)); }

.form-row { display: flex; flex-wrap: wrap; gap: var(--s3); }
.paste-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--s3); margin-top: var(--s3); }
.analytics-duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4); }
.analytics-duo .section { min-width: 0; }

@media (max-width: 879px) {
  .analytics-duo { grid-template-columns: 1fr; }
}

/* ---------- character tools ---------- */

.tool-grid {
  margin-top: var(--s5);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--s4);
  align-items: start;
}
.tool-card {
  display: grid;
  gap: var(--s4);
}
.tool-disclosure { border-top: 1px solid rgb(var(--line)); padding-top: var(--s3); }
.tool-disclosure summary:hover { text-decoration: underline; }
.tool-disclosure summary { color: rgb(var(--ink-info)); font-size: var(--fs-13); font-weight: 600; cursor: pointer; }
.tool-disclosure .tool-fields { margin-top: var(--s3); }
/* the imbuement calculator lists 23 cards: in one grid column it stood
   ~1,300px tall beside empty space and pushed Profit a screen below its
   neighbours. It goes last and full width, where its cards sit 3-4 across. */
#level-tool { order: 3; }
#profit-tool { order: 4; }
#imbuement-tool { order: 5; grid-column: 1 / -1; }
/* Reuses .filter-bar for its field layout only — inside a tool card it needs
   no surface of its own. The position/backdrop resets it used to carry went
   with the sticky bar itself. */
#imb-filter-bar { margin: 0; padding: 0; border: 0; background: transparent; }
/* the subtitle drops below the title when both don't fit, instead of the
   two squeezing each other into three-line columns */
.tool-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s1) var(--s3);
}
.tool-head h2 {
  font-size: var(--fs-18);
  letter-spacing: -.2px;
}
.tool-fields {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  align-items: end;
}
.tool-fields .lbl {
  min-width: 0;
}
.tool-result {
  display: grid;
  gap: var(--s3);
}
/* The stamina plan reads as a sentence, so it gets prose measure and leading
   rather than the tight metric styling around it. */
.stamina-plan {
  margin: 0;
  max-width: 60ch;
  font-size: var(--fs-14);
  line-height: 1.5;
}
.stamina-plan b { font-weight: 600; }
/* tool KPIs come in threes and fours: two per row with an odd last cell
   spanning, so a three never leaves one cell stranded beside a gap */
.tool-kpis {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s2);
}
.tool-kpis > :last-child:nth-child(odd) { grid-column: 1 / -1; }
/* a card wide enough keeps a three-KPI result on one row (as it was at
   desktop width); only narrow cards pair up. Sized by the card, not the
   viewport: the same card is narrow in a three-column grid at 1440 and wide
   in a two-column grid at 1280. */
.tool-card { container-type: inline-size; }
@container (min-width: 400px) {
  .tool-kpis:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .tool-kpis:has(> :nth-child(3):last-child) > :last-child { grid-column: auto; }
}
.tool-kpis > span {
  display: grid;
  gap: 2px;
  min-height: 66px;
  padding: 10px var(--s3);
  background: rgb(var(--surface-2));
  border-radius: var(--r-md);
}
.tool-kpis b {
  font-size: var(--fs-18);
  line-height: 24px;
  font-variant-numeric: tabular-nums;
}
.tool-kpis small {
  color: rgb(var(--ink-2));
  font-size: var(--fs-12);
  line-height: 16px;
}
.mini-list {
  display: grid;
  gap: 6px;
  font-size: var(--fs-13);
  line-height: 18px;
}
.mini-list > span {
  padding: var(--s2) 0;
  border-top: 1px solid rgb(var(--line));
}
.tool-creature-line {
  display: flex;
  align-items: center;
  gap: var(--s3);
}
.tool-creature-line > div {
  display: grid;
  gap: 2px;
}
.tool-result-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: var(--s3);
}
.tool-mini-card {
  display: grid;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid rgb(var(--stroke)); /* nested in a panel: --line vanishes in dark */
  border-radius: var(--r-input);
  background: rgb(var(--surface-raised));
  transition: background var(--t-hover) var(--e-standard), box-shadow var(--t-hover) var(--e-standard);
}
.tool-mini-card:hover {
  background: rgba(var(--overlay), var(--overlay-a));
  box-shadow: var(--shadow-card);
}
.tool-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s5);
}
.tool-columns .eyebrow {
  margin: 0 0 var(--s2);
}

/* ---------- imbuement calculator modal ---------- */

#imb-modal {
  border: 0;
  border-radius: var(--r-lg);
  padding: 0;
  background: transparent;
  width: min(760px, 92vw);
  max-height: 96vh;
  overflow: auto;
  color: rgb(var(--ink));
  animation: page-settle var(--t-hover) var(--e-confident);
}
#imb-modal::backdrop {
  background: rgba(0, 0, 0, .5);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}
#imb-modal > .panel {
  display: grid;
  gap: var(--s4);
  box-shadow: var(--shadow-float);
}
/* a <button>: without its own font it rendered at the UA's 13.33px */
.imb-card { display: grid; gap: var(--s2); text-align: left; width: 100%; border: 1px solid rgb(var(--stroke)); color: inherit; font: inherit; font-size: var(--fs-14); cursor: pointer; }
.imb-card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s3);
}
.imb-card-id {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
}
.imb-card-id > span {
  display: grid;
  min-width: 0;
}
.imb-card-id b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.imb-card-price {
  display: flex;
  align-items: center;
  gap: var(--s1);
  font-size: var(--fs-15);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.imb-card-badge {
  width: fit-content;
}
.imb-item-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 6px 0;
}
.imb-item-row-label {
  display: flex;
  align-items: center;
  gap: 6px;
}
.imb-item-row-label svg { width: 15px; height: 15px; color: rgb(var(--ink-2)); }
#imb-tier-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.imb-row-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.imb-method-svg { color: rgb(var(--ink-2)); }
.imb-tier-card {
  display: flex;
  flex-direction: column;
  border: 1px solid rgb(var(--stroke)); /* nested in a panel: --line vanishes in dark */
  border-radius: var(--r-input);
  position: relative;
  overflow: visible;
}
.imb-tier-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3);
  background: rgb(var(--surface-2));
  border-bottom: 1px solid rgb(var(--line));
}
.imb-tier-head-text {
  display: grid;
  gap: var(--s1);
}
.imb-icon-lg { width: 44px; height: 44px; flex-shrink: 0; }
.imb-tier-bonus {
  margin: 0;
  font-size: var(--fs-13);
  font-weight: 600;
}
.imb-tier-text-basic { color: rgb(var(--ink-success)); }
.imb-tier-text-intricate { color: rgb(var(--ink-info)); }
.imb-tier-text-powerful { color: rgb(var(--ink-error)); }
.imb-tier-items {
  padding: var(--s2) var(--s3);
  background: rgb(var(--surface-raised));
}
.imb-tier-items .imb-item-row + .imb-item-row {
  border-top: 1px solid rgb(var(--line));
}
.imb-tier-total {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: var(--tap);
  padding: 0 var(--s3);
  background: rgb(var(--surface-2));
  border-top: 1px solid rgb(var(--line));
}
.imb-total-info {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--tap);
  padding: 0; border: 0; background: transparent; color: rgb(var(--ink));
  font: inherit; font-weight: 700; cursor: pointer;
}
.imb-total-label { font-weight: 700; }
.imb-total-info svg { width: 15px; height: 15px; color: rgb(var(--ink-2)); }
.imb-total-info[aria-expanded="true"] svg { color: rgb(var(--ink)); }
.imb-tier-total b {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-size: var(--fs-16);
  font-variant-numeric: tabular-nums;
}
.imb-tier-total-right {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.imb-tier-pop {
  margin: var(--s2);
  padding: var(--s3);
  background: rgb(var(--surface-raised));
  border: 1px solid rgb(var(--stroke));
  border-radius: var(--r-md);
  box-shadow: var(--shadow-float);
}
.imb-compare-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3);
}
.imb-compare-row {
  display: grid;
  gap: var(--s1);
  font-size: var(--fs-13);
}
.imb-compare-row span,
.imb-compare-row strong {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
}
.imb-compare-row strong { font-variant-numeric: tabular-nums; }
.imb-compare-row em { color: rgb(var(--ink-error)); font-style: normal; font-weight: 700; }
.imb-tier-pop p { margin: var(--s3) 0 0; padding: var(--s2); text-align: center; background: rgb(var(--surface-2)); border-radius: var(--r-sm); }
/* IconButton "ghost" variant verbatim: transparent, fully circular, hover-only wash */
.imb-copy-btn {
  display: grid;
  place-items: center;
  width: var(--tap); height: var(--tap);
  padding: 0; border: 0; background: transparent;
  color: rgb(var(--ink-2)); cursor: pointer; border-radius: var(--r-circle);
  transition: background var(--t-hover) var(--e-standard);
}
.imb-copy-btn svg { width: 15px; height: 15px; }
.imb-copy-btn:hover { background: rgba(var(--overlay), var(--overlay-a)); color: rgb(var(--ink)); }
.imb-price-input {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.imb-price-input input {
  width: 100px;
  min-width: 0;
}
.imb-price-input-market input {
  border-color: rgb(var(--blue));
  background: rgba(var(--blue), 0.14);
  box-shadow: 0 0 0 1px rgb(var(--blue)) inset;
}
.imb-card:focus-visible {
  border-color: rgb(var(--line-strong));
}

/* ---------- character dashboard ---------- */

/* The whole character page is one flex column with a uniform 24px gap between
   sections (matches the reference's top-level gap:var(--ig-space-6)); the gap
   is the single source of vertical spacing, so sections carry no own margins. */
.character-page { display: flex; flex-direction: column; gap: var(--s6); }
.character-page > * { margin: 0; }

/* Identity header — matches the reference: a large gradient-ring avatar, then
   the name row (name + online badge + share), the vocation line, and the level
   meter. */
.character-hero {
  display: flex;
  align-items: center;
  gap: var(--s6);
  flex-wrap: wrap;
}
.hero-identity { flex: 1; min-width: 240px; display: flex; flex-direction: column; gap: var(--s2); }
.character-hero .ring { width: 96px; height: 96px; flex: 0 0 96px; }
.character-hero .ring-core { width: 90px; height: 90px; font-size: var(--fs-32); }
.hero-name-row { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.character-profile-line {
  margin: 0;
  color: rgb(var(--ink-2));
  font-size: var(--fs-14);
  line-height: 18px;
}
.dashboard-metrics {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s6);
}
/* four KPI cards at a tablet canvas leave ~140px each — too narrow for a
   figure plus its inline sparkline; two per row until the full sidebar */
@media (max-width: 1000px) {
  .dashboard-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* featured KPI card — value and its sparkline share one baseline-aligned row,
   matching the reference (spark sits top-right of the number, no axis labels) */
/* a figure never breaks mid-number: the value keeps its width and the spark
   shrinks, then drops below it when the card is too narrow for both */
.metric-value-row { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--s3); }
.metric-value-row > b { flex: 0 0 auto; }
.metric-value-row .metric-spark { margin: 0; flex: 1 1 64px; max-width: 96px; }
/* the one signed-delta vocabulary (Character KPIs, Progress deltas); size comes from its context */
.metric-delta { font-style: normal; font-weight: 700; }
.metric-delta.up { color: rgb(var(--ink-success)); }
.metric-delta.down { color: rgb(var(--ink-error)); }
.metric-delta.even { color: rgb(var(--ink-2)); }
.metric-spark { margin-top: 2px; }
.metric-spark svg { display: block; width: 100%; height: auto; }

/* ---------- activity heatmap + recent deaths ---------- */

.activity-duo { display: grid; grid-template-columns: 1.4fr 1fr; gap: var(--s6); }
.activity-duo > .panel { min-width: 0; }
.heatmap { display: flex; gap: var(--s1); overflow-x: auto; padding-bottom: var(--s1); }
.hm-week { display: flex; flex-direction: column; gap: var(--s1); position: relative; padding-top: var(--s4); }
.hm-month { position: absolute; top: 0; left: 0; height: 14px; font-size: var(--fs-11); line-height: 13px; color: rgb(var(--ink-2)); white-space: nowrap; }
/* 11px data cells — sized so 26 tracked weeks fit a panel, not a spacing token */
.hm { width: 11px; height: 11px; border-radius: var(--r-sm); background: rgb(var(--track)); flex-shrink: 0; }
.hm-void { background: transparent; }
.hm-null { background: transparent; border: 1px dashed rgb(var(--line-strong)); box-sizing: border-box; }
/* purple intensity ramp — the reference's Hunting-activity heatmap uses
   rgb(118,56,250) (= --brand-purple) at .28 / .5 / .74 / 1, not blue */
.hm-1 { background: rgba(var(--brand-purple), .28); }
.hm-2 { background: rgba(var(--brand-purple), .5); }
.hm-3 { background: rgba(var(--brand-purple), .74); }
.hm-4 { background: rgb(var(--brand-purple)); }
.hm-legend { display: flex; align-items: center; gap: var(--s1); margin-top: var(--s3); }
.hm-legend .hm-null { margin-left: var(--s3); }
.death-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s4); }
.death-list li { display: flex; gap: var(--s3); align-items: flex-start; }
.death-list p { margin: 0; font-size: var(--fs-14); line-height: 18px; }
.death-dot { width: 8px; height: 8px; border-radius: var(--r-circle); background: rgb(var(--red)); margin-top: var(--s1); flex-shrink: 0; }
@media (max-width: 860px) { .activity-duo { grid-template-columns: 1fr; } }
.pace-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  margin-top: var(--s3);
  padding: var(--s4) var(--s5);
  border: 1px solid rgb(var(--line));
  border-radius: var(--r-input);
  background: rgb(var(--surface-2));
}
.pace-message { display: grid; gap: var(--s1); min-width: 0; }
.pace-message > b { font-size: var(--fs-18); line-height: 24px; }
.pace-down .pace-message > b { color: rgb(var(--ink-error)); }
.pace-up .pace-message > b { color: rgb(var(--ink-success)); }
.pace-support { display: flex; align-items: center; flex-shrink: 0; }
.pace-support > span { display: grid; gap: 2px; min-width: 100px; padding-left: var(--s4); border-left: 1px solid rgb(var(--line)); }
.pace-support b { font-size: var(--fs-18); line-height: 24px; }
.pace-support small { color: rgb(var(--ink-2)); font-size: var(--fs-11); line-height: 13px; }
.character-level-progress { display: grid; gap: var(--s2); width: min(360px, 100%); margin-top: var(--s1); }
.character-level-progress > div:first-child { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); font-size: var(--fs-12); font-weight: 600; }
.character-level-progress .track { height: 6px; overflow: hidden; border-radius: var(--r-pill); background: rgb(var(--track)); }
.character-level-progress .track i { display: block; height: 100%; border-radius: inherit; background: var(--grad-hero); }
.character-level-progress small { color: rgb(var(--ink-2)); font-size: var(--fs-11); }
.empty-action { display: flex; align-items: center; justify-content: space-between; gap: var(--s5); min-height: 132px; padding: var(--s5); background: rgb(var(--surface-2)); border-radius: var(--r-input); }
.empty-action h3, .empty-action p { margin: 0; }
.empty-action h3 { font-size: var(--fs-18); } /* was the browser's UA default (18.72px), not a token */
.empty-action p { margin-top: var(--s2); max-width: 58ch; color: rgb(var(--ink-2)); font-size: var(--fs-14); line-height: 18px; }
/* the action keeps its label on one line (it wrapped to 'Log a / hunt' at 768) */
.empty-action .btn { flex-shrink: 0; white-space: nowrap; }
/* inside a result grid the no-match state spans every column, not one cell */
.tiles > .empty-action { grid-column: 1 / -1; }
.mini-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: var(--s2);
}
.mini-metrics > span {
  display: grid;
  gap: 2px;
  min-width: 0;
}
.mini-metrics b {
  font-size: var(--fs-15);
  line-height: 18px;
  overflow-wrap: anywhere;
}
.mini-metrics small {
  color: rgb(var(--ink-2));
  font-size: var(--fs-11);
  line-height: 13px;
}
/* the stats strip above the Daily XP chart: four figures, two per row on phones */
.dxp-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); margin-bottom: var(--s4); }
.dxp-stats b em { color: rgb(var(--ink-2)); font-size: var(--fs-13); font-style: normal; font-weight: 600; }
@media (max-width: 700px) { .dxp-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s3); } }
.skill-card {
  display: grid;
  min-width: 0;
  gap: var(--s3);
  padding: var(--s4);
}
.skill-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s1) var(--s3);
}
.skill-card-head h3 {
  font-size: var(--fs-15);
  line-height: 19px;
}
.skill-card-head > div { min-width: 0; }
.skill-card-head > b {
  flex-shrink: 0;
  font-size: var(--fs-20);
  line-height: 24px;
  white-space: nowrap;
  text-align: right;
}
.skill-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s6); margin-top: var(--s3); }
/* bare skill item inside the one SKILLS panel — no per-item border (matches
   the reference, which lays these out flat in a single card) */
.skill-item { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.skill-item-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); }
.skill-item-name { font-size: var(--fs-13); font-weight: 600; }
.skill-item-head > b { font-size: var(--fs-13); font-weight: 700; font-variant-numeric: tabular-nums; }
.skill-item small { font-size: var(--fs-12); }
/* 145px min so all six point-stat cards sit on one row at the canvas width
   (the reference keeps them in a single row); wraps responsively below that */
.stat-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(145px, 1fr)); gap: var(--s4); }
/* six cards: 4+2 at tablet width becomes 3+3 */
@media (min-width: 701px) and (max-width: 1000px) { .stat-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Experience panel — title + year segmented on one row, month segmented full
   width below, then the single XP-gained area chart and its legend. */
.exp-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s4); }
.exp-title { margin: 0; }
.exp-month { display: flex; margin-bottom: var(--s4); max-width: 100%; }
.exp-legend { margin-top: var(--s3); }

/* "Next hunts" story-tray — a horizontally scrolling row of ground shortcuts,
   each a gradient-ringed avatar with the ground's initials, matching the
   source's conic-gradient ring treatment used for its own story tray. */
.next-hunts { display: flex; gap: var(--s5); overflow-x: auto; margin: 0; padding: 0 0 var(--s1); list-style: none; }
.next-hunts > li { flex-shrink: 0; }
.next-hunts-item { display: flex; flex-direction: column; align-items: center; gap: var(--s2); width: 76px; flex-shrink: 0; text-align: center; }
.next-hunts-ring {
  width: 64px; height: 64px; border-radius: var(--r-circle); padding: 2.5px; box-sizing: border-box;
  background: var(--grad-story);
}
.next-hunts-ring > span {
  width: 100%; height: 100%; box-sizing: border-box; border-radius: var(--r-circle);
  background: rgb(var(--surface-2)); border: 2.5px solid rgb(var(--surface-raised));
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: var(--fs-14); color: rgb(var(--ink));
}
.next-hunts-item small {
  font-size: var(--fs-11); color: rgb(var(--ink-2)); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 76px;
}
.sparkline {
  min-height: 58px;
  display: grid;
  align-items: center;
}
/* inside a compact sparkline slot the standard empty state stays small and left-aligned */
.sparkline .viz-empty { min-height: 0; padding: 0; font-size: var(--fs-12); text-align: left; justify-items: start; }
.sparkline-svg {
  width: 100%;
  height: auto;
  display: block;
}

@media (max-width: 700px) {
  .codex-grid, .planner-grid { grid-template-columns: minmax(0, 1fr); }
  /* two-up like every other page's KPI row (the ≤1000px rule spans an odd last
     card): a one-column stack pushed Home's Next hunt below the first screen */
  .home-metric-grid { gap: var(--s2); margin-top: var(--s5); }
  .metric-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); margin-top: var(--s5); }
  .metric { padding: var(--s3); }
  .metric > b, .metric-value-row > b { font-size: var(--fs-24); }
  .home-next-hunt, .home-attention { padding: var(--s4); margin-top: var(--s4); }
  .home-card-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .home-card-actions .btn { width: 100%; padding-inline: var(--s2); }
  .home-attention-list > div { align-items: flex-start; padding: var(--s3) 0; }
  .home-shortcuts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
  .home-shortcuts a { min-width: 0; border-radius: var(--r-md); }
  .admin-rule-filter { min-height: var(--tap); }
  /* the rest of the 44pt contract: controls outside the shared primitives */
  .home-shortcuts a { min-height: var(--tap); }
  .back-link, .home-attention-list a { display: inline-flex; align-items: center; min-height: var(--tap); }
  .tool-disclosure summary { line-height: var(--tap); }
  /* inline link chips keep their visual size; an invisible halo takes the hit */
  a.pill, table.grid a, .stepper-btn { position: relative; }
  a.pill::after, table.grid a::after { content: ''; position: absolute; inset: calc((100% - var(--tap)) / 2) 0; }
  /* a number-only step (labels hidden ≤520px) is 26px wide: widen the hit, not the connector gaps */
  .stepper-btn::after { content: ''; position: absolute; inset: 0 calc((100% - var(--tap)) / 2); }
  #imb-tier-cards { grid-template-columns: 1fr; }
  .tool-grid { grid-template-columns: minmax(0, 1fr); }
  .tool-fields .lbl,
  .tool-fields .lbl.lbl-wide,
  .tool-fields .lbl.lbl-narrow { width: 100%; }
  .tool-fields .lbl > input,
.tool-fields .lbl > select,
.tool-fields .lbl.lbl-wide > input,
.tool-fields .lbl.lbl-narrow > input { min-width: 0; width: 100%; }
  .dashboard-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
  .pace-band { align-items: stretch; flex-direction: column; padding: var(--s4); }
  .pace-support { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pace-support > span { min-width: 0; padding: 0 var(--s2); }
  .pace-support > span:first-child { padding-left: 0; border-left: 0; }
  .empty-action { align-items: stretch; flex-direction: column; }
  .empty-action .btn { width: 100%; }
  .donut-board { justify-content: center; }
  .donut-legend { min-width: 0; flex-basis: 100%; }
}

/* ---------- charts ---------- */

.viz { position: relative; }
.viz svg { display: block; width: 100%; height: auto; }
/* shared hover readout (attachVizHover): value leads, label follows */
/* placed by attachVizHover from its measured size (kept inside the viewport,
   flipped below the point when there is no room above); long labels wrap */
.viz-tip {
  position: absolute; width: max-content; max-width: min(280px, calc(100vw - 16px));
  display: grid; gap: 1px; text-align: center;
  padding: var(--s1) var(--s3); pointer-events: none; z-index: 5;
  background: rgb(var(--surface-raised)); border: 1px solid rgb(var(--stroke));
  border-radius: var(--r-input); box-shadow: var(--shadow-float);
}
.viz-tip b { font-size: var(--fs-13); font-variant-numeric: tabular-nums; white-space: nowrap; }
.viz-tip span { color: rgb(var(--ink-2)); font-size: var(--fs-11); }
.viz-tip span:empty { display: none; }
/* standard chart empty state — one look wherever a chart has no rows yet */
.viz-empty {
  display: grid; place-items: center; min-height: 140px;
  padding: var(--s5); margin: 0;
  color: rgb(var(--ink-2)); font-size: var(--fs-13); text-align: center;
}
/* the leader bar's fill comes from the url(#...) gradient set on the path
   itself (bars() in viz/svg.js) — this rule must not set a competing fill */
.viz .vbar-rest { fill: rgba(var(--ink-2), .35); }
/* hover focus: the picked mark stays, its siblings step back */
.viz .vhit { fill: none; pointer-events: all; }
.viz .vrow, .viz .vcol, .viz .vdonut-seg { transition: opacity var(--t-hover) var(--e-standard); }
.viz svg.has-active .vrow:not(.is-active), .viz svg.has-active .vcol:not(.is-active), .viz svg.has-active .vdonut-seg:not(.is-active) { opacity: .4; }
.hm.is-active { outline: 2px solid rgb(var(--ink)); outline-offset: 1px; }
.viz .vlabel { fill: rgb(var(--ink)); font-size: var(--fs-12); font-family: var(--f-ui); }
.viz .vvalue { fill: rgb(var(--ink-2)); font-size: var(--fs-12); font-family: var(--f-ui); font-variant-numeric: tabular-nums; }
.viz .vaxis { stroke: rgb(var(--line)); }
.viz .vtick, .sparkline .vtick, .metric-spark .vtick { fill: rgb(var(--ink-2)); font-size: var(--fs-11); font-family: var(--f-ui); font-variant-numeric: tabular-nums; }
/* line + area colour comes from the flowGradientDefs() url(#...) fill/stroke set on the path itself */
.viz .vline, .sparkline .vline, .metric-spark .vline { fill: none; stroke-width: 2.5; stroke-linecap: round; }
/* dots wear a 2px surface ring so they stay legible crossing the line */
.viz .vdot, .sparkline .vdot, .metric-spark .vdot { fill: var(--grad-purple); stroke: rgb(var(--surface-raised)); stroke-width: 2; }
.viz .vevent { pointer-events: none; }
.viz .vevent-level { fill: rgb(var(--green)); stroke: rgb(var(--surface-raised)); stroke-width: 2; }
.viz .vevent-death { fill: rgb(var(--red)); stroke: rgb(var(--surface-raised)); stroke-width: 2; }
.viz .vevent-dense { stroke-width: 1; }
/* a span with no tracker reading on a time-true chart: shaded, never drawn across */
.viz .vgap { fill: rgba(var(--ink-2), .1); }
.viz .vgap-label { fill: rgb(var(--ink-2)); font-size: var(--fs-11); font-family: var(--f-ui); }
/* columns(): the analytic bar + line combo (Progress Daily XP gain, Character
   Experience). One loud gradient, everything else quiet: the columns carry the
   hero gradient from url(#...) on the element (no competing CSS fill), the
   trend is primary ink over a surface halo, the reference marks stay neutral. */
.viz .vtrend, .viz .vtrend-halo { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.viz .vtrend { stroke: rgb(var(--ink)); stroke-width: 2; }
.viz .vtrend-halo { stroke: rgb(var(--surface-raised)); stroke-width: 5; }
.viz .vbase { stroke: rgb(var(--line-strong)); }
.viz .vavg { stroke: rgb(var(--ink-2)); stroke-width: 1; stroke-dasharray: 4 4; }
.viz .vavg-label, .viz .vunit, .viz .vnodata-label { fill: rgb(var(--ink-2)); font-size: var(--fs-11); font-family: var(--f-ui); font-variant-numeric: tabular-nums; }
/* a surface-coloured halo keeps the no-data label legible over its hatching */
.viz .vnodata-label { paint-order: stroke; stroke: rgb(var(--surface-raised)); stroke-width: 3px; stroke-linejoin: round; }
.viz .vnodata-hatch { stroke: rgba(var(--ink-2), .28); stroke-width: 1.5; }
.viz .vrug-level { fill: rgb(var(--green)); }
.viz .vrug-death { fill: rgb(var(--red)); }
.viz .vdonut-total { fill: rgb(var(--ink)); font-size: var(--fs-14); font-weight: 700; font-family: var(--f-ui); }
.viz-legend { display: flex; flex-wrap: wrap; gap: var(--s4); list-style: none; margin: var(--s3) 0 0; padding: 0; font-size: var(--fs-12); color: rgb(var(--ink-2)); }
.viz-legend li { display: flex; align-items: center; gap: var(--s1); }
.viz-legend b { color: rgb(var(--ink)); font-weight: 700; }
.viz-legend-swatch.viz-legend-line { width: 16px; height: 3px; border-radius: var(--r-pill); background: var(--grad-hero); }
.viz-legend-level { background: rgb(var(--green)); }
.viz-legend-death { background: rgb(var(--red)); }
.viz-legend-swatch.viz-legend-col { border-radius: var(--r-xs); background: var(--grad-hero); }
.viz-legend-swatch.viz-legend-trend { width: 16px; height: 2px; border-radius: var(--r-pill); background: rgb(var(--ink)); }
.viz-legend-swatch.viz-legend-avg { width: 16px; height: 0; border-radius: 0; border-top: 1px dashed rgb(var(--ink-2)); }
.viz-legend-swatch.viz-legend-nodata { border-radius: var(--r-xs); background: repeating-linear-gradient(45deg, rgba(var(--ink-2), .35) 0 1.5px, transparent 1.5px 4px); }
.viz-legend-swatch.viz-legend-rug { width: 2px; height: 10px; border-radius: 0; background: rgb(var(--green)); }
.viz-legend-swatch.viz-legend-rug-death { background: rgb(var(--red)); }
.viz-legend-swatch.viz-legend-gap { border-radius: var(--r-xs); background: rgba(var(--ink-2), .2); }
.donut-board { display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap; }
/* fixed 160px — matches the svg's own viewBox, so donut text renders at token size */
.donut-chart svg { width: 160px; height: 160px; flex-shrink: 0; }
.donut-legend { display: grid; gap: var(--s2); flex: 1 1 200px; min-width: 200px; list-style: none; margin: 0; padding: 0; }
.donut-legend li { display: flex; align-items: center; gap: var(--s2); }
.donut-legend span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.donut-legend b { margin-left: auto; flex-shrink: 0; font-size: var(--fs-13); }
.donut-legend small { flex-shrink: 0; width: 34px; color: rgb(var(--ink-2)); font-size: var(--fs-11); text-align: right; }
/* same shape as .viz-legend-swatch (.viz-legend-line reshapes it above) — kept as one
   declaration so the two legend dots can't drift apart in size/radius */
.donut-swatch, .viz-legend-swatch { width: 10px; height: 10px; flex-shrink: 0; border-radius: var(--r-circle); }

/* one page-header pattern — the h1 and its lede previously carried per-page inline styles */
.page-head { padding: var(--s2) 0 var(--s1); }
.page-head > p { max-width: 64ch; }
.page-head-roomy { padding-bottom: var(--s5); }

/* chartInto() placeholder — fills its slot so the chart measures a true width */
.chart-mount { width: 100%; min-width: 0; }
.panel-eyebrow { margin: 0 0 var(--s3); }

.section { margin-top: var(--s8); }
/* higher specificity than .section alone, so a .show-more CTA keeps its
   smaller top gap even when it's also a .section (source order otherwise
   lets the plain .section rule above win) */
.section.show-more, .section.section-tight { margin-top: var(--s5); }
/* a dossier's first section sits right under its masthead, or s6 under its KPIs */
.masthead + .section { margin-top: 0; }
#detail .metric-row + .section { margin-top: var(--s6); }
.section > .section-bar { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s4); flex-wrap: wrap; }
.section > .section-bar h2 { font-size: var(--fs-20); letter-spacing: -.3px; }
/* a sub-heading inside a section (e.g. Creature matchups under Battle plan) */
.section > .section-bar h3 { font-size: var(--fs-16); }
.section > .section-bar .fine { min-width: 0; overflow-wrap: anywhere; }

/* a creature name with its sprite, in dossier tables */
.critter-link { display: inline-flex; align-items: center; gap: var(--s2); }

/* the "← Planner / ← Codex" control that closes an inline dossier (shell.js backLink) */
.back-link { padding: 0; border: 0; background: none; color: rgb(var(--ink-2)); font: inherit; cursor: pointer; }
.back-link:hover { color: rgb(var(--ink)); }

/* skeleton shimmer — the page shells' placeholder until the controller renders */
.ghost {
  border-radius: var(--r-input);
  background: linear-gradient(90deg, rgb(var(--surface-2)) 25%, rgb(var(--wash)) 50%, rgb(var(--surface-2)) 75%);
  background-size: 200% 100%;
  animation: ghost 1.3s var(--e-standard) infinite;
}
@keyframes ghost { to { background-position: -200% 0; } }
.ghost-head { height: 56px; margin-bottom: var(--s4); }
.ghost-body { height: 340px; }
