/*
 * char-dashboard.css — ShadowDark DM Companion
 * Scoped styles for the alternate "Dashboard" character view
 * (renderCharacterDashboard in character.js).
 *
 * REBUILD (2026-05-28): from-scratch reproduction of Ken's reference photo.
 * The previous version captured the regions but missed the photo's geometry.
 * This version reproduces the photo's exact layout — top bar with leading
 * icons + stacked stat cards, an underlined icon tab row, and a 3-column
 * Overview grid whose card structure, dense rows, chips, slot grids, and
 * thin-line icons mirror the photo — translated into the OSR dark theme.
 *
 * THEME: dark OSR palette via base.css tokens only. No raw hex.
 *
 * FONTS: the photo is a clean sans UI. Only the ShadowDark wordmark and the
 * character title use JSL-Blackletter (--font-heading, already shipped).
 * Everything else — labels, numbers, headings — uses a clean system sans
 * stack defined here (--dash-sans). We deliberately do NOT use --font-body /
 * --font-heading-ui / --font-mono, which reference WOFF2 files that are not
 * installed and would 404.
 *
 * All classes are prefixed char-dash__ so they never collide with the
 * existing sheet/roster/loadout component classes.
 */

.char-dash {
  /* Page-local clean sans — the photo's UI font. Not Crimson/Cinzel/Share Tech. */
  --dash-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Subtle radius to echo the photo's rounded cards while honoring the dark
     theme. base.css forces border-radius:0 globally; we re-enable a small
     radius only inside the dashboard, scoped, so geometry matches the photo. */
  --dash-radius: 10px;
  --dash-radius-sm: 8px;

  font-family: var(--dash-sans);
  color: var(--color-text);
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  max-width: 1480px;
  margin: 0 auto 0 0;   /* #5 — left-anchored, not centered */
  padding-bottom: var(--space-2xl);
}

/* Re-enable rounded corners inside the dashboard only (base.css zeroes them
   globally). The photo's whole language is rounded cards/buttons/chips. */
.char-dash *,
.char-dash *::before,
.char-dash *::after {
  border-radius: inherit;
}

/* Thin-line icon baseline — every dashboard SVG inherits text color and the
   app's established line-glyph weight. */
.char-dash svg {
  display: block;
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* ============================================================
   TOP BAR (restructured 2026-06-28) — a vertical COLUMN of two bands:
     ROW 1 (.char-dash__topbar-main): identity (portrait + name/meta, prominent)
            with the AC/HP/XP stat cards trailing on the same row.
     ROW 2 (.char-dash__topbar-actions): the action buttons on their OWN full-width
            line below a divider.
   Two prior spacing-only passes (batch 8 + batch 11 #4) didn't satisfy because the
   identity, stat cards, AND action buttons were all competing in ONE horizontal
   row, so the name/meta were crushed into a narrow column. Ken's directive: move the
   identity to the TOP, above the buttons. Stacking the actions onto their own band
   removes the competition — the identity now has the full top row's width to read
   as the prominent line, with the meta breathing beneath it. (Ken, 2026-06-28.)
   ============================================================ */

.char-dash__topbar {
  --dash-radius: var(--dash-radius);
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  /* Default row model — kept for the SHARED skin (e.g. the commoner dash reuses
     .char-dash__topbar with identity | chips | actions side-by-side and has no
     .char-dash__topbar-main wrapper). The ADVENTURER topbar overrides to a column
     below (scoped via :has(.char-dash__topbar-main)) so its restructure doesn't
     touch the commoner header I don't own. */
  flex-wrap: nowrap;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius);
  /* Vertical padding bumped md->lg (#3, 2026-06-27) so the enlarged 6rem hero
     medallion sits in a comfortable banner band instead of crowding the edges. */
  padding: var(--space-lg);
}

/* ADVENTURER topbar only (the one that carries .char-dash__topbar-main): a vertical
   COLUMN — identity band on top, actions band below. Scoped via :has() so the shared
   commoner topbar (no -main wrapper) keeps its original single-row model. */
.char-dash__topbar:has(.char-dash__topbar-main) {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-md);
}

/* THE HEADER BAND (2026-07-02 consolidation). One wrapping flex row: identity
   grows to fill the left; the AC/HP/XP stat cards and the action cluster follow to
   its right on the SAME band (the former separate actions row + divider are gone).
   flex-wrap:wrap lets the stat cards and actions drop onto further lines gracefully
   as width shrinks — no divider bar is reintroduced. The action row keeps its own
   priority-plus "..." collapse as the narrow-width firewall (see wireDashOverflow),
   so buttons never force page-level horizontal overflow. */
.char-dash__topbar-main {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  flex-wrap: wrap;
}

.char-dash__wordmark {
  font-family: var(--font-heading);
  font-size: 1.7rem;
  line-height: 1;
  color: var(--color-heading);
  white-space: nowrap;
}

/* Shield glyph + name + chevron + subtitle. Grows to absorb the topbar's slack
   so the right-hand cluster (stat cards + actions + dice) stays together.
   min-width FLOOR (not 0): with the topbar now flex-wrap:nowrap, a 0-floor
   identity would silently collapse to portrait-width and the topbar would never
   report overflow — so wireDashOverflow would never collapse the action buttons.
   The floor keeps room for the portrait + an ellipsized name, which forces the
   topbar to genuinely overflow when identity + cards + actions don't fit; that
   overflow is what drives the action-button collapse. The inner
   .char-dash__identity-text keeps min-width:0 so the NAME still ellipsizes within
   this floor as the true last resort. (Measure-topbar revision 2026-06-07.) */
.char-dash__identity {
  display: flex;
  align-items: center;
  /* #4 (2026-06-28) — bump the portrait->text gap sm->md so the name/meta column
     has air against the enlarged 6rem hero medallion instead of crowding it. */
  gap: var(--space-md);
  flex: 1 1 16rem;
  min-width: 10rem;
}

/* Character portrait — a framed circular cameo of authentic ShadowDark ink
   art (class-mapped). The source art sits on a light ground, so the dark frame
   + ring reads as a classic portrait medallion against the OSR theme. Falls
   back to a centered class glyph when no portrait is mapped. */
.char-dash__identity-portrait {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Hero portrait — enlarged 3rem->6rem (#3, 2026-06-27, Ken: "much larger") so
     the medallion reads as a prominent banner image. The topbar is flex-wrap:nowrap
     with an ellipsizing name, so the larger fixed-size portrait stays put while the
     name/meta absorb the slack. */
  width: 6rem;
  height: 6rem;
  flex: none;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  overflow: hidden;
}

.char-dash__portrait-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Pull the framing toward the head/shoulders of the source art. */
  object-position: 50% 18%;
  display: block;
}

.char-dash__portrait-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
}

.char-dash__portrait-fallback svg {
  /* Scaled up with the 6rem hero frame (#3) — keeps the glyph fallback centered
     and proportionate so it doesn't look lost in the larger medallion. */
  width: 2.75rem;
  height: 2.75rem;
}

/* Portrait-as-upload-button (#3b). The medallion becomes a button: a camera
   overlay fades in on hover/focus to advertise "click to upload an image". The
   button resets default chrome so it keeps the circular framed look. */
button.char-dash__identity-portrait--btn {
  position: relative;
  padding: 0;
  cursor: pointer;
  font: inherit;
  /* Re-assert the medallion geometry the base rule sets, since a <button> ships
     its own box defaults. */
  border: 1px solid var(--color-border);
  border-radius: 50%;
}
button.char-dash__identity-portrait--btn:hover {
  border-color: var(--color-accent);
}
button.char-dash__identity-portrait--btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.char-dash__portrait-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: color-mix(in srgb, var(--color-bg) 64%, transparent);
  color: var(--color-text);
  opacity: 0;
  transition: opacity var(--transition-fast);
}
.char-dash__portrait-overlay svg {
  width: 1.25rem;
  height: 1.25rem;
}
button.char-dash__identity-portrait--btn:hover .char-dash__portrait-overlay,
button.char-dash__identity-portrait--btn:focus-visible .char-dash__portrait-overlay {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .char-dash__portrait-overlay { transition: none; }
}

/* Raster gear/weapon glyphs reuse the same box dimensions as their SVG
   siblings (attack-name-icon / inv-icon / inv-stat svg). object-fit keeps the
   line art crisp and centered; brightness lift compensates for the dark
   theme so the white-on-transparent strokes stay legible. */
.char-dash__glyph-img {
  object-fit: contain;
  filter: brightness(1.08);
}

.char-dash__identity-text {
  display: flex;
  flex-direction: column;
  /* #4 (2026-06-28) — vertical rhythm between the name row and the meta line(s).
     A small consistent gap keeps the lines from running together; the per-line
     ellipsis rules keep each on one row, so this only opens up the stack. */
  gap: 0.2rem;
  min-width: 0;
}

.char-dash__identity-namerow {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.char-dash__identity-name {
  font-family: var(--font-heading);
  font-size: 1.4rem;
  /* #4 (2026-06-28) — open the name's line-height 1.15->1.25 so the blackletter
     ascenders/descenders aren't clipped and the name reads as the prominent line,
     not jammed against the meta below it. */
  line-height: 1.25;
  color: var(--color-heading);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.char-dash__identity-chevron {
  width: 1rem;
  height: 1rem;
  color: var(--color-text-muted);
}

/* Single-line + ellipsis so the identity column has a FIXED height. If these
   wrapped, a squeezed identity would relieve pressure vertically (growing the
   topbar taller) instead of horizontally — and the horizontal fit test would
   never see overflow, so the action buttons would never collapse. Keeping every
   identity line on one row converts the squeeze into real topbar overflow, which
   drives the collapse. (Measure-topbar revision 2026-06-07.) */
.char-dash__identity-meta {
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Secondary identity line — shared skin. The ADVENTURER dashboard no longer uses
   it for Title / Deity / Background / Languages (that ran off-screen on phones and
   forced page-level horizontal overflow — those fields moved into the wrapping
   "Details" body card, see .char-dash__details). The class is RETAINED here because
   the commoner / buildings / item dashboards still use it for their read-only badge
   and item-tag lines. */
.char-dash__identity-meta--sub {
  font-size: 0.72rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Identity edit (batch-8 #6) — a small pencil beside the name that reveals an
   inline Name + Gender editor (mirrors the commoner identity edit). The button
   is a quiet icon affordance that lights up on hover/focus; it sits on the same
   row as the name and must not grow the topbar height. */
.char-dash__identity-edit-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--dash-radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}

.char-dash__identity-edit-btn svg {
  width: 1rem;
  height: 1rem;
}

.char-dash__identity-edit-btn:hover,
.char-dash__identity-edit-btn:focus-visible {
  color: var(--color-accent);
  border-color: var(--color-border);
  background-color: var(--color-surface-hover);
}

/* The inline editor itself — a small stacked form below the meta lines. Hidden
   until toggled (the `hidden` attribute), so it adds no height when closed. */
.char-dash__identity-edit {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-surface);
}

.char-dash__identity-edit[hidden] {
  display: none;
}

.char-dash__identity-edit-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.char-dash__identity-edit-label {
  font-family: var(--dash-sans);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__identity-edit-input,
.char-dash__identity-edit-select {
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
}

.char-dash__identity-edit-input {
  min-width: 12rem;
  max-width: 100%;
}

.char-dash__identity-edit-input:focus,
.char-dash__identity-edit-select:focus {
  outline: none;
  border-color: var(--color-accent);
}

.char-dash__identity-edit-btns {
  display: flex;
  gap: var(--space-xs);
}

/* ============================================================
   DETAILS CARD — Title / Deity / Background / Languages
   Relocated 2026-07-01 from the identity header (where it was one long
   non-wrapping line that overflowed narrow viewports) into a labelled body card
   in the Overview left column. Fields are label+value rows; Languages wrap as
   chips so a long polyglot list flows onto multiple lines instead of running
   off-screen. min-width:0 everywhere so nothing forces the page wider. */
.char-dash__details {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-width: 0;
}

.char-dash__detail-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem var(--space-sm);
  min-width: 0;
}

.char-dash__detail-label {
  flex: 0 0 auto;
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__detail-value {
  min-width: 0;
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  color: var(--color-text);
  /* Long values (e.g. a wordy title) wrap rather than clip. */
  overflow-wrap: anywhere;
}

/* Languages: the label sits on its own line above a wrapping chip set so the
   list never forces horizontal overflow, however many languages a character has. */
.char-dash__detail-row--langs {
  flex-direction: column;
  gap: 0.35rem;
}

.char-dash__detail-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  min-width: 0;
}

.char-dash__detail-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.5rem;
  font-family: var(--dash-sans);
  font-size: 0.78rem;
  color: var(--color-text);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  overflow-wrap: anywhere;
}

/* Stat cards cluster — AC / HP / XP, each icon + stacked text. Torch was
   removed 2026-06-07, so three cards sit comfortably on one line; keep them on a
   single row (nowrap) so they never split into a ragged 2 + 1, and let the whole
   block be the natural break point against the actions cluster. */
.char-dash__stat-cards {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: nowrap;
  flex: 0 0 auto;
  align-items: stretch;
}

/* Section-level "i" leading the vitals / building-attributes stat cards (info_
   tooltips_app_wide follow-up, 2026-06-28). Centered so it doesn't stretch with
   the cards. */
.char-dash__stat-cards-info {
  display: inline-flex;
  align-items: center;
  align-self: center;
}

.char-dash__stat-card {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 6.5rem;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
}

.char-dash__stat-card-icon {
  width: 1.4rem;
  height: 1.4rem;
  color: var(--color-text-muted);
}

.char-dash__stat-card-body {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}

.char-dash__stat-card-label {
  font-family: var(--dash-sans);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__stat-card-value {
  font-family: var(--dash-sans);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--color-text);
}

.char-dash__stat-card-sub {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  color: var(--color-text-muted);
}

/* Action icon cluster — shared base (also used by the commoner topbar's actions). */
.char-dash__topbar-actions {
  display: flex;
  align-items: center;
}

/* ADVENTURER actions cluster (2026-07-02 consolidation): the action buttons now
   share the header band with the stat cards instead of sitting on their own
   full-width row below a divider. Content-sized (flex 0 0 auto) and non-shrinking,
   so identity (the only growing item) absorbs the slack and keeps the stat cards +
   actions grouped tidily to the right. When the band can't hold identity + cards +
   actions, this cluster wraps whole onto the next line — a clean flex-wrap, no
   divider reintroduced. NO border/padding divider. Scoped via the -main wrapper so
   the shared commoner actions are untouched.
   Narrow-width firewall: the mobile rule below (<=760px) forces width:100%, giving
   the action row's priority-plus "..." collapse (wireDashOverflow) a full-width band
   to measure against so the buttons never force page-level horizontal overflow. At
   wider widths the six buttons fit within their content width, so no collapse is
   needed and none fires. */
.char-dash__topbar:has(.char-dash__topbar-main) .char-dash__topbar-actions {
  flex: 0 0 auto;
}

.char-dash__icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}

.char-dash__icon-btn svg {
  width: 1.15rem;
  height: 1.15rem;
}

/* Foundry export uses the FoundryVTT raster mark (fvtt-d20.png, 512×512) in place
   of a vector glyph. Match the 1.15rem svg footprint so the icon row stays even;
   object-fit: contain keeps the logo crisp inside the fixed icon-button box. */
.char-dash__icon-btn .char-dash__foundry-icon {
  width: 1.15rem;
  height: 1.15rem;
  object-fit: contain;
}

.char-dash__icon-btn:hover {
  color: var(--color-text);
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}

/* Action Log toggle (fix G-LOG-3): give the log control a visible text label so
   a first-time user finds it among the icon-only header actions. Auto-width to
   fit the label; the icon + word read together as "Log". */
.char-dash__log-btn {
  width: auto;
  gap: 0.4rem;
  padding-inline: 0.6rem;
}
.char-dash__log-btn-text {
  font-family: var(--dash-sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* AC stat card carries a hover/focus tooltip (fix G-AC-2) via title. Keep it
   keyboard-reachable (tabindex=0) with a visible focus ring and a help cursor
   so the affordance reads as "more info here". */
.char-dash__stat-card--info {
  cursor: help;
}
.char-dash__stat-card--info:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* ============================================================
   TAB ROW — full-width thin underline, icons + labels
   ============================================================ */

/* Tab row wrapper — tabs on the left, quick-dice pool on the right of the SAME
   horizontal line (relocated 2026-06-07 from a standalone row beneath the topbar
   to reclaim that header row). The shared underline lives here so it runs the
   full width beneath both the tablist and the dice. align-items:flex-end keeps
   the dice baseline-aligned with the tabs' underline. When the dice can't fit at
   narrow widths the wrap lets them drop to a second line below the tabs (handled
   in the media queries) rather than forcing the tablist to wrap raggedly. */
.char-dash__tabrow {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--color-border);
}

/* The tablist no longer carries the underline (the row wrapper does) so the line
   spans tabs + dice. flex:1 1 auto lets it take the row's lead width and keeps the
   dice pushed right via the wrapper's space-between. */
.char-dash__tabs {
  display: flex;
  gap: var(--space-lg);
  padding: 0 var(--space-sm);
  flex: 1 1 auto;
}

.char-dash__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-sm) 0;
  background-color: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  cursor: pointer;
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-text-muted);
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.char-dash__tab svg {
  width: 1.05rem;
  height: 1.05rem;
}

.char-dash__tab:hover {
  color: var(--color-text);
}

.char-dash__tab--active {
  color: var(--color-text);
  border-bottom-color: var(--color-accent);
  font-weight: 600;
}

.char-dash__tab[disabled] {
  cursor: default;
  opacity: 0.4;
}

/* ============================================================
   3-COLUMN OVERVIEW GRID
   ============================================================ */

/* Left column now holds only Ability Scores, so it is narrower; the center
   (Attacks / Spells / Talents / Effects) carries the most content and gets the
   widest track. */
.char-dash__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.35fr) minmax(0, 1.1fr);
  gap: var(--space-lg);
  align-items: start;
}

@media (max-width: 1180px) {
  .char-dash__grid {
    grid-template-columns: 1fr 1fr;
  }
  .char-dash__col--right {
    grid-column: 1 / -1;
  }
}

@media (max-width: 760px) {
  .char-dash__grid {
    grid-template-columns: 1fr;
  }
  .char-dash__col--right {
    grid-column: auto;
  }
  /* SHARED topbar (commoner) keeps its phone wrap: identity row, then stat cards,
     then actions each on their own full-width line. */
  .char-dash__topbar {
    align-items: flex-start;
    flex-wrap: wrap;
  }
  /* ADVENTURER topbar is already a column; on phones let its identity BAND wrap so
     the portrait+identity take the first line and the stat-cards drop beneath, then
     the actions band sits below as usual. */
  .char-dash__topbar-main {
    align-items: flex-start;
    flex-wrap: wrap;
  }
  /* Below 760 the identity line takes the full first row; the stat cards become
     their own full-width row and MAY wrap (graceful 2 + 1 here is acceptable on
     a phone, unlike at desktop widths). min-width:0 drops the desktop 10rem floor
     so the identity can never force the page wider than the viewport; the name
     still ellipsizes within it. */
  .char-dash__topbar-main {
    min-width: 0;
  }
  .char-dash__identity {
    flex: 1 1 100%;
    min-width: 0;
  }
  /* The stat cards share the full-width row and shrink to fit (min-width:0), so
     AC/HP/XP never push past the viewport; they wrap to a second line only if the
     three genuinely can't fit. Contained, not page-widening. */
  .char-dash__stat-cards {
    flex-wrap: wrap;
    width: 100%;
    min-width: 0;
    max-width: 100%;
  }
  .char-dash__stat-card {
    flex: 1 1 6rem;
    min-width: 0;
  }
  /* Comfortable ~44px tap targets for the header action icons on touch screens. */
  .char-dash__icon-btn {
    width: 2.75rem;
    height: 2.75rem;
  }
}

.char-dash__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  min-width: 0;
}

/* ============================================================
   FOCUSED TAB VIEWS (batch C) — Combat / Spells / Inventory / Notes
   Each is a roomier single-topic surface composed from the same panel
   renderers as Overview. Two-column focus views collapse to one column on
   narrow viewports; single-panel views are simply max-width-capped for
   readability.
   ============================================================ */

/* Combat & Inventory: main content + a narrower rail. */
.char-dash__focus--combat,
.char-dash__focus--inventory {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: var(--space-lg);
  align-items: start;
}

.char-dash__focus-main,
.char-dash__focus-rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  min-width: 0;
}

/* Spells & Notes: a single roomy column, capped so lines stay readable. */
.char-dash__focus--spells,
.char-dash__focus--notes {
  display: block;
}
.char-dash__focus--spells .char-dash__panel,
.char-dash__focus--notes .char-dash__panel {
  max-width: 920px;
}

/* Image: a single full-width column so the portrait stage can fill the body. */
.char-dash__focus--image {
  display: block;
}

@media (max-width: 900px) {
  .char-dash__focus--combat,
  .char-dash__focus--inventory {
    grid-template-columns: 1fr;
  }
}

/* Combat — Defense panel (AC / HP / Rest cluster). */
.char-dash__defense {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.char-dash__defense-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius);
  background-color: var(--color-bg, transparent);
  text-align: left;
}

button.char-dash__defense-stat--action {
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
button.char-dash__defense-stat--action:hover {
  border-color: var(--color-accent);
}
button.char-dash__defense-stat--action:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.char-dash__defense-label {
  font-family: var(--dash-sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__defense-value {
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--color-text);
}

.char-dash__defense-hint {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

.char-dash__defense-rest {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
}
.char-dash__defense-rest svg {
  width: 1.05rem;
  height: 1.05rem;
}

/* Spells focused view — tier grouping. */
.char-dash__spell-tier + .char-dash__spell-tier {
  margin-top: var(--space-lg);
}

.char-dash__spell-tier-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--color-border);
  font-family: var(--dash-sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-text);
}

.char-dash__spell-tier-dc {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

/* Notes focused view — large editor. */
.char-dash__notes-textarea {
  width: 100%;
  min-height: 320px;
  resize: vertical;
  box-sizing: border-box;
  padding: var(--space-md);
  background-color: var(--color-bg, var(--color-surface));
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius);
  color: var(--color-text);
  font-family: var(--dash-sans);
  font-size: 0.95rem;
  line-height: 1.55;
}
.char-dash__notes-textarea:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
  border-color: var(--color-accent);
}

/* Image focused view — the portrait shown LARGE/centered. Mirrors the
   item-dashboard Image-tab pattern: the panel is a flex column whose stage
   (flex:1, centered) fills the body and holds the portrait at object-fit:contain
   so the whole image is visible (never cropped). A muted ~45% glyph is the floor
   for portrait-less adventurers. Read-only-safe (no controls live here). */
.char-dash__panel--image {
  display: flex;
  flex-direction: column;
}
.char-dash__image-stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: min(60vh, 32rem);
}
.char-dash__image-well {
  width: 100%;
  height: 100%;
  max-width: min(60vh, 32rem);
  display: flex;
  align-items: center;
  justify-content: center;
}
.char-dash__image-img {
  max-width: 100%;
  max-height: min(60vh, 32rem);
  object-fit: contain;
  border-radius: var(--dash-radius);
}
/* Portrait-less floor — the class glyph, muted and scaled to ~45% of the stage.
   Styled on the glyph span itself (not the well modifier) so it also applies when
   an <img> 404s and the resolver swaps the glyph in as its deepest fallback. */
.char-dash__image-glyph {
  display: inline-flex;
  color: var(--color-text-muted);
  opacity: 0.45;
}
.char-dash__image-glyph svg {
  width: clamp(7rem, 45%, 16rem);
  height: auto;
}

/* ============================================================
   PANEL — rounded card used throughout
   ============================================================ */

.char-dash__panel {
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius);
  padding: var(--space-md);
}

.char-dash__panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.char-dash__panel-head-left {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}

.char-dash__panel-title {
  font-family: var(--dash-sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-text);
}

.char-dash__panel-icon {
  width: 1rem;
  height: 1rem;
  color: var(--color-text-muted);
}

.char-dash__panel-note {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  color: var(--color-text-muted);
}

.char-dash__panel-tools {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

/* Small square tool button (search/filter/plus on panel titles) */
.char-dash__tool {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.85rem;
  height: 1.85rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.char-dash__tool svg {
  width: 0.95rem;
  height: 0.95rem;
}

.char-dash__tool:hover {
  color: var(--color-text);
  border-color: var(--color-accent);
}

/* Empty / placeholder state (dashed) */
.char-dash__empty {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md);
  border: 1px dashed var(--color-border);
  border-radius: var(--dash-radius-sm);
  color: var(--color-text-muted);
}

.char-dash__empty svg {
  width: 1.25rem;
  height: 1.25rem;
  color: var(--color-text-muted);
}

.char-dash__empty-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.char-dash__empty-title {
  font-size: 0.9rem;
  color: var(--color-text);
}

.char-dash__empty-help {
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

/* ============================================================
   ABILITY SCORES — dense rows: id | score | mod
   ============================================================ */

.char-dash__abilities {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.char-dash__ability {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.45rem var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
}

.char-dash__ability-id {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}

.char-dash__ability-abbr {
  font-family: var(--dash-sans);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-text);
}

.char-dash__ability-full {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  color: var(--color-text-muted);
}

.char-dash__ability-score {
  font-family: var(--dash-sans);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--color-text);
  min-width: 1.6rem;
  text-align: center;
}

.char-dash__ability-mod {
  font-family: var(--dash-sans);
  font-size: 0.95rem;
  font-weight: 600;
  min-width: 2.1rem;
  text-align: center;
  color: var(--color-text-muted);
}

/* ============================================================
   ATTACKS TABLE — blank | To Hit | Attack btn | Damage | Notes
   ============================================================ */

.char-dash__attacks {
  display: flex;
  flex-direction: column;
}

.char-dash__attack-head,
.char-dash__attack-row {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) 3.2rem 4.6rem minmax(0, 1fr) minmax(0, 1.4fr);
  align-items: center;
  gap: var(--space-sm);
  padding: 0.55rem var(--space-xs);
}

.char-dash__attack-head {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
  padding-bottom: var(--space-xs);
}

.char-dash__attack-row {
  border-bottom: 1px solid var(--color-border);
}

.char-dash__attack-row:last-child {
  border-bottom: none;
}

.char-dash__attack-name {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}

.char-dash__attack-name-icon {
  width: 1.1rem;
  height: 1.1rem;
  color: var(--color-text-muted);
}

.char-dash__attack-name-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}

.char-dash__attack-name-main {
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.char-dash__attack-name-sub {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  color: var(--color-text-muted);
}

.char-dash__attack-cell {
  font-family: var(--dash-sans);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--color-text);
}

.char-dash__attack-cell--notes {
  font-weight: 400;
  color: var(--color-text-muted);
  font-size: 0.82rem;
}

/* Outlined inline button used in attacks + dice action tiles */
.char-dash__btn-outline {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast),
    background-color var(--transition-fast);
}

.char-dash__btn-outline:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
  background-color: var(--color-surface-hover);
}

/* ============================================================
   SPELLS — one row per known spell: icon + name/tier + cast check
   ============================================================ */

.char-dash__spells {
  display: flex;
  flex-direction: column;
}

.char-dash__spell {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--color-border);
}

.char-dash__spell:last-child {
  border-bottom: none;
}

.char-dash__spell-icon {
  width: 1.1rem;
  height: 1.1rem;
  color: var(--color-text-muted);
}

.char-dash__spell-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}

.char-dash__spell-name {
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  color: var(--color-text);
}

.char-dash__spell-sub {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  color: var(--color-text-muted);
}

.char-dash__spell-cast {
  margin-left: auto;
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* ============================================================
   TALENTS & FEATURES — stacked name + description
   ============================================================ */

.char-dash__feature-list {
  display: flex;
  flex-direction: column;
}

.char-dash__feature {
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--color-border);
}

.char-dash__feature:first-child {
  padding-top: 0;
}

.char-dash__feature:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.char-dash__feature-name {
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-text);
  margin-bottom: 2px;
}

.char-dash__feature-desc {
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}

/* ============================================================
   ACTIVE EFFECTS — name + sub + tag + timer
   ============================================================ */

.char-dash__effect {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
}

.char-dash__effect-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  min-width: 0;
}

.char-dash__effect-name {
  font-family: var(--dash-sans);
  font-size: 0.88rem;
  color: var(--color-text);
}

.char-dash__effect-sub {
  font-family: var(--dash-sans);
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

.char-dash__tag {
  margin-left: auto;
  font-family: var(--dash-sans);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding: 2px 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
}

.char-dash__effect-timer {
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-text);
}

/* ============================================================
   INVENTORY
   ============================================================ */

.char-dash__inv-searchwrap {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
}

.char-dash__inv-searchwrap svg {
  position: absolute;
  left: 0.6rem;
  width: 1rem;
  height: 1rem;
  color: var(--color-text-muted);
  pointer-events: none;
}

.char-dash__inv-search {
  width: 100%;
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  color: var(--color-text);
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  padding: 0.4rem 0.7rem 0.4rem 2rem;
  transition: border-color var(--transition-fast);
}

.char-dash__inv-search::placeholder {
  color: var(--color-text-muted);
}

.char-dash__inv-search:focus {
  border-color: var(--color-accent);
  outline: none;
}

/* Gear-slots + coins summary strip (2-up). */
.char-dash__inv-summary {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.char-dash__inv-stat {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
}

.char-dash__inv-stat svg,
.char-dash__inv-stat .char-dash__glyph-img {
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  color: var(--color-text-muted);
}

/* Per-item inventory glyph (authentic gear/weapon line art). */
.char-dash__inv-icon {
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
}

.char-dash__inv-stat-body {
  display: flex;
  flex-direction: column;
  line-height: 1.1;
  min-width: 0;
}

.char-dash__inv-stat-label {
  font-family: var(--dash-sans);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__inv-stat-value {
  font-family: var(--dash-sans);
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--color-text);
}

/* Over-capacity gear slots (Ken 2026-07-02): when carried > max, the "N / M"
   tally turns danger-red so encumbrance reads at a glance. Toggled in
   renderDashInventory (initial) and syncInvSummary (in-place) on used > limit. */
.char-dash__gearslots--over {
  color: var(--color-danger);
}

/* Coins readout: per-denomination chip (glyph image + amount), inline. The
   coins cell drops the leading single glyph so each chip carries its own. */
.char-dash__inv-stat--coins {
  align-items: center;
}

.char-dash__coins {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

.char-dash__coin {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}

.char-dash__inv-stat .char-dash__coin-img {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
}

.char-dash__coin-amt {
  font-family: var(--dash-sans);
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--color-text);
}

.char-dash__inv-list {
  display: flex;
  flex-direction: column;
}

.char-dash__inv-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--color-border);
}

.char-dash__inv-row:last-child {
  border-bottom: none;
}

.char-dash__inv-check {
  width: 1rem;
  height: 1rem;
  border: 1px solid var(--color-border);
  border-radius: 3px;
  flex: none;
}

.char-dash__inv-name {
  font-family: var(--dash-sans);
  font-size: 0.88rem;
  color: var(--color-text);
}

.char-dash__inv-count {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  font-weight: 600;
  color: var(--color-text-muted);
  padding: 1px 0.4rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
}

.char-dash__inv-weight {
  margin-left: auto;
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

.char-dash__inv-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-border);
  font-family: var(--dash-sans);
  font-size: 0.82rem;
}

.char-dash__inv-foot-link {
  color: var(--color-accent);
  cursor: pointer;
}

.char-dash__inv-foot-total {
  font-weight: 600;
  color: var(--color-text);
}

/* ============================================================
   STASHED GROUP — a separate collapsible <details> beneath the carried list
   for items the adventurer has set aside (base/cache). Its own count; excluded
   from the carried Gear Slots tally. Rows reuse .char-dash__inv-row, so they
   stay identical (identity, description, image, slot cost, delete) to carried.
   ============================================================ */
.char-dash__stash {
  margin-top: var(--space-sm);
  border-top: 1px solid var(--color-border);
}

/* Summary row is the toggle: chevron + "Stashed" + count. Full-width tap target. */
.char-dash__stash-head {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.6rem 0;
  min-height: 44px;
  cursor: pointer;
  list-style: none;
  font-family: var(--dash-sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  transition: color var(--transition-fast);
}
/* Remove the native disclosure triangle (we draw our own chevron). */
.char-dash__stash-head::-webkit-details-marker { display: none; }
.char-dash__stash-head:hover { color: var(--color-text); }
.char-dash__stash:not([open]) .char-dash__stash-head { color: var(--color-text-muted); }
.char-dash__stash-head:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: var(--dash-radius-sm);
}

.char-dash__stash-chevron {
  width: 0.9rem;
  height: 0.9rem;
  flex: none;
  color: currentColor;
  transition: transform var(--transition-fast);
}
/* Chevron points right when collapsed, down when open. */
.char-dash__stash:not([open]) .char-dash__stash-chevron {
  transform: rotate(-90deg);
}

.char-dash__stash-title { color: inherit; }

.char-dash__stash-count {
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--color-text-muted);
  padding: 1px 0.45rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
}

/* Quiet "all items stashed" note in the carried region. */
.char-dash__empty-sub {
  display: block;
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

/* ============================================================
   EQUIPPED GEAR — 2-column slot grid: icon + label + value
   ============================================================ */

/* Three real loadout slots (Armor / Main Hand / Off-Hand) stack as one column
   so each reads as a full-width row rather than an unbalanced 2-up grid. */
.char-dash__slot-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
}

.char-dash__slot {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  min-width: 0;
}

.char-dash__slot svg {
  width: 1.1rem;
  height: 1.1rem;
  color: var(--color-text-muted);
}

.char-dash__slot-body {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  min-width: 0;
}

.char-dash__slot-label {
  font-family: var(--dash-sans);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__slot-value {
  font-family: var(--dash-sans);
  font-size: 0.84rem;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.char-dash__slot-value--empty {
  color: var(--color-text-muted);
  font-style: italic;
}

/* ============================================================
   QUICK ITEMS — three groups (Consumables / Abilities / Attacks & Spells) of
   one-tap tiles. Each tile shows a count/badge, a glyph, and a label. Tiles are
   responsive (auto-fill grid, no overflow at 1440/820/700) and re-render in
   place, so the click handlers reach them through the shared inventory / attacks
   / spells wirings plus the delegated per-day ability handler.
   ============================================================ */

.char-dash__quick-groups {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.char-dash__quick-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.char-dash__quick-grouplabel {
  font-family: var(--dash-sans);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.char-dash__quick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.4rem, 1fr));
  gap: var(--space-sm);
}

/* Base tile (shared by the legacy empty placeholder + the live action tiles). */
.char-dash__quick {
  position: relative;
  min-height: 4.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  padding: var(--space-sm) 0.35rem 0.45rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
}

.char-dash__quick svg,
.char-dash__quick-glyph {
  width: 1.5rem;
  height: 1.5rem;
  flex: 0 0 auto;
}

img.char-dash__quick-glyph {
  object-fit: contain;
  opacity: 0.92;
}

.char-dash__quick-label {
  max-width: 100%;
  font-family: var(--dash-sans);
  font-size: 0.64rem;
  line-height: 1.15;
  font-weight: 600;
  text-align: center;
  color: var(--color-text);
  /* Two-line clamp keeps every tile the same height without ragged overflow. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.char-dash__quick--empty {
  border-style: dashed;
}

/* Interactive action tiles. */
.char-dash__quick--tile {
  cursor: pointer;
  font: inherit;
  transition: border-color 0.12s ease, background-color 0.12s ease, color 0.12s ease;
}

.char-dash__quick--tile:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
  color: var(--color-text);
}

.char-dash__quick--tile:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.char-dash__quick--tile:active {
  transform: translateY(1px);
}

.char-dash__quick-count {
  position: absolute;
  top: 3px;
  right: 5px;
  font-family: var(--dash-sans);
  font-size: 0.6rem;
  font-weight: 700;
  color: var(--color-text-muted);
}

/* Per-day ability tile at 0 remaining — visibly inert, not tappable. */
.char-dash__quick--spent {
  cursor: default;
  opacity: 0.5;
}

.char-dash__quick--spent:hover {
  border-color: var(--color-border);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
}

/* ---- Quick Items: show/hide EDIT mode --------------------------------------
   The panel's pencil tool toggles a per-character checklist of every available
   quick-action, grouped by type. Checked = shown as a tile; unchecking hides it
   (persisted to char.quickItems.hidden). The active state on the pencil tool
   reuses the accent treatment so it reads as "editing this panel". */
.char-dash__tool--active {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}

.char-dash__quick-editlist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.char-dash__quick-editrow {
  display: flex;
}

/* Full-row click target — the label wraps the checkbox + name so a tap anywhere
   on the row toggles visibility (larger target, clearer affordance). */
.char-dash__quick-editlabel {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  min-width: 0;
  padding: 0.4rem var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}

.char-dash__quick-editlabel:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}

.char-dash__quick-editcheck {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  margin: 0;
  cursor: pointer;
  accent-color: var(--color-accent);
}

.char-dash__quick-editcheck:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.char-dash__quick-editname {
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  color: var(--color-text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* An unchecked (hidden) action reads dimmer so the shown set stands out. */
.char-dash__quick-editlabel:has(.char-dash__quick-editcheck:not(:checked)) .char-dash__quick-editname {
  color: var(--color-text-muted);
}

/* ============================================================
   NOTES
   ============================================================ */

.char-dash__notes {
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--color-text);
  white-space: pre-wrap;
}

.char-dash__notes--empty {
  color: var(--color-text-muted);
  font-style: italic;
}

/* ============================================================
   LUCK TOKEN — single boolean rendered as a coin-style toggle
   ============================================================ */

/* Luck token control — relocated 2026-06-02 from a draggable grid card into the
   dashboard's upper bar (char-dash__topbar-actions) as a literal checkbox. The
   former coin-style switch and its .char-dash__luck* rules were retired with the
   card. ShadowDark luck is max-1: checked = holding the one token. */
.char-dash__luck-check {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 2.4rem;
  padding: 0 0.7rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}

.char-dash__luck-check:hover {
  border-color: var(--color-luck);
  color: var(--color-text);
}

.char-dash__luck-check input[type="checkbox"] {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  margin: 0;
  cursor: pointer;
  accent-color: var(--color-luck);
}

.char-dash__luck-check-text {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--dash-sans);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.char-dash__luck-check-icon {
  width: 1rem;
  height: 1rem;
}

/* When the token is held, tint the whole control gold so it reads as "active". */
.char-dash__luck-check:has(input:checked) {
  border-color: var(--color-luck);
  color: var(--color-luck);
  background-color: color-mix(in srgb, var(--color-luck) 14%, var(--color-bg));
}

.char-dash__luck-check input:focus-visible {
  outline: 2px solid var(--color-luck);
  outline-offset: 2px;
}

/* ============================================================
   LAYOUT EDIT MODE — Customize button, toolbar, drag affordances
   ============================================================ */

/* Customize button reuses the topbar icon-btn chrome; pressed state tints it
   so it's clearly "active" while editing. */
.char-dash__icon-btn[aria-pressed="true"] {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}

/* Edit-mode toolbar sits between the tab row and the grid. Hidden until the
   "Customize Layout" button toggles it — the [hidden] guard must win over the
   author display rule, so it's declared explicitly. */
.char-dash__editbar[hidden] {
  display: none;
}
.char-dash__editbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-surface);
  border: 1px dashed var(--color-accent);
  border-radius: var(--dash-radius-sm);
}

.char-dash__editbar-cue {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--space-sm);
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  color: var(--color-text-muted);
  max-width: 46ch;
}

.char-dash__editbar-cue-line {
  display: block;
  font-weight: 700;
  color: var(--color-text);
}

/* One-line clarification of the two persistence layers + global scope (US-CD-03/04). */
.char-dash__editbar-help {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.74rem;
  line-height: 1.4;
  color: var(--color-text-muted);
}

.char-dash__editbar-cue-icon {
  flex: none;
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.1rem;
  color: var(--color-accent);
}

.char-dash__editbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

/* Primary variant of the existing outline button — fills with the accent for
   the affirmative Save action. */
.char-dash__btn-outline--primary {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.char-dash__btn-outline--primary:hover {
  background-color: var(--color-accent);
  color: var(--color-bg);
}

/* While editing: every draggable card gets a dashed accent outline + grab
   cursor, and a visible drag handle in the corner. */
.char-dash--editing .char-dash__panel[data-card-id] {
  outline: 1px dashed var(--color-accent);
  outline-offset: 2px;
  cursor: grab;
  position: relative;
}

.char-dash--editing .char-dash__panel[data-card-id]:active {
  cursor: grabbing;
}

.char-dash__drag-handle {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--color-accent);
  pointer-events: none; /* the whole card is the drag target, not just the dots */
}

.char-dash__drag-handle svg {
  width: 1rem;
  height: 1rem;
  fill: currentColor;
  stroke: none;
}

/* The card being dragged dims; the region under the pointer highlights. */
.char-dash__panel--dragging {
  opacity: 0.5;
}

.char-dash__col--dragover {
  outline: 1px solid var(--color-accent);
  outline-offset: 4px;
  border-radius: var(--dash-radius);
}

/* Edit mode is for ARRANGING, not playing — suppress in-card interaction so a
   drag never fires a control. We block pointer events on everything inside a
   card except the card surface itself, and hide the inventory search/tools. */
.char-dash--editing .char-dash__panel[data-card-id] > *:not(.char-dash__drag-handle) {
  pointer-events: none;
  user-select: none;
}

.char-dash--editing .char-dash__inv-search,
.char-dash--editing .char-dash__tool,
.char-dash--editing [data-dash-roll],
.char-dash--editing [data-dash-attack],
.char-dash--editing [data-dash-cast],
.char-dash--editing [data-dash-use],
.char-dash--editing [data-dash-swap],
.char-dash--editing [data-dash-check] {
  pointer-events: none;
}

/* The HP stat card and Rest button live in the TOPBAR, outside the draggable
   grid, so the panel-scoped pointer-events:none rule does not reach them.
   Suppress them explicitly while arranging. (The JS guards on dashIsEditing
   too — this is the visual + interaction belt-and-braces.) */
.char-dash--editing #char-dash-hp-card,
.char-dash--editing #char-dash-rest {
  pointer-events: none;
  opacity: 0.5;
}

/* ============================================================
   ACTIONABLE AFFORDANCES (charStep 22 full action surface)
   ============================================================ */

/* HP stat card promoted to a button — same look as the static cards plus a
   pointer affordance and a focusable hover/focus state. */
.char-dash__stat-card--action {
  cursor: pointer;
  font: inherit;
  text-align: left;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.char-dash__stat-card--action:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__stat-card--action:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Ability rows become check buttons — subtle hover + clear focus ring. */
.char-dash__ability--action {
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.char-dash__ability--action:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__ability--action:hover .char-dash__ability-mod {
  color: var(--color-accent);
}
.char-dash__ability--action:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Inventory "Use" button — small outlined action sitting before the slot count. */
.char-dash__inv-use {
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--dash-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast),
    background-color var(--transition-fast);
}
.char-dash__inv-use:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__inv-use:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Stash / Carry toggle — a quiet outlined text button matching the "Use"
   affordance, sitting between the slot count and the trash control. Muted by
   default (a secondary, non-destructive action) and picks up the accent on
   hover/focus. Text label ("Stash" / "Carry") makes the direction legible. */
.char-dash__inv-stash {
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--dash-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast),
    background-color var(--transition-fast);
}
.char-dash__inv-stash:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__inv-stash:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Magic-item INVOKE / RECOVER controls + state chips (wands & scrolls that hold
   a spell, RAW pg. 288-289). The INVOKE button reuses the inv-use base look but
   takes the accent hue to read as the row's primary action; the chips mirror the
   spell LOST/PENANCE chips for visual consistency. */
.char-dash__inv-invoke {
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--color-accent);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-accent);
  font-family: var(--dash-sans);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast),
    background-color var(--transition-fast);
}
.char-dash__inv-invoke:hover {
  background-color: var(--color-surface-hover);
}
.char-dash__inv-invoke:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.char-dash__inv-invoke:disabled {
  border-color: var(--color-border);
  color: var(--color-text-muted);
  opacity: 0.55;
  cursor: not-allowed;
}
.char-dash__inv-recover {
  margin-left: 0.4rem;
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--color-warning, #d8a23a);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-warning, #d8a23a);
  font-family: var(--dash-sans);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  cursor: pointer;
  white-space: nowrap;
}
.char-dash__inv-recover:hover { background-color: var(--color-surface-hover); }
.char-dash__inv-recover:focus-visible {
  outline: 2px solid var(--color-warning, #d8a23a);
  outline-offset: 2px;
}
.char-dash__inv-chip {
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  font-family: var(--dash-sans);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.char-dash__inv-chip--spent {
  color: var(--color-text-muted);
  border-color: var(--color-border);
  opacity: 0.75;
}
.char-dash__inv-chip--disabled {
  color: var(--color-warning, #d8a23a);
  border-color: var(--color-warning, #d8a23a);
}
.char-dash__inv-chip--broken {
  color: var(--color-danger);
  border-color: var(--color-danger);
}
.char-dash__inv-result {
  flex-basis: 100%;
  margin-top: 0.3rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.char-dash__inv-result-line {
  font-family: var(--dash-sans);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  margin: 0;
}
.char-dash__inv-result-line--success strong { color: var(--color-heal); }
.char-dash__inv-result-line--fail strong,
.char-dash__inv-result-line--fumble strong { color: var(--color-danger); }
.char-dash__inv-result-line--crit strong { color: rgba(240, 200, 100, 0.96); }
.char-dash__inv-result-line--mishap { color: var(--color-danger); }
.char-dash__inv-result-line--mishap-unavailable { font-style: italic; }

/* Equipped slot Swap button. The slot row gains a trailing action; keep the
   icon + label compact and right-aligned. */
.char-dash__slot {
  position: relative;
}
.char-dash__slot-swap {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: auto;
  padding: 0.25rem 0.55rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--dash-sans);
  font-size: 0.68rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast),
    background-color var(--transition-fast);
}
.char-dash__slot-swap svg {
  width: 0.9rem;
  height: 0.9rem;
}
.char-dash__slot-swap:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__slot-swap:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Spell cast button + lost chip + inline cast receipt. */
.char-dash__spell-cast-btn {
  margin-left: auto;
  padding: 0.3rem 0.7rem;
  font-size: 0.75rem;
}
.char-dash__spell-chip {
  margin-left: auto;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  font-family: var(--dash-sans);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.char-dash__spell-chip--lost {
  color: var(--color-danger);
  border-color: var(--color-danger);
}
.char-dash__spell--lost {
  opacity: 0.6;
}
/* Priest penance (deity-revoked) — distinct from the ordinary LOST chip. A
   warning hue, not danger, and the row stays fully legible since it carries the
   unlock control + consequence message the player must act on. */
.char-dash__spell-chip--penance {
  color: var(--color-warning, #d8a23a);
  border-color: var(--color-warning, #d8a23a);
}
.char-dash__spell--penance {
  opacity: 0.85;
}
.char-dash__spell-penance-btn {
  margin-left: 0.4rem;
  white-space: nowrap;
}
.char-dash__spell-result-line--penance {
  color: var(--color-warning, #d8a23a);
}
.char-dash__spell-result {
  flex-basis: 100%;
  margin-top: 0.3rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.char-dash__spell-result-line {
  font-family: var(--dash-sans);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  margin: 0;
}
.char-dash__spell-result-line--success strong { color: var(--color-heal); }
.char-dash__spell-result-line--fail strong,
.char-dash__spell-result-line--fumble strong { color: var(--color-danger); }
.char-dash__spell-result-line--crit strong { color: rgba(240, 200, 100, 0.96); }
.char-dash__spell-result-line--mishap { color: var(--color-danger); }
.char-dash__spell-result-line--mishap-unavailable { font-style: italic; }

/* ============================================================
   FULL SPELL CARDS (#4) — Spells tab. Each known spell shows its complete entry:
   name + tier/cast line, a Class/Range/Duration facts strip, and the full
   licensed description. On-brand OSR card: bordered surface, sparkle glyph,
   uppercase fact labels. Cast/Remove reuse the shared row affordances, and the
   inline cast receipt (.char-dash__spell-result) drops in unchanged.
   ============================================================ */
.char-dash__spellcards {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.char-dash__spellcard {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
}
.char-dash__spellcard.char-dash__spell--lost { opacity: 0.6; }
.char-dash__spellcard-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
}
.char-dash__spellcard-head .char-dash__spell-icon {
  margin-top: 0.15rem;
  width: 1.15rem;
  height: 1.15rem;
  color: var(--color-text-muted);
}
.char-dash__spellcard-title {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}
.char-dash__spellcard-name {
  font-family: var(--dash-sans);
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-text);
  line-height: 1.2;
}
.char-dash__spellcard-sub {
  font-family: var(--dash-sans);
  font-size: 0.7rem;
  color: var(--color-text-muted);
}
.char-dash__spellcard-actions {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  flex: none;
}
.char-dash__spellcard-actions .char-dash__spell-cast-btn { margin-left: 0; }
/* Facts strip — Class / Range / Duration as compact label+value pairs. */
.char-dash__spellcard-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  padding: var(--space-xs) 0;
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}
.char-dash__spellcard-fact {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.char-dash__spellcard-fact-k {
  font-family: var(--dash-sans);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.char-dash__spellcard-fact-v {
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  color: var(--color-text);
}
.char-dash__spellcard-body { min-height: 1rem; }
.char-dash__spellcard-desc {
  margin: 0;
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--color-text);
  white-space: pre-wrap;
}
/* Quiet "no entry" fallback when a spell can't be resolved. */
.char-dash__spellcard-noentry {
  margin: 0;
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  font-style: italic;
  color: var(--color-text-muted);
}
.char-dash__spell-granted {
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  font-family: var(--dash-sans);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* ============================================================
   NEW MODAL VARIANT — Stat check (adv / normal / disadv)
   Reuses .action-modal scrim + .action-modal__card chrome.
   ============================================================ */
.stat-check-modal__formula {
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0;
}
.stat-check-modal__modes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xs);
}
.stat-check-modal__mode {
  padding: 0.45rem 0.4rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--dash-sans);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast),
    background-color var(--transition-fast);
}
.stat-check-modal__mode:hover {
  border-color: var(--color-accent);
  color: var(--color-text);
}
.stat-check-modal__mode--active {
  border-color: var(--color-accent);
  color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.stat-check-modal__mode:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.stat-check-modal__dice {
  display: flex;
  justify-content: center;
  gap: var(--space-md);
  min-height: 3.2rem;
  align-items: center;
}
.stat-check-modal__dice:empty {
  min-height: 0;
}

/* Ken #5 (2026-06-11) — kill the HAZE behind the character-page dice roller.
   The shared .die-tile-3d carries a two-layer drop-shadow "glow" filter (the
   celebration halo for the attack/cast roll modal). In the stat-check modal that
   glow reads as a fog/backdrop behind each die. Strip the filter here so the dice
   render cleanly on the modal card; the polyhedral artwork, number overlay, the
   kept/dropped states, and the tumble animation are all unaffected (tumble sets
   its own transform; we only drop the resting/hover glow on this surface). */
.stat-check-modal__dice .die-tile-3d,
.stat-check-modal__dice .die-tile-3d:hover {
  filter: none;
}
/* The dropped die keeps its desaturation (so it still reads as "not counted"),
   just without the glow halo. Re-applied after the blanket filter:none above. */
.stat-check-modal__dice .die-tile-3d--dropped {
  filter: grayscale(0.6);
}
/* When a stat-check die is tumbling, the shared keyframes re-introduce the glow
   via the animation's filter steps — null it out so no haze flashes mid-roll. */
.stat-check-modal__dice .die-tile-3d.is-rolling {
  animation-name: dieTumbleNoGlow;
}
@keyframes dieTumbleNoGlow {
  0%   { transform: rotate(-8deg) scale(1); filter: none; }
  50%  { transform: rotate( 6deg) scale(1.05); filter: none; }
  100% { transform: rotate(-8deg) scale(1); filter: none; }
}

/* Kept / dropped die highlight for advantage / disadvantage.
   --glow is now inert on this surface (filter:none above); the kept die instead
   reads via the brighter currentColor it inherits and the dropped die via opacity. */
.die-tile-3d--kept {
  --glow: rgba(240, 200, 100, 0.7);
  --glow-wide: rgba(200, 140, 30, 0.35);
  color: rgba(255, 230, 170, 0.96);
}
.die-tile-3d--dropped {
  opacity: 0.4;
  filter: grayscale(0.6);
}
.stat-check-modal__result {
  text-align: center;
  min-height: 1.2rem;
}
.stat-check-modal__result-head {
  display: block;
  font-family: var(--dash-sans);
  font-size: 1rem;
  color: var(--color-text);
}
.stat-check-modal__result-detail {
  display: block;
  font-family: var(--dash-sans);
  font-size: 0.78rem;
  color: var(--color-text-muted);
}
.stat-check-modal__result--crit .stat-check-modal__result-head strong { color: rgba(240, 200, 100, 0.96); }
.stat-check-modal__result--fumble .stat-check-modal__result-head strong { color: var(--color-danger); }

/* ============================================================
   NEW MODAL VARIANT — HP damage / heal
   ============================================================ */
.hp-modal__card { min-width: min(340px, 100%); }
.hp-modal__current {
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin: 0;
}
.hp-modal__row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.hp-modal__label {
  font-family: var(--dash-sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.hp-modal__amount {
  flex: 1;
  padding: 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--dash-sans);
  font-size: 1rem;
}
.hp-modal__amount:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}
.hp-modal__actions { justify-content: flex-end; }

/* ============================================================
   NEW MODAL VARIANT — Equipped per-slot swap
   ============================================================ */
.swap-modal__card { min-width: min(380px, 100%); }
.swap-modal__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  max-height: 50vh;
  overflow-y: auto;
}
.swap-modal__option {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding: 0.6rem var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.swap-modal__option:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.swap-modal__option:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.swap-modal__option--current { border-color: var(--color-accent); }
.swap-modal__option-name { flex: 1; }
.swap-modal__option-meta {
  font-size: 0.75rem;
  color: var(--color-text-muted);
}
.swap-modal__option-tag {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
}
.swap-modal__option--empty { color: var(--color-text-muted); }
.swap-modal__locked,
.swap-modal__empty {
  font-family: var(--dash-sans);
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0;
  line-height: 1.4;
}

/* Respect reduced-motion: drop all transitions on the drag layer. */
@media (prefers-reduced-motion: reduce) {
  .char-dash__luck-check,
  .char-dash__panel--dragging,
  .char-dash__col--dragover {
    transition: none;
  }
}

/* ============================================================
   SEER — Omen + Destined (Phase 3d)
   ============================================================ */
.char-dash__seer {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.char-dash__seer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.char-dash__seer-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.char-dash__seer-name {
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-text);
}
.char-dash__seer-uses {
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.char-dash__seer .char-dash__btn-outline {
  gap: 6px;
  flex: 0 0 auto;
}
.char-dash__seer .char-dash__btn-outline svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
}
.char-dash__seer .char-dash__btn-outline:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.char-dash__seer-hint,
.char-dash__seer-destined {
  margin: 0;
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}
.char-dash__seer-destined {
  padding-top: var(--space-xs);
  border-top: 1px solid var(--color-border);
  font-style: italic;
}
.char-dash__seer-modal-note {
  margin: 0 0 var(--space-md);
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  color: var(--color-text-muted);
  line-height: 1.45;
}

/* ============================================================
   DESERT RIDER — mount stat-block + Mounted toggle (Phase 3d)
   ============================================================ */
.char-dash__mount {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.char-dash__mount-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.char-dash__mount-name {
  font-family: var(--font-heading);
  font-size: 1rem;
  color: var(--color-heading);
}
.char-dash__mount-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--dash-sans);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-text);
  cursor: pointer;
  flex: 0 0 auto;
}
.char-dash__mount-toggle input {
  accent-color: var(--color-accent);
  cursor: pointer;
}
.char-dash__mount-stats {
  display: flex;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.char-dash__mount-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  min-width: 3.5rem;
}
.char-dash__mount-stat-label {
  font-family: var(--dash-sans);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}
.char-dash__mount-stat-val {
  font-family: var(--dash-sans);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-text);
}
.char-dash__mount-stat-base {
  font-weight: 400;
  font-size: 0.75rem;
  color: var(--color-text-muted);
}
.char-dash__mount-bonus {
  margin: 0;
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}
.char-dash__mount-bonus.is-active {
  color: var(--color-accent);
}
.char-dash__mount-attacks {
  margin: 0;
  padding-left: 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.char-dash__mount-attack {
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  color: var(--color-text);
}
.char-dash__mount-desc {
  margin: 0;
  font-family: var(--dash-sans);
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}

/* ============================================================
   TOP BAR ACTIONS — stacked: button row + quick-dice block
   (Batch B #7b, resized #7c 2026-06-07). The actions column holds the icon
   button row on top and the dice block (dice row + result readout) beneath.
   Per Ken's #7c feedback the earlier "header must not grow" budget is dropped:
   the dice are now comfortably clickable (2.1rem squares) and the readout adds
   a line below them, so this column is now taller than the stat cards. That is
   intentional.
   ============================================================ */
.char-dash__topbar-actions {
  /* Holds only the action row (the dice pool moved to its own row beneath the
     topbar, 2026-06-07). On the SHARED skin (commoner) this is a content-sized,
     non-shrinking cluster on the topbar's single row. The ADVENTURER restructure
     (2026-06-28) overrides this to a full-width band via the :has() rule above. */
  align-items: center;
  flex-wrap: nowrap;
  flex: 0 0 auto;
}

/* ADVENTURER only: the action row stretches to fill the full-width band so its
   flex-end alignment pushes the buttons to the right edge; it stays nowrap (the
   overflow collapse handles tight widths) and the more-menu anchors beneath the
   "..." trigger. The commoner action row keeps its content-sized default. */
.char-dash__topbar:has(.char-dash__topbar-main) .char-dash__action-row {
  flex: 1 1 auto;
}

/* The action row never ragged-wraps: JS (wireDashOverflow) collapses the
   lowest-priority controls into the "..." popover before the row would run out
   of room. nowrap keeps it a single line; position:relative anchors the popover
   beneath the trigger. The dice pool is a separate sibling and still wraps. */
.char-dash__action-row {
  position: relative;
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--space-sm);
  justify-content: flex-end;
}

/* "..." trigger is hidden whenever nothing is collapsed. */
.char-dash__more-btn[hidden] {
  display: none;
}

/* Overflow popover — anchored under the "..." button, right-aligned so it never
   runs off the right edge of the viewport. Dark tokens; a thin elevated card. */
.char-dash__more-menu {
  position: absolute;
  top: calc(100% + var(--space-xs));
  right: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  min-width: 11rem;
  max-width: min(16rem, 90vw);
  padding: var(--space-xs);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
.char-dash__more-menu[hidden] {
  display: none;
}

/* Collapsed controls become full-width menu rows. They keep their own chrome
   (icon-btn / luck-check) but stretch to fill the popover and left-align so the
   menu reads as a tidy list. */
.char-dash__more-item.char-dash__more-item {
  width: 100%;
  justify-content: flex-start;
}
/* The log button's auto width and the icon buttons' fixed 2.4rem box both yield
   to the full-width menu row. */
.char-dash__more-menu .char-dash__icon-btn,
.char-dash__more-menu .char-dash__luck-check {
  width: 100%;
  height: auto;
  min-height: 2.4rem;
  gap: 0.5rem;
  padding-inline: 0.7rem;
}

/* Quick-dice pool — shares the tab row, right-aligned (moved 2026-06-07 from a
   standalone row beneath the topbar onto the tablist line to reclaim that row).
   flex:0 0 auto keeps it content-sized so the tablist takes the lead width; a
   small bottom padding lifts the chips off the row's shared underline so they
   don't sit flush on the 1px line. The wrapper's space-between pushes it right;
   margin-left:auto is a belt-and-braces right-align if the tablist ever shrinks
   to content width. */
.char-dash__minidice {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex: 0 0 auto;
  margin-left: auto;
  padding-bottom: 4px;
}

/* The dice + Clear control share one wrapping row; the readout is a sibling
   below, so the outcome never wraps inline among the chips. */
.char-dash__minidice-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  justify-content: flex-end;
}

/* Uniform square chip — every die is the SAME size regardless of label (d4
   vs d100). 2.1rem box (~34px) per Ken's resize: comfortably clickable in the
   dark-token header while staying a tidy square. position:relative anchors the
   count badge for pooled dice (e.g. "×2"). */
.char-dash__minidie {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  flex: none;
  padding: 0;
  box-sizing: border-box;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--font-mono, var(--dash-sans));
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}

/* The label/face fills the chip and centers; numbers and dN labels share the
   same metrics so the box never reflows when a roll lands. */
.char-dash__minidie-label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Pool count badge — sits in the top-right corner of the chip when a die is
   queued more than once (e.g. "×2"). Hidden (empty) otherwise. */
.char-dash__minidie-count {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 1.05rem;
  height: 1.05rem;
  padding: 0 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background-color: var(--color-accent);
  color: var(--color-bg);
  font-family: var(--dash-sans);
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1;
}
.char-dash__minidie-count:empty {
  display: none;
}

.char-dash__minidie:hover {
  color: var(--color-text);
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}

.char-dash__minidie:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.char-dash__minidie:active {
  background-color: color-mix(in srgb, var(--color-accent) 20%, var(--color-bg));
}

/* A die currently in the pool reads as "selected" (accent border) so the
   composition is legible before the roll resolves. */
.char-dash__minidie--queued {
  color: var(--color-text);
  border-color: var(--color-accent);
}

.char-dash__minidie--crit {
  color: var(--color-luck);
  border-color: var(--color-luck);
}

.char-dash__minidie--fumble {
  color: var(--color-danger);
  border-color: var(--color-danger);
}

/* Clear-pool control — same square footprint as the resized dice chips. */
.char-dash__minidice-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  flex: none;
  padding: 0;
  box-sizing: border-box;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--dash-sans);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}
/* Inactive (no pool): keep the slot's footprint so the dice never shift when a
   pool starts/clears — just hide it and make it non-interactive. */
.char-dash__minidice-clear--inactive {
  visibility: hidden;
  pointer-events: none;
}
.char-dash__minidice-clear:hover {
  color: var(--color-danger);
  border-color: var(--color-danger);
}
.char-dash__minidice-clear:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* The under-dice result readout was removed (2026-06-07): the per-die breakdown
   + total now lives in the Action Log drawer (and a transient toast). Rolling
   the header pool still tumbles + crits; only the persistent readout line moved. */

/* Stack the actions block full-width on narrow viewports so the dice row never
   forces horizontal overflow. On the mobile layout the dice take their own
   full-width line under the tablist and left-align with the tabs. */
@media (max-width: 760px) {
  .char-dash__topbar-actions {
    align-items: flex-start;
    width: 100%;
  }
  .char-dash__action-row,
  .char-dash__minidice-row {
    justify-content: flex-start;
  }
  .char-dash__minidice {
    align-items: flex-start;
    width: 100%;
    margin-left: 0;
  }
  .char-dash__minidice-row {
    flex-wrap: wrap;
  }
}

/* ============================================================
   COINS — clickable summary chip + add/remove editor (Batch B #4)
   ============================================================ */
/* The coins summary is now a <button>; it keeps the framed inv-stat tile look
   (border/padding/bg come from .char-dash__inv-stat) and just gains button
   semantics + a clear interactive affordance on hover/focus. */
.char-dash__inv-stat--action {
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.char-dash__inv-stat--action:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__inv-stat--action:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.coins-editor {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.coins-editor__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
}

.coins-editor__denom {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
}

.coins-editor__glyph {
  width: 1.4rem;
  height: 1.4rem;
  flex: none;
}

.coins-editor__label {
  font-family: var(--dash-sans);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text);
}

.coins-editor__stepper {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}

.coins-editor__step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-surface);
  color: var(--color-text);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.coins-editor__step:hover {
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.coins-editor__step:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.coins-editor__amount {
  width: 4rem;
  height: 1.9rem;
  text-align: center;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-mono, var(--dash-sans));
  font-size: 0.95rem;
  font-weight: 700;
}
.coins-editor__amount:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* ============================================================
   ACTION LOG DRAWER (2026-06-07)
   Persistent dashboard chrome: header toggle (#char-dash-log-toggle) opens a
   right-anchored slide-out panel reachable from any tab. Lists recent actions
   newest-first (chip + summary + timestamp), scrolls, and has a Clear control.
   Dark-token only; matches the topbar icon-button language.
   ============================================================ */

/* Backdrop — dims the dashboard behind the open drawer; click to close. Fixed
   so it covers the viewport regardless of scroll position. */
.char-dash__log-backdrop {
  position: fixed;
  inset: 0;
  /* Above the corner torch widget (z-index 100, torch.css) so it can't steal
     clicks on the drawer; still below the overlay band (confirm modals + toasts
     at ~1000) so those render above the open drawer. */
  z-index: 200;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity var(--transition-ui);
}
.char-dash__log-backdrop[hidden] {
  display: none;
}
.char-dash__log-backdrop--open {
  opacity: 1;
}

/* Drawer — fixed to the right edge, full-height, off-canvas until --open. */
.char-dash__log-drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 201;
  display: flex;
  flex-direction: column;
  width: min(24rem, 92vw);
  height: 100vh;
  height: 100dvh;
  background-color: var(--color-surface);
  border-left: 1px solid var(--color-border);
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.45);
  transform: translateX(100%);
  transition: transform var(--transition-reveal);
}
.char-dash__log-drawer[hidden] {
  display: none;
}
.char-dash__log-drawer--open {
  transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
  .char-dash__log-drawer,
  .char-dash__log-backdrop {
    transition: none;
  }
}

.char-dash__log-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-md);
  border-bottom: 1px solid var(--color-border);
  flex: none;
}

.char-dash__log-title {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  font-family: var(--dash-sans);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--color-text);
}
.char-dash__log-title-icon {
  width: 1.05rem;
  height: 1.05rem;
  color: var(--color-text-muted);
}

.char-dash__log-head-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}

.char-dash__log-clear {
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--dash-sans);
  font-size: 0.74rem;
  font-weight: 600;
  padding: 0.3rem 0.55rem;
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}
.char-dash__log-clear:hover {
  color: var(--color-danger);
  border-color: var(--color-danger);
}
.char-dash__log-clear:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.char-dash__log-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex: none;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background-color var(--transition-fast);
}
.char-dash__log-close svg {
  width: 1rem;
  height: 1rem;
}
.char-dash__log-close:hover {
  color: var(--color-text);
  border-color: var(--color-accent);
  background-color: var(--color-surface-hover);
}
.char-dash__log-close:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* Scrollable body — fills remaining height; the list itself does the scrolling. */
.char-dash__log-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-sm) var(--space-md) var(--space-md);
}
.char-dash__log-body:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.char-dash__log-empty {
  margin: var(--space-md) 0 0;
  color: var(--color-text-muted);
  font-size: 0.82rem;
  line-height: 1.45;
}

.char-dash__log-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* One entry: a fixed-width chip, a flexible summary, a trailing timestamp. */
.char-dash__log-entry {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: var(--space-sm);
  padding: 0.42rem 0;
  border-bottom: 1px solid var(--color-border);
}
.char-dash__log-entry:last-child {
  border-bottom: 0;
}

.char-dash__log-entry-kind {
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 3.4rem;
  padding: 0.12rem 0.4rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background-color: var(--color-bg);
  color: var(--color-text-muted);
  font-family: var(--dash-sans);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.4;
  text-transform: uppercase;
}

.char-dash__log-entry-summary {
  color: var(--color-text);
  font-size: 0.82rem;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

.char-dash__log-entry-ts {
  color: var(--color-text-muted);
  font-family: var(--font-mono, var(--dash-sans));
  font-size: 0.68rem;
  white-space: nowrap;
}

/* Kind accents — borrow the existing semantic tokens so chips read at a glance
   without inventing new colors. */
.char-dash__log-entry[data-kind="dice"]  .char-dash__log-entry-kind,
.char-dash__log-entry[data-kind="check"] .char-dash__log-entry-kind {
  color: var(--color-accent);
  border-color: color-mix(in srgb, var(--color-accent) 50%, var(--color-border));
}
.char-dash__log-entry[data-kind="attack"] .char-dash__log-entry-kind,
.char-dash__log-entry[data-kind="hp"]     .char-dash__log-entry-kind {
  color: var(--color-danger);
  border-color: color-mix(in srgb, var(--color-danger) 55%, var(--color-border));
}
.char-dash__log-entry[data-kind="luck"] .char-dash__log-entry-kind,
.char-dash__log-entry[data-kind="omen"] .char-dash__log-entry-kind {
  color: var(--color-luck);
  border-color: color-mix(in srgb, var(--color-luck) 50%, var(--color-border));
}

/* On small screens the drawer goes full-width so nothing overflows. */
@media (max-width: 700px) {
  .char-dash__log-drawer {
    width: 100vw;
  }

  /* Phase 1 mobile optimization (KDL 2026-06-27, 2026-06-14): hide the two heavy
     desktop-only dashboard affordances below the --bp-tab (700px) breakpoint.
     - #char-dash-customize: the drag-to-customize layout editor relies on native
       HTML5 drag-and-drop with no touch/keyboard fallback (audit Blocker, Phase 3
       work). Hidden on mobile per Ken's decision rather than reflowed now.
     - #char-dash-pdf: the PDF character-sheet export is awkward on a phone and
       deferred to desktop.
     Both stay fully present on desktop. Hidden in BOTH the topbar row and the
     "..." overflow menu (the menu clones the same #id button into a list), so we
     match on the id, which is stable in both contexts. display:none also removes
     them from the tab order, so there is no orphaned keyboard target. */
  #char-dash-customize,
  #char-dash-pdf,
  #char-dash-foundry,
  .char-dash__more-menu #char-dash-customize,
  .char-dash__more-menu #char-dash-pdf,
  .char-dash__more-menu #char-dash-foundry {
    display: none !important;
  }

  /* Mobile touch-target floor (KDL 2026-06-27, 2026-06-14): the dashboard header icon
     buttons are a fixed 2.4rem (~38px) square. Below --bp-tab (700px) grow the
     hit area to the ~44px minimum (the 1.15rem glyph is unchanged). Lives here,
     not components.css, because this fixed width/height would otherwise win the
     cascade (this file loads later). Desktop density unchanged. */
  .char-dash__icon-btn {
    width: 44px;
    height: 44px;
  }

  /* Phase 2 Batch B mobile reflow (KDL 2026-06-27, 2026-06-14): the focus-view tab strip
     (Overview / Combat / Spells / Inventory / Notes) is a flex child with
     flex:1 1 auto and a --space-lg gap; with flex-wrap on the PARENT row it
     refused to shrink and summed to ~409px, forcing a ~36px horizontal scroll on
     the WHOLE page at 390px (the layout viewport widened to 426px). Below
     --bp-tab (700px) turn the tablist itself into a self-contained horizontal
     scroll strip: nowrap so tabs stay one swipeable row, overflow-x:auto so the
     STRIP scrolls (not the page), min-width:0 so it can shrink inside the row,
     a tighter gap, and the active-tab underline kept via the row wrapper. This is
     the standard mobile tab pattern and satisfies the "tab bar scrolls/wraps"
     requirement. Tabs keep a no-shrink basis so their labels never truncate.
     Desktop (> 700px) is unchanged. */
  .char-dash__tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    min-width: 0;
    gap: var(--space-md);
    /* scrollbar should not eat the tab labels; keep it thin where supported */
    scrollbar-width: thin;
  }

  .char-dash__tab {
    flex: 0 0 auto;
    white-space: nowrap;
  }

  /* Inventory rows now carry an extra control (Stash/Carry) alongside Use +
     delete. On narrow phones let the row WRAP so the action cluster drops to a
     second line instead of forcing page-level horizontal scroll at 375px; the
     name flexes to fill and its text wraps rather than overflowing. */
  .char-dash__inv-row {
    flex-wrap: wrap;
  }
  .char-dash__inv-name {
    flex: 1 1 8rem;
    min-width: 0;
  }
  /* ~44px tap-target floor on the new/adjacent row action buttons (touch). */
  .char-dash__inv-stash,
  .char-dash__inv-use {
    min-height: 44px;
  }
}

/* ============================================================
   LEVEL-UP WIZARD (level_up_wizard.md §1)
   The topbar Level Up badge + the stepped overlay. The overlay reuses the
   .action-modal scrim + .action-modal__card chrome (components.css) and the
   .die-tile-3d die markup for the HP tumble; the rules below add only the
   wizard-specific scaffolding (badge, step dots, talent reveal, stat
   allocator, spell options, review list). Tokens are the shared OSR set.
   ============================================================ */

/* Topbar Level Up button — a quiet "ready" badge in cornflower, distinct from
   the icon-only header buttons so it reads as a call to action without shouting.
   It sits in the action row and collapses into the "..." menu last (priority 7). */
.char-dash__levelup-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--color-accent);
  border-radius: var(--dash-radius-sm);
  background-color: color-mix(in srgb, var(--color-accent) 14%, transparent);
  color: var(--color-accent);
  font-family: var(--font-heading-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--transition-fast), color var(--transition-fast),
    border-color var(--transition-fast);
}
.char-dash__levelup-btn:hover {
  background-color: var(--color-accent);
  color: var(--color-bg);
}
.char-dash__levelup-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.char-dash__levelup-icon {
  width: 1rem;
  height: 1rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* When the badge has been collapsed into the more-menu it gets the menu-item
   layout from the existing .char-dash__more-menu rules; keep its text visible
   there (the header version may hide text on narrow widths via overflow). */
@media (max-width: 520px) {
  .char-dash__levelup-text { display: none; }
}

/* Graduate-to-Level-1 reuses the level-up badge shell but is tinted with the
   luck/heading accent so the 0->1 milestone reads as distinct from a routine
   level-up (it's a one-time RAW milestone, not an XP-gated advance). Keep the
   Graduate label visible at all widths — its meaning is not obvious from the
   icon alone. */
.char-dash__graduate-btn {
  border-color: var(--color-luck, var(--color-accent));
  background-color: color-mix(in srgb, var(--color-luck, var(--color-accent)) 14%, transparent);
  color: var(--color-luck, var(--color-accent));
}
.char-dash__graduate-btn:hover {
  background-color: var(--color-luck, var(--color-accent));
  color: var(--color-bg);
}
.char-dash__graduate-btn:focus-visible {
  outline-color: var(--color-luck, var(--color-accent));
}
@media (max-width: 520px) {
  .char-dash__graduate-btn .char-dash__levelup-text { display: inline; }
}

/* The wizard card is a touch wider than a plain confirm so spell lists breathe. */
.levelup-wizard__card {
  max-width: 560px;
  width: min(560px, 100%);
}
.levelup-wizard__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.levelup-wizard__lead {
  font-family: var(--font-heading-ui);
  font-size: 1rem;
  letter-spacing: 0.04em;
  margin: 0;
}
.levelup-wizard__loading,
.levelup-wizard__error {
  margin: var(--space-sm) 0;
}
.levelup-wizard__error { color: var(--color-danger); }

/* Step dots — a compact progress indicator. Done steps fill, the active step
   gets the accent, future steps stay hollow. Decorative (aria-hidden). */
.levelup-wizard__dots {
  display: flex;
  gap: 0.4rem;
  margin-bottom: var(--space-xs);
}
.levelup-wizard__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background-color: transparent;
}
.levelup-wizard__dot--done { background-color: var(--color-text-muted); border-color: var(--color-text-muted); }
.levelup-wizard__dot--active { background-color: var(--color-accent); border-color: var(--color-accent); }

/* HP tumble — center the single die over its result line. .die-tile-3d carries
   its own size/animation from the roll-modal styles; we only position it. */
.levelup-wizard__dice {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 4rem;
  margin: var(--space-xs) 0;
}
.levelup-wizard__hp-result {
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--color-text);
  margin: 0;
  min-height: 1.3rem;
}
.levelup-wizard__hp-result strong { font-family: var(--font-mono); color: var(--color-accent); }

/* Reroll affordance (HP + Talent steps). Reuses the secondary modal-cancel
   button base; a dashed accent border marks it as a "roll again" action,
   visually distinct from the plain Back/Cancel button beside it. The
   margin-right:auto pins it to the left of the row so Back/Continue stay
   grouped on the right. */
.levelup-wizard__reroll {
  margin-right: auto;
  border-style: dashed;
  border-color: var(--color-accent);
  color: var(--color-accent);
}
.levelup-wizard__reroll:hover:not(:disabled) {
  background-color: color-mix(in srgb, var(--color-accent) 12%, transparent);
}
.levelup-wizard__reroll:disabled { opacity: 0.5; cursor: not-allowed; }

/* Talent reveal card — quotes the rolled effect text. */
.levelup-wizard__talent-reveal {
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--dash-radius-sm);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-surface);
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--color-text);
}

/* Option lists (talent choices + spell picks). Vertical list of selectable
   rows; the active row gets the accent border. Reuses the .dash-picker__option
   name/meta/tag spans from the existing add-spell picker for visual parity. */
.levelup-wizard__options {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  max-height: 46vh;
  overflow-y: auto;
}
.levelup-wizard__option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  width: 100%;
  text-align: left;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.levelup-wizard__option .dash-picker__option-name { font-weight: 600; }
.levelup-wizard__option .dash-picker__option-meta {
  font-family: var(--dash-sans);
  font-size: 0.74rem;
  color: var(--color-text-muted);
}
.levelup-wizard__option .dash-picker__option-tag {
  font-family: var(--font-heading-ui);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
}
.levelup-wizard__option:hover { border-color: var(--color-accent); }
.levelup-wizard__option:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
.levelup-wizard__option--active {
  border-color: var(--color-accent);
  background-color: color-mix(in srgb, var(--color-accent) 12%, transparent);
}

/* Free-form talent field (roll 2). */
.levelup-wizard__field-label {
  font-family: var(--font-heading-ui);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.levelup-wizard__field {
  width: 100%;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 0.95rem;
}
.levelup-wizard__field:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
  border-color: var(--color-accent);
}

/* +2-stats allocator (roll 12 -> stat:+2 option). A six-row grid of ability
   rows with +/- steppers and a live (score -> new score, mod) readout. */
.levelup-wizard__alloc-note { margin: var(--space-xs) 0; }
.levelup-wizard__alloc-grid {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.levelup-wizard__alloc-row {
  display: grid;
  grid-template-columns: 2.5rem 1fr auto;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-surface);
}
.levelup-wizard__alloc-id {
  font-family: var(--font-heading-ui);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  color: var(--color-text);
}
.levelup-wizard__alloc-score {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--color-text);
}
.levelup-wizard__alloc-arrow { color: var(--color-accent); }
.levelup-wizard__alloc-mod { color: var(--color-text-muted); }
.levelup-wizard__alloc-controls {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.levelup-wizard__alloc-val {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  min-width: 1.6rem;
  text-align: center;
  color: var(--color-text-muted);
}
.levelup-wizard__alloc-btn {
  width: 1.7rem;
  height: 1.7rem;
  border: 1px solid var(--color-border);
  border-radius: var(--dash-radius-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast);
}
.levelup-wizard__alloc-btn:hover:not(:disabled) { border-color: var(--color-accent); color: var(--color-accent); }
.levelup-wizard__alloc-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.levelup-wizard__alloc-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

/* Review — a key/value summary of everything the commit will write. */
.levelup-wizard__review {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.levelup-wizard__review li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-md);
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--color-border);
}
.levelup-wizard__review li:last-child { border-bottom: none; }
.levelup-wizard__review-k {
  font-family: var(--font-heading-ui);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  flex: none;
}
.levelup-wizard__review-v {
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--color-text);
  text-align: right;
}

/* Action Log chip colour for the level entry — match the cornflower accent so a
   level-up reads as a milestone, distinct from the XP/coins admin edits. */
.char-dash__log-entry[data-kind="level"] .char-dash__log-entry-kind {
  color: var(--color-accent);
  border-color: color-mix(in srgb, var(--color-accent) 55%, var(--color-border));
}

@media (prefers-reduced-motion: reduce) {
  .levelup-wizard .die-tile-3d.is-rolling { animation: none; }
}

/* ============================================================
   FOUNDRY EXPORT PICKER — two stacked choice cards inside the
   shared info modal. Mirrors maps.css .maps-foundry-modal so the
   character and map Foundry-export modals read identically.
   ============================================================ */
.char-foundry-modal {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.char-foundry-modal__choice {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  width: 100%;
  padding: 0.85rem 1rem;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border, var(--color-surface-hover));
  border-left: 3px solid var(--color-accent);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--transition-fast, 150ms),
    border-color var(--transition-fast, 150ms);
}

.char-foundry-modal__choice:hover {
  background: var(--color-surface-hover);
}

.char-foundry-modal__choice:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.char-foundry-modal__choice-title {
  font-family: var(--font-heading-ui);
  font-size: 0.95rem;
  color: var(--color-text);
}

.char-foundry-modal__choice-desc {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  line-height: 1.4;
}
