/* askdnd shared stylesheet. Tokens and rules: docs/DESIGN.md */
:root {
  /* Colors. Pages use these names only; a test fails on a color typed anywhere else (tests/test_design_system.py). */
  --bg: #15171a; --surface: #1f2226; --surface-2: #272b30; --line: #34383e; --line-strong: #4a4f57; --chrome: #0f1113;
  --text: #ebe7df; --text-strong: #fff; --muted: #a29d93; --faint: #6f6b64; --link: #f08a8a;
  --story-bg: #221f1a; --story-line: #3b3428; --story-text: #e2dccf;
  /* The faint scroll behind story text (Brett, 2026-11-05: Umber, Inter, Faint): its parchment, the rods of a recap's, the first letter and the fleuron */
  --parch-1: #211e19; --parch-2: #1f1c18; --parch-3: #1a1916; --rod-1: #2f2920; --rod-2: #221e18; --rod-3: #1a1714;
  --story-cap: #a99070; --story-fleuron: #5c5040; --rod-cap: #57462f; --rod-hi: #4a3c2c; --story-head: #f1e9da;
  --red: #d64545; --red-hover: #e35a5a; --red-soft: rgba(214, 69, 69, .14);
  --amber: #e8b34b; --amber-soft: rgba(232, 179, 75, .14);
  --green: #5bbf7f; --green-soft: rgba(91, 191, 127, .15);
  --blue: #7ea6e0; --blue-soft: rgba(126, 166, 224, .15);
  --surface-3: #30353b; --line-soft: #2c3036; --row-hover: #20242a; --bg-glass: rgba(21, 23, 26, .97);
  --note: #d8c9a8;  /* a player's note under a row */
  --red-line: rgba(214, 69, 69, .55); --blue-line: rgba(126, 166, 224, .45); --green-line: rgba(91, 191, 127, .45);
  --blue-hover: #95b7e8; --story-link: #f3b9a8; --amber-glow: rgba(232, 179, 75, .85);
  --spell-bg: #1b1e2a; --spell-line: #3a4260;  /* the spell bar on Battle */
  /* Shadows and dimming behind panels; the lift is for anything that floats over the page (a menu, a window, a card) */
  --lift: var(--lift);
  --shadow: rgba(0, 0, 0, .45); --backdrop: rgba(0, 0, 0, .45); --backdrop-strong: rgba(0, 0, 0, .55); --backdrop-soft: rgba(0, 0, 0, .35);
  --backdrop-dark: #000;  /* thrown dice: Dice tuning's Darkness mixes it in (app/dice.py) */
  /* Player colors in table rolls (background, then text), one per character */
  --p0-bg: #3a3226; --p1-bg: #2c2f3a; --p2-bg: #2a3329; --p3-bg: #332a33; --p3: #c4a0f0; --p4-bg: #3a2828; --p5-bg: #283536; --p5: #7fd0cf;
  /* Dice you can see (Table rolls): gem sets, light to dark, then the number; a natural 20's glow */
  --die-gold-hi: #f7d98f; --die-gold-mid: #d9a441; --die-gold-lo: #8a5f1c; --die-gold-ink: #2a1a05;
  --die-ruby-hi: #f3a196; --die-ruby-mid: #c4473d; --die-ruby-lo: #6e1b17; --die-ruby-ink: #fff4f2;
  --die-jade-hi: #aee9c3; --die-jade-mid: #4fae73; --die-jade-lo: #1f5a35; --die-jade-ink: #06180d;
  --die-glow: rgba(91, 191, 127, .6);
  /* Thrown dice (app/dice.py): each set's swatch in the dice picker and on who-rolled tags, and the dice tray's felt */
  --set-astralsea: #e8e6f0; --set-bloodmoon: #6f0000; --set-dragons: #7e934e; --set-starynight: #233660; --set-glitterparty: #c9a0ff;
  --set-bronze: #7a4e06; --set-fire: #f43c04; --set-ice: #9fd8f0; --set-radiant: #fff2cc; --set-necrotic: #4a0000; --set-poison: #504099;
  --set-black: #111111; --set-white: #eeeeee; --set-rainbow: #e05a9a; --felt-hi: #2c3a2c; --felt-lo: #171c17;
  --violet: #b9a3e3; --violet-soft: rgba(185, 163, 227, .15);  /* Reminders (the bell) */
  /* Action economy: the only colored headings (DESIGN.md › Section headings) */
  --k-action: #ef6b6b; --k-bonus: var(--amber); --k-reaction: #6fa8ff; --k-other: #b48ae8;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --sans: Inter, system-ui, -apple-system, sans-serif;
  --tale-cap: "Cormorant Garamond", Georgia, serif;  /* a recap's first letter only */
  --cond: "Roboto Condensed", "Arial Narrow", sans-serif;
  /* Text sizes, 12 · 14 · 16 · 19 plus titles 26 · 30 (Brett, 2026-10-22). Use these, not one-off sizes. */
  --fs-xs: .75rem; --fs-sm: .875rem; --fs-md: 1rem; --fs-lg: 1.2rem; --fs-xl: 1.625rem; --fs-2xl: 1.875rem;
  /* Space: a 4px scale for padding, margins and gaps */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;
  /* Corners: tiny marks, controls, cards and sections, pills */
  --r-xs: 3px; --r-sm: 6px; --r-md: 8px; --r-lg: 12px; --r-pill: 999px;
  /* Controls (search box, selects, buttons): 32px tall on desktop, 44px on phones (Brett, 2026-10-22) */
  --control-h: 32px; --control-h-sm: 26px; --chip-h: 28px; --bar-h: 36px;  /* the last is everything in the menu bar */
  --selected-bg: #2a3550;  /* a chip or view that's on: blue on a dark tint */
  /* Panels and modals: side panel (also inside a modal) · regular modal (forms) · large (a Wild Shape form, combos,
     leveling, the Spell Book) */
  --panel-w: 420px; --modal-w: 720px; --modal-lg-w: 1180px;
  /* Screen widths (media queries can't use variables): phone ≤ 640px, tablet ≤ 900px, wide ≥ 1100px */
  /* Conditions (docs/mockups/conditions-on-battle.html): each one's colour, and Blinded's black on the page's edge */
  --cond-blinded: #e6e9ee; --cond-charmed: #ec84b8; --cond-deafened: #c3b08a; --cond-exhaustion: #d08a4c; --cond-frightened: #a983e6; --cond-grappled: #e59a52; --cond-incapacitated: #ef6a5e; --cond-invisible: #f4f6f9; --cond-paralyzed: #f0d24a;
  --cond-petrified: #b3aa9b; --cond-poisoned: #86c95b; --cond-prone: #c9a47b; --cond-restrained: #d9774d; --cond-stunned: #f2b93b; --cond-unconscious: #8f97ab; --cond-enlarged: #4fc7b0; --cond-flying: #8fc6f2; --cond-water-breathing: #5aa7e8;
  --cond-concentration: #7ea6e0; --cond-dark: #000; --cond-dark-edge: #6b7079; --cond-dark-glow: rgba(0, 0, 0, .6);
  --coin-pp: #d9dde3; --coin-gp: #e8b34b; --coin-ep: #b9c4a3; --coin-sp: #a9adb3; --coin-cp: #c07a4a; --coin-ink: #1a1a1a;
}
@media (max-width: 640px) { :root { --control-h: 44px; --control-h-sm: 36px; --chip-h: 36px; --bar-h: 44px; } }
* { box-sizing: border-box; }
html { color-scheme: dark; scrollbar-gutter: stable; }  /* pages that scroll and pages that don't line up (Brett, 2026-10-23) */
/* The page's scrollbar lane on a computer is always there and drawn as part of the page, so the menu bar and its red line
   reach the window's edge, on a page that scrolls or not (Brett, 2026-10-31) */
@media (hover: hover) and (pointer: fine) {
  html { overflow-y: scroll; }
  html::-webkit-scrollbar { width: 12px; }
  html::-webkit-scrollbar-track { background: linear-gradient(var(--chrome) 0 60px, var(--red) 60px 62px, var(--bg) 62px); }
  html::-webkit-scrollbar-thumb { background: var(--line-strong); border: 3px solid transparent; border-radius: var(--r-pill); background-clip: padding-box; }
  html::-webkit-scrollbar-thumb:hover { background: var(--muted); border: 3px solid transparent; background-clip: padding-box; }
}
body { margin: 0; background: var(--bg); color: var(--text); font: 16px/1.6 var(--sans); overflow-wrap: break-word; }  /* a long web address breaks rather than widening the page */
a { color: var(--link); }
button { font: inherit; cursor: pointer; }
:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
[hidden] { display: none !important; }
img { max-width: 100%; }

/* Top bar */
.topbar { background: var(--chrome); border-bottom: 2px solid var(--red); position: sticky; top: 0; z-index: 5; }
.topbar-inner { max-width: 1360px; margin: 0 auto; padding: 0 24px; height: 60px; display: flex; align-items: center; gap: 14px; }
.brand { font-family: var(--cond); font-weight: 700; font-size: var(--fs-lg); color: var(--text-strong); text-decoration: none; display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.brand img { width: 34px; height: 34px; border-radius: var(--r-md); }
.nav { display: flex; gap: 2px; align-items: center; }
.nav a { display: inline-flex; align-items: center; min-height: var(--bar-h); color: var(--muted); text-decoration: none; font-weight: 500; font-size: var(--fs-md); padding: 0 12px; border-radius: var(--r-sm); }
.nav a:hover { color: var(--text); background: var(--surface); }
.nav a.active { color: var(--text-strong); background: var(--surface); }
/* Menu bar (Brett, 2026-10-22, Option B): the campaign's name | Story, Campaign | your character, Party ▾ | Live, Manage, you */
.nav-sep { width: 1px; height: 24px; background: var(--line); flex: none; }
.campaign-name { font-weight: 600; font-size: var(--fs-md); color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px; }
.campaign-switch { position: relative; min-width: 0; }
.campaign-switch > button { min-height: var(--bar-h); background: none; border: none; padding: 0 8px; border-radius: var(--r-sm); display: block; max-width: 100%; }
.campaign-switch > button:hover { background: var(--surface); }
.campaign-switch .menu { left: 0; right: auto; }
/* When the bar runs short, nothing wraps onto two lines: the Live pill shortens first (below), then the campaign's
   name gives up room (it's the one item that shrinks, with "…") */
.nav a, .char-btn, .bar-end > * { white-space: nowrap; flex-shrink: 0; }
.topbar .menu { white-space: normal; }  /* the menus they open wrap as usual (the Live popover's text) */
.nav, .table-nav, .bar-end { flex-shrink: 0; }
.table-nav { display: flex; gap: 6px; align-items: center; }
.char-btn { display: inline-flex; align-items: center; gap: 8px; min-height: var(--bar-h); padding: 0 12px; background: none; border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--text); text-decoration: none; font-weight: 500; font-size: var(--fs-md); white-space: nowrap; }
a.char-btn { padding-left: 5px; max-width: 200px; }
a.char-btn > span:not(.char-av, .cond-dots) { overflow: hidden; text-overflow: ellipsis; }
.char-btn:hover { background: var(--surface); }
.char-btn.active { background: var(--surface); border-color: var(--line-strong); color: var(--text-strong); }
.char-av { position: relative; width: 24px; height: 24px; border-radius: 50%; display: inline-grid; place-items: center; flex: none; overflow: hidden; background: var(--line-strong); color: var(--text); font-size: var(--fs-xs); font-weight: 700; }
.char-av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.party-switch { position: relative; }
@media (max-width: 1100px) { .brand-word { display: none; } }
.menu.party-menu { left: auto; right: 0; width: 290px; max-width: calc(100vw - 24px); }  /* opens leftward: Party sits right of center, so it never runs off the screen */
.menu a.party-row { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 10px; align-items: center; }
.party-row .char-av { grid-row: span 2; width: 32px; height: 32px; font-size: var(--fs-sm); }
.party-row .party-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu a.party-all { border-bottom: 1px solid var(--line); margin-bottom: var(--sp-1); padding-bottom: var(--sp-3); font-weight: 600; border-radius: var(--r-sm) var(--r-sm) 0 0; }
.menu .menu-note { margin: 0; padding: 8px 10px; color: var(--muted); font-size: var(--fs-sm); }
.bar-end { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.manage { position: relative; }
.manage > button { min-height: var(--bar-h); background: var(--surface); border: 1px solid var(--line); color: var(--text); border-radius: var(--r-sm); padding: 0 12px; font-weight: 500; font-size: var(--fs-md); white-space: nowrap; }
.manage > button.active { border-color: var(--red); }
.menu { position: absolute; right: 0; top: 44px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: 6px; width: 240px; box-shadow: var(--lift); }
.menu a { display: block; color: var(--text); text-decoration: none; padding: 8px 10px; border-radius: var(--r-sm); }
.menu .menu-me { pointer-events: none; }  /* the account menu's name and email: a label, not a link */
.menu a small { display: block; color: var(--muted); font-size: var(--fs-xs); }
.menu a:hover, .menu a.active { background: var(--surface); }
.menu a.btn { display: inline-flex; padding: 0 var(--sp-3); }  /* a button that's a link stays a button */
.menu a.btn:not(.primary):hover { background: var(--surface-2); }
.account { position: relative; }
.avatar { width: var(--bar-h); height: var(--bar-h); border-radius: 50%; background: var(--line-strong); display: grid; place-items: center; font-weight: 600; font-size: var(--fs-sm); color: var(--text); border: none; padding: 0; }
.avatar img { width: 100%; height: 100%; border-radius: 50%; }
.help-btn { width: var(--bar-h); height: var(--bar-h); border-radius: 50%; display: grid; place-items: center; border: 1px solid var(--line-strong); color: var(--muted); font-weight: 600; font-size: var(--fs-sm); text-decoration: none; flex: none; }
.help-btn:hover, .help-btn.active { color: var(--text-strong); border-color: var(--muted); }
.help-menu { position: relative; }
button.help-btn { background: none; cursor: pointer; padding: 0; font-family: inherit; }
.menu form button { width: 100%; text-align: left; background: none; border: none; color: var(--text); padding: 8px 10px; border-radius: var(--r-sm); }
.menu form button:hover { background: var(--surface); }
/* Live (app/live.py): whether your sheet is connected */
.live { position: relative; }
.live-pill { display: inline-flex; align-items: center; gap: 8px; min-height: var(--bar-h); background: var(--surface); color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 0 12px; font-size: var(--fs-sm); white-space: nowrap; }
.live-pill:hover { color: var(--text); }
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); flex: none; }
.live-word, .live-noext, .live-noext-box { display: none; }
body.no-bridge:not(.bridge-elsewhere) .live-pill[data-wants-bridge] .live-noext { display: inline; }  /* the extension isn't in this browser (app.js), and this campaign has the bridge: say so, with the steps in the window */
body.no-bridge:not(.bridge-elsewhere) .live-pill[data-wants-bridge] .live-word { display: none; }
body.no-bridge .live-noext-box { display: grid; gap: var(--sp-3); }
.bridge-head { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: var(--sp-3); align-items: center; }
.bridge-head img { border-radius: var(--r-md); display: block; }
.bridge-head h3 { margin: 0; font-size: var(--fs-md); font-weight: 700; color: var(--text-strong); }
.bridge-head p, .bridge-small, .bridge-other { margin: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.45; }
.bridge-steps { list-style: none; counter-reset: step; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.bridge-steps li { counter-increment: step; display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; min-height: 32px; }
.bridge-steps li::before { content: counter(step); width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 700; color: var(--muted); border: 1px solid var(--line-strong); }
.bridge-steps li:first-child::before { background: var(--red); border-color: var(--red); color: var(--text-strong); }
.bridge-steps li:first-child span { color: var(--text-strong); font-weight: 600; }
.bridge-steps .btn { white-space: nowrap; width: 136px; }
.bridge-other { display: none; }
body.bridge-elsewhere .bridge-other { display: block; font-size: var(--fs-sm); }
body.bridge-elsewhere .bridge-steps, body.bridge-elsewhere .bridge-small { display: none; }
body.no-bridge:not(.bridge-elsewhere) .live-noext-box ~ .live-actions .btn.primary { background: var(--surface-2); border-color: var(--line); color: var(--text); font-weight: 500; }  /* one main action: adding the bridge */
body.no-bridge:not(.bridge-elsewhere) .live-pill[data-wants-bridge] .live-ago { display: none; }
/* Its colour (DM Backup, Brett 2026-11-02): green your own sheet, yellow through the DM, red can't while the table plays
   or something to make by hand, grey no one's connected; warn: couldn't refresh */
.live-pill.warn .live-dot { background: var(--amber); }
.live-pill.step .live-dot { background: var(--green); }
.live-pill.via { border-color: var(--amber); color: var(--text); }
.live-pill.via .live-dot { background: var(--amber); }
.live-pill.bad { border-color: var(--red-line); color: var(--text); }
.live-pill.bad .live-dot { background: var(--red); }
body.no-bridge:not(.bridge-elsewhere) .live-pill.via[data-wants-bridge] .live-noext { display: none; }  /* (a phone: the DM carries it, no nag) */
body.no-bridge:not(.bridge-elsewhere) .live-pill.via[data-wants-bridge] .live-word { display: inline; }
.live-pill.htmx-request { opacity: .7; }
@media (max-width: 1240px) { .live-ago { display: none; } .live-word { display: inline; } }  /* "● Live" or "● Game on"; the dot's colour says in step or to make by hand, the window the rest */
@media (max-width: 1240px) {  /* without the extension too: "Add the askdnd bridge" pushed the menu bar past a 1000px window (the layout sweep, Nov 2); the window still has the steps */
  body.no-bridge:not(.bridge-elsewhere) .live-pill[data-wants-bridge] .live-noext { display: none; }
  body.no-bridge:not(.bridge-elsewhere) .live-pill[data-wants-bridge] .live-word { display: inline; }
}
.menu.live-menu { width: 360px; max-width: calc(100vw - 24px); padding: 0; }
.live-menu:empty { display: none; }
.live-pop { padding: 14px 16px; display: grid; gap: 12px; font-size: var(--fs-sm); }
/* The list (Brett, 2026-11-02, docs/mockups/live-window/ Option B): every sheet two lines, its name and then how it's
   connected, so no row has to line up with another but for its ↗; the whole row opens the sheet on D&D Beyond */
.live-list { display: grid; gap: 2px; }
body.no-bridge .live-noext-box + .live-list { border-top: 1px solid var(--line); padding-top: 10px; }  /* under the steps to add the bridge */
.live-head { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); margin-bottom: 2px; }
.live-row, .menu a.live-row { display: grid; grid-template-columns: minmax(0, 1fr) 28px; gap: 10px; align-items: center; margin: 0 -8px; padding: 6px 8px; border-radius: var(--r-md); color: inherit; text-decoration: none; }
.live-who { display: grid; gap: 1px; min-width: 0; }
.live-name { color: var(--text); font-size: var(--fs-md); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.live-state { display: flex; align-items: center; gap: 7px; min-width: 0; font-size: var(--fs-xs); color: var(--muted); }
.live-state > span:last-child { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.live-mark { width: 8px; height: 8px; border-radius: 50%; flex: none; box-shadow: inset 0 0 0 1.5px var(--faint); }
.live-state.ok { color: var(--green); } .live-state.ok .live-mark { background: var(--green); box-shadow: none; }
.live-state.warn { color: var(--amber); } .live-state.warn .live-mark { background: var(--amber); box-shadow: none; }
.live-state.via { color: var(--amber); } .live-state.via .live-mark { background: var(--amber); box-shadow: none; }  /* through the DM (DM Backup) */
.live-state.bad { color: var(--link); } .live-state.bad .live-mark { background: var(--red); box-shadow: none; }
.live-go { width: 28px; height: 28px; display: grid; place-items: center; border-radius: var(--r-sm); color: var(--faint); }
a.live-row:hover .live-go { color: var(--text); }
.sheet-open { display: inline-grid; place-items: center; width: 24px; height: 24px; border: 1px solid var(--line-strong); border-radius: var(--r-sm); color: var(--muted); text-decoration: none; font-size: var(--fs-xs); line-height: 1; }
.menu a.sheet-open { display: inline-grid; padding: 0; }
.menu a.sheet-open:hover, .sheet-open:hover { color: var(--text-strong); border-color: var(--muted); background: var(--surface); }
.nav a.nav-table { white-space: nowrap; }
.start-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: var(--sp-3); margin-top: var(--sp-4); }
.start-card { min-width: 0; display: grid; gap: var(--sp-2); align-content: start; justify-items: start; padding: var(--sp-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); color: var(--muted); text-decoration: none; }
.start-card:hover { border-color: var(--line-strong); }
.start-card b { color: var(--text-strong); font-size: var(--fs-md); }
.start-card .btn { margin-top: auto; }
.pin-hint { margin: 0 0 var(--sp-3); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md); color: var(--muted); font-size: var(--fs-sm); }
.rn-short, .res-row.short .rn-full, .pt-res.short .rn-full { display: none; }
.res-row.short .rn-short, .pt-res.short .rn-short { display: inline; }
.res-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0; padding: 0 10px; border-left: 1px solid var(--line); }
.res-gname { color: var(--muted); margin-right: 6px; }
.res-group .res { border-left: none; padding: 0 8px 0 0; }
.res-more { display: none; }
.rs-latest { display: none; }
/* A small window's buttons (DESIGN.md › Where a window's actions go; the UI pass, 2026-11-02): the main action first, then
   others, then Cancel as a quiet link */
.use-foot { justify-content: flex-start; }
.count-n { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--muted); }  /* a pool's How many (Lay On Hands) */
.count-n input { width: 5.5em; }
.cond-rest { display: inline-flex; align-items: center; min-height: var(--chip-h); padding: 0 var(--sp-3); background: var(--surface-2); border: 1px dashed var(--line); border-radius: var(--r-pill); color: var(--muted); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.cond-rest:hover { color: var(--text-strong); border-color: var(--muted); }
.cond-rest { gap: var(--sp-1); border-style: solid; color: var(--text); }
.cond-rest svg { color: var(--amber); flex: none; }  /* (a moon: Rest stands out from the chips beside it, Brett 2026-11-02) */
.sheet-hd { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-2); }  /* Sheet: Rest, then Hit dice */
.planner-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-2) var(--sp-3); flex-wrap: wrap; }
.row > .planner-prep { margin-left: auto; }  /* Prepare spells ends its class's counts, at the far right */
.res-row > .cond-rest { margin-left: auto; }  /* Rest ends Resources, at the far right */
/* Heroic Inspiration: a chip you can press, lit when you have it, grey and hollow when you don't */
.res-row .res.res-insp, .res-insp { display: inline-flex; align-items: center; gap: var(--sp-1); min-height: var(--chip-h); margin-left: var(--sp-2); padding: 0 var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface-2); color: var(--muted); font: 600 var(--fs-md)/1 var(--sans); cursor: pointer; }
.res-insp:hover, .res-insp:focus-visible { color: var(--text); border-color: var(--muted); }
.res-row .res.res-insp.on, .res-insp.on { color: var(--amber); border-color: var(--amber); background: var(--amber-soft); }
.status-phone { display: none; }
/* Party's rows, while a fight runs (Brett, 2026-10-31, docs/mockups/usability/) */
.pt-view { display: inline-flex; gap: 2px; padding: 2px; margin-bottom: var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md); }
.pt-view button { min-height: var(--control-h-sm); padding: 0 var(--sp-3); background: none; border: 0; border-radius: var(--r-sm); color: var(--muted); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.pt-view button[aria-pressed=true] { background: var(--surface-2); color: var(--text-strong); }
.pt-track-wrap { display: none; overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
body.pt-rows .pt-track-wrap { display: block; }
body.pt-rows .pt-with-track { display: none; }
.pt-track { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.pt-track th { text-align: left; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); color: var(--muted); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; }
.pt-track td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); vertical-align: middle; }
.pt-track tr:last-child td { border-bottom: 0; }
.pt-track tr.now td { background: var(--surface-2); }
.pt-track tr.now td:first-child { color: var(--amber); font-weight: 600; }
.pt-track .pt-hp { background: none; border: 1px solid var(--line-strong); border-radius: var(--r-sm); padding: 2px 8px; color: var(--text-strong); font: inherit; font-weight: 700; white-space: nowrap; cursor: pointer; }
.pt-track .pt-hp:hover { border-color: var(--muted); }
.pt-track .pt-hp.low { color: var(--amber); }
/* The fight's rows (_fight_rows.html): Party's Rows and the middle of askdnd's table, which shows them whatever Party's choice */
.pt-track-wrap.always { display: block; }
.pt-track tr[data-panel-url] { cursor: pointer; }
.pt-track tr[data-panel-url]:hover td { background: var(--surface-2); }
.pt-track .ft-init { font-weight: 700; color: var(--muted); white-space: nowrap; }
.pt-track .ft-now { display: block; font-size: var(--fs-xs); color: var(--amber); }
.pt-track .ft-who { display: flex; align-items: center; gap: var(--sp-2); min-width: 9em; }  /* (a phone scrolls the rows sideways: slice 5 makes them its own) */
.pt-track .ft-who .roll-av { width: 28px; height: 28px; font-size: var(--fs-xs); }
.pt-track .ft-who .roll-av.enemy { box-shadow: 0 0 0 2px var(--red); }
.pt-track .ft-who b { color: var(--text-strong); }
.pt-track .ft-conds .ft-rem { display: block; font-size: var(--fs-xs); color: var(--muted); }
/* The DM changes the order (Initiative from askdnd, app/initiative.py; docs/mockups/initiative/ board 5): a handle, the
   number to press and type, ↑ ↓; a number on its way to D&D Beyond's VTT underlined amber; the dashed line where a
   dragged row lands, with the number it gets */
.ft-wait { margin: 0; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line); color: var(--muted); font-size: var(--fs-sm); }
.ft-wait b { color: var(--text-strong); }
.pt-track.ft-order .ft-init { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; }
.pt-track .ft-grip { color: var(--faint); cursor: grab; display: inline-grid; place-items: center; }
.pt-track .ft-num { background: none; border: 1px solid transparent; border-radius: var(--r-sm); padding: 2px 6px; color: var(--text-strong); font: inherit; font-weight: 700; cursor: pointer; min-width: 2.4em; }
.pt-track .ft-num:hover { border-color: var(--line-strong); }
.pt-track .ft-pending { text-decoration: underline dashed var(--amber); text-underline-offset: 4px; }
.pt-track .ft-ud { display: inline-flex; gap: 2px; }
.pt-track .ft-mv { width: 26px; height: 26px; display: inline-grid; place-items: center; padding: 0; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--text); cursor: pointer; }
.pt-track .ft-mv:hover { border-color: var(--line-strong); }
.pt-track .ft-type { width: 4.5em; font: inherit; font-weight: 700; padding: 2px 6px; }
.pt-track tr[draggable="true"] { cursor: grab; }
.pt-track tr.ft-lifted { opacity: .45; }
.pt-track tr.ft-drop td { padding: var(--sp-1) var(--sp-3); border: 2px dashed var(--green); color: var(--green); font-size: var(--fs-xs); font-weight: 600; }
.table-page .tb-middle { display: grid; gap: var(--sp-3); }
.live-row.here .live-name { font-weight: 600; }
.live-say { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: center; border: 1px solid var(--line); border-radius: var(--r-md); padding: 8px 10px; }
.live-say.via { border-color: var(--amber); }
.live-say.bad { border-color: var(--red-line); }
.live-made { font-size: var(--fs-xs); color: var(--muted); }
.live-made b { color: var(--text); font-weight: 600; }
.live-made small { color: var(--faint); }
/* A change that stopped short of D&D Beyond, under its character, with Try again and Undo (live.stopped) */
.live-stopped { display: grid; gap: var(--sp-2); margin: 0 0 var(--sp-2) var(--sp-3); padding: var(--sp-2) var(--sp-3); border-left: 2px solid var(--red); background: var(--red-soft); border-radius: 0 var(--r-sm) var(--r-sm) 0; font-size: var(--fs-sm); }
.live-stopped b { color: var(--text-strong); font-weight: 600; }
.live-stopped .note { display: block; font-size: var(--fs-xs); }
.live-stopped.on { border-left-color: var(--amber); background: var(--amber-soft); }  /* on its way, trying again: not yours to do yet */
.live-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.live-actions .btn { flex: 1 1 0; white-space: nowrap; }  /* the spinner never wraps the label */
/* Phones: a bottom bar with the main pages; More holds the rest (base.html) */
.bottom-nav { display: none; }

/* Page */
.page { max-width: 1120px; margin: 0 auto; padding: 32px 24px 120px; }
.page.narrow { max-width: 760px; }
/* Manage pages share one width so the tab bar never jumps; reading pages too (Brett, 2026-10-23) */
.crumbs { color: var(--muted); font-size: var(--fs-sm); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--text); }
.title-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 8px 0 4px; }
.story-search { margin-left: auto; width: min(320px, 100%); }  /* Story's search, beside its title */
mark.story-hit { background: var(--amber-soft); color: var(--text-strong); border-radius: var(--r-xs); padding: 0 1px; }
h1 { font-size: var(--fs-2xl); font-weight: 700; margin: 0; letter-spacing: -.01em; line-height: 1.25; text-wrap: balance; }
@media (max-width: 640px) { main h1 { font-size: var(--fs-xl); } }  /* (long titles kept to one line on phones: the UI pass, 2026-11-02) */
.subtitle { color: var(--muted); margin: 0 0 32px; }
.layout { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 32px; align-items: start; }

/* Sections */
.section { background: var(--surface); border-radius: var(--r-lg); padding: 28px 30px; margin-bottom: 24px; }
.section-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 6px; }
h2 { font-size: var(--fs-lg); font-weight: 600; margin: 0; flex: 1; }
h2 .num { color: var(--red); margin-right: 8px; font-weight: 700; }
h3 { font-size: var(--fs-md); font-weight: 600; margin: 1.4em 0 .5em; }
.hint { color: var(--muted); font-size: var(--fs-sm); margin: 0 0 20px; }
.count { color: var(--muted); font-size: var(--fs-sm); }
.muted { color: var(--muted); }
.note { color: var(--muted); font-size: var(--fs-sm); }

/* Chip (DESIGN.md › Components): clickable and pill-shaped, with an on state. One look for filters, Wild Shape
   forms, spells to cast, conditions and the roll window's choices. */
.ls-chip, .ws-chip, .spell-chip, .cond-pill > summary, .chip { display: inline-flex; align-items: center; gap: 6px; min-height: var(--chip-h); padding: 0 var(--sp-3);
  font: inherit; font-size: var(--fs-sm); color: var(--text); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); cursor: pointer; white-space: nowrap; }
.ls-chip:hover, .ws-chip:hover, .spell-chip:hover, .spell-chip:focus-visible, .cond-pill > summary:hover, .chip:hover { border-color: var(--faint); }
.ls-chip[aria-pressed="true"], .ls-chip.on, .ws-chip.on, .chip[aria-pressed="true"], .chip.on, .cond-pill > summary { background: var(--selected-bg); border-color: var(--blue); }
.ws-chip:has(img) { padding-left: var(--sp-1); }

/* Tags */
.tag { display: inline-block; font-size: var(--fs-xs); font-weight: 600; padding: 0 8px; border-radius: var(--r-pill); background: var(--surface-2); color: var(--muted); white-space: nowrap; line-height: 1.6; }
.tag.draft { background: var(--amber-soft); color: var(--amber); }
.tag.published, .tag.done { background: var(--green-soft); color: var(--green); }
.tag.edited, .tag.new, .tag.working { background: var(--blue-soft); color: var(--blue); }
.tag.error { background: var(--red-soft); color: var(--link); }

/* Buttons and forms */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); border: 1px solid var(--line); background: var(--surface-2); color: var(--text); border-radius: var(--r-md); padding: 0 var(--sp-4); font-size: var(--fs-sm); font-weight: 500; min-height: var(--control-h); text-decoration: none; line-height: 1.25; white-space: nowrap; }  /* a button's label stays on one line; the row around it wraps instead */
/* In anything smaller than a page (the roll window, a side panel, a modal, a corner card) a long label wraps inside the
   button instead of running out of the box (Brett, 2026-10-30: "Roll damage (1d8 force + +4 spellcasting…)"). */
.roll-win .btn, .peek .btn, dialog .btn, .card .btn { max-width: 100%; min-width: 0; white-space: normal; text-align: center; padding-block: var(--sp-1); overflow-wrap: anywhere; }
.btn.block { width: 100%; }  /* the one action of a small card (Sign in, Publish) */
.btn:hover { border-color: var(--line-strong); }
.btn.primary { background: var(--red); border-color: var(--red); color: var(--text-strong); font-weight: 600; }
.btn.primary:hover { background: var(--red-hover); }
.btn.link { background: none; border: none; color: var(--muted); padding: var(--sp-1) var(--sp-2); min-height: 0; }
.btn.link:hover { color: var(--text); }
.btn.danger:hover { color: var(--link); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.stack { display: grid; gap: 10px; }
/* Spacing helpers on the space scale (DESIGN.md › Space), for a block whose parent isn't a .row or .stack: space above or
   below it, or none. They win over component rules, as the inline styles they replaced did (2026-10-27: no inline spacing
   on any page; tests/test_design_system.py) */
.m-0 { margin: 0 !important; }
.mt-1 { margin-top: var(--sp-1) !important; } .mt-2 { margin-top: var(--sp-2) !important; } .mt-3 { margin-top: var(--sp-3) !important; }
.mt-4 { margin-top: var(--sp-4) !important; } .mt-5 { margin-top: var(--sp-5) !important; }
.mb-1 { margin-bottom: var(--sp-1) !important; } .mb-2 { margin-bottom: var(--sp-2) !important; } .mb-3 { margin-bottom: var(--sp-3) !important; }
.byline { margin: calc(-1 * var(--sp-1)) 0 var(--sp-2) !important; }  /* who added it and when, tucked under what it's about */
.push { margin-left: auto !important; }  /* a button at the far end of its row */
.grow { flex: 1; min-width: 0; }  /* the part of a row that takes the room left */
.row.tight { gap: 6px; }  /* small controls that belong together (Keep it / Forget, uses and pips) */
summary { cursor: pointer; }
form.inline { display: inline; }
form.inline.row { display: inline-flex; flex-wrap: nowrap; }
form.inline.row select { width: auto; max-width: 100%; }  /* its controls stay on one line, like a row (feedback 9: Move fell under its menu) */
textarea, input[type=text], input[type=url], input[type=date], input[type=number], select { width: 100%; font: inherit; font-size: var(--fs-sm); color: var(--text); background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-md); padding: 6px 10px; }
input[type=text], input[type=url], input[type=date], input[type=number], select { min-height: var(--control-h); padding-block: 0; }
input[type=number] { width: 6em; }
@media (max-width: 640px) { textarea, input[type=text], input[type=url], input[type=date], input[type=number], input[type=search], select { font-size: var(--fs-md); } }
textarea:focus, input:focus, select:focus { border-color: var(--red); outline: none; }
select { width: auto; max-width: 100%; padding: 0 var(--sp-2); }  /* a long option (a campaign's name) never pushes the page wider */
input[type=file] { color: var(--muted); font: inherit; font-size: var(--fs-sm); max-width: 100%; }
input[type=file]::file-selector-button { min-height: var(--control-h); margin-right: var(--sp-3); padding: 0 var(--sp-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); color: var(--text); font: inherit; font-weight: 500; cursor: pointer; }
label.field { display: grid; gap: 4px; font-size: var(--fs-sm); color: var(--muted); }
label.field.short { max-width: 220px; }  /* a choice of a few words (a status) */
label.check { display: inline-flex; gap: 8px; align-items: center; color: var(--muted); font-size: var(--fs-sm); }
input[type=checkbox] { accent-color: var(--red); width: 16px; height: 16px; }

/* Messages */
.flash { border-radius: var(--r-md); padding: 12px 16px; margin: 0 0 24px; background: var(--surface); border-left: 3px solid var(--green); }
.flash.bad { border-left-color: var(--red); background: var(--red-soft); }
.flash.warn { border-left-color: var(--amber); background: var(--amber-soft); }
.flash ul { margin: .4em 0 0; padding-left: 1.2em; }

/* Lists of items (questions, suggestions) */
.item { padding: 18px 0; }
.item + .item { border-top: 1px solid var(--line); }
.item-title { font-weight: 500; margin: 0 0 12px; }
.answer { font-family: var(--serif); color: var(--story-text); background: var(--story-bg); border-left: 3px solid var(--green); border-radius: 0 var(--r-md) var(--r-md) 0; padding: 10px 14px; margin: 0 0 10px; }
details.done { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 14px; }
details.done > summary { cursor: pointer; color: var(--muted); font-weight: 500; list-style: none; display: flex; align-items: center; gap: 8px; }
details.done > summary::-webkit-details-marker { display: none; }
details.done > summary::before { content: "▸"; transition: transform .15s; }
details.done[open] > summary::before { transform: rotate(90deg); }
.all-clear { color: var(--green); margin: 0; }
.ask-card { border-left: 3px solid var(--amber); }
.waiting { margin-top: 18px; }
.waiting h3 { margin: 0 0 2px; color: var(--amber); }
.waiting .count { font-weight: 400; }
.callout { margin-top: 16px; padding: 16px; background: var(--amber-soft); border-radius: var(--r-md); }
.callout.ready { background: var(--green-soft); }  /* Claude has enough to draft */

/* Add Session conversation */
.messages { display: grid; gap: 12px; margin: 8px 0 16px; }
.msg { border-radius: var(--r-md); padding: 12px 16px; max-width: 92%; }
.msg.reviewer { background: var(--surface-2); justify-self: end; }
.msg.claude { background: var(--story-bg); border: 1px solid var(--story-line); }
.msg-who { font-size: var(--fs-xs); font-weight: 600; color: var(--muted); margin-bottom: 4px; }
.msg-body p { margin: 0 0 .6em; } .msg-body > :last-child { margin-bottom: 0; }
.msg-body ul, .msg-body ol { margin: .2em 0 .6em; padding-left: 1.3em; }
.msg.thinking { display: none; color: var(--muted); font-style: italic; }
.msg.thinking.htmx-request { display: block; }
details.conversation > summary { list-style: none; }
details.conversation[open] > summary { margin-bottom: 12px; }

/* Story text on a faint scroll (Brett, 2026-11-05; docs/mockups/world-entries/ board 5: Umber, Inter, Faint): askdnd's own
   type on a parchment a shade warmer than the page that fades into it at the sides, never a box. A session's recap adds the
   rods (.scroll around it), a first letter and a fleuron where the tale ends; an entry's story is the parchment alone. */
.story { position: relative; isolation: isolate; font-family: var(--sans); font-size: var(--fs-md); line-height: 1.7; color: var(--story-text); padding: 22px 34px; }
.story::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: radial-gradient(ellipse 120% 160% at 50% 50%, var(--parch-1) 0%, var(--parch-2) 70%, var(--parch-3) 100%); opacity: .45; filter: saturate(.7); mask-image: linear-gradient(90deg, transparent 0, var(--bg) 5%, var(--bg) 95%, transparent 100%); }
.scroll::before, .scroll::after { content: ""; display: block; height: clamp(12px, 1.25vw, 18px); margin: 0 -22px; border-radius: var(--r-pill);
  background: radial-gradient(circle at 7px 50%, var(--rod-cap) 0 6px, transparent 6.5px), radial-gradient(circle at calc(100% - 7px) 50%, var(--rod-cap) 0 6px, transparent 6.5px),
              linear-gradient(180deg, var(--rod-hi) 0%, var(--rod-2) 50%, var(--rod-3) 100%); }
.scroll .story { font-size: 1.0625em; line-height: 1.75; padding: 40px 64px 34px; }
.scroll .story::before { opacity: .7; filter: saturate(.8); mask-image: linear-gradient(90deg, transparent 0, var(--bg) 4%, var(--bg) 96%, transparent 100%); }
.scroll .tale-text > p:first-of-type::first-letter { float: left; font-family: var(--tale-cap); font-weight: 500; font-size: 3.4em; line-height: .8; padding: 6px 10px 0 0; color: var(--story-cap); }
.tale-text > :last-child { margin-bottom: 0; }
.tale-end { text-align: center; color: var(--story-fleuron); margin-top: var(--sp-4); }
/* The Story page, option A (Brett, 2026-11-05; docs/mockups/story-page/): one reading column, the tale on its scroll,
   then what came of it; All sessions the same column, each session a rolled scroll, the newest open */
.tale { max-width: 820px; margin: var(--sp-5) auto 0; display: flex; flex-direction: column; gap: var(--sp-6); }
.tale > .subtitle { margin: 0; }
.tale-head { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; margin-bottom: var(--sp-5); }
.tale-head h2 { flex: none; margin: 0; font-size: var(--fs-xl); font-weight: 600; color: var(--story-head); text-wrap: balance; }
.tale-kicker { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--story-cap); }
.tale-dm { border-top: 1px solid var(--story-line); margin-top: var(--sp-4); padding-top: var(--sp-4); font-size: var(--fs-sm); line-height: 1.6; }
.tale-dm b { display: block; font-size: var(--fs-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--amber); }
.tale-dm p { margin: var(--sp-1) 0 0; color: var(--story-text); }
.tale-after { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-6); padding: 0 var(--sp-2); }
.tale-after:has(> section:only-child) { grid-template-columns: minmax(0, 1fr); }
.tale-label { margin: 0 0 var(--sp-3); font-size: var(--fs-xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--amber); }
.tale-moments .ev, .tale-moments .ev + .ev { display: block; padding: 0 0 var(--sp-3) var(--sp-4); border: 0; }
.tale-moments .kind { display: block; font-size: var(--fs-xs); }
.tale-threads li { display: flex; flex-wrap: wrap; gap: 0 var(--sp-1); padding: var(--sp-1) 0; }
.tale-threads li > .muted { flex-basis: 100%; font-size: var(--fs-sm); }
.tale-theory i { color: var(--muted); }
.tale-foot { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--sp-4); padding: 0 var(--sp-2); }
details.tale-roll { background: none; border: 0; padding: 0; margin: 0; }
details.tale-roll > summary { list-style: none; cursor: pointer; display: grid; grid-template-columns: 11em minmax(0, 1fr) auto; gap: var(--sp-4); align-items: baseline; padding: 22px var(--sp-6);
  border-radius: var(--r-sm); background: linear-gradient(180deg, var(--rod-hi), var(--rod-3)) top / 100% 9px no-repeat, linear-gradient(180deg, var(--rod-hi), var(--rod-3)) bottom / 100% 9px no-repeat, var(--parch-2); }
details.tale-roll > summary::-webkit-details-marker { display: none; }
details.tale-roll > summary:hover { background-color: var(--parch-1); }
details.tale-roll[open] > summary { background: none; padding: 0 var(--sp-2); margin-bottom: var(--sp-3); }  /* open: its row is the scroll's title */
details.tale-roll[open] .tale-roll-text .note { display: none; }
details.tale-roll[open] .tale-roll-text b { font-size: var(--fs-lg); }
.tale-roll-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tale-roll-text b { color: var(--story-head); font-weight: 600; }
.tale-roll-text .note { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tale-roll-count { white-space: nowrap; }
details.tale-roll .session-body { display: flex; flex-direction: column; gap: var(--sp-5); }
#t-sessions .tale { gap: var(--sp-4); }
.tale-order { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.tale-order .subtitle { margin: 0; }
.seg a { display: inline-flex; align-items: center; border-radius: var(--r-sm); color: var(--muted); padding: 0 var(--sp-3); min-height: calc(var(--control-h) - 6px); font-size: var(--fs-sm); font-weight: 500; text-decoration: none; white-space: nowrap; }
.seg a:hover { color: var(--text); }
.seg a[aria-current=true] { background: var(--selected-bg); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--blue); }
details.tale-more { margin: 0; }
details.tale-more > summary { cursor: pointer; padding: 0 0 var(--sp-2) var(--sp-4); color: var(--link); font-size: var(--fs-sm); }
details.tale-more[open] > summary { display: none; }  /* opened, its moments simply carry on the list */
.story p, .story li { max-width: 66ch; }
.story p { margin: 0 0 1em; }
.story > :last-child { margin-bottom: 0; }
.story strong, .story b { color: var(--story-link); font-weight: 600; }

/* Key moments */
.events { list-style: none; margin: 0; padding: 0; }
.ev { display: flex; gap: 14px; align-items: baseline; padding: 12px 0; }
.ev + .ev { border-top: 1px solid var(--line); }
.ev-text { flex: 1; }
.ev-text small { display: block; color: var(--faint); font-size: var(--fs-sm); }
.ev .btn.link { opacity: 0; }
.ev:hover .btn.link, .ev:focus-within .btn.link { opacity: 1; }
@media (hover: none) { .ev .btn.link { opacity: 1; } }
.kind { flex: none; width: 76px; font-size: var(--fs-xs); font-weight: 600; text-transform: capitalize; }
.kind.event { color: var(--muted); }
.kind.reveal { color: var(--violet); }
.kind.decision { color: var(--blue); }
.kind.loot { color: var(--amber); }
.kind.theory { color: var(--muted); font-style: italic; }
.ev.theory .ev-text { font-style: italic; color: var(--muted); }
.ev-form { display: grid; gap: 10px; width: 100%; }

/* Suggestions */
.sug-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.sug-head b { font-size: var(--fs-md); }
.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
.compare.single { grid-template-columns: 1fr; }
.compare > div { font-size: var(--fs-md); padding: 12px 14px; border-radius: var(--r-md); background: var(--surface-2); }
.compare > div.after { background: var(--story-bg); border: 1px solid var(--story-line); color: var(--story-text); }
.compare span { display: block; font-size: var(--fs-xs); color: var(--muted); margin-bottom: 4px; }
/* A marked-up edit (render.word_diff): someone's words with Claude's changes, so nothing is replaced unseen */
.markup del { color: var(--muted); background: var(--red-soft); text-decoration: line-through; border-radius: var(--r-xs); }
.markup ins { color: var(--text-strong); background: var(--green-soft); text-decoration: none; border-radius: var(--r-xs); }
.recap-markup { white-space: pre-wrap; font-family: var(--serif); color: var(--story-text); background: var(--story-bg); border-radius: var(--r-md); padding: 12px 14px; margin: 0 0 10px; max-height: 24em; overflow: auto; }
.kept-words { padding-top: 0; }
.guide-del { color: var(--muted); background: var(--red-soft); }
.guide-ins { color: var(--text-strong); background: var(--green-soft); text-decoration: none; }

/* Progress panel */
.aside { position: sticky; top: 84px; }
.panel { background: var(--surface); border-radius: var(--r-lg); padding: 22px; }
.panel h3 { font-size: var(--fs-md); margin: 0 0 12px; }
.checklist { list-style: none; padding: 0; margin: 0 0 20px; }
.checklist li { display: flex; gap: 12px; align-items: center; padding: 7px 0; }
.tick { width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--line-strong); flex: none; display: grid; place-items: center; font-size: var(--fs-xs); color: var(--chrome); font-weight: 700; }
.tick.on { background: var(--green); border-color: var(--green); }
.checklist small { color: var(--muted); margin-left: auto; font-size: var(--fs-sm); }
.panel .btn.primary { width: 100%; min-height: 48px; font-size: var(--fs-md); }
.btn.primary.is-done, .btn.primary.is-done:hover { background: var(--green-soft); border-color: transparent; color: var(--green); cursor: default; }
.panel .note { margin: 12px 0 0; }
.next { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); display: grid; gap: 8px; }
.next .btn { width: 100%; text-align: center; }
.source { margin-top: 16px; padding: 0 22px; color: var(--muted); font-size: var(--fs-sm); }
.mobile-bar { display: none; }

/* Stat tiles */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 24px; }
.stat { background: var(--surface); border-radius: var(--r-lg); padding: 18px 20px; }
.stat span { display: block; color: var(--muted); font-size: var(--fs-sm); }
.stat b { display: block; font-size: var(--fs-2xl); font-weight: 700; margin: 2px 0; }
.stat small { color: var(--muted); }
.stat.attention { box-shadow: inset 3px 0 0 var(--red); }
.stat.attention small { color: var(--red); }
@media (max-width: 900px) { .stats { grid-template-columns: 1fr; } }
.table-scroll { overflow-x: auto; }  /* every wide table sits in one: it scrolls inside its own frame, never the page (DESIGN.md › Narrow screens) */
.usage-calls { width: 100%; margin-top: var(--sp-3); }
.usage-calls .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Tables and lists */
table.list { width: 100%; border-collapse: collapse; }
table.list td.term { width: 30%; }  /* Campaign › Glossary: the word, then what it means */
table.list th { text-align: left; font-size: var(--fs-sm); font-weight: 600; color: var(--muted); padding: 0 10px 8px; }
table.list td { padding: 14px 10px; border-top: 1px solid var(--line); vertical-align: top; }
table.list td a { color: var(--text); font-weight: 500; text-decoration: none; }
table.list td a:hover { color: var(--text-strong); text-decoration: underline; }
.entry b { display: block; }
.entry p { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-sm); }
.ev-text small a { color: var(--muted); text-decoration: none; }
.ev-text small a:hover { color: var(--text); text-decoration: underline; }

/* Entity pages */
.rels, .facts { list-style: none; margin: 10px 0 0; padding: 0; }
.rels li, .facts li { padding: 10px 0; }
.rels li + li, .facts li + li { border-top: 1px solid var(--line); }
.rels a { color: var(--text); font-weight: 500; }
.facts li { font-family: var(--serif); color: var(--story-text); font-size: var(--fs-md); }
.facts small { display: block; font-family: var(--sans); color: var(--faint); font-size: var(--fs-sm); }
.add-fact { margin-top: 18px; }
.wait { display: none; margin-top: 10px; font-style: italic; }
.wait.htmx-request { display: block; }

/* Rendered documents (synced notes) */
.doc { line-height: 1.6; }
.doc h1 { font-size: var(--fs-xl); margin-top: 2.2rem; padding-bottom: .3rem; border-bottom: 1px solid var(--line); }
.doc h2 { font-size: var(--fs-lg); margin-top: 1.8rem; }
.doc h3, .doc h4, .doc h5, .doc h6 { font-size: var(--fs-md); margin-top: 1.25rem; }
.doc ul { padding-left: 1.4rem; margin: .3rem 0; }
.doc li { margin: .15rem 0; }
.doc table { border-collapse: collapse; width: 100%; margin: .75rem 0; font-size: var(--fs-sm); }
.doc td, .doc th { border: 1px solid var(--line); padding: .4rem .6rem; vertical-align: top; text-align: left; }
.doc th { background: var(--surface-2); }
.doc img { display: block; margin: .6rem 0; border-radius: var(--r-sm); background: var(--text-strong); }

/* Family tree: nested lists with connector lines */
.tree, .tree ul { list-style: none; margin: 0; padding: 0; }
.tree { overflow-x: auto; padding-bottom: 4px; }
.tree > li { padding: 6px 0; }
.tree > li + li { margin-top: 10px; padding-top: 16px; border-top: 1px solid var(--line); }
.tree ul { margin-left: 14px; padding-left: 22px; }
.tree ul > li { position: relative; padding: 5px 0; }
.tree ul > li::before { content: ""; position: absolute; left: -22px; top: 0; bottom: 0; border-left: 2px solid var(--line-strong); }
.tree ul > li:last-child::before { bottom: auto; height: 25px; }
.tree ul > li::after { content: ""; position: absolute; left: -22px; top: 24px; width: 16px; border-top: 2px solid var(--line-strong); }
.person { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; background: var(--surface-2); border-radius: var(--r-md); padding: 7px 12px; min-height: 38px; }
.person a { color: var(--text); text-decoration: none; font-weight: 500; }
.person a:hover { text-decoration: underline; }
.person .ring { color: var(--red); font-size: var(--fs-md); line-height: 1; }

/* @-mention suggestions */
.mention-box { position: absolute; z-index: 20; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: 4px; box-shadow: var(--lift); max-width: calc(100vw - 32px); }
.mention-opt { padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer; min-height: 40px; color: var(--text); }
.mention-opt small { display: block; color: var(--muted); font-size: var(--fs-xs); }
.mention-opt[aria-selected=true], .mention-opt:hover { background: var(--surface); box-shadow: inset 2px 0 0 var(--red); }

/* Toast */

/* Login */
.login { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.login .section { width: 100%; max-width: 400px; text-align: center; }
.login img { width: 72px; height: 72px; border-radius: var(--r-lg); }
.login h1 { margin: var(--sp-4) 0 6px; }
.login .tagline { margin: 0 0 var(--sp-5); }
.login .flash { text-align: left; }

@media (max-width: 900px) {
  .layout { grid-template-columns: 1fr; }
  .aside { position: static; }
  .aside .publish-block { display: none; }
  .mobile-bar { display: flex; position: fixed; left: 0; right: 0; bottom: 0; padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); background: var(--chrome); border-top: 2px solid var(--red); gap: 12px; align-items: center; z-index: 4; }
  .mobile-bar .btn.primary { flex: 1; min-height: 48px; }
  .nav, .nav-sep, .table-nav, .manage, .account, .brand-word { display: none; }
  .campaign-name { max-width: none; font-size: var(--fs-sm); }
  .campaign-switch, span.campaign-name { flex: 1; }
  .bottom-nav { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); position: fixed; left: 0; right: 0; bottom: 0; z-index: 6; background: var(--chrome); border-top: 1px solid var(--line); padding: 4px 4px env(safe-area-inset-bottom); }
  .bottom-nav > a, .bottom-nav > button { display: grid; place-items: center; min-height: 52px; color: var(--muted); text-decoration: none; font-size: var(--fs-xs); font-weight: 500; background: none; border: none; border-radius: var(--r-md); }
  .bottom-nav > a.active, .bottom-nav > button.active { color: var(--text-strong); font-weight: 700; box-shadow: inset 0 2px 0 var(--red); }
  .bottom-nav > a.bn-char { align-content: center; gap: 2px; padding: 0 2px; position: relative; }
  .bn-char .cond-dots { position: absolute; top: 4px; left: calc(50% + 8px); }
  .bn-char > span:not(.char-av, .cond-dots) { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.2; }
  .menu.sheet-menu { position: fixed; z-index: 40; top: auto; left: auto; right: 8px; bottom: calc(64px + env(safe-area-inset-bottom)); max-height: calc(100vh - 140px); overflow-y: auto; }
  body { padding-bottom: calc(60px + env(safe-area-inset-bottom)); }
  .mobile-bar { bottom: calc(60px + env(safe-area-inset-bottom)) !important; }
  .live-ago { display: none; }
  .live-word { display: inline; }
  .live .menu.live-menu { position: fixed; top: 64px; right: 12px; }
  .topbar-inner { gap: 10px; padding: 0 16px; }
  .page { padding: 24px 16px 120px; }
  .section { padding: 22px 18px; }
  .story { padding: 18px 20px; }
  .scroll::before, .scroll::after { margin: 0 -4px; }
  .scroll .story { padding: 24px 18px 20px; }
  .tale-after { grid-template-columns: minmax(0, 1fr); }
  details.tale-roll > summary { grid-template-columns: minmax(0, 1fr); gap: var(--sp-1); padding: var(--sp-3) var(--sp-4); }
  .compare { grid-template-columns: 1fr; }
}

/* Linked names and the peek panel */
a.ent { color: inherit; text-decoration: underline; text-decoration-color: var(--red-line); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a.ent:hover { text-decoration-color: var(--red); color: var(--text-strong); }
.story a.ent { color: var(--story-link); }
.peek { position: fixed; top: 62px; right: 0; bottom: 0; width: var(--panel-w); max-width: 100%; background: var(--surface); border-left: 1px solid var(--line); box-shadow: -12px 0 30px var(--shadow); z-index: 8; display: flex; flex-direction: column; transform: translateX(100%); transition: transform .18s ease; }
.peek.open { transform: none; }
.peek-head { display: flex; gap: 12px; align-items: flex-start; justify-content: space-between; padding: 20px 22px 8px; }
.peek-head h2 { font-size: var(--fs-lg); margin: 2px 0; }
.peek-body { padding: 8px 22px 16px; overflow-y: auto; flex: 1; }
.peek-body .story { padding: 14px 18px; }
.peek-body h3 { font-size: var(--fs-md); margin: 18px 0 6px; }
.peek-foot { padding: 14px 22px; border-top: 1px solid var(--line); }
ul.plain { list-style: none; padding: 0; margin: 0; display: grid; gap: 6px; }
.peek-backdrop { position: fixed; inset: 0; z-index: 7; background: transparent; }
@media (max-width: 900px) {
  .peek { top: auto; left: 0; right: 0; width: 100%; max-height: 78vh; border-left: none; border-top: 2px solid var(--red); border-radius: var(--r-lg) var(--r-lg) 0 0; transform: translateY(100%); }
  .peek.open { transform: none; }
  .peek-backdrop { background: var(--backdrop); }
}

/* Family tree diagram */

/* Suggest a correction or addition */
details.suggest { margin-top: 14px; }
details.suggest > summary { cursor: pointer; color: var(--muted); font-size: var(--fs-sm); }
details.suggest > summary:hover { color: var(--text); }
details.suggest[open] > summary { margin-bottom: 10px; }
.suggest-thanks { margin: 10px 0; }

/* Characters */
.quiet-warn { color: var(--amber); }
.ability { background: var(--surface-2); border-radius: var(--r-md); padding: 10px 12px; display: grid; align-content: start; }
.ability small { color: var(--muted); font-size: var(--fs-xs); line-height: 1.3; }
.abilities { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; margin-top: 12px; text-align: center; }
.ability b { font-size: var(--fs-lg); line-height: 1.2; }  /* the same size as the number tiles above */
.ability > span:not(.note) { color: var(--muted); font-size: var(--fs-sm); }
details.feat { border-top: 1px solid var(--line); }
details.feat:last-of-type { border-bottom: 1px solid var(--line); }
details.feat > summary { cursor: pointer; padding: 10px 0; font-weight: 500; list-style-position: outside; }
details.feat > summary .tag { margin-left: 6px; font-weight: 600; }
details.feat > summary:hover { color: var(--text-strong); }
.skills { list-style: none; margin: 12px 0 8px; padding: 0; display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px 28px; }
.skills li { display: flex; justify-content: space-between; padding: 5px 0; color: var(--muted); }
.skills li.prof { color: var(--text); }
.skills li.prof b { color: var(--green); }
.profs { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; margin: 18px 0 0; }
.profs dt { color: var(--muted); }
.profs dd { margin: 0; }
@media (max-width: 900px) {
  .abilities { grid-template-columns: repeat(3, 1fr); }
  .skills { grid-template-columns: 1fr; }
  .profs { grid-template-columns: 1fr; gap: 0; }
  .profs dd { margin-bottom: 10px; }
}

/* People */

/* Busy buttons (app.js): something is happening */
@keyframes spin { to { transform: rotate(360deg); } }
.btn.busy, form.htmx-request button[type=submit], .btn.htmx-request { cursor: progress; opacity: .85; }
.btn.busy::before, form.htmx-request button[type=submit]::before, .btn.htmx-request::before {
  content: ""; display: inline-block; width: 13px; height: 13px; margin: 0 8px -1px 0; border-radius: 50%;
  border: 2px solid currentColor; border-right-color: transparent; animation: spin .7s linear infinite;
}
.btn.link.busy::before { margin-right: 6px; }
.busy-note { margin: -18px 0 24px; color: var(--muted); font-size: var(--fs-sm); }

/* Battle, Character, Party */
.btn.small { min-height: var(--control-h-sm); padding: 0 var(--sp-3); }
.btn.link.small { min-height: 0; padding: var(--sp-1) var(--sp-2); }
.char-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 14px; }
.strip > div { background: var(--surface); border-radius: var(--r-md); padding: 8px 14px; }
.strip small { display: block; color: var(--muted); font-size: var(--fs-xs); } .strip b { font-size: var(--fs-lg); }
.abilities-row { padding: 16px 18px; }
.abil6 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; }
.basic-one { white-space: nowrap; }  /* (a name and its comma stay together: never ", Utilize" at a line's start) */
.ab { background: var(--surface-2); border-radius: var(--r-md); padding: 8px 10px; }
.ab .top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 0 6px; }  /* a long name (Intelligence) puts the number under it, not past the tile's edge */
.ab small { color: var(--muted); font-size: var(--fs-xs); } .ab b { font-size: var(--fs-lg); }
.ab .sv { font-size: var(--fs-sm); color: var(--muted); } .ab .sv.prof { color: var(--text); }
.ab .sv.prof::before { content: "● "; color: var(--green); font-size: var(--fs-xs); }
.tabbar { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin: 0 0 12px; overflow-x: auto; scrollbar-width: none; }
/* A tab row that scrolls sideways fades the edge it's cut at (app.js: .cut-l, .cut-r; the UI pass, 2026-11-02) */
.tabbar.cut-r { mask-image: linear-gradient(to right, var(--bg) calc(100% - 36px), transparent); }
.tabbar.cut-l { mask-image: linear-gradient(to left, var(--bg) calc(100% - 36px), transparent); }
.tabbar.cut-l.cut-r { mask-image: linear-gradient(to right, transparent, var(--bg) 36px, var(--bg) calc(100% - 36px), transparent); }
.tabbar button { background: none; border: none; padding: 9px 13px; color: var(--muted); font-weight: 500; white-space: nowrap; min-height: 40px; }
.tabbar button[aria-selected=true] { color: var(--text-strong); box-shadow: inset 0 -2px 0 var(--red); }
.tabbar .count { font-size: var(--fs-xs); }
details.opt { border-top: 1px solid var(--line); }
details.opt > summary { list-style: none; cursor: pointer; padding: 9px 0; display: flex; gap: 8px; align-items: baseline; }
details.opt > summary::-webkit-details-marker { display: none; }
details.opt > summary .grow, .situ .grow { flex: 1; min-width: 0; }
details.opt > summary b:hover { text-decoration: underline; text-decoration-color: var(--faint); }
.meta { display: block; color: var(--muted); font-size: var(--fs-sm); font-weight: 400; margin-top: 14px; }
.hit { font-weight: 700; white-space: nowrap; }
.pick { background: none; border: none; padding: 0 2px; min-height: 0; color: var(--faint); line-height: 1.2; }
.pick .ico-pin, .pick .ico-bookmark { display: inline-block; vertical-align: -3px; } .pick .ico-pin path, .pick .ico-bookmark path { fill: none; }
.pick.on .ico-pin path, .pick.on .ico-bookmark path { fill: currentColor; }  /* pin (kind star) and bookmark (shortlist) */
.pick.star.on { color: var(--amber); }
.pick.static { cursor: default; }
.situ { display: flex; gap: 10px; align-items: baseline; padding: 9px 0; border-top: 1px solid var(--line); }
.situ .ic { width: 18px; text-align: center; font-weight: 700; flex: none; }
.ic.advantage { color: var(--amber); } .ic.disadvantage { color: var(--link); } .ic.immunity { color: var(--green); }
.ic.resistance, .ic.bonus { color: var(--blue); } .ic.reaction { color: var(--violet); }
/* Manage › Table: a person and their character on one row (docs/mockups/the-table.html) */
.manage-page .section.seats > h2:not(:first-of-type) { margin-top: var(--sp-5); }
.seat { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); margin-top: var(--sp-2); }
.seat[open] { border-color: var(--blue-line); }
.seat-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: var(--sp-3); align-items: center; padding: var(--sp-2) var(--sp-3); cursor: pointer; list-style: none; }
.seat-row::-webkit-details-marker { display: none; }
.seat-pic { position: relative; width: 38px; height: 38px; border-radius: 50%; overflow: hidden; display: grid; place-items: center; background: var(--surface-3); color: var(--muted); font-weight: 700; }
.seat-pic img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.seat-pic.none { background: none; border: 1px dashed var(--line-strong); }
.seat-who { display: grid; min-width: 0; }
.seat-who b, .seat-who .note { overflow-wrap: anywhere; }
.seat-dm { color: var(--amber); border-color: var(--amber); }
.seat-panel { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); padding: var(--sp-2) var(--sp-3) var(--sp-3); border-top: 1px solid var(--line); }
.seat-panel .form-narrow { grid-template-columns: minmax(0, 1fr); }
.seat-panel .field { min-width: 0; }
.seat-panel select, .seat-panel input[type="text"] { width: 100%; max-width: 100%; min-width: 0; }
.seat-field { max-width: 320px; }
.seat-link { display: grid; gap: var(--sp-1); }
.seat-name { display: flex; align-items: flex-end; gap: var(--sp-1); max-width: 420px; }
.seat-name .field { flex: 1; }
.seat-remove { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: flex-start; }
.seat-ask > summary { list-style: none; cursor: pointer; }
.seat-ask > summary::-webkit-details-marker { display: none; }
.seat-ask[open] { flex-basis: 100%; }
.seat-ask p { margin: var(--sp-2) 0; font-size: var(--fs-sm); }
.seat-add { margin-bottom: var(--sp-3); }
.seat-add > summary { list-style: none; cursor: pointer; }
.seat-add > summary::-webkit-details-marker { display: none; }
.seat-add .seat-panel { border: 1px solid var(--line); border-radius: var(--r-md); margin-top: var(--sp-2); }
details.fold { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 8px; }
details.fold > summary { cursor: pointer; color: var(--muted); font-size: var(--fs-sm); }
.vitals { display: flex; gap: 8px; flex-wrap: wrap; }
.vital { background: var(--surface-2); border-radius: var(--r-md); padding: 6px 12px; min-width: 80px; }
.vital small { display: block; color: var(--muted); font-size: var(--fs-xs); } .vital b { font-size: var(--fs-lg); }
.pips { display: inline-flex; gap: 3px; vertical-align: -1px; }
.pip { width: 13px; height: 13px; border-radius: 50%; border: 2px solid var(--green); background: var(--green); display: inline-block; }
.pip.used { background: transparent; border-color: var(--faint); }
.res-row { display: flex; gap: 6px 0; flex-wrap: wrap; align-items: center; font-size: var(--fs-sm); margin-top: 10px; }
.res-row span { white-space: nowrap; }
/* Battle's numbers scroll with the page: stuck at the top they took over a third of a laptop's screen (Brett, 2026-10-27:
   not worth the complexity of shrinking them; docs/mockups/battle-header.html was the alternative) */
/* Battle's blocks (the numbers, Wild Shape, the Status row, the spell bar, the action columns) stack with one gap and
   no margins of their own, so each sits the same distance from the next (Brett, 2026-10-29; DESIGN.md › Space) */
#battle-body { display: flex; flex-direction: column; gap: var(--sp-4); }
#battle-body > * { margin-top: 0; margin-bottom: 0; }
.battlebar { padding: 8px 0 12px; border-bottom: 1px solid var(--line); margin-bottom: 16px; }
.sec-head { display: flex; align-items: baseline; gap: 10px; justify-content: space-between; flex-wrap: wrap; margin-bottom: 8px; }
.switch { position: relative; width: 42px; height: 24px; border-radius: var(--r-pill); background: var(--surface-2); border: 1px solid var(--line); padding: 0; flex: none; }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--muted); transition: left .15s; }
.switch[aria-checked=true] { background: var(--green-soft); border-color: var(--green); }
.switch[aria-checked=true]::after { left: 21px; background: var(--green); }
.switch:disabled { opacity: .6; cursor: default; }
button.switch { cursor: pointer; }
/* The VTT switch (Manage › Settings › Virtual tabletop) and a test campaign's tag (VTT 0.1) */
.vtt-test { display: flex; align-items: flex-start; gap: var(--sp-3); margin: var(--sp-3) 0 var(--sp-4); }
.vtt-test b { display: block; color: var(--text-strong); }
.vtt-test .note { display: block; }
.campaign-test { font-size: var(--fs-xs); margin-left: 4px; vertical-align: 1px; }
.econ { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.econ .section { position: relative; margin: 0; padding: 16px 18px; display: flex; flex-direction: column; }
.econ h2 small { display: block; color: var(--muted); font-size: var(--fs-sm); font-weight: 400; }
.move { border-top: 1px solid var(--line); padding: 9px 0; cursor: pointer; }
.move.static { cursor: default; }
.basic-act { all: unset; cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; text-decoration-color: var(--muted); }  /* Battle's Dash, Dodge…: each opens its rule (feedback 8) */
.basic-act:hover, .basic-act:focus-visible { color: var(--amber); text-decoration-color: currentColor; }
.move:not(.static):hover b { color: var(--text-strong); text-decoration: underline; text-decoration-color: var(--faint); }
.move .hit { float: right; margin-left: 8px; }
.seeall { margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line); }
.lower { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.lower .section { margin: 0; }
.peek.modal { top: 50%; left: 50%; right: auto; bottom: auto; width: min(var(--modal-w), calc(100% - 32px)); max-height: 86vh; overflow: hidden; transform: translate(-50%, -46%); opacity: 0; border: none; border-top: 3px solid var(--red); border-radius: var(--r-lg); transition: transform .18s ease, opacity .18s ease; }
.peek.modal.open { transform: translate(-50%, -50%); opacity: 1; }
.peek-backdrop.dim { background: var(--backdrop-strong); }
.peek-foot.row { display: flex; gap: 8px; align-items: center; }
/* A window that is a form (DESIGN.md › Windows): the form holds the scrolling body and the foot, so its actions sit at
   the window's bottom edge however long the body is */
.peek-form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
@media (max-width: 900px) {
  .econ, .lower { grid-template-columns: 1fr; }
  .abil6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .peek.modal { top: auto; left: 0; bottom: 0; width: 100%; transform: translateY(100%); border-radius: var(--r-lg) var(--r-lg) 0 0; }
  .peek.modal.open { transform: none; }
}
.econ h2, .lower h2, .sec-head h2 { flex: none; }
details.opt .meta, .move .meta, .situ .meta { margin-top: 2px; }
.forget { list-style: none; margin: 6px 0 0; padding: 0; }
.forget li { display: flex; gap: 10px; justify-content: space-between; align-items: baseline; padding: 7px 0; border-top: 1px solid var(--line); }
.forget li:first-child { border-top: none; }
.forget-x { background: none; border: none; color: var(--faint); font-size: var(--fs-lg); line-height: 1; padding: 0 4px; }
.forget-x:hover { color: var(--link); }
[data-sort-column] .move { cursor: grab; }
.drag-ghost { opacity: .4; background: var(--surface-2); }
/* Spell planner */
.planner-classes { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; margin-bottom: 14px; }
.planner-class { background: var(--surface-2); border-radius: var(--r-md); padding: 10px 14px; }
/* A switch is a track; the choice that's on sits in it as a rounded piece with a small gap all round, so its corners and
   the track's always agree (Brett, 2026-11-02: no dividers to meet a rounded corner) */
.seg { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--line); border-radius: var(--r-md); }
.seg button { background: none; border: none; border-radius: var(--r-sm); color: var(--muted); padding: 0 var(--sp-3); min-height: calc(var(--control-h) - 6px); font-size: var(--fs-sm); font-weight: 500; }
/* A switch's choice that's on looks like a chip that's on (Brett, 2026-11-02: red switches read as buttons; red stays for
   the one main action and the page's own Battle · Sheet · Plan) */
.seg button[aria-pressed=true] { background: var(--selected-bg); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--blue); border-radius: var(--r-sm); }
.filters { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-bottom: 8px; }
.filters button { background: none; border: 1px solid var(--line); color: var(--muted); border-radius: var(--r-pill); padding: 3px 11px; font-size: var(--fs-sm); min-height: 30px; }
.filters button[aria-pressed=true] { border-color: var(--blue); color: var(--text-strong); background: var(--selected-bg); }  /* (blue when on, as every chip: the UI pass, 2026-11-02) */
.filters .sep { width: 1px; height: 18px; background: var(--line); margin: 0 4px; }
.spell-row.not-ready > summary b { color: var(--muted); font-weight: 500; }
.pick.fav.on { color: var(--blue); }

/* Character page v3: dense rows, side panel for details (laptop first) */
.page.wide { max-width: 1360px; }
h3.divider.big::after { height: 2px; background: var(--line-strong); }
[id^=t-] > h3.divider:first-child, [id^=t-] > p + h3.divider { margin-top: 12px; }
.rows { display: grid; grid-template-columns: 1fr; column-gap: 28px; }
@media (min-width: 1100px) { .rows:not(.one) { grid-template-columns: 1fr 1fr; } }
.ri { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto auto; gap: 2px 10px; align-items: center;
  padding: 6px 6px; border-bottom: 1px solid var(--line); cursor: pointer; border-radius: var(--r-sm); min-height: 40px; }
.ri:hover { background: var(--surface-2); }
.ri-pk { display: inline-flex; gap: 2px; min-width: 18px; }
.ri-pk.end { justify-content: flex-end; }
.ri-main { min-width: 0; }
.ri-name { font-weight: 600; }
.ri-main .tag { margin-left: 4px; }
.tag.faint { background: transparent; border: 1px solid var(--line); color: var(--faint); }
.ri-what { display: block; color: var(--muted); font-size: var(--fs-sm); line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ri-mech { color: var(--muted); font-size: var(--fs-xs); text-align: right; white-space: nowrap; max-width: 210px; overflow: hidden; text-overflow: ellipsis; }
.ri-hit { font-weight: 700; white-space: nowrap; min-width: 44px; text-align: right; }
.ri .pick.remind { font-size: var(--fs-xs); padding: 0 7px; }
.ri.not-ready .ri-name { color: var(--muted); font-weight: 500; }
.kind-advantage { color: var(--amber) !important; background: var(--amber-soft) !important; }
.kind-immunity { color: var(--green) !important; background: var(--green-soft) !important; }
.kind-resistance, .kind-bonus { color: var(--blue) !important; background: var(--blue-soft) !important; }
.kind-reaction { color: var(--violet) !important; background: var(--violet-soft) !important; }
.kind-disadvantage, .kind-vulnerability { color: var(--link) !important; background: var(--red-soft) !important; }
@media (max-width: 640px) {
  .ri { grid-template-columns: auto minmax(0, 1fr) auto auto; }
  .ri-mech { display: none; }
}
/* Side panel details */
dl.mech { display: grid; grid-template-columns: max-content 1fr; gap: 5px 14px; margin: 0 0 6px; padding: 10px 12px; background: var(--surface-2); border-radius: var(--r-md); font-size: var(--fs-sm); }
dl.mech dt { color: var(--muted); } dl.mech dd { margin: 0; }
.detail-what { font-size: var(--fs-md); white-space: pre-line; }
.detail-full { color: var(--muted); font-size: var(--fs-sm); white-space: pre-line; }
.peek-body.detail h3 { font-size: var(--fs-sm); color: var(--text); margin: 16px 0 4px; }
.admin-move form { margin: 6px 0; }
@media (min-width: 1100px) { .peek { width: 480px; } }
.ri-line { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#t-situ .ri-line { white-space: normal; }
.bell { vertical-align: -3px; }
.pick.remind { font-size: var(--fs-xs); border: none; border-radius: var(--r-pill); padding: 0 3px; color: var(--faint); background: none; white-space: nowrap; }
.pick.remind.on { color: var(--violet); background: none; border: none; }
.pick.remind:hover { color: var(--violet); }
.insp small { display: block; } .insp.on b { color: var(--amber); }
.strip .insp.on { box-shadow: inset 0 0 0 1px var(--amber); }
.vital.insp.on { box-shadow: inset 0 0 0 1px var(--amber); }
/* The Dying tile (_char.html hp_tile; app/death_saves.py): Hit points at 0, with the death save marks and the roll */
.strip > .dying { grid-column: span 2; }
.strip > div.dying, .vital.dying { box-shadow: inset 0 0 0 1px var(--red-line); background: color-mix(in srgb, var(--red) 8%, var(--surface)); }
.dying small { color: var(--red-hover); font-weight: 600; }
.strip > div.dying.stable, .vital.dying.stable { box-shadow: inset 0 0 0 1px var(--green-line); background: color-mix(in srgb, var(--green) 7%, var(--surface)); }
.dying.stable small { color: var(--green); }
.ds-marks { display: inline-flex; gap: 12px; align-items: center; margin-left: 10px; vertical-align: middle; }
.ds-set { display: inline-flex; gap: 4px; align-items: center; }
.ds-pip { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--line-strong); display: inline-block; }
.ds-pip.s { background: var(--green); border-color: var(--green); }
.ds-pip.f { background: var(--red); border-color: var(--red); }
.ds-k { color: var(--muted); font-size: var(--fs-xs); margin-left: 2px; }
.ds-rule { display: block; }
.admin-move form.row { display: grid; grid-template-columns: 130px minmax(0, 1fr) auto; gap: 8px; align-items: center; flex-wrap: nowrap; }
.admin-move form.row select { width: 100%; }
/* Admin regrouping in the side panel: option buttons that save on click */
.admin-move { margin-top: 20px; padding: 12px 14px; border: 1px dashed var(--line-strong); border-radius: var(--r-md); background: var(--surface-2); }
.admin-head { font-size: var(--fs-sm); margin-bottom: 8px; }
.admin-head b { color: var(--amber); }
.correct-pick { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-2); font-size: var(--fs-sm); }
.correct-pick label { display: inline-flex; align-items: center; gap: var(--sp-1); }
.correct [data-scope="everyone"] { display: none; }
.correct:has(input[value="everyone"]:checked) [data-scope="everyone"] { display: block; }
.correct:has(input[value="everyone"]:checked) [data-scope="campaign"] { display: none; }
.ov-field { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 4px 10px; align-items: start; padding: 7px 0; border-top: 1px solid var(--line); }
.ov-field:first-of-type { border-top: none; }
.ov-label { color: var(--muted); font-size: var(--fs-sm); padding-top: 4px; }
.ov-options { display: flex; flex-wrap: wrap; gap: 4px; }
.ov-opt { background: none; border: 1px solid var(--line); color: var(--muted); border-radius: var(--r-pill); padding: 2px 10px; font-size: var(--fs-xs); min-height: 28px; }
.ov-opt:hover { color: var(--text); border-color: var(--line-strong); }
.ov-opt.on { background: var(--amber-soft); border-color: var(--amber); color: var(--amber); font-weight: 600; }
.ov-opt.htmx-request { opacity: .6; }
.ov-saved { grid-column: 2; color: var(--green); font-size: var(--fs-xs); min-height: 1em; }
.admin-foot { margin: 8px 0 0; }

/* Rule terms in descriptions: a quiet dotted underline; definition in a small pop-up */
.term { text-decoration: underline dotted; text-decoration-color: var(--faint); text-underline-offset: 3px; text-decoration-thickness: 1px; cursor: help; border-radius: var(--r-xs); }
.term:hover, .term.open { text-decoration-color: var(--amber); color: var(--text); }
.term:focus-visible { outline: 2px solid var(--amber); outline-offset: 1px; }
.term-tip { position: fixed; z-index: 30; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: 12px 14px; box-shadow: var(--lift); font-size: var(--fs-sm); line-height: 1.45; }
.term-kind { color: var(--faint); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; }
.term-name { font-weight: 600; margin: 1px 0 6px; }
.term-text { margin: 0; color: var(--text); white-space: pre-line; }
.term-where { color: var(--faint); font-size: var(--fs-xs); margin-top: 8px; }
.term-admin { display: flex; align-items: center; gap: 6px; margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line); color: var(--muted); font-size: var(--fs-xs); }
.tag.custom { background: var(--violet-soft); color: var(--violet); }

/* Pictures: D&D Beyond portraits on character pages, uploaded pictures on campaign pages */
.portrait { border-radius: 50%; object-fit: cover; background: var(--surface-2); flex: none; }
.portrait.md { width: 44px; height: 44px; }
.peek-head .portrait { margin-right: 12px; }
.entity-head { display: flex; gap: 24px; align-items: flex-start; justify-content: space-between; margin-bottom: 8px; }
.entity-head-text { flex: 1; min-width: 0; }
.entity-pic { width: 170px; flex: none; margin: 0; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.entity-pic img { width: 100%; max-height: 220px; object-fit: cover; border-radius: var(--r-lg); border: 1px solid var(--line); display: block; }
.entity-pic figcaption { margin: 0; }
.pic-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pic-form label.btn { cursor: pointer; white-space: nowrap; }
.peek-pic { width: 100%; max-height: 240px; object-fit: cover; border-radius: var(--r-md); margin-bottom: 12px; border: 1px solid var(--line); }
@media (max-width: 640px) { .entity-head { flex-direction: column-reverse; } .entity-pic { width: 100%; } .entity-pic img { max-height: 280px; } }
.dm-notes { margin-top: 14px; border-left: 3px solid var(--amber); background: var(--amber-soft); border-radius: 0 var(--r-md) var(--r-md) 0; padding: 10px 14px; }
.dm-notes p { margin: 6px 0 0; }
.detail-note { white-space: pre-line; border-left: 3px solid var(--line); padding: 4px 0 4px 12px; font-size: var(--fs-md); }

/* About and FAQ */
.help .section ul { padding-left: 20px; margin: 10px 0 0; }
.help .section li { margin: 6px 0; }
details.faq { border-top: 1px solid var(--line); padding: 12px 0; }
details.faq:first-of-type { margin-top: 12px; }
details.faq > summary { cursor: pointer; font-weight: 600; list-style: none; display: flex; gap: 8px; }
details.faq > summary::-webkit-details-marker { display: none; }
details.faq > summary::before { content: "▸"; color: var(--muted); transition: transform .15s; }
details.faq[open] > summary::before { transform: rotate(90deg); }
.faq-a { color: var(--muted); padding: 4px 0 0 18px; }
.faq-a p { margin: 8px 0 0; }
.faq-a b { color: var(--text); }
.site-foot { text-align: center; color: var(--faint); font-size: var(--fs-sm); padding: 28px 16px 36px; }
.site-foot a { color: var(--muted); }
/* Character › Background */
.traits-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px 18px; margin: 8px 0 4px; }
.traits-grid dt { color: var(--faint); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; }
.traits-grid dd { margin: 0; }
.story-block { margin: 8px 0 12px; }
.story-block p { margin: 3px 0 0; white-space: pre-line; color: var(--muted); }
/* Character page: the tabs and the current section heading stay in view while scrolling.
   --stick-top (the top bar) and --tabs-h are measured by app.js. */
.sheet .tabbar { position: sticky; top: var(--stick-top, 62px); z-index: 4; background: var(--surface); margin: 0 -30px 12px; padding: 0 30px; }
.sheet [id^=t-] h3.divider { scroll-margin-top: calc(var(--stick-top, 62px) + var(--tabs-h, 41px) + 8px); position: sticky; top: calc(var(--stick-top, 62px) + var(--tabs-h, 41px)); z-index: 3; background: var(--surface); padding: 8px 0 6px; margin-bottom: 4px; }
@media (max-width: 900px) { .sheet .tabbar { margin: 0 -18px 12px; padding: 0 18px; } }  /* sections' own padding from tablet down */
/* Resources: one line for limited uses, one for spell slots, with dividers between items */
.res-row .res { padding: 0 10px; border-left: 1px solid var(--line); }
.res-row .res:first-child, .res-row .res-label + .res { border-left: none; padding-left: 0; }
.res-row.slots { margin-top: 6px; }
.level-up { background: var(--surface); border: 1px solid var(--amber); border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); display: grid; gap: var(--sp-1); margin-bottom: var(--sp-2); }
.lu-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-1); }
.res-go { background: none; border: 0; color: inherit; font: inherit; cursor: pointer; }
.res-go:hover { color: var(--amber); }
.pips-go { display: inline-flex; align-items: center; padding: 0; vertical-align: middle; border-radius: var(--r-xs); }  /* a count's pips that open Use one and Give one back (count_pips) */
.pips-go:hover, .pips-go:focus-visible { outline: 1px solid var(--amber); outline-offset: 2px; }
.tile-short { display: none; }
.ph-short { display: none; }
@media (max-width: 640px) { .tile-full, .ph-full { display: none; } .tile-short, .ph-short { display: inline; } }  /* Battle's tiles are narrow on a phone: the whole label would be cut off */
@container battle-head (max-width: 1000px) { .tile-full, .ph-full { display: none; } .tile-short, .ph-short { display: inline; } }  /* (Nov 5: "Hit…" beside − and +) */
@media (max-width: 640px) { .res-row { flex-direction: column; align-items: flex-start; } .res-row .res { padding: 0; border-left: none; } }  /* each on its own line: a divider there only marks the wrap */
.res-label { color: var(--faint); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; margin-right: 12px; }
/* What an option uses up, next to it */
.spend { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-xs); color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 0 8px; margin-left: 4px; white-space: nowrap; vertical-align: middle; line-height: 1.6; }
.spend .pips { margin: 0; }
.spend.none { color: var(--link); border-color: var(--red-line); }
.mech dd > div + div { margin-top: 4px; }
.ri-spend { display: block; margin: 2px 0 1px; }
.ri-spend .spend:first-child { margin-left: 0; }
/* Number tiles open a panel explaining them */
.vital[data-panel-url], .strip > div[data-panel-url] { cursor: pointer; transition: border-color .12s, background .12s; }
.vital[data-panel-url]:hover { background: var(--surface-3); }
.strip > div[data-panel-url]:hover { background: var(--surface-2); }
/* The change mark (app.js, showInPlace): a number that changed glows for a moment with the change, whatever brought it */
.marked { animation: mark-glow 2.2s ease-out; }
@keyframes mark-glow { 0% { box-shadow: 0 0 0 2px var(--green), 0 0 18px var(--green-soft); } 100% { box-shadow: 0 0 0 0 transparent; } }
.pips.marked { border-radius: var(--r-pill); }
.mark-delta { display: inline-block; vertical-align: middle; margin-left: 6px; font-size: var(--fs-xs); font-weight: 700; line-height: 1; padding: 3px 7px; border-radius: var(--r-pill); background: var(--surface-2); animation: mark-fade 2.6s ease-out forwards; pointer-events: none; }
.mark-delta.down { color: var(--link); }
.mark-delta.up { color: var(--green); }
@keyframes mark-fade { 0%, 70% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .marked { animation: none; box-shadow: 0 0 0 2px var(--green); } .mark-delta { animation: none; } }
.ds-mark-sample.marked { animation-iteration-count: infinite; } .ds-mark-sample .mark-delta { animation: none; }  /* Manage › Design system's sample keeps glowing */
table.calc { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); margin: 4px 0 6px; }
table.calc td { padding: 5px 0; border-bottom: 1px solid var(--line); color: var(--muted); }
table.calc td.num { text-align: right; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; padding-left: 12px; }
table.calc tr.total td { border-bottom: none; border-top: 2px solid var(--line-strong); color: var(--text); font-weight: 700; }
.calc-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 640px) { .calc-pair { grid-template-columns: 1fr; } }
/* Rows inside the side panel: narrow, so stack details under the name instead of clipping */
.peek .ri { grid-template-columns: auto minmax(0, 1fr) auto auto; grid-template-areas: "pk main hit end" "pk mech mech end"; align-items: start; row-gap: 0; }
.peek .ri > .ri-pk:first-child { grid-area: pk; padding-top: 2px; }
.peek .ri > .ri-main { grid-area: main; }
.peek .ri > .ri-mech { grid-area: mech; text-align: left; white-space: normal; max-width: none; overflow: visible; }
.peek .ri > .ri-hit { grid-area: hit; padding-top: 1px; }
.peek .ri > .ri-pk.end { grid-area: end; padding-top: 2px; }
.peek .ri-line { white-space: normal; }
.peek .ri-what { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.peek .ri-mech:empty { display: none; }
/* Back, in the side panel, to what was open before */
.peek-back { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-sm); color: var(--muted); padding: 0; margin: 0 0 6px; min-height: 0; }
.peek-back:hover { color: var(--text); }
/* Party: resources as dots; spells and items in tabs with jump links */
.nowrap { white-space: nowrap; }  /* a phrase that reads wrong split ("Warlock 2", "Int +7 Adv"); the line breaks between them */
/* Party at a glance: each resource after a divider. The list starts 13px left of where it shows and its own first
   13px are clipped away, so the item that begins each line hides its divider (no "| Luck Points" when a row wraps) */
/* Character quick view (Party › name) */
.peek-body h3.divider { margin: 20px 0 8px; }
.brief-res > div { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 5px 0; border-bottom: 1px solid var(--line); }
.brief-res > div:last-child { border-bottom: none; }
.brief-res .note { margin-left: auto; }
.slots-inline { display: inline-flex; flex-wrap: wrap; gap: 4px 0; }
.slots-inline > span { padding: 0 10px; border-left: 1px solid var(--line); }
.slots-inline > span:first-child { border-left: none; padding-left: 0; }
.brief-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.brief-chips .chip { text-decoration: none; }
.brief-group { margin: 6px 0 10px; }
.brief-group-title { color: var(--faint); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 2px; }
.brief-list { list-style: none; margin: 0; padding: 0; }
.brief-list li { padding: 5px 0 5px 12px; border-left: 2px solid var(--line); margin: 3px 0; }
.brief-counted { margin-top: 8px; }
/* Character › Skills: grouped by ability */
.skills-top { display: flex; justify-content: space-between; gap: 10px 24px; flex-wrap: wrap; align-items: baseline; margin: 2px 0 12px; font-size: var(--fs-sm); color: var(--muted); }
.skills-top .passives { display: inline-flex; gap: 16px; }
.skills-top .passives b { color: var(--text); }
.skill-groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 14px; align-items: start; }
.skill-group { background: var(--surface-2); border-radius: var(--r-md); padding: 4px 12px 8px; }
.skill-head { display: flex; justify-content: space-between; align-items: baseline; padding: 8px 0 6px; border-bottom: 1px solid var(--line); margin-bottom: 2px; }
.skill-head span { text-transform: uppercase; letter-spacing: .06em; font-size: var(--fs-xs); font-weight: 700; color: var(--muted); }
.skill-head b { font-size: var(--fs-md); color: var(--muted); }
.skill { display: grid; grid-template-columns: 18px 1fr auto; gap: 8px; align-items: center; padding: 6px 4px; border-radius: var(--r-sm); cursor: pointer; }
.skill:hover { background: var(--line); }
.skill-name { color: var(--muted); }
.skill.proficient .skill-name, .skill.expertise .skill-name { color: var(--text); font-weight: 600; }
.skill-bonus { font-variant-numeric: tabular-nums; font-size: var(--fs-md); }
.skill.proficient .skill-bonus, .skill.expertise .skill-bonus { color: var(--green); }
.pm { font-size: var(--fs-xs); color: var(--faint); text-align: center; }
.pm.proficient, .pm.prof { color: var(--green); }
.pm.expertise { color: var(--amber); }
.pm.half { color: var(--muted); }
/* Section headings (Brett, 2026-10-18: option A, modified): quiet all-caps labels with a fading
   rule everywhere; only action-economy headings take a color and symbol, the same on Battle,
   Character and the side panel: ◆ Action, ▲ Bonus action, ↺ Reaction, ● Other. */
.k-action { --k: var(--k-action); --k-glyph: "◆"; } .k-bonus { --k: var(--k-bonus); --k-glyph: "▲"; }
.k-reaction { --k: var(--k-reaction); --k-glyph: "↺"; } .k-other { --k: var(--k-other); --k-glyph: "●"; }
h3.divider { display: flex; align-items: center; gap: 10px; padding: 0; border: none; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--k, var(--amber)); margin: 26px 0 8px; }
h3.divider::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, color-mix(in srgb, var(--k, var(--amber)) 50%, transparent), transparent); }
h3.divider .count { color: color-mix(in srgb, var(--k, var(--amber)) 70%, transparent); letter-spacing: 0; font-weight: 600; font-size: var(--fs-xs); }
h3.divider[class*="k-"]::before { content: var(--k-glyph); font-size: var(--fs-xs); letter-spacing: 0; margin-right: -2px; }
h3.divider.big { font-size: var(--fs-sm); color: var(--k, var(--amber)); margin-top: 30px; }
.econ h2.econ-head { font-size: var(--fs-sm); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--k); }
.econ h2.econ-head::before { content: var(--k-glyph); margin-right: 8px; letter-spacing: 0; }
.econ h2.econ-head small { text-transform: none; letter-spacing: 0; font-weight: 400; margin-top: 2px; }
.k-hit { --k: var(--k-other); --k-glyph: "✦"; }
.riders { margin-top: 14px; }
.riders h2.econ-head { font-size: var(--fs-sm); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--k); }
.riders h2.econ-head::before { content: var(--k-glyph); margin-right: 8px; letter-spacing: 0; }
.riders h2.econ-head small { display: block; text-transform: none; letter-spacing: 0; font-weight: 400; margin-top: 2px; color: var(--muted); }
.rider-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px 18px; margin-top: 10px; }
.rider { padding: 8px 0; border-top: 1px solid var(--line); }
.rider .meta { color: var(--muted); font-size: var(--fs-sm); margin-top: 2px; }
.rider-when { color: var(--text); }
h2.econ-title { color: var(--k); }
h2.econ-title::before { content: var(--k-glyph); font-size: .9em; margin-right: 8px; }
/* side panel subheadings (What it does, How it adds up…) in the same quiet caps */
.peek-body h3:not(.divider), .peek-body.detail h3:not(.divider) { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); margin: 20px 0 6px; }
/* Sessions: recap on the left, key moments and DM notes on the right (wide screens) */
.session-body { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); gap: 32px; align-items: start; }
.session-side h3.divider { margin-top: 18px; }
@media (max-width: 1100px) { .session-body { grid-template-columns: 1fr; gap: 0; } }

/* Map tab: AideDD's Sword Coast atlas in a frame */
/* The Campaign page's Map tab (app/atlas.py) */
.map-head { align-items: center; margin-bottom: 12px; }
.map-head .hint { margin: 0; flex: 1 1 320px; }
.map-head .map-open { margin-left: auto; }
.map-frame { display: block; width: 100%; height: calc(100vh - 300px); min-height: 420px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
@media (max-width: 640px) { .map-frame { height: calc(100vh - 260px); } }

/* Notes on shortlist picks and reminders */
.detail-marks { display: grid; gap: 6px; margin-bottom: 14px; }
.mark-bar { display: flex; flex-wrap: wrap; gap: 6px 20px; }
.mark-row { gap: 6px; }
.pick-note-form { display: grid; gap: 2px; margin: 4px 0 0; }
.pick-note-form textarea { width: 100%; font: inherit; font-size: var(--fs-sm); background: var(--surface-2); color: var(--text); border: 1px solid var(--line); border-radius: var(--r-md); padding: 6px 8px; resize: vertical; }
.pick-note-saved { min-height: 1em; font-size: var(--fs-xs); color: var(--faint); }
.pick-note-text { margin: 2px 0 0; color: var(--muted); font-size: var(--fs-sm); }
.move-note { font-style: italic; color: var(--note); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ri-note { display: block; color: var(--violet); font-size: var(--fs-sm); line-height: 1.35; white-space: normal; overflow-wrap: anywhere; }
.forget-note { display: block; color: var(--violet); font-size: var(--fs-sm); margin-top: 2px; }

/* Wild Shape: planner rows, side panel and Battle switch */
.ri-pic { width: 34px; height: 34px; border-radius: var(--r-md); object-fit: cover; vertical-align: middle; margin-right: 6px; border: 1px solid var(--line); background: var(--surface-2); }
.form-row.not-ready .ri-name { color: var(--muted); }
.fm-vitals .vital b.small, .ws-form .vital b.small { font-size: var(--fs-md); }
.form-abilities { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; margin: 10px 0; }
.fab { background: var(--surface-2); border-radius: var(--r-md); padding: 6px 4px; text-align: center; border: 1px solid transparent; }
.fab.yours { border-color: var(--blue-line); }
.fab small { display: block; color: var(--muted); font-size: var(--fs-xs); } .fab b { display: block; font-size: var(--fs-lg); }
.fab span { display: block; color: var(--faint); font-size: var(--fs-xs); } .fab em { display: block; font-style: normal; font-size: var(--fs-xs); color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.fab.yours em { color: var(--blue); }
.move.yours b { color: var(--muted); }
.move-more summary { cursor: pointer; color: var(--faint); font-size: var(--fs-xs); }
.move-more p { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-sm); }
.form-notes { margin: 6px 0 10px; padding-left: 18px; color: var(--muted); font-size: var(--fs-sm); }
.form-notes li { margin: 3px 0; }
.ws-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
.ws-row { margin: -4px 0 16px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.ws-label { font-family: var(--cond); text-transform: uppercase; letter-spacing: .06em; font-size: var(--fs-xs); color: var(--amber); margin-right: 4px; }
.ws-chip img { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; }
/* In a Wild Shape form: a band naming it (its page and Leave at the right) above Battle's usual top row, whose form numbers
   are edged green (Brett, 2026-11-03, docs/mockups/wild-shape-play/) */
.ws-form { display: flex; gap: var(--sp-3); align-items: center; margin: 10px 0 var(--sp-2); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--green); border-radius: var(--r-lg); background: var(--green-soft); }
.ws-pic { width: 48px; height: 48px; border-radius: var(--r-md); object-fit: cover; flex: none; }
.ws-form-main { min-width: 0; flex: 1; display: grid; gap: 2px; }
.ws-form-acts { display: flex; gap: var(--sp-2); flex: none; }
.nt.in-form { border-color: color-mix(in srgb, var(--green) 55%, var(--line)); }  /* a Wild Shape form's number (Battle, Party) */
.ws-sheet-band { margin: 0 0 var(--sp-3); }
.ri.form-no { opacity: .55; } .ri.form-no .ri-what { color: var(--amber); }  /* what a Wild Shape form can't do, on Sheet */
.tag.form-note { color: var(--muted); }
.tag.pt-form, .tag.ft-form { color: var(--green); border-color: color-mix(in srgb, var(--green) 50%, var(--line)); }
@media (max-width: 640px) { .ws-pic { width: 32px; height: 32px; } .ws-form-note { display: none; } .form-abilities { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Large modal (Wild Shape forms): wide on desktop, a full-height sheet on phones */
.peek.modal.large { width: min(var(--modal-lg-w), calc(100% - 48px)); max-height: 90vh; height: 90vh; }
.fm-head { align-items: center; padding: 18px 22px 12px; border-bottom: 1px solid var(--line); }
.fm-pic { width: 88px; height: 88px; border-radius: var(--r-lg); object-fit: cover; border: 1px solid var(--line); flex: none; }
.fm-title { flex: 1; min-width: 0; }
.fm-title h2 { font-size: var(--fs-xl); }
.fm-actions { display: flex; align-items: center; gap: 10px; flex: none; }
.fm-body { padding: 16px 22px 22px; }
.fm-vitals { margin-bottom: 6px; }
.fm-grid { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 28px; align-items: start; }
.fm-side .detail-marks { margin-top: 12px; }
.fm-side .form-abilities { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fm-econ { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; }
.fm-col { background: var(--surface-2); border-radius: var(--r-lg); padding: 10px 14px 6px; }
.fm-col h4 { margin: 0 0 4px; }
.fm-col .move:first-of-type { border-top: none; }
.fm-traits p { margin: 0 0 8px; color: var(--muted); }
.fm-main .form-notes { columns: 2; column-gap: 28px; }
.fm-main .form-notes li { break-inside: avoid; }
@media (max-width: 640px) {
  .fm-grid { grid-template-columns: 1fr; }
  .fm-econ { grid-template-columns: 1fr; }
}
@media (max-width: 900px) { .fm-main .form-notes { columns: 1; } }
@media (max-width: 900px) {
  .peek.modal.large { width: 100%; height: 94vh; max-height: 94vh; }
  .fm-head { flex-wrap: wrap; }
  .fm-pic { width: 64px; height: 64px; }
  .fm-actions { width: 100%; justify-content: space-between; order: 3; }
  /* ✕ stays at the head's top right when its other buttons take a row of their own (the UI pass, 2026-11-02) */
  .peek-head:has(> .fm-actions .peek-close) { position: relative; padding-right: 52px; }
  .fm-actions .peek-close { position: absolute; top: 16px; right: 14px; }
  .fm-actions:not(:has(> :not(.peek-close))) { width: 0; order: 0; }  /* (✕ alone: no empty row) */
}
.fm-col h4.econ-head { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--k); }
.fm-col h4.econ-head::before { content: var(--k-glyph); margin-right: 8px; letter-spacing: 0; }
.fm-col h4.econ-head small { display: block; text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--faint); font-size: var(--fs-xs); }

/* Manage: the menu's two groups */
.menu .menu-group { padding: 8px 12px 2px; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); }
.menu a + .menu-group { border-top: 1px solid var(--line); margin-top: var(--sp-1); padding-top: var(--sp-3); }
.menu a.menu-count { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); }
.menu .menu-more { margin: 0; padding: 2px 12px 8px; display: flex; flex-wrap: wrap; gap: 2px var(--sp-3); font-size: var(--fs-xs); }
.menu .menu-more a { display: inline; padding: 0; color: var(--muted); background: none; }
.menu .menu-more a:hover, .menu .menu-more a.active { color: var(--text); text-decoration: underline; }
.mcount { background: var(--red); color: var(--text-strong); font: 700 var(--fs-xs)/1 var(--sans); border-radius: var(--r-pill); padding: 3px 7px; }
/* Every Manage page: its group's pages in a list down the left (wide screens), else from a "jump to" control. The
   list sits in the page's left padding, so each page's own layout keeps the width it was built for (1240px: where
   the content is still at least as wide as on a 1000px screen without it). */
.manage-side { display: none; }
.manage-side a, .manage-jump-list a { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); color: var(--muted); text-decoration: none; padding: 5px 10px; border-radius: var(--r-sm); font-size: var(--fs-sm); }
.manage-side a:hover, .manage-jump-list a:hover { color: var(--text); background: var(--surface); }
.manage-side a.on, .manage-jump-list a.on { color: var(--text-strong); background: var(--surface-2); font-weight: 600; }
.manage-sec { display: block; padding: var(--sp-3) 10px 2px; font: 700 var(--fs-xs) var(--cond); letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.manage-jump { margin: 0 0 var(--sp-4); }
.manage-jump > summary { list-style: none; display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); min-height: var(--control-h); padding: 6px 12px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; font-size: var(--fs-sm); }
.manage-jump > summary::-webkit-details-marker { display: none; }
.manage-jump > summary b { color: var(--text-strong); }
.manage-jump > summary span { color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.manage-jump-list { display: grid; align-content: start; margin-top: var(--sp-2); padding: 6px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
/* Which half of Manage you're in: a band in its colour, red for this campaign, blue for askdnd admin */
.manage-band { position: relative; height: 3px; margin: 0 0 var(--sp-5); border-radius: var(--r-xs); background: var(--red); }  /* the half's colour, as a line (Brett, 2026-10-31) */
.manage-band.tool { background: var(--blue); }
.manage-band a { display: block; position: absolute; right: 0; top: calc(var(--sp-5) + var(--control-h) + var(--sp-4) + 9px); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }  /* beside the page's title, under the "All pages" control (feedback 10: it covered it); a block, since a flex button centres its text and cuts both ends (feedback 10) */
.manage-band.camp ~ .title-row { padding-right: 96px; }  /* ⚙ Admin */
.manage-band.tool ~ .title-row { padding-right: 216px; }  /* a campaign's name */
@media (max-width: 640px) { .manage-band a { max-width: 140px; top: calc(var(--sp-5) + var(--control-h) + var(--sp-4) - 7px); } .manage-band.tool ~ .title-row { padding-right: 152px; } }
.menu .menu-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); margin: -6px -6px var(--sp-1); padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--red-line); border-radius: var(--r-md) var(--r-md) 0 0; background: var(--red-soft); font-size: var(--fs-sm); }
.menu .menu-head.tool { border-bottom-color: var(--blue-line); background: var(--blue-soft); }
.menu .menu-head b { color: var(--text-strong); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu .menu-head span { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.manage.admin > button.active { border-color: var(--blue); }
@media (min-width: 1240px) {
  .page.manage-page { position: relative; max-width: calc(1120px + 228px); padding-left: calc(24px + 228px); }
  .page.manage-page > .manage-band { margin-left: -228px; }
  .manage-band a { top: calc(var(--sp-5) + 9px); }  /* (no "All pages" control here: the list is down the left) */  /* the page's own band, across the list's column too (not a sample in a section) */
  .manage-side { display: grid; align-content: start; position: absolute; left: 24px; top: calc(32px + 3px + var(--sp-5)); width: 200px; }  /* under the line */
  .manage-jump { display: none; }
}
/* Manage › askdnd › Overview: a card per admin page */
.ov-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-3); }
.ov-card { display: grid; gap: var(--sp-1); align-content: start; padding: var(--sp-3) var(--sp-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); color: var(--text); text-decoration: none; }
.ov-card:hover { border-color: var(--line-strong); background: var(--row-hover); }
.ov-card b { color: var(--text-strong); }
.ov-state { font-size: var(--fs-sm); }
.ov-state.ok { color: var(--green); } .ov-state.warn { color: var(--amber); }
/* Manage lists and grids: Needs you, Table, Accounts, Campaigns, Sessions (Manage layout, 2026-10-23) */
.sub-tabs { display: flex; gap: var(--sp-4); border-bottom: 1px solid var(--line); margin: 0 0 var(--sp-4); font-size: var(--fs-sm); }
.sub-tabs a { padding-bottom: var(--sp-2); color: var(--muted); text-decoration: none; }
.sub-tabs a.on { color: var(--text-strong); border-bottom: 2px solid var(--red); }
.tag.faint { background: transparent; border: 1px solid var(--line); color: var(--faint); }

/* Player notes (Plan, Battle combos, and the note form) */
.note-mech { margin: 4px 0 8px; }
/* In the side panel (a quest, a combo) each label sits above its text: the panel is too narrow for two columns */
.peek dl.note-mech { grid-template-columns: 1fr; gap: 0; }
.peek dl.note-mech dd + dt { margin-top: 10px; }
.combo-turns { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: 10px; margin: 6px 0; }
.combo-turn { background: var(--surface); border-radius: var(--r-md); padding: 8px 12px; }
.combo-turn-label { font-weight: 700; margin-bottom: 4px; }
.combo-step { display: grid; grid-template-columns: 136px minmax(0, 1fr); gap: 8px; padding: 3px 0; border-top: 1px solid var(--line); font-size: var(--fs-sm); }
.combo-k { white-space: nowrap; font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--k, var(--muted)); padding-top: 2px; }
.combo-k::before { content: var(--k-glyph, ""); margin-right: 4px; }
.combo-payoff, .combo-risks { margin: 6px 0 0; } .combo-payoff b, .combo-risks b { color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; margin-right: 6px; }
.combo-fold { border-top: 1px solid var(--line); padding: 8px 0; } .combo-fold summary { cursor: pointer; }
.level-plan td, .level-plan th { padding: 6px 8px; vertical-align: top; } .level-plan tr.done td { color: var(--muted); }
.quest-steps { list-style: none; padding: 0; margin: 6px 0; } .quest-steps li.done { color: var(--muted); text-decoration: line-through; }
.note-body p:first-child { margin-top: 0; }
a.sheet-ref { text-decoration-style: dotted; }
.note-form { display: grid; gap: 12px; padding: 16px 22px 22px; }
.note-form .field { display: grid; gap: 4px; } .note-form .field > span { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
.note-form .field.k-action > span, .note-form .field.k-bonus > span, .note-form .field.k-reaction > span { color: var(--k); }
.note-form input[type=text], .note-form textarea, .note-form select { width: 100%; font: inherit; color: var(--text); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: 7px 10px; }
.note-form h3 { margin: 6px 0 0; }
.form-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.repeat { display: grid; gap: 10px; }
.repeat-row { border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px; margin: 0; display: grid; gap: 8px; }
.combo-edit .turn-label { font-weight: 700; flex: 1; }
.level-edit { grid-template-columns: 60px 120px minmax(0, 1fr) minmax(0, 1fr) 110px auto; align-items: center; }
.quest-edit { grid-template-columns: minmax(0, 1fr) 110px auto; align-items: center; }
@media (max-width: 640px) { .form-2 { grid-template-columns: 1fr; } .level-edit { grid-template-columns: 60px minmax(0, 1fr) auto; } .level-edit input[name=step_take], .level-edit input[name=step_why] { grid-column: 1 / -1; } }

/* Manage › People: the access chart, and a line under each person saying what their access allows */
.access-chart-wrap { margin-top: 10px; }
.access-chart { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); min-width: 620px; }
.access-chart th, .access-chart td { padding: 7px 10px; border-top: 1px solid var(--line); text-align: center; }
.access-chart thead th { border-top: none; color: var(--muted); font-size: var(--fs-xs); font-weight: 600; }
.access-chart tbody th[scope=row] { text-align: left; font-weight: 400; }
.access-chart .access-group th { text-align: left; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); padding-top: 14px; border-top: none; }
.ac-yes { color: var(--green); font-weight: 700; } .ac-no { color: var(--faint); } .ac-own { color: var(--violet); font-size: var(--fs-sm); }
.access-chart .dm-col { border-left: 1px solid var(--line); font-size: var(--fs-sm); }

/* Manage › Data: import preview */
.import-summary { margin: 10px 0; padding-left: 18px; }
.warn-text { color: var(--amber); }
.import-change { border-top: 1px solid var(--line); padding: 10px 0; display: grid; gap: 6px; }
.import-change .row { gap: 8px; }
.import-diff { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.import-diff > div { background: var(--surface-2); border-radius: var(--r-md); padding: 8px 10px; }
.import-diff small { color: var(--muted); font-size: var(--fs-xs); }
.import-diff p { margin: 4px 0 0; font-size: var(--fs-sm); }
.import-confirm { display: grid; gap: 10px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
@media (max-width: 640px) { .import-diff { grid-template-columns: 1fr; } }

/* Manage › History */
.hist-row { border-top: 1px solid var(--line); padding: var(--sp-3) 0; display: grid; gap: var(--sp-2); }
.hist-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.hist-head .hist-restore { margin-left: auto; }
.hist-short { margin: 0; }
.import-diff.one { grid-template-columns: 1fr; }
.keep-box { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 4px; }
.keep-opt { display: flex; gap: 8px; align-items: flex-start; padding: 6px 0; border-top: 1px solid var(--line); }
.keep-opt input { margin-top: 4px; }

/* Wild Shape window: plan first, Action and Bonus action side by side, Reactions as a row */
.fm-plan { margin: 10px 0 4px; }
/* Your plan for this form: Shortlist and Remind me, then your note, full width */
.fm-plan-stack { display: grid; gap: 8px; }
.fm-plan-stack .detail-marks textarea { min-height: 64px; }
.fm-reactions { margin-top: 14px; }
.fm-reaction-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); column-gap: 18px; }
.fm-reaction-list .move:first-child, .fm-reaction-list .move:nth-child(2) { border-top: none; }
.fm-skills { grid-template-columns: 1fr; }
.fm-small { margin-top: 10px; }
.keep-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); column-gap: 18px; }
.keep-group { border: 0; margin: 10px 0 0; padding: 0; min-width: 0; }
.keep-group legend, .keep-legend { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); padding: 0; margin: 0 0 2px; }
.keep-group legend small { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--faint); margin-left: 8px; font-size: var(--fs-xs); }
.fm-rules { margin-top: 14px; }

/* Advantage, the same badge everywhere (the adv macro in _char.html); outlined when it needs a condition */
.adv-badge { display: inline-block; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .03em; line-height: 1; padding: 2px 5px 3px;
  border-radius: var(--r-sm); background: var(--amber-soft); color: var(--amber); border: 1px solid var(--amber); vertical-align: middle; cursor: help; white-space: nowrap; }
.adv-badge.cond { background: transparent; border-style: dashed; }

/* Saving throws (and Wild Shape skills) in aligned rows: marker, name, number, Adv */
.saves-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(7.4rem, 1fr)); gap: 0 14px; }
.save-row { display: grid; grid-template-columns: 1em minmax(0, 1fr) auto 2.3em; align-items: center; gap: 6px; padding: 3px 0; border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
.save-row .pm { font-size: var(--fs-xs); }
.save-row .save-name { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.save-row.prof .save-name { color: var(--text); }
.save-row b { text-align: right; font-variant-numeric: tabular-nums; }
.pm.form { color: var(--muted); }
.save-all { margin: 6px 0 0; }
.saves-block.six .saves-list { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.saves-block.three .saves-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.saves-block.six .save-row, .saves-block.three .save-row { grid-template-columns: .9em 2.1em 2.3em minmax(0, 1fr); }
@media (max-width: 640px) { .saves-block.six .saves-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px) { .saves-block.six .saves-list, .saves-block.three .saves-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Battle header (Brett, 2026-10-19): the Character page's number strip and ability-card look, kept
   tight because the bar is sticky. Saves are one row of six tiles the same width as the strip. */
/* Battle's numbers and saves in one row (Brett, 2026-11-01, docs/mockups/battle-band/ Option E): the numbers share the room
   left, the saves stay stacked to the right of a line. When the row runs short, by its own width (Table rolls open, a
   half-screen window), the saves drop under the numbers. Phones have their own order below. */
.battle-head { margin-top: 8px; container: battle-head / inline-size; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: var(--sp-2) var(--sp-3); }
.battle-head > .strip.compact { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--sp-2); margin: 0; }
.battle-head > .save-tiles { grid-template-columns: repeat(3, 1fr); gap: var(--sp-1) 6px; padding-left: var(--sp-3); border-left: 1px solid var(--line); }
.battle-head .st { padding: 3px 8px; gap: 6px; align-items: center; }
.battle-head .st b { font-size: var(--fs-md); }
.battle-head > .save-all { grid-column: 1 / -1; margin: 0; }
@container battle-head (max-width: 760px) {
  .battle-head > .save-tiles { grid-column: 1 / -1; grid-template-columns: repeat(6, minmax(0, 1fr)); padding-left: 0; border-left: none; }
  .battle-head > .strip.compact { grid-column: 1 / -1; }
}
.strip.compact { grid-template-columns: repeat(6, minmax(0, 1fr)); margin-bottom: 10px; }
.strip.compact small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 640px) { .strip.compact { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.strip.compact > div { padding: 6px 12px; }
.strip.compact b { font-size: var(--fs-lg); }
.save-tiles { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
.st { position: relative; background: var(--surface-2); border-radius: var(--r-md); padding: 5px 12px; display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.st small { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.st.prof small { color: var(--text); }
.st.prof small::before { content: "● "; color: var(--green); font-size: var(--fs-xs); }
.st b { font-size: var(--fs-lg); font-variant-numeric: tabular-nums; margin-left: auto; white-space: nowrap; }  /* ("+4" never splits: the feedback picture split it, note 11) */
.st .adv-badge { font-size: var(--fs-xs); padding: 0 4px; line-height: 1.35; order: 1; }  /* inside its tile, before the number (the UI pass, 2026-11-02) */
.st b { order: 2; }
.save-all { margin: 6px 0 0; }
@media (max-width: 640px) { .save-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px) {
  .strip.compact { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .strip.compact > div { padding: 5px 8px; }
  .strip.compact small { font-size: var(--fs-xs); }
  .strip.compact b { font-size: var(--fs-lg); }
  .save-tiles { gap: 6px; }
  .st { padding: 5px 6px; gap: 3px; }
}

/* Rolls (app.js): private dice rolls. Roll buttons look like the numbers they roll. */
.roll-hit { font: inherit; font-weight: 700; color: var(--text); background: var(--surface-2); border: 1px solid var(--line); box-shadow: 0 1px 0 var(--shadow); padding: 0 8px; border-radius: var(--r-sm); cursor: pointer; line-height: 1.5; font-variant-numeric: tabular-nums; }
.roll-hit:hover, .roll-hit:focus-visible { border-color: var(--amber); color: var(--amber); background: var(--amber-soft); }
/* Row action (DESIGN.md › Buttons; Brett, 2026-10-29): an action inside a list's row (the Spell Book's Roll and
   Concentrate), compact so the row keeps its room; amber when it rolls, blue for Concentration's icon */
.row-act { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1); min-height: 24px; min-width: 24px; padding: 0 var(--sp-2); font: 600 var(--fs-xs)/1 var(--sans); color: var(--text); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); cursor: pointer; white-space: nowrap; }
.row-act:hover, .row-act:focus-visible { color: var(--amber); border-color: var(--amber); }
.row-act.conc-act { padding: 0; width: 24px; color: var(--cond-concentration); }
.row-act.conc-act:hover, .row-act.conc-act:focus-visible { color: var(--cond-concentration); border-color: var(--cond-concentration); background: color-mix(in srgb, var(--cond-concentration) 14%, var(--surface-2)); }
.row-act .cond-icon { width: 14px; height: 14px; }
@media (max-width: 640px) { .row-act { min-height: 32px; min-width: 32px; } .row-act.conc-act { width: 32px; } }
.roll-chip { font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--muted); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 0 8px; margin-left: 4px; cursor: pointer; vertical-align: middle; }
.roll-chip:hover, .roll-chip:focus-visible { color: var(--amber); border-color: var(--amber); }
.tile-roll { float: right; margin-top: 4px; }
.roll-hit.tile-hit { display: inline-block; max-width: 100%; font-size: var(--fs-lg); line-height: 1.3; padding-block: 0; }  /* (no taller than a number beside it) */  /* Initiative's number is its roll, like an attack's to-hit */
@media (max-width: 640px) {
  .roll-hit.tile-hit { padding: 0 var(--sp-1); min-width: 0; font-size: var(--fs-md); }
  /* Initiative's number stays a 36px button to tap (DESIGN.md › Principles 8), so every number in Battle's row sits
     centred in a row that tall: the numbers line up with it, and the row is no taller than it already was */
  .battle-head .nt > .nt-val { min-height: 36px; display: flex; align-items: center; gap: .25em; }  /* (gap: a flex row drops "56 / 66"'s space) */
}
.st[data-roll], .save-row[data-roll] { cursor: pointer; }
.st[data-roll]:hover { box-shadow: inset 0 0 0 1px var(--amber); }
.save-row[data-roll]:hover b { color: var(--amber); }
.rc[data-roll], .skill .roll-hit { cursor: pointer; }
.roll-modes { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--line); border-radius: var(--r-pill); }  /* (a track, as .seg) */
.roll-modes button { border-radius: var(--r-pill); }
.roll-modes button { font: inherit; font-size: var(--fs-xs); background: none; border: 0; color: var(--muted); padding: 2px 8px; cursor: pointer; }
.roll-modes button[aria-pressed="true"] { background: var(--surface-2); color: var(--text); }
.skill-head .roll-hit { font-size: inherit; }

/* The roll window (app.js): opens next to the roll button with Normal / Adv / Dis, situational
   bonuses and who sees it, then turns into the result in place. Above side panels and windows. */
.roll-win { position: fixed; z-index: 20; width: min(360px, calc(100vw - 16px)); max-height: calc(100vh - 16px); overflow-y: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 14px 16px; box-shadow: var(--lift); font-size: var(--fs-sm); animation: roll-pop .14s ease-out; }
.roll-win.nat20 { border-color: var(--green); } .roll-win.nat1, .roll-win.failed { border-color: var(--red); }
/* An item on Battle (Brett, 2026-10-30): its section in a column, its button, its window */
.seeall.items-all { margin-top: 0; }
.items-head { margin: 10px 0 0; font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.item-go { color: var(--text); }
.item-go:disabled { color: var(--faint); cursor: default; border-color: var(--line); }
.use-cost { margin: 0 0 6px; font-size: var(--fs-sm); color: var(--muted); }
.use-what { margin: 0 0 6px; font-size: var(--fs-sm); }
.use-said { margin: 6px 0 0; font-size: var(--fs-sm); color: var(--amber); }
/* Wild Shape's window (app/shifting.py): what shifting takes and does, a line each */
.shift-lines { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 12px; margin: 4px 0 10px; font-size: var(--fs-sm); max-width: 30rem; }
.shift-lines dt { color: var(--muted); }
.shift-lines dd { margin: 0; }
.shift-lines dd b { color: var(--text-strong); }
.shift-lines select { margin-top: 4px; display: block; }
.shift-warn { color: var(--amber); }
.rw-title { font-weight: 700; font-size: var(--fs-md); padding-right: 24px; margin-bottom: 8px; }
.rw-title .roll-sub { font-weight: 400; }
.rw-row { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; margin: 8px 0; }
.rest-conc .note { flex-basis: 100%; padding-left: 24px; }
.rw-label { width: 70px; flex: none; color: var(--muted); font-size: var(--fs-xs); }
.rw-mode .roll-modes { display: flex; width: 100%; }
.rw-mode .roll-modes button { flex: 1; padding: 7px 4px; font-size: var(--fs-sm); }
.roll-win .roll-modes button[aria-pressed="true"] { background: var(--selected-bg); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--blue); font-weight: 700; border-radius: var(--r-pill); }  /* (rounded like its track) */
.rw-add { margin: 8px 0; }
.rw-add summary, .rs-fold summary { cursor: pointer; font-weight: 600; color: var(--text); font-size: var(--fs-sm); }
.rw-add .dice-pick, .rs-fold .dice-pick { margin-top: 8px; }
/* The dice picker (roll window, Manual roll) */
.dice-pick { display: flex; flex-direction: column; gap: 8px; }
.dp-dice { display: flex; flex-wrap: wrap; gap: 6px; }
.dp-die { font: inherit; font-size: var(--fs-xs); font-weight: 600; min-width: 36px; padding: 5px 0; background: var(--surface-2); color: var(--text); border: 1px solid var(--line); border-radius: var(--r-md); }
.dp-die:hover { border-color: var(--amber); color: var(--amber); }
.dp-minus[aria-pressed="true"] { background: var(--red); border-color: var(--red); color: var(--text-strong); }
.dp-formula { font-weight: 700; color: var(--text); font-size: var(--fs-md); min-height: 1.4em; }
.dp-formula.empty { font-weight: 400; color: var(--muted); font-size: var(--fs-sm); }
.dp-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dp-bonus { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-xs); }
.dice-pick input.dp-mod { width: 64px; padding: 4px 8px; font-size: var(--fs-sm); }
.dp-row [data-manual-roll] { margin-left: auto; }
.roll-win .roll-modes button { font-size: var(--fs-xs); padding: 4px 10px; }
.roll-win input.rw-text { width: auto; }
.rw-text { font: inherit; font-size: var(--fs-sm); background: var(--bg); color: var(--text); border: 1px solid var(--line); border-radius: var(--r-md); padding: 4px 8px; min-width: 0; flex: 1; }
.rw-text.bad { border-color: var(--red); }
.rw-notes { list-style: none; margin: 6px 0; padding: 8px 10px; background: var(--bg); border-radius: var(--r-md); font-size: var(--fs-xs); color: var(--muted); display: flex; flex-direction: column; gap: 4px; }
.rw-notes b { color: var(--amber); font-weight: 600; margin-right: 4px; }
.rw-note { font-size: var(--fs-xs); color: var(--muted); margin: 6px 0; }
.rw-note.rw-warn { color: var(--amber); background: var(--amber-soft); border-radius: var(--r-sm); padding: 6px 8px; }
.rw-house b { color: var(--violet); font-weight: 600; margin-right: 4px; }
.rw-go { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.rw-go .note { font-size: var(--fs-xs); }
.rw-result { display: flex; gap: 14px; align-items: flex-start; }
.rp-x { position: absolute; top: 8px; right: 10px; background: none; border: 0; color: var(--muted); font-size: var(--fs-lg); line-height: 1; padding: 4px; }
.rp-die { width: 56px; height: 56px; flex: none; fill: var(--surface-2); stroke: var(--amber); stroke-width: 1.5; }
.nat20 .rp-die { stroke: var(--green); } .nat1 .rp-die { stroke: var(--red); }
.rp-die text { fill: var(--text); stroke: none; font: 700 9px var(--sans); }
.rp-die.tumble { animation: roll-tumble .55s ease-out; }
.rp-body { min-width: 0; flex: 1; padding-right: 18px; }
.rp-label { color: var(--muted); font-size: var(--fs-xs); }
.rp-res b { font-size: var(--fs-2xl); margin-right: 6px; font-variant-numeric: tabular-nums; line-height: 1.1; }
.rp-res u, .rp-line u, .roll-dice u { text-decoration: none; color: var(--text); font-weight: 700; }
.rp-line { font-size: var(--fs-sm); color: var(--muted); margin-top: 2px; }
.rp-line b { color: var(--text); }
.rp-flag { font-size: var(--fs-sm); color: var(--amber); margin-top: 2px; } .nat20 .rp-flag { color: var(--green); } .nat1 .rp-flag { color: var(--link); }
.rp-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.roll-x { color: var(--faint); }

/* Table rolls (app.js, _table_rolls.html): a left sidebar at the table. Open, it pushes
   the page over (wide windows) or lies over it (narrower); closed, only the edge tab shows.
   Phones: a bottom sheet above the bottom bar. Under the side panel and windows (z-index 4). */
.rolls-side { display: none; }
body.at-table .rolls-side { display: flex; flex-direction: column; position: fixed; z-index: 4; left: 0; top: var(--stick-top, 62px); bottom: 0; width: 340px; background: var(--chrome); border-right: 1px solid var(--line); transition: transform .25s ease, visibility 0s; }
body.at-table.rolls-open { --rolls-w: 340px; }
@media (max-width: 1099px) and (min-width: 901px) { body.at-table.rolls-open { --rolls-w: 0px; } body.at-table.rolls-open .rolls-side { box-shadow: 12px 0 30px var(--shadow); } }
body.at-table { padding-left: var(--rolls-w, 0); }
body.at-table .topbar { margin-left: calc(-1 * var(--rolls-w, 0px)); }
.rs-head { display: flex; align-items: center; gap: 8px; padding: 12px 12px 10px 16px; color: var(--amber); }
.rs-grip { display: none; }
.rs-title { flex: 1; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; }
.rs-toggle { flex: none; width: 30px; height: 30px; background: var(--surface); border: 1px solid var(--line); color: var(--muted); border-radius: var(--r-sm); line-height: 1; }
.rs-toggle:hover { color: var(--text); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.rs-log { list-style: none; margin: 0; padding: 0 12px 12px; display: flex; flex-direction: column; gap: 8px; flex: 1; overflow-y: auto; overscroll-behavior: contain; }
.roll-item { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; gap: 4px 12px; align-items: start; background: var(--surface); border: 1px solid transparent; border-radius: var(--r-lg); padding: 12px 14px; font-size: var(--fs-sm); line-height: 1.4; }
.roll-item.mine { border-color: var(--line); }
.roll-item.fresh { animation: roll-fresh 1.6s ease-out; }
@keyframes roll-fresh { from { border-color: var(--amber); background: var(--surface-2); } }
.roll-av { position: relative; width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; font-weight: 700; overflow: hidden; flex: none; }
.roll-av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hue0 .roll-av, .roll-av.hue0 { background: var(--p0-bg); color: var(--amber); }
.hue1 .roll-av, .roll-av.hue1 { background: var(--p1-bg); color: var(--blue); }
.hue2 .roll-av, .roll-av.hue2 { background: var(--p2-bg); color: var(--green); }
.hue3 .roll-av, .roll-av.hue3 { background: var(--p3-bg); color: var(--p3); }
.hue4 .roll-av, .roll-av.hue4 { background: var(--p4-bg); color: var(--link); }
.hue5 .roll-av, .roll-av.hue5 { background: var(--p5-bg); color: var(--p5); }
.hue6 .roll-av, .roll-av.hue6 { background: var(--red-soft); color: var(--link); }
.roll-main { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.roll-who { font-size: var(--fs-sm); }
.roll-who time, .roll-to { color: var(--muted); font-size: var(--fs-xs); }
.roll-to { border: 1px solid var(--line); border-radius: var(--r-pill); padding: 0 6px; }
.roll-what { font-weight: 600; font-size: var(--fs-md); }
.roll-what .roll-sub { font-weight: 400; }
/* Dice still in the air: the row says what was rolled, its number shows as they land (app.js LANDING) */
.roll-item.landing :is(.roll-total, .roll-dice, .roll-dmg-line b, .roll-flag) { visibility: hidden; }
.roll-what .roll-sub, .roll-dice { color: var(--muted); font-size: var(--fs-xs); }
.roll-sub { color: var(--muted); }
.roll-old s { color: var(--faint); }
.roll-pl { display: block; }
.rp-res .roll-sub { display: inline-block; vertical-align: middle; }
.rw-sum { font-size: var(--fs-sm); margin: 8px 0; }
.rw-sum .note { font-size: var(--fs-xs); }
.rw-ev-list { display: flex; flex-wrap: wrap; gap: 6px; }
.roll-win select.rw-slot { width: auto; padding: 4px 8px; }
.roll-flag { font-size: var(--fs-xs); color: var(--amber); }
.nat20 .roll-flag, .nat20 > .roll-total, b.nat20 { color: var(--green); }
.nat1 .roll-flag, .nat1 > .roll-total, b.nat1 { color: var(--link); }
.kind-heal > .roll-total { color: var(--green); }
.roll-total { font-size: var(--fs-2xl); line-height: 1.1; font-variant-numeric: tabular-nums; }
.roll-lock { color: var(--muted); display: grid; place-items: center; padding-top: 8px; }
.hidden-roll .roll-what { color: var(--muted); font-weight: 400; }
.roll-dmg-line { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line); font-size: var(--fs-xs); color: var(--muted); }
.roll-dmg-line b { font-size: var(--fs-lg); color: var(--text); font-variant-numeric: tabular-nums; }
.roll-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.roll-pending { border: 1px dashed var(--line); border-radius: var(--r-lg); padding: 10px 14px; color: var(--muted); font-size: var(--fs-sm); }
.roll-empty { color: var(--muted); font-size: var(--fs-sm); padding: 6px 4px; }
.rs-controls { border-top: 1px solid var(--line); padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 10px; font-size: var(--fs-xs); color: var(--muted); max-height: 50%; overflow-y: auto; }
.rs-row { display: flex; align-items: center; gap: 8px; }
.rc-group { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
.rc-head { flex-basis: 100%; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .08em; color: var(--faint); }
.rc { font: inherit; font-size: var(--fs-xs); background: var(--surface); color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 9px; }
.rc b { color: var(--text); margin-left: 2px; }
.rc.prof { border-color: var(--green-line); }
.rc:hover { border-color: var(--amber); color: var(--text); }
/* Closed: the sidebar slides away and the edge tab brings it back; on phones the sheet's bar stays, with one
   avatar per person and their latest roll (app.js buildPeople), pulsing on a new one */
body.at-table.rolls-closed { --rolls-w: 0px; }
body.rolls-snap .rolls-side { transition: none !important; }
body.at-table.rolls-closed .rolls-side { transform: translateX(-100%); visibility: hidden; transition: transform .25s ease, visibility 0s .25s; }
.rs-edge { display: none; }
body.at-table.rolls-closed .rs-edge { position: fixed; z-index: 4; left: 0; top: calc(var(--stick-top, 62px) + 12px); width: 28px; height: 44px; display: grid; place-items: center; background: var(--chrome); color: var(--amber); border: 1px solid var(--line); border-left: 0; border-radius: 0 var(--r-md) var(--r-md) 0; }
.rs-edge:hover { color: var(--text); }
.rs-people { display: none; }
.rs-person { display: flex; flex-direction: column; align-items: center; gap: 3px; background: none; border: 0; padding: 0; color: var(--text); font: inherit; cursor: pointer; }
.rs-person .roll-av { width: 38px; height: 38px; }
.rs-p-total { font-size: var(--fs-md); font-variant-numeric: tabular-nums; line-height: 1.2; }
.rs-person.nat20 .rs-p-total { color: var(--green); } .rs-person.nat1 .rs-p-total { color: var(--link); }
.rs-person.nat20 .roll-av { box-shadow: 0 0 0 2px var(--green); } .rs-person.nat1 .roll-av { box-shadow: 0 0 0 2px var(--red); }
.rs-person .roll-lock { color: var(--muted); padding: 0; }
.rs-person.pulse .roll-av { animation: rs-pulse 1.2s ease-out 2; }
@keyframes rs-pulse { 0% { box-shadow: 0 0 0 0 var(--amber-glow); } 100% { box-shadow: 0 0 0 10px transparent; } }
.rs-tip { position: fixed; z-index: 12; max-width: 260px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: 6px 10px; font-size: var(--fs-sm); box-shadow: 0 6px 20px var(--shadow); }

@keyframes roll-pop { from { transform: translateY(8px); opacity: 0; } }
@keyframes roll-note { from { transform: translateX(-12px); opacity: 0; } }
@keyframes roll-tumble { 0% { transform: translateY(-40px) rotate(0) scale(.6); opacity: 0; } 40% { opacity: 1; } 70% { transform: translateY(6px) rotate(540deg) scale(1.05); } 100% { transform: none; } }
@media (prefers-reduced-motion: reduce) { .roll-win, .card, .roll-item.fresh { animation: none; } }
/* Dice you can see (app.js "dice you can see"; docs/mockups/dice-on-screen.html, option A) */
.gem { --size: 112px; --hi: var(--die-gold-hi); --mid: var(--die-gold-mid); --lo: var(--die-gold-lo); --ink: var(--die-gold-ink);
  position: relative; width: var(--size); height: var(--size); perspective: 500px; flex: none; }
.gem.ruby { --hi: var(--die-ruby-hi); --mid: var(--die-ruby-mid); --lo: var(--die-ruby-lo); --ink: var(--die-ruby-ink); }
.gem.jade { --hi: var(--die-jade-hi); --mid: var(--die-jade-mid); --lo: var(--die-jade-lo); --ink: var(--die-jade-ink); }
.gem.mid { --size: 76px; } .gem.tiny { --size: 30px; }
.gem-body { position: absolute; inset: 0; display: block; filter: drop-shadow(0 6px 10px var(--shadow)); }
.gem-body svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.gem polygon { stroke: var(--hi); stroke-width: 1.2; stroke-linejoin: round; stroke-opacity: .7; }
.gem .f-hi { fill: var(--hi); } .gem .f-mid { fill: var(--mid); } .gem .f-lo { fill: var(--lo); }
.gem .f-top { fill: color-mix(in srgb, var(--hi) 45%, var(--mid)); }
.gem-n { position: absolute; left: 50%; transform: translate(-50%, -50%); font: 800 calc(var(--size) * .3) var(--sans); color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1; text-shadow: 0 1px 0 var(--hi); }
.gem-shadow { position: absolute; left: 15%; right: 15%; bottom: -10%; height: 14%; border-radius: 50%; background: var(--backdrop-strong); filter: blur(4px); }
.gem.tiny .gem-shadow, .gem.tiny .gem-body { filter: none; } .gem.tiny .gem-shadow { display: none; }
.gem.tiny .gem-n { text-shadow: none; }
.gem.rolling .gem-body { animation: gem-tumble .85s cubic-bezier(.2, .75, .3, 1) var(--delay, 0ms) both; }
.gem.rolling .gem-shadow { animation: gem-shadow .85s ease-out var(--delay, 0ms) both; }
.gem.landed .gem-body { animation: gem-land .28s ease-out; }
.gem.crit .gem-body { filter: drop-shadow(0 0 16px var(--die-glow)) drop-shadow(0 6px 10px var(--shadow)); }
.gem.crit.landed .gem-body { animation: gem-land .28s ease-out, gem-glow 1.4s ease-in-out .28s 2; }
.gem.fumble.landed .gem-body { filter: grayscale(.7) brightness(.75); animation: gem-shake .4s ease-in-out; }
.gem.off { opacity: .45; transform: scale(.82); }  /* the d20 not kept (Advantage or Disadvantage) */
.gem-spark { position: absolute; left: 50%; top: 50%; width: 6px; height: 6px; margin: -3px; border-radius: 50%; background: var(--die-jade-hi); animation: gem-spark .7s ease-out forwards; }
@keyframes gem-tumble {
  0% { transform: translate(var(--fx, 200px), var(--fy, 120px)) rotateX(0) rotateY(0) rotateZ(0) scale(.45); opacity: 0; }
  12% { opacity: 1; }
  55% { transform: translate(-14px, 10px) rotateX(calc(var(--spin, 540deg) * .9)) rotateY(calc(var(--spin, 540deg) * .6)) rotateZ(var(--spin, 540deg)) scale(1.08); }
  75% { transform: translate(5px, -8px) rotateX(360deg) rotateY(360deg) rotateZ(calc(var(--spin, 540deg) + 12deg)) scale(.97); }
  100% { transform: translate(0, 0) rotateX(360deg) rotateY(360deg) rotateZ(var(--spin, 540deg)); } }
@keyframes gem-shadow { 0% { opacity: 0; transform: scale(.3); } 55% { opacity: .5; transform: scale(.7); } 100% { opacity: 1; transform: none; } }
@keyframes gem-land { 0% { transform: scale(1.14); } 100% { transform: none; } }
@keyframes gem-glow { 50% { filter: drop-shadow(0 0 26px var(--die-glow)) drop-shadow(0 6px 10px var(--shadow)); } }
@keyframes gem-shake { 25% { transform: translateX(-5px) rotate(-4deg); } 75% { transform: translateX(5px) rotate(4deg); } }
@keyframes gem-spark { to { transform: rotate(var(--a)) translateY(calc(var(--size) * -.85)) scale(.3); opacity: 0; } }
.dice-stage { position: fixed; inset: 0; z-index: 19; display: grid; place-items: center; align-content: center; gap: var(--sp-4); pointer-events: none;
  background: radial-gradient(ellipse at center, var(--backdrop-soft), transparent 65%); animation: roll-note .2s ease-out; }
.dice-stage.gone { opacity: 0; transition: opacity .4s; }
.ds-dice { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-4); max-width: min(640px, 90vw); }
.ds-card { display: grid; justify-items: center; gap: 2px; min-width: 220px; padding: var(--sp-3) var(--sp-5); background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); box-shadow: var(--lift); opacity: 0; transform: translateY(8px); transition: opacity .25s, transform .25s; }
.dice-stage.landed .ds-card { opacity: 1; transform: none; }
.ds-card:empty { display: none; }
.ds-what { color: var(--muted); font-size: var(--fs-sm); }
.ds-total { font-size: var(--fs-2xl); font-weight: 800; color: var(--text-strong); line-height: 1.1; }
.ds-total.nat20 { color: var(--green); } .ds-total.nat1 { color: var(--red); }
/* Corner cards (app.js "corner cards"; Brett, 2026-10-29, docs/mockups/temporary-items.html): everything you read and
   dismiss, bottom right, newest at the bottom; an icon and colour by kind, ✕, at most one button. Beside an open side
   panel, not under it; on phones one at a time above the bottom bar, or above the Table rolls sheet. */
.cards { position: fixed; z-index: 16; right: calc(14px + env(safe-area-inset-right, 0px)); bottom: calc(14px + env(safe-area-inset-bottom, 0px)); width: min(400px, calc(100vw - 28px)); display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-2); pointer-events: none; }
body:has(.peek.open:not(.modal)) .cards { right: calc(var(--panel-w) + 14px); }
@media (min-width: 1100px) { body:has(.peek.open:not(.modal)) .cards { right: 494px; } }
.card { pointer-events: auto; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--sp-2) 10px; align-items: center; background: var(--surface-2); color: var(--text); border: 1px solid var(--line-strong); border-left: 3px solid var(--k); border-radius: var(--r-lg); padding: 9px 6px 9px 11px; font-size: var(--fs-sm); line-height: 1.4; box-shadow: var(--lift); animation: card-in .18s ease-out; }
.card.out { opacity: 0; transform: translateY(6px); transition: opacity .25s, transform .25s; }
@keyframes card-in { from { opacity: 0; transform: translateY(8px); } }
.card-ic { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--k) 18%, transparent); color: var(--k); font: 700 var(--fs-xs)/1 var(--sans); font-style: normal; }
.card .gem.card-ic { background: none; border-radius: 0; margin: 0 -2px; }
.card-tx { min-width: 0; overflow-wrap: anywhere; }
.card-tx b { color: var(--text-strong); font-weight: 600; }
.card-tx b.nat20 { color: var(--green); } .card-tx b.nat1 { color: var(--red); }
.card-acts { grid-column: 2 / -1; display: flex; flex-wrap: wrap; gap: var(--sp-2); }  /* its buttons, a row under the text */
/* A card following its change to D&D Beyond (app.js "following a change"; Bridge Review, Nov 3): its icon turns until
   it's settled, and a line under the text says how it stands */
.card .card-sub { display: block; color: var(--muted); font-size: var(--fs-xs); margin-top: 2px; }
.card.following .card-ic { animation: card-turn 1.2s linear infinite; }
@keyframes card-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .card.following .card-ic { animation: none; opacity: .6; } }
.card-act.main { background: color-mix(in srgb, var(--k) 16%, transparent); }
.card-act { font: 600 var(--fs-xs)/1 var(--sans); color: var(--k); background: transparent; border: 1px solid color-mix(in srgb, var(--k) 55%, transparent); border-radius: var(--r-md); padding: 6px 9px; cursor: pointer; white-space: nowrap; }
.card-x { width: 26px; height: 26px; border: 0; background: transparent; color: var(--muted); border-radius: var(--r-sm); cursor: pointer; font-size: var(--fs-sm); }
.card-x:hover { background: var(--surface-3); color: var(--text-strong); }
.k-ok { --k: var(--green); } .k-info { --k: var(--blue); } .k-roll { --k: var(--amber); } .k-warn { --k: var(--amber); } .k-error { --k: var(--red); }
@media (max-width: 900px) {
  .cards { left: calc(10px + env(safe-area-inset-left, 0px)); right: calc(10px + env(safe-area-inset-right, 0px)); width: auto; bottom: calc(var(--bottom-nav-h, 64px) + 10px + env(safe-area-inset-bottom, 0px)); }
  body:has(.peek.open:not(.modal)) .cards { right: calc(10px + env(safe-area-inset-right, 0px)); }
  body.at-table.rolls-open .cards { bottom: calc(280px + env(safe-area-inset-bottom, 0px)); }
  body.at-table.rolls-closed .cards { bottom: calc(112px + env(safe-area-inset-bottom, 0px)); } }
/* Thrown dice (app.js "thrown dice"): your roll across the page, which dims; others' in the corner tray */
/* Timing and the backdrop come from Dice tuning (app/dice.py; app.js sets --dt-in, --dt-out, --dt-dim, --dt-blur) */
.dice-throw { position: fixed; inset: 0; z-index: 19; pointer-events: none; visibility: hidden; opacity: 0; transition: opacity var(--dt-out, .3s), visibility 0s var(--dt-out, .3s); }
.dice-throw.on { visibility: visible; opacity: 1; transition: opacity var(--dt-in, .2s); }
.dt-dim { position: absolute; inset: 0; background: color-mix(in srgb, var(--backdrop-dark) var(--dt-dim, 55%), transparent); backdrop-filter: blur(var(--dt-blur, 2px)); }
.dt-box { position: absolute; inset: 0; }
.dt-who { position: absolute; top: calc(var(--stick-top, 62px) + 12px); left: 50%; transform: translateX(-50%); display: none; align-items: center; gap: 8px; padding: 5px 14px 5px 8px;
  border-radius: var(--r-pill); background: var(--surface); border: 1px solid var(--line-strong); font-size: var(--fs-sm); white-space: nowrap; box-shadow: 0 6px 18px var(--shadow); }
.dice-throw.others .dt-who { display: flex; }
.dt-who b { color: var(--text-strong); }
/* The result: top centre, clear of the roll window, which opens by the button or at the bottom (Brett, 2026-10-29);
   below the "who rolled" line when it's someone else's */
.dt-card { position: absolute; left: 50%; top: calc(var(--stick-top, 62px) + 16px); transform: translate(-50%, -8px); display: grid; justify-items: center; gap: 2px; min-width: 220px; padding: var(--sp-3) var(--sp-5);
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: var(--lift); opacity: 0; transition: opacity .25s, transform .25s; }
.dice-throw.landed .dt-card { opacity: 1; transform: translate(-50%, 0); }
.dice-throw.others .dt-card { top: calc(var(--stick-top, 62px) + 60px); }
/* (grows with the window, from Dice tuning's 300 × 150 to twice that: feedback 7 found it small) */
.dice-tray { position: fixed; z-index: 9; left: calc(var(--rolls-w, 0px) + 16px); bottom: 16px; width: clamp(300px, 32vw, 600px); aspect-ratio: 2 / 1; max-height: 45vh; overflow: hidden; pointer-events: none;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: var(--lift);
  transform: translateY(calc(100% + 24px)); transition: transform var(--dtr-slide, .3s) ease; }
.dice-tray.up { transform: none; }
.dtr-felt { position: absolute; inset: 0 0 46px; background: radial-gradient(ellipse at center, var(--felt-hi), var(--felt-lo)); }
.dtr-box { position: absolute; inset: 0; }
.dtr-foot { position: absolute; left: 0; right: 0; bottom: 0; height: 46px; display: flex; align-items: center; gap: 8px; padding: 0 12px; font-size: var(--fs-sm); border-top: 1px solid var(--line); }
.dtr-what { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--muted); }
.dtr-what b { color: var(--text-strong); }
.dtr-total { margin-left: auto; font-size: var(--fs-lg); color: var(--text-strong); }
.dtr-total.nat20 { color: var(--green); } .dtr-total.nat1 { color: var(--red); }
/* Manage › Dice tuning (manage_dice.html; docs/mockups/dice-tuning.html): sliders on the left, a preview that throws on the right */
.tn-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: var(--sp-5); align-items: start; margin-bottom: var(--sp-5); }
.section.tn-controls { padding: var(--sp-4) var(--sp-5) 0; margin: 0; }
.tn-controls .sub-tabs { margin-bottom: var(--sp-2); }
.tn-why { font-size: var(--fs-sm); color: var(--muted); margin: 0 0 var(--sp-2); }
.tn-group h3 { margin: var(--sp-4) 0 0; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--amber); }
.tn-row { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 2px var(--sp-4); padding: var(--sp-3) 0; border-bottom: 1px solid var(--line-soft); align-items: center; }
.tn-row:last-child { border-bottom: 0; }
.tn-label b { display: block; color: var(--text-strong); font-weight: 600; font-size: var(--fs-sm); }
.tn-label .note { font-size: var(--fs-xs); }
.tn-ctl { display: flex; align-items: center; gap: var(--sp-2); }
.tn-range { position: relative; flex: 1; display: flex; }
.tn-range input { width: 100%; accent-color: var(--red); margin: 0; }
/* a small tick under the slider: the saved value */
.tn-range::after { content: ""; position: absolute; left: calc(8px + (100% - 16px) * var(--now, 0)); bottom: -7px; width: 2px; height: 6px; margin-left: -1px; background: var(--faint); border-radius: var(--r-xs); }
.tn-ctl output { min-width: 56px; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-strong); font-size: var(--fs-sm); font-weight: 600; }
.tn-now { grid-column: 2; display: flex; justify-content: space-between; font-size: var(--fs-xs); color: var(--faint); min-height: 18px; }
.tn-row.changed output { color: var(--amber); }
.tn-reset { background: none; border: 0; padding: 0; color: var(--link); font: inherit; cursor: pointer; }
.tn-save { position: sticky; bottom: 0; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; background: var(--surface); border-top: 1px solid var(--line);
  margin: var(--sp-3) calc(-1 * var(--sp-5)) 0; padding: var(--sp-3) var(--sp-5); border-radius: 0 0 var(--r-lg) var(--r-lg); }
.tn-save .note { margin-right: auto; }
.tn-preview { position: sticky; top: calc(var(--stick-top, 62px) + 8px); display: grid; gap: var(--sp-2); }
.tn-throws { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }
.tn-throws .spacer { flex: 1; }
.tn-throws [data-full][aria-pressed=true] { border-color: var(--blue); background: var(--selected-bg); color: var(--text-strong); }
.tn-read { display: flex; gap: var(--sp-2) var(--sp-4); flex-wrap: wrap; font-size: var(--fs-sm); color: var(--muted); padding: 0 2px; }
.tn-read b { color: var(--text-strong); font-variant-numeric: tabular-nums; }
.tn-stage { position: relative; height: 430px; border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; background: var(--bg); }
.tn-loading { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); z-index: 8; pointer-events: none; }
/* Stand-ins: a character's Battle page (your roll, the tray), a map (the VTT) */
.tn-stage .stand-in { display: none; }
.tn-stage:not([data-stage="vtt"]) .tn-sheet, .tn-stage[data-stage="vtt"] .tn-map { display: grid; }
.tn-sheet { position: absolute; inset: 0; padding: var(--sp-4); gap: var(--sp-3); align-content: start; font-size: var(--fs-sm); color: var(--muted); }
.tn-sheet-head { display: flex; gap: var(--sp-3); align-items: center; } .tn-sheet-head b { font-size: var(--fs-xl); color: var(--text-strong); } .tn-sheet-head .note { margin-left: auto; }
.tn-av, .tn-tok { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; font-weight: 700; font-size: var(--fs-xs); color: var(--text-strong); background: var(--set-dragons); border: 2px solid var(--line-strong); }
.tn-tiles, .tn-cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-2); }
.tn-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tn-tiles > div { display: flex; justify-content: space-between; align-items: center; padding: var(--sp-2) var(--sp-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); }
.tn-tiles b { color: var(--text-strong); font-size: var(--fs-lg); }
.tn-cols > div { background: var(--surface); border-radius: var(--r-md); padding: var(--sp-3); min-height: 170px; }
.tn-cols h4 { margin: 0 0 var(--sp-2); color: var(--amber); font-size: var(--fs-xs); letter-spacing: .1em; text-transform: uppercase; }
.tn-cols p { display: flex; justify-content: space-between; margin: 0; padding: 5px 0; border-bottom: 1px solid var(--line-soft); }
.tn-map { position: absolute; inset: 0; background: repeating-linear-gradient(0deg, transparent 0 47px, var(--line-soft) 47px 48px), repeating-linear-gradient(90deg, transparent 0 47px, var(--line-soft) 47px 48px),
  radial-gradient(ellipse at 40% 45%, var(--felt-hi), var(--felt-lo) 70%); }
.tn-room { position: absolute; left: 12%; top: 18%; width: 60%; height: 55%; background: var(--surface-3); border: 6px solid var(--line-strong); box-shadow: inset 0 0 40px var(--shadow); }
.tn-tok { position: absolute; width: 40px; height: 40px; font-size: var(--fs-sm); border: 3px solid var(--text-strong); box-shadow: 0 3px 8px var(--shadow); }
.tn-tok:nth-of-type(1) { left: 30%; top: 40%; } .tn-tok:nth-of-type(2) { left: 38%; top: 52%; background: var(--set-starynight); }
.tn-tok:nth-of-type(3) { left: 25%; top: 60%; background: var(--set-bronze); } .tn-tok:nth-of-type(4) { left: 62%; top: 34%; background: var(--set-bloodmoon); }
/* The preview's throw: the same look as .dice-throw and .dice-tray, inside the stage (or across the window: Full screen) */
.tn-layer { position: absolute; inset: 0; z-index: 5; pointer-events: none; opacity: 0; transition: opacity var(--dt-out, .3s); }
.tn-layer.on { opacity: 1; transition: opacity var(--dt-in, .2s); }
.tn-dice { position: absolute; inset: 0; }
.tn-layer .tn-who { top: 14px; }
.tn-layer .tn-card { top: 14px; }
.tn-stage[data-stage="vtt"] .tn-card { top: 58px; }
.tn-stage[data-stage="vtt"] .tn-who { display: flex; }
.tn-layer.landed .tn-card { opacity: 1; transform: translate(-50%, 0); }
.tn-tray { position: absolute; z-index: 7; left: 14px; bottom: 14px; width: 300px; height: 150px; overflow: hidden; background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); box-shadow: var(--lift); transform: translateY(190px); transition: transform var(--dtr-slide, .3s) ease; }
.tn-tray.up { transform: none; }
.tn-stage[data-stage="tray"] .tn-layer, .tn-stage:not([data-stage="tray"]) .tn-tray { display: none; }
.tn-stage.full .tn-layer, .tn-stage.full .tn-tray { position: fixed; z-index: 30; }
.tn-stage.full .tn-tray { left: 16px; bottom: 16px; }
.tn-stage.full .tn-layer .tn-who { top: calc(var(--stick-top, 62px) + 12px); }
.tn-stage.full .tn-layer .tn-card { top: calc(var(--stick-top, 62px) + 16px); }
.tn-stage.full[data-stage="vtt"] .tn-layer .tn-card { top: calc(var(--stick-top, 62px) + 60px); }
@media (max-width: 900px) {
  .tn-grid { grid-template-columns: 1fr; }
  .tn-preview { position: static; order: -1; }
  .tn-stage { height: 320px; }
  .tn-row { grid-template-columns: 1fr; }
  .tn-now { grid-column: 1; }
  .tn-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tn-cols { grid-template-columns: 1fr; } .tn-cols > div { min-height: 0; }
}
/* A dice set's swatch: a small diamond */
[class^="sw-"] { display: inline-block; width: 14px; height: 14px; border-radius: var(--r-xs); transform: rotate(45deg); flex: none; background: var(--sw); border: 1px solid var(--line-strong); }
.sw-astralsea { --sw: var(--set-astralsea); } .sw-bloodmoon { --sw: var(--set-bloodmoon); } .sw-dragons { --sw: var(--set-dragons); } .sw-starynight { --sw: var(--set-starynight); }
.sw-glitterparty { --sw: var(--set-glitterparty); } .sw-bronze { --sw: var(--set-bronze); } .sw-fire { --sw: var(--set-fire); } .sw-ice { --sw: var(--set-ice); }
.sw-radiant { --sw: var(--set-radiant); } .sw-necrotic { --sw: var(--set-necrotic); } .sw-poison { --sw: var(--set-poison); } .sw-black { --sw: var(--set-black); }
.sw-white { --sw: var(--set-white); } .sw-rainbow { --sw: var(--set-rainbow); }
/* The dice picker (_dice_picker.html) */
.dice-picker h3 { margin: var(--sp-4) 0 var(--sp-2); font-size: var(--fs-xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--amber); }
.dice-picker h3:first-child { margin-top: 0; }
.dp-grid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--sp-5); align-items: start; }
.dp-sets { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-2); }
.dp-set, .dp-mat { display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; font-size: var(--fs-sm); }
.dp-set input, .dp-mat input { position: absolute; opacity: 0; pointer-events: none; }
.dp-set:has(input:checked), .dp-mat:has(input:checked) { border-color: var(--blue); background: var(--selected-bg); color: var(--text-strong); }
.dp-set:has(input:focus-visible), .dp-mat:has(input:focus-visible) { outline: 2px solid var(--blue); outline-offset: 2px; }
.dp-mats { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.dp-preview { position: relative; height: 320px; border-radius: var(--r-lg); overflow: hidden; background: radial-gradient(ellipse at center, var(--felt-hi), var(--felt-lo)); display: grid; place-items: center; }
.dp-loading { position: absolute; }
.dp-opts { display: grid; gap: var(--sp-2); justify-items: start; }  /* each option on its own line (Brett, 2026-10-29) */
.dp-about { margin-top: var(--sp-3); }
@media (max-width: 640px) { .dp-grid { grid-template-columns: 1fr; } .dp-preview { height: 220px; order: -1; }
  .dice-tray { left: 8px; right: 8px; width: auto; bottom: calc(var(--bottom-nav-h, 64px) + 8px); } }
@media (max-width: 640px) { .gem { --size: 84px; } .gem.mid { --size: 60px; } .gem.tiny { --size: 28px; } }

/* Phones: the log is a bottom sheet over the bottom bar: the newest rolls, its bar alone
   (collapsed), or every roll with the dice (tap or swipe up). Notices show under the menu bar. */
@media (max-width: 900px) {
  body.at-table { padding-left: 0; --rolls-w: 0px; }
  body.at-table .topbar { margin-left: 0; }
  body.at-table .rolls-side, body.at-table.rolls-closed .rolls-side { transform: none; visibility: visible; top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); width: auto; right: 0; max-height: 210px; border-right: 0; border-top: 1px solid var(--line); border-radius: var(--r-lg) var(--r-lg) 0 0; box-shadow: 0 -10px 30px var(--shadow); }
  body.at-table.rolls-full .rolls-side { max-height: 80vh; }
  body.at-table.rolls-open { padding-bottom: calc(270px + env(safe-area-inset-bottom)); }
  body.at-table.rolls-closed { padding-bottom: calc(108px + env(safe-area-inset-bottom)); }
  body.at-table.rolls-closed .rs-edge { display: none; }
  body.rolls-closed .rs-people, body.rolls-closed .rs-grip { display: none; }
  body.rolls-closed .rs-head { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-3); min-height: 40px; }
  body.rolls-closed .rs-title { flex: none; font-size: var(--fs-xs); }
  body.rolls-closed .rs-latest { display: block; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-sm); color: var(--text); }
  .rs-person .roll-av { width: 32px; height: 32px; }
  .rs-head { position: relative; padding: 14px 14px 6px; cursor: pointer; }
  .rs-grip { display: block; position: absolute; top: 6px; left: calc(50% - 20px); width: 40px; height: 4px; border-radius: var(--r-xs); background: var(--line); }
  .rs-toggle span[aria-hidden] { display: inline-block; transform: rotate(-90deg); }
  body.rolls-closed .rs-toggle span[aria-hidden] { transform: rotate(90deg); }
  body.rolls-closed :is(.rs-log, .rs-controls) { display: none; }
  body.at-table.rolls-open:not(.rolls-full) .rs-controls { display: none; }
  .rs-controls { max-height: none; }
  .roll-item { grid-template-columns: 32px minmax(0, 1fr) auto; padding: 8px 10px; }
  body.at-table.rolls-open:not(.rolls-full) .roll-item { background: none; border: 0; padding: 3px 0; align-items: center; }
  body.at-table.rolls-open:not(.rolls-full) :is(.roll-dice, .roll-flag, .roll-dmg-line, .roll-acts, .roll-who time) { display: none; }
  .roll-av { width: 32px; height: 32px; font-size: var(--fs-xs); }
  .roll-total { font-size: var(--fs-lg); }
  .roll-win { width: calc(100vw - 16px); max-height: 85vh; box-shadow: 0 -10px 40px var(--shadow); }
}
/* Combo turns as ordered steps (a Bonus action can come first) */
.combo-steps-edit { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.combo-step-edit { display: grid; grid-template-columns: 140px minmax(0, 1fr) auto; gap: 8px; align-items: center; }
@media (max-width: 640px) { .combo-step-edit { grid-template-columns: 1fr auto; } .combo-step-edit input { grid-column: 1 / -1; grid-row: 2; } }
.combo-step.k-bonus .combo-k { color: var(--k-bonus); } .combo-step.k-action .combo-k { color: var(--k-action); } .combo-step.k-reaction .combo-k { color: var(--k-reaction); }

/* One Character page, three modes (Brett, 2026-10-19): the same header in each, with the
   Battle / Sheet / Plan switch on the right. */
.char-page .char-head { flex-wrap: nowrap; margin-bottom: 12px; }
.char-title { flex: 1; min-width: 0; }
.char-title h1 { margin: 0; font-size: var(--fs-xl); line-height: 1.2; }
.char-sub { font-size: var(--fs-sm); }
.char-about { display: none; place-items: center; width: 24px; height: 24px; padding: 0; border: 1px solid var(--line-strong); border-radius: 50%; background: none; color: var(--muted); font: 600 var(--fs-xs) var(--serif); position: relative; }
.char-about::after { content: ""; position: absolute; inset: -10px 0; }  /* (a finger-sized target) */
.char-about[aria-expanded=true] { color: var(--text-strong); border-color: var(--muted); }
.modes { display: inline-flex; flex: none; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 3px; gap: 2px; }
.modes a { color: var(--muted); text-decoration: none; padding: 6px 16px; border-radius: var(--r-md); font-weight: 500; white-space: nowrap; }  /* (feedback 20: "Battl/e" in a tight header) */
.modes a:hover { color: var(--text); }
.modes a[aria-current=page] { background: var(--red); color: var(--text-strong); font-weight: 600; }
.forget-tab .sec-head h2 { display: none; }  /* Plan's tab already says Reminders */
.forget-tab .sec-head { justify-content: flex-end; }
.econ-tabs { display: none; }
@media (max-width: 640px) {
  /* Name, ↗ and ⓘ beside the tabs; the line about the character behind ⓘ (Brett chose B, Nov 4, feedback 11) */
  .char-page .char-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 8px 10px; }
  .char-page .char-head .portrait { display: none; }
  .char-page .char-title { display: contents; }  /* (its line about the character spans the row, under the tabs) */
  .char-page .char-name { grid-column: 1; grid-row: 1; min-width: 0; }
  .char-page .char-head > .modes { grid-column: 2; grid-row: 1; }
  .char-page .char-title > :not(.char-name) { grid-column: 1 / -1; }
  .modes a { text-align: center; padding: 6px 10px; }
  .char-title h1 { font-size: var(--fs-lg); }
  .char-about { display: inline-grid; }
  .char-sub:not(.open) { display: none; }
  .econ-tabs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 12px 0 8px; }
  .econ-tabs button { background: none; border: none; border-bottom: 2px solid var(--line); color: var(--muted); padding: 8px 0; font-weight: 500; min-height: 44px; }
  .econ-tabs button[aria-selected=true] { color: var(--k); border-bottom-color: var(--k); font-weight: 700; }
  .econ-tabs .count { font-size: var(--fs-xs); opacity: .8; }
  .econ[data-econ-show=action] > [data-econ-col]:not([data-econ-col=action]),
  .econ[data-econ-show=bonus] > [data-econ-col]:not([data-econ-col=bonus]),
  .econ[data-econ-show=reaction] > [data-econ-col]:not([data-econ-col=reaction]) { display: none; }
}
@media (max-width: 900px) { .live-ago { display: none; } .live-word { display: inline; } }

/* Spell bar on Battle and the Spell Book (Brett, 2026-10-21: mockups on the spellcasting canvas) */
.spell-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 0 0 14px; padding: 8px 12px; background: var(--spell-bg); border: 1px solid var(--spell-line); border-radius: var(--r-lg); }
.spellbook-btn { display: inline-flex; align-items: center; gap: 8px; background: var(--blue); color: var(--chrome); border-color: var(--blue); font-weight: 700; }
.spellbook-btn:hover { background: var(--blue-hover); }
.sb-slots { display: inline-flex; flex-wrap: wrap; gap: 4px 12px; font-size: var(--fs-sm); }
.sb-slot { white-space: nowrap; }
.sb-starred { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-left: auto; }
/* Concentration's pill: one look wherever it is (Battle's Status row, the Spell Book's head; Brett, Nov 3) */
.conc-pill { position: relative; display: inline-flex; align-items: center; gap: 10px; border: 1px solid var(--c); background: color-mix(in srgb, var(--c) 14%, var(--surface)); border-radius: var(--r-pill); min-height: var(--chip-h); padding: 3px 5px 3px var(--sp-2); font-size: var(--fs-sm); }
.conc-pill > .cond-icon { margin-right: -4px; }
.conc-k { color: var(--muted); font-size: var(--fs-xs); }
.conc-next { font: inherit; font-size: var(--fs-xs); background: none; border: 1px solid var(--line); color: var(--muted); border-radius: var(--r-pill); padding: 0 7px; cursor: pointer; }
.conc-pop { position: relative; }
.conc-pop > summary { list-style: none; cursor: pointer; }
.conc-pop > summary::-webkit-details-marker { display: none; }
.conc-save { background: var(--blue); color: var(--chrome); border-radius: var(--r-pill); padding: 3px 10px; font-size: var(--fs-xs); font-weight: 700; }
.conc-x { color: var(--muted); width: 24px; text-align: center; }
.conc-box { position: absolute; top: calc(100% + 8px); left: 0; z-index: 20; width: 300px; background: var(--surface); border: 1px solid var(--blue); border-radius: var(--r-lg); padding: 12px 14px;
  box-shadow: var(--lift); display: flex; flex-direction: column; gap: 10px; font-size: var(--fs-sm); }
.conc-box.warn { border-color: var(--red); width: 270px; right: 0; left: auto; }
.conc-head { display: flex; justify-content: space-between; align-items: baseline; }
.conc-dmg { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.conc-dmg input { width: 64px; }
.conc-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 12px; margin: 0; font-size: var(--fs-sm); }
.conc-facts dt { color: var(--muted); } .conc-facts dd { margin: 0; }
.sb-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; }
.sb-head .fm-title { flex: 1 1 220px; min-width: 0; }
.sb-conc { margin-left: 12px; }
.sb-switch .conc-box { left: auto; right: 0; text-align: left; }  /* (Concentrate sits at a row's right end) */
.sb-switch > summary { list-style: none; }
.sb-switch > summary::-webkit-details-marker { display: none; }
/* Concentrating (Brett, 2026-10-28: lightly): a thin amber line under the Spell Book's header, and the spell's row edged */
.sb-head.sb-concentrating { box-shadow: inset 0 -1px 0 var(--cond-concentration); }
.ls-row.sb-conc-row { box-shadow: inset 3px 0 0 var(--cond-concentration); background: color-mix(in srgb, var(--cond-concentration) 10%, transparent); }  /* edged and lightly tinted */
/* The spell you're concentrating on (Brett, 2026-10-29, option B): its "Conc." tag becomes "◎ Concentration", filled in
   Concentration's color; the ◎ pulses once as the Spell Book opens, then stays still */
.sb-conc-tag { display: inline-flex; align-items: center; gap: 4px; color: var(--bg); border-color: var(--c); background: var(--c); font-weight: 600; }
.sb-conc-tag .cond-icon { width: 12px; height: 12px; color: var(--bg); animation: conc-pulse 1.2s ease-out 1; }
@keyframes conc-pulse { 0% { transform: scale(1); } 35% { transform: scale(1.45); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .sb-conc-tag .cond-icon { animation: none; } }
@media (max-width: 640px) {
  .sb-conc { margin-left: 0; }
  .sb-starred { margin-left: 0; }
  .conc-box, .conc-box.warn { position: fixed; left: 8px; right: 8px; top: auto; bottom: 80px; width: auto; }
}

/* Lists and filters (DESIGN.md › Lists and filters; _lists.html, app.js "Lists"): a filter column on
   the left, then search, the active-filter row, a toolbar and compact rows. */
.ls-layout { display: grid; grid-template-columns: 230px minmax(0, 1fr); gap: 20px; align-items: start; }
.ls-fold > summary { display: none; }
.ls-filters { display: flex; flex-direction: column; gap: 14px; position: sticky; top: 0; }
.ls-filters-head { display: flex; align-items: center; gap: 10px; min-height: var(--control-h); }  /* the search box's height: Filters lines up with it */
.ls-h { font-size: var(--fs-xs); line-height: 1; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--amber); }
.ls-rule { flex: 1; height: 1px; background: linear-gradient(90deg, var(--line-strong), transparent); }
.ls-group { display: flex; flex-direction: column; gap: 6px; }
.ls-label { font-size: var(--fs-sm); color: var(--muted); }
.ls-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.ls-chip.zero { color: var(--faint); }
.ls-n { color: var(--muted); font-size: var(--fs-xs); margin-left: 2px; font-variant-numeric: tabular-nums; }
.ls-chip.zero .ls-n { color: var(--faint); }
.ls-more { border-top: 1px solid var(--line); padding-top: 10px; }
.ls-more > summary { cursor: pointer; font-weight: 600; font-size: var(--fs-sm); }
.ls-more-body { display: flex; flex-direction: column; gap: 14px; margin-top: 12px; }
.ls-main { display: flex; flex-direction: column; gap: 8px; min-width: 0; container: list / inline-size; }
/* Each list's columns need some room (Spells about 900px, Wild Shape 720, Features 680); when the list's own box is
   narrower (a phone, a tablet, or a laptop with the filters beside it), its rows switch to the compact layout below
   each table, whatever the screen width (container queries, 2026-10-26) */
.ls-search { display: flex; align-items: center; gap: var(--sp-2); background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-md); height: var(--control-h); padding: 0 var(--sp-3); color: var(--muted); }
.ls-search:focus-within { border-color: var(--blue); }
.ls-search input { flex: 1; min-width: 0; min-height: 0; padding: 0; background: none; border: 0; color: var(--text); font: inherit; font-size: var(--fs-sm); outline: none; }
.ls-searching { margin: 0; padding: 6px 10px; border-radius: var(--r-md); background: var(--amber-soft); color: var(--text); font-size: var(--fs-sm); }
.ls-is-searching .ls-filters { opacity: .45; pointer-events: none; }
.ls-active { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.ls-active-chips { display: inline-flex; flex-wrap: wrap; gap: 5px; }
.ls-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; }
.ls-toolbar > .note:first-child { margin-right: auto; }
.ls-select { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); color: var(--muted); }
.ls-select select { background: var(--surface-2); }
.ls-head { font-size: var(--fs-xs); color: var(--muted); letter-spacing: .04em; padding: 6px 4px; border-bottom: 1px solid var(--line); }
.ls-grouphead { padding: 12px 4px 4px; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--amber); }
.ls-row { font-size: var(--fs-sm); align-items: center; padding: 7px 4px; border-top: 1px solid var(--line-soft); cursor: pointer; border-radius: var(--r-sm); }
.ls-row:hover, .ls-row:focus-visible { background: var(--row-hover); }
.ls-pk { display: inline-flex; }
.ls-name { min-width: 0; display: flex; flex-direction: column; }
.ls-name b { font-size: var(--fs-md); }
.ls-row .note { font-size: var(--fs-sm); }
.ls-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ls-note { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--note); font-size: var(--fs-xs); font-style: italic; }
.ls-empty { margin: 8px 0; }
@media (max-width: 640px) { .ls-head { display: none; } }
/* Narrower than 900px (a tablet, a phone, the Spell Book's full-screen modal) the filters stack above the list, folded
   into one Filters line until opened (app.js init uses the same width; the UI pass, 2026-11-02) */
@media (max-width: 900px) {
  .ls-layout { grid-template-columns: 1fr; }
  .ls-fold > summary { display: block; cursor: pointer; font-weight: 600; padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--r-md); }
  .ls-fold[open] > summary { margin-bottom: 10px; }
  .ls-filters { position: static; }
}

/* Sheet › Spells: marks · spell · time · range · duration · roll · damage / healing · bell (same idea as below) */
.sh-cols { display: grid; grid-template-columns: 1.25rem minmax(14rem, 1fr) max-content fit-content(9rem) fit-content(7rem) max-content fit-content(11rem) max-content; column-gap: 14px; }
.sh-cols .ls-line { white-space: normal; }  /* a page-width table: let the name and its tags wrap rather than cut off */
.sh-cols > .ls-head, .sh-cols > .ls-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.sh-cols > .ls-grouphead { grid-column: 1 / -1; }
.ls-row .ls-pk { gap: 2px; align-items: center; }
.ls-marks { display: grid; grid-template-columns: repeat(3, 22px); justify-items: center; align-items: center; }
.ls-what { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--muted); font-size: var(--fs-xs); }
.ls-row.not-ready .ls-name b { color: var(--muted); font-weight: 500; }
.sh-status { font-size: var(--fs-md); font-weight: 700; line-height: 1; justify-self: center; }
.sh-status.on { color: var(--green); }
.sh-status.off { color: var(--faint); }
.tag.sh-always { color: var(--green); background: var(--green-soft); }
.ls-views button[aria-pressed="true"] { background: var(--selected-bg); color: var(--text); }
@container list (max-width: 920px) {
  .sh-cols { display: block; }
  .sh-cols > .ls-head { display: none; }
  .sh-cols > .ls-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
  .sh-cols > .ls-row > .ls-name { flex: 1 1 60%; }
  .sh-cols > .ls-row > :not(.ls-marks):not(.ls-name):not(.sh-status) { order: 2; font-size: var(--fs-xs); }
}

/* The Spell Book's table: pin · spell · time · range · roll · casting damage · actions. The header and
   rows share one grid (subgrid), so each column is as wide as its widest cell; the spell name takes
   the space left over, and Range and Casting Damage wrap beyond a sensible width. */
.sb-cols { display: grid; grid-template-columns: 1.25rem minmax(11rem, 1fr) max-content fit-content(11rem) max-content fit-content(13rem) max-content; column-gap: 12px; }
.sb-cols > .ls-head, .sb-cols > .ls-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.sb-cols > .ls-grouphead { grid-column: 1 / -1; }
.sb-roll-col { display: flex; flex-direction: column; }
.sb-onsave { font-size: var(--fs-xs); }
.sb-onsave.half { color: var(--blue); } .sb-onsave.none { color: var(--amber); } .sb-onsave.lands { color: var(--red); }
.sb-avg-col b { font-size: var(--fs-md); }
.sb-avg-col.heal b { color: var(--green); }
.sb-avg-col .note { font-size: var(--fs-xs); }
.sb-range { color: var(--muted); }
.sb-actions { display: inline-flex; gap: var(--sp-1); justify-content: flex-end; align-items: center; }
.sb-foot-note { margin-top: 14px; }
@container list (max-width: 760px) {  /* narrow: name and actions on top, the numbers on one line under them (columns fit the large modal's 1180px; Brett, 2026-10-29) */
  .sb-cols { display: block; }
  .sb-cols > .ls-head { display: none; }
  .sb-cols > .ls-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
  .sb-cols > .ls-row > .ls-name { flex: 1 1 55%; }
  .sb-cols .ls-line { white-space: normal; }
  .sb-cols > .ls-row > .sb-time, .sb-cols > .ls-row > .sb-range, .sb-cols > .ls-row > .sb-roll-col, .sb-cols > .ls-row > .sb-avg-col { order: 2; font-size: var(--fs-xs); flex-direction: row; gap: 6px; }
}

/* The top panel layer in a modal (the Spell Book): a side panel that slides in from
   the modal's right edge, inside it (a spell's details in the Spell Book). */
.peek > .peek.top { position: absolute; top: 0; right: 0; bottom: 0; left: auto; width: min(var(--panel-w), 92%); max-height: none; z-index: 6;
  border: none; border-left: 1px solid var(--line); border-radius: 0; box-shadow: -12px 0 30px var(--shadow); transform: translateX(100%); }
.peek > .peek.top.open { transform: none; }
.peek > .peek-backdrop.top { position: absolute; inset: 0; z-index: 5; background: var(--backdrop-soft); }
.peek:focus { outline: none; }

/* Plan's notes (roadmap step 6): compact rows for combos, leveling and quests; tiles for general notes,
   three per row with a capped height. A click opens the full note in the side panel; drag to reorder. */
.note-rows { display: flex; flex-direction: column; }
.note-item { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; border-radius: var(--r-md); }
.note-row { padding: 9px 8px; border-top: 1px solid var(--line-soft); }
.note-row:hover, .note-tile:hover, .note-item:focus-visible { background: var(--row-hover); }
.note-item-main { flex: 1; min-width: 0; }
.note-item-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.note-item-head b { font-size: var(--fs-md); }
.note-item-head .note { font-size: var(--fs-xs); }
.note-item-sum { color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.note-edit { flex: none; }
.drag-handle { color: var(--faint); cursor: grab; font-size: var(--fs-sm); letter-spacing: -2px; padding-top: 3px; user-select: none; }
.note-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.note-tile { background: var(--surface-2); padding: 12px 14px; max-height: 190px; overflow: hidden; position: relative; }
.note-tile::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 36px; background: linear-gradient(transparent, var(--surface-2)); pointer-events: none; }
.note-tile-body { font-size: var(--fs-sm); margin-top: 4px; }
.note-tile-body p { margin: 0 0 6px; }
@media (max-width: 900px) { .note-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .note-tiles { grid-template-columns: 1fr; } }
.forget-add { display: flex; gap: 8px; margin-top: 10px; }
.forget-add input { flex: 1; min-width: 0; font: inherit; font-size: var(--fs-sm); background: var(--surface-2); color: var(--text); border: 1px solid var(--line); border-radius: var(--r-md); padding: 6px 10px; }

/* Reminders as compact rows (Battle and Plan) */
.forget .forget-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 6px; border-top: 1px solid var(--line-soft); border-radius: var(--r-sm); list-style: none; }
.forget .forget-row[role=button] { cursor: pointer; }
.forget .forget-row[role=button]:hover, .forget .forget-row:focus-visible { background: var(--row-hover); }
.forget-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.forget-kind { font-size: var(--fs-xs); color: var(--muted); }
.forget-text { font-size: var(--fs-sm); }
.forget .forget-note { display: block; font-size: var(--fs-xs); }

/* Conditions on Battle (app/conditions.py): pills like Concentration, with the rules in a pop-up */
.cond-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 10px; }  /* (centered: Concentration's pill holds buttons) */
.cond-pill { position: relative; }
.cond-pill > summary { list-style: none; gap: var(--sp-2); }
.cond-pill > summary::-webkit-details-marker { display: none; }
.cond-box { position: absolute; top: calc(100% + 8px); left: 0; z-index: 20; width: 320px; background: var(--surface); border: 1px solid var(--blue); border-radius: var(--r-lg); padding: 12px 14px; box-shadow: var(--lift); font-size: var(--fs-sm); }
.cond-box p { margin: 0 0 8px; }
.cond-go { margin-top: 6px; }
/* Each condition's colour (docs/mockups/conditions-on-battle.html; app/conditions.py): its pill, emblem, Party icon and
   menu bar dot all take --c from [data-cond] */
[data-cond] { --c: var(--amber); }
[data-cond="blinded"] { --c: var(--cond-blinded); } [data-cond="charmed"] { --c: var(--cond-charmed); } [data-cond="deafened"] { --c: var(--cond-deafened); }
[data-cond="exhaustion"] { --c: var(--cond-exhaustion); } [data-cond="frightened"] { --c: var(--cond-frightened); } [data-cond="grappled"] { --c: var(--cond-grappled); }
[data-cond="incapacitated"] { --c: var(--cond-incapacitated); } [data-cond="invisible"] { --c: var(--cond-invisible); } [data-cond="paralyzed"] { --c: var(--cond-paralyzed); }
[data-cond="petrified"] { --c: var(--cond-petrified); } [data-cond="poisoned"] { --c: var(--cond-poisoned); } [data-cond="prone"] { --c: var(--cond-prone); }
[data-cond="restrained"] { --c: var(--cond-restrained); } [data-cond="stunned"] { --c: var(--cond-stunned); } [data-cond="unconscious"] { --c: var(--cond-unconscious); }
[data-cond="concentration"] { --c: var(--cond-concentration); } [data-cond="enlarged"] { --c: var(--cond-enlarged); } [data-cond="flying"] { --c: var(--cond-flying); } [data-cond="water-breathing"] { --c: var(--cond-water-breathing); }
.cond-icon { width: 16px; height: 16px; flex: none; color: var(--c); }
/* Concentration first in the Status row, like a condition pill in its color, then a divider before the conditions */
.cond-sep { align-self: stretch; width: 1px; margin: 2px var(--sp-1); background: var(--line); }
.cond-sep:last-child { display: none; }
.cond-pill:not(.cond-add) > summary { border-color: var(--c); background: color-mix(in srgb, var(--c) 14%, var(--surface)); padding-left: var(--sp-2); }
/* The edge: the newest three conditions' own colours around the page (--edge, from _cond_icons.html), blending into
   each other, subdued: a thin line and a soft glow. It's the page's own edge, so it shows wherever you've scrolled. */
.cond-halo { position: fixed; inset: 0; z-index: 9; pointer-events: none; }  /* over the Table rolls sidebar and the side panel too (below menus and pop-ups) */
.cond-halo i { position: absolute; inset: 0; border: 0 solid transparent; border-image: conic-gradient(from -45deg, var(--edge)) 1; }
.cond-halo .cond-line { border-width: 3px; opacity: .9; }
.cond-halo .cond-glow { border-width: 19px; filter: blur(15px); opacity: .4; }  /* (a third narrower: Brett, 2026-10-29) */
.cond-halo.dark::after { content: ""; position: absolute; inset: 0; background: radial-gradient(ellipse at center, transparent 55%, var(--cond-dark-glow)); }
.cond-halo.starting i { animation: cond-flash 1.8s ease-out; }
@keyframes cond-flash { 0% { opacity: 1; } }
/* How long a condition lasts (app/conditions.py; Brett, 2026-10-29): a few words on its pill, amber when it ends or asks
   for its save this turn; the whole of it in the pill's box; an amber dot on its badge */
.cond-time.soon, .cond-lasts.soon b { color: var(--amber); }
.cond-lasts { margin-bottom: var(--sp-1); }
.cond-soon { position: absolute; right: 1px; top: 1px; width: 8px; height: 8px; border-radius: 50%; background: var(--amber); border: 2px solid var(--bg); }
.cond-lasts-form { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: end; margin-bottom: var(--sp-2); padding-bottom: var(--sp-2); border-bottom: 1px solid var(--line-soft); }
.cond-lasts-form .field { display: grid; gap: 2px; font-size: var(--fs-xs); color: var(--muted); }
.cond-lasts-form input[type=number] { width: 72px; }
.cond-lasts-form .note { flex-basis: 100%; margin: 0; }
/* The fight band (app/fight.py, _fight_band.html; Brett, 2026-10-29): above the name on every character page and on
   Party while a fight runs in D&D Beyond's VTT */
/* The fight band: a war banner (Brett, 2026-10-30): a pennant, whose turn in big letters, and the order as tokens */
.fight-band { --fb: var(--line-strong); --fb-text: var(--text-strong); --fb-soft: var(--surface-2); display: flex; align-items: stretch; margin-bottom: var(--sp-3);
  background: linear-gradient(90deg, var(--fb-soft), transparent 60%), var(--chrome); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: 0 6px 18px var(--shadow); overflow: hidden; }
.fight-band[data-turn="yours"] { --fb: var(--amber); --fb-text: var(--amber); --fb-soft: var(--amber-soft); border-color: var(--amber); }
.fight-band[data-turn="enemy"] { --fb: var(--red); --fb-text: var(--red-hover); --fb-soft: var(--red-soft); border-color: var(--red-line); }
.fight-band[data-turn="ally"] { --fb: var(--blue); --fb-text: var(--blue); --fb-soft: var(--blue-soft); border-color: var(--blue-line); }
.fb-round { display: flex; align-items: center; gap: var(--sp-2); flex: none; padding: var(--sp-2) var(--sp-6) var(--sp-2) var(--sp-4); background: var(--fb); color: var(--chrome);
  clip-path: polygon(0 0, 100% 0, calc(100% - 16px) 50%, 100% 100%, 0 100%); }
.fb-swords { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; flex: none; }
.fb-round-tx { display: grid; justify-items: center; line-height: 1; }
.fb-round-tx small { font: 700 var(--fs-xs)/1 var(--cond); letter-spacing: .1em; text-transform: uppercase; }
.fb-round-tx b { font: 700 var(--fs-2xl)/1.1 var(--cond); color: inherit; }
.fb-call { display: grid; align-content: center; gap: 2px; flex: none; min-width: 0; max-width: 40%; padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-2); }
.fb-head { font: 700 var(--fs-2xl)/1.05 var(--cond); letter-spacing: .02em; text-transform: uppercase; color: var(--fb-text); text-wrap: balance; }
.fb-sub { font-size: var(--fs-xs); color: var(--muted); }
.fb-sub .roll-to { border: 0; padding: 0; background: none; color: var(--faint); }
.fb-mine { color: var(--amber); font-weight: 700; }
.fb-order { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: flex-end; gap: var(--sp-1) var(--sp-2); flex: 1 1 0; min-width: 0; padding: var(--sp-2) var(--sp-3); }
.fb-who { display: grid; justify-items: center; gap: 3px; width: 64px; font-size: var(--fs-xs); color: var(--muted); }
.fb-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fb-pic { position: relative; width: 40px; height: 40px; border-radius: 50%; overflow: hidden; display: grid; place-items: center; background: var(--surface-3); color: var(--muted); font-weight: 700; box-shadow: 0 0 0 2px var(--line-strong); }
.fb-pic img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.fb-who.enemy .fb-pic { box-shadow: 0 0 0 2px var(--red); }  /* the VTT's monsters: the enemy (Brett, 2026-10-29) */
.fb-who:has(~ .now) { opacity: .45; }  /* acted this round */
.fb-who.now { width: 84px; color: var(--text-strong); font-weight: 700; }
.fb-who.now .fb-pic { width: 56px; height: 56px; box-shadow: 0 0 0 3px var(--fb), 0 0 18px var(--fb); }
.fight-band[data-turn="yours"] .fb-who.now .fb-pic { animation: fb-mine 2.4s ease-in-out infinite; }
@keyframes fb-mine { 50% { box-shadow: 0 0 0 3px var(--amber), 0 0 30px var(--amber-glow); } }
@media (prefers-reduced-motion: reduce) { .fight-band[data-turn="yours"] .fb-who.now .fb-pic { animation: none; } }
.fb-who.down .fb-pic img { filter: grayscale(1) brightness(.6); }  /* at 0 Hit Points (app/death_saves.py) */
.fb-who.down .fb-pic::after { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(135deg, transparent 0 3px, var(--shadow) 3px 6px); }
.fb-who.down .fb-pic { box-shadow: 0 0 0 2px var(--red-hover); }
.fb-zero { font-size: var(--fs-xs); font-weight: 700; color: var(--red-hover); }
.fb-who.hid { opacity: .6; }
.fb-who.hid .fb-pic { outline: 1px dashed var(--muted); outline-offset: 3px; }
@media (max-width: 900px) { .fight-band { flex-wrap: wrap; } .fb-call { flex: 1 1 0; max-width: none; } .fb-order { flex: 1 0 100%; justify-content: flex-start; border-top: 1px solid var(--line); } }
@media (max-width: 640px) { .fb-head { font-size: var(--fs-xl); } .fb-who { width: 34px; } .fb-pic { width: 34px; height: 34px; } .fb-who.now { width: 60px; } .fb-who.now .fb-pic { width: 46px; height: 46px; } .fb-who:not(.now) .fb-name { display: none; } }  /* phones: pictures, and whose turn by name */
/* The badges after the name, on every character page (Brett, 2026-10-29; docs/mockups/temporary-items.html): small,
   each in its condition's colour; hover, focus or a tap shows what it does below it */
.char-name { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) 10px; }
.cond-badges { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1); }
.cond-badge { position: relative; width: 26px; height: 26px; border-radius: var(--r-md); border: 1.5px solid var(--c); background: color-mix(in srgb, var(--c) 16%, var(--bg)); color: var(--c); display: grid; place-items: center; padding: 0; cursor: pointer; }
.cond-badge[data-cond="concentration"] { border-radius: 50%; }
.cond-badge .cond-icon { width: 16px; height: 16px; }
.cond-badge[data-cond="blinded"] { background: var(--cond-dark); border-color: var(--cond-dark-edge); }
[data-cond="prone"] .cond-icon { transform: rotate(-30deg); }
.cond-lv { position: absolute; right: 0; bottom: 0; min-width: 13px; height: 13px; border-radius: var(--r-md); background: var(--c); color: var(--bg); font: 700 var(--fs-xs)/13px var(--sans); text-align: center; padding: 0 2px; }  /* inside the badge */
.cond-badge.new { animation: cond-arrive .9s cubic-bezier(.2, 1.4, .4, 1); }
@keyframes cond-arrive { 0% { transform: scale(.3) rotate(-20deg); opacity: 0; } 100% { transform: none; opacity: 1; } }
.cond-tip { position: absolute; left: 0; top: calc(100% + 8px); z-index: 12; width: 270px; max-width: calc(100vw - 32px); text-align: left; background: var(--surface); border: 1px solid var(--c); border-radius: var(--r-lg); padding: 10px 12px; color: var(--text); font-size: var(--fs-sm); line-height: 1.45; box-shadow: var(--lift); display: none; }
.cond-tip b { display: block; margin-bottom: 2px; color: var(--c); }
.cond-tip .note { display: block; margin-top: 4px; }
.cond-badge:hover .cond-tip, .cond-badge:focus-visible .cond-tip, .cond-badge[aria-expanded="true"] .cond-tip { display: block; }
.cond-more { font-size: var(--fs-xs); font-weight: 600; color: var(--muted); padding: 0 var(--sp-1); }
/* Signature touches: Invisible fades your portrait (Blinded and Unconscious darken the edges, a ring above) */
.char-page:has(#battle-body.cond-ghost) .char-head .portrait { opacity: .38; outline: 2px dashed var(--cond-invisible); outline-offset: 2px; }
/* Party: the same badges after each name (Brett, 2026-10-29), and the card's edge in the newest one's colour */
.pt-card[data-conds] { box-shadow: inset 3px 0 0 var(--h); }
/* The menu bar: a dot per condition on your character's button */
.cond-dots { display: inline-flex; gap: 3px; }
.cond-dots:empty { display: none; }
.cond-dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--c); }
.cond-dots i[data-cond="blinded"] { background: var(--cond-dark); box-shadow: 0 0 0 1px var(--cond-dark-edge); }
@media (prefers-reduced-motion: reduce) { .cond-halo.starting i, .cond-badge.new { animation: none; } }
/* A change on its way to D&D Beyond (ddb_bridge.as_shown, the sync macros in _char.html): shown as made at once, with
   one quiet mark until D&D Beyond has it, then the green change mark. One mark everywhere (Brett, 2026-11-02,
   docs/artifacts/ui-ux-pass.html, question 1): a grey ↻ right after the value, in the flow so it never covers anything
   (a tile's in its label row, .nt-mark), and only once the change has waited 2 seconds, since most arrive sooner; ! in
   amber at once when it was given up, to make on the sheet; a condition ending faded until it's gone. */
[data-sync] { --sync: color-mix(in srgb, var(--muted) 70%, transparent); }
[data-sync="failed"] { --sync: var(--amber); }
[data-sync]:not(.nt):not(.cond-pill)::after, .cond-pill[data-sync] > summary::after, .nt[data-sync] > .nt-lab > .nt-mark::after {
  content: "↻"; margin-left: 3px; font: 400 var(--fs-xs)/1 var(--sans); color: var(--sync); pointer-events: none; animation: sync-late .2s 2s both; }
[data-sync="failed"]:not(.nt):not(.cond-pill)::after, .cond-pill[data-sync="failed"] > summary::after, .nt[data-sync="failed"] > .nt-lab > .nt-mark::after { content: "!"; font-weight: 700; animation: none; }
@keyframes sync-late { from { opacity: 0; } to { opacity: 1; } }
.cond-pill[data-sync="ending"] > summary, .cond-badge[data-sync="ending"] { opacity: .5; }

/* A number tile (_char.html ntile; Brett, 2026-11-02, question 4): the label row (the ↻ and any buttons at its right),
   the value on one line, a foot for badges and small notes. Nothing inside wraps or covers anything, whatever is on
   its way. */
.nt { display: flex; flex-direction: column; justify-content: flex-start; gap: 2px; min-width: 0; }  /* (top-aligned: a row of tiles keeps its labels on one line, Brett 2026-11-02) */
.nt > .nt-lab { display: flex; align-items: center; gap: var(--sp-1); min-height: 18px; }
.battle-head .nt > .nt-lab { min-height: var(--chip-h); }  /* (as tall as Hit points' − and +, so every label in the row lines up) */
.nt > .nt-lab > small { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nt > .nt-lab > .nt-mark:empty { display: none; }
.nt[data-sync] > .nt-lab > .nt-mark { display: inline; }
.nt > .nt-val { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.3; }
.nt > .nt-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 2px var(--sp-1); font-size: var(--fs-xs); color: var(--muted); }
.nt > .nt-foot .note { font-size: var(--fs-xs); }
/* The change mark in a tile ("−11", app.js showMarks): for its moment it takes the place of "/ 66", so the value row
   keeps its one line and nothing in the tile moves (Brett, 2026-11-02: the HP tile with a change in it) */
.nt:has(.mark-delta) > .nt-val > .note { display: none; }
.nt > .nt-val > .mark-delta { margin-left: var(--sp-1); }

.cond-add > summary { background: var(--surface-2); border-style: dashed; border-color: var(--line); color: var(--muted); }
.cond-box .ls-chips { margin-top: 4px; }

/* Wild Shape forms as rows (roadmap step 8): known · picture · form · AC · Temp HP · to hit · damage · speed · marks */
.fm-cols { display: grid; grid-template-columns: 1.25rem 44px minmax(14rem, 1fr) max-content max-content max-content max-content fit-content(12rem) max-content; column-gap: 14px; }
.fm-cols > .ls-head, .fm-cols > .ls-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.fm-cols > .ls-grouphead { grid-column: 1 / -1; }
.fm-cols .fm-pic { width: 44px; height: 44px; border-radius: var(--r-md); overflow: hidden; background: var(--surface-2); }
.fm-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ls-marks.two { grid-template-columns: repeat(2, 22px); }
.form-row.not-ready .ls-name b { color: var(--muted); font-weight: 500; }
@container list (max-width: 740px) {
  .fm-cols { display: block; }
  .fm-cols > .ls-head { display: none; }
  .fm-cols > .ls-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
  .fm-cols > .ls-row > .ls-name { flex: 1 1 50%; }
  .fm-cols > .ls-row > :not(.ls-marks):not(.ls-name):not(.sh-status):not(.fm-pic) { order: 2; font-size: var(--fs-xs); }
}

/* Quiet header actions (Brett, 2026-10-22): "+ Add" on Battle's Action, Bonus action and Reaction
   columns and on Battle combos. Muted until hovered, so the headings stay the loudest thing. */
.head-add { font-size: var(--fs-sm); font-weight: 500; color: var(--muted); text-decoration: none; }
.head-add:hover, .head-add:focus-visible { color: var(--text); text-decoration: underline; }
.econ-top { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px var(--sp-3); }
.econ-word { white-space: nowrap; }  /* (Nov 5: "Bonus / action" split beside Rules and + Add) */
.econ-top > .econ-head { flex: 1; min-width: min-content; }  /* (its name never squeezed: Rules and + Add wrap under it instead) */
.econ-tools { flex: none; display: flex; align-items: center; gap: var(--sp-3); }
/* "Rules (n)" on a Battle column: rules from classes and feats that change it (app/class_rules.py), on hover or tap */
.rules-pop { position: relative; display: inline-flex; outline: none; }
.rules-chip { cursor: help; border: 1px dashed var(--line-strong); background: none; }
.rules-pop:hover .rules-chip, .rules-pop:focus-within .rules-chip { color: var(--k); border-color: var(--k); }
.rules-box { display: none; position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; width: min(320px, calc(100vw - 32px)); padding: var(--sp-3);
  background: var(--surface-2); border: 1px solid var(--line); border-top: 2px solid var(--k); border-radius: var(--r-md); box-shadow: var(--lift);
  font-size: var(--fs-sm); color: var(--text); text-transform: none; letter-spacing: 0; font-weight: 400; }
.rules-box span { display: block; }
.rules-box span + span { margin-top: var(--sp-2); padding-top: var(--sp-2); border-top: 1px solid var(--line); }
.rules-pop:hover .rules-box, .rules-pop:focus-within .rules-box { display: block; }

/* Battle: resources above the number tiles */
.battlebar > .res-more + .res-row { margin: 0 0 10px; }
.battlebar > .res-row.slots + .battle-head { margin-top: 10px; }

/* Notes editor: formatting buttons over an editable area; saved as Markdown (app.js richNotes) */
.rich-bar { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 6px; }
.rich-bar button { font: inherit; font-size: var(--fs-sm); min-width: 34px; padding: 4px 9px; color: var(--muted); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer; }
.rich-bar button:hover { color: var(--text); }
.rich-bar button[aria-pressed=true] { color: var(--text); border-color: var(--muted); }
.rich-area { min-height: 300px; max-height: 55vh; overflow: auto; color: var(--text); background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 10px 14px; line-height: 1.5; }
.rich-area:focus { border-color: var(--red); outline: none; }
.rich-area:empty::before { content: attr(data-placeholder); color: var(--muted); }
.rich-area > :first-child { margin-top: 0; }
.rich-area h3 { font-size: var(--fs-lg); margin: 14px 0 6px; }
.rich-area p { margin: 0 0 8px; }
.rich-area ul, .rich-area ol { margin: 0 0 8px; padding-left: 22px; }
.rich-area h1, .rich-area h2 { font-size: var(--fs-lg); margin: 14px 0 6px; }

/* Lists: "Full notes" in the toolbar shows each row's note in full */
.ls-check { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); color: var(--muted); cursor: pointer; white-space: nowrap; }
.ls-check input { margin: 0; accent-color: var(--red); }
.ls-notes-full .ls-note { white-space: pre-line; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }

/* Manage › Design system (the living component page): only layout for the samples; the samples use the real styles */
.ds-jump { display: flex; gap: var(--sp-2); margin: 0 0 var(--sp-5); position: sticky; top: var(--stick-top, 62px); z-index: 4; background: var(--bg); padding-block: var(--sp-2); overflow-x: auto; scrollbar-width: none; }  /* one row that stays in reach as you scroll (sideways when there's no room) */
.ds-jump a { flex: none; white-space: nowrap; font-size: var(--fs-sm); color: var(--muted); text-decoration: none; padding: var(--sp-1) var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-pill); }
.ds-jump a:hover { color: var(--text); }
.ds-jump a[aria-current="true"] { color: var(--text); border-color: var(--blue); background: var(--selected-bg); }  /* the section you're in */
.ds-jump .ds-top { color: var(--muted); position: sticky; left: 0; z-index: 1; background: var(--bg); box-shadow: 6px 0 8px var(--bg); }  /* stays at the row's start while it scrolls */
.ds-page .section { scroll-margin-top: calc(var(--stick-top, 62px) + 64px); }  /* (below the sticky navigation) */
.ds-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3); margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-sm); }
.ds-meta:first-of-type { margin-top: var(--sp-3); }
.ds-meta code, .ds-page code { font-size: var(--fs-xs); color: var(--muted); background: var(--surface-2); border-radius: var(--r-sm); padding: 1px var(--sp-1); }
.ds-later { flex-basis: 100%; font-size: var(--fs-xs); color: var(--amber); }
.ds-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); }
.ds-row + .ds-row { margin-top: var(--sp-3); }
.ds-sample { margin: 0; }
.ds-edge { position: relative; height: 96px; max-width: 520px; border-radius: var(--r-lg); background: var(--bg); overflow: hidden; }
.ds-edge .cond-halo { position: absolute; z-index: auto; }  /* (on the page it's fixed to the window) */
.ds-sample .title-row h1 { margin: 0; }
.ds-h2 { margin: 0; }
.ds-narrow { max-width: 420px; }
.ds-menus { align-items: flex-start; }
.ds-menu { position: static; }  /* a menu drawn open on the design page */
.ds-econ .section { background: var(--surface-2); min-height: 70px; }
.ds-rows { max-width: 640px; }
.ds-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-3); }
.ds-swatch { display: flex; align-items: center; gap: var(--sp-2); }
.ds-swatch span { width: 36px; height: 36px; border-radius: var(--r-md); border: 1px solid var(--line-strong); flex: none; }
.ds-type { display: grid; gap: var(--sp-3); }
.ds-type > div { display: flex; flex-direction: column; }
.ds-space, .ds-corners { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-5); margin-top: var(--sp-3); font-size: var(--fs-sm); }
.ds-space div, .ds-corners div { display: flex; flex-direction: column; gap: var(--sp-1); }
.ds-space span { background: var(--red); display: block; }
.ds-corners span { width: 64px; height: 40px; background: var(--surface-2); border: 1px solid var(--line-strong); display: block; }
.ds-sizes { overflow-x: auto; }
.ds-sizes td { vertical-align: middle; font-size: var(--fs-sm); }


/* Icon labels (app.js): what an icon button does, on hover, focus or a long press */
.icon-tip { position: fixed; z-index: 40; max-width: 240px; padding: var(--sp-1) var(--sp-2); background: var(--text); color: var(--bg);
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.4; border-radius: var(--r-sm); pointer-events: none; box-shadow: 0 4px 14px var(--shadow); }
/* Toast options on the design page (demo until one is chosen) */
[data-inline-ok].is-done { color: var(--green); border-color: var(--green-line); background: var(--green-soft); }
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--lift); transform: translateY(12px); }
  border-radius: var(--r-md); box-shadow: var(--lift); font-weight: 500; }

/* One line, then the rest on click (app.js): long summary lines on class cards */
.clamp { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; cursor: pointer; }
.clamp.open { display: block; }
.clamp:hover { color: var(--text); }

/* Sheet › Inventory (_item_list.html): equipped · item · kind · uses · details · marks */
.it-cols { display: grid; grid-template-columns: 1.25rem minmax(14rem, 1fr) max-content max-content fit-content(14rem) max-content; column-gap: 14px; }
.it-cols > .ls-head, .it-cols > .ls-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.it-cols > .ls-grouphead { grid-column: 1 / -1; }
.it-cols .it-rarity { display: block; font-size: var(--fs-xs); }
@container list (max-width: 680px) {
  .it-cols { display: block; }
  .it-cols > .ls-head { display: none; }
  .it-cols > .ls-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
  .it-cols > .ls-row > .ls-name { flex: 1 1 50%; }
  .it-cols > .ls-row > :not(.ls-marks):not(.ls-name):not(.sh-status) { order: 2; font-size: var(--fs-xs); }
}

/* Sheet › Features (_feature_list.html): feature · from · level · used as · uses · marks */
.ft-cols { display: grid; grid-template-columns: minmax(16rem, 1fr) max-content max-content max-content max-content max-content; column-gap: 14px; }
.ft-cols > .ls-head, .ft-cols > .ls-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.ft-cols > .ls-grouphead { grid-column: 1 / -1; }
.feature-row.not-ready b { font-weight: 500; }
@container list (max-width: 700px) {
  .ft-cols { display: block; }
  .ft-cols > .ls-head { display: none; }
  .ft-cols > .ls-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
  .ft-cols > .ls-row > .ls-name { flex: 1 1 60%; }
  .ft-cols > .ls-row > :not(.ls-marks):not(.ls-name) { order: 2; font-size: var(--fs-xs); }
}

/* FAQ: search, and the diagram of how askdnd's information is organized */
.faq-search { margin: 0 0 var(--sp-5); }
.faq-fig { margin: var(--sp-3) 0; overflow-x: auto; }
.faq-dg { display: block; width: 100%; min-width: 560px; height: auto; color: var(--text); }
.faq-dg .box { fill: var(--surface-2); stroke: var(--line); stroke-width: 1.5; }
.faq-dg .code { fill: var(--violet-soft); stroke: var(--violet); stroke-width: 1.5; }
.faq-dg .camp { fill: var(--blue-soft); stroke: var(--blue); stroke-width: 1.5; }
.faq-dg .char { fill: var(--green-soft); stroke: var(--green); stroke-width: 1.5; }
.faq-dg text { fill: currentColor; font-size: var(--fs-sm); }
.faq-dg .t { font-weight: 700; font-size: var(--fs-sm); }
.faq-dg .s { fill: var(--muted); font-size: var(--fs-xs); }
.faq-dg .lab { fill: var(--muted); font-size: var(--fs-xs); font-style: italic; }
.faq-dg .ln { stroke: currentColor; stroke-width: 1.5; fill: none; }
.faq-dg .head { fill: currentColor; }

/* Battle's "See all" / "+ Add" panels: search and kind chips above the groups */
.opt-tools { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-2); position: sticky; top: calc(-1 * var(--sp-4)); z-index: 2; background: var(--surface); padding: var(--sp-2) 0; }
.opt-group h3.divider { margin-top: var(--sp-4); }
.opt-group:first-of-type h3.divider { margin-top: var(--sp-2); }

/* A campaign entry's side panel (_peek.html): the table's facts, each with Done for the DM */
.peek-facts li { display: block; }
.peek-facts .btn.link.small { padding: 0 var(--sp-1); }
.peek-pending { margin-top: var(--sp-2); }
/* The entry's Edit window: the window's title already names it, and its sections sit as compact cards */
.entity-edit .entity-head .title-row h1 { display: none; }
.entity-edit .entity-head .title-row { margin: 0; }
.entity-edit .entity-head .subtitle { margin: 0 0 var(--sp-3); }
.entity-edit .section { background: var(--surface-2); padding: var(--sp-4) var(--sp-5); margin: 0 0 var(--sp-3); border-radius: var(--r-lg); }
.entity-edit details.section > summary { cursor: pointer; }
.entity-edit .section-head { margin-bottom: var(--sp-2); }
.entity-edit .section .story { background: var(--bg); }

/* Manage › DM guide: the flow at the top, the backlog checklist, tips */
.guide-flow { display: grid; gap: var(--sp-2); margin: var(--sp-3) 0; }
.gf-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.gf-box { padding: var(--sp-1) var(--sp-3); border-radius: var(--r-md); border: 1px solid var(--line); font-size: var(--fs-sm); font-weight: 600; }
.gf-in { background: var(--violet-soft); border-color: var(--violet); }
.gf-you { background: var(--amber-soft); border-color: var(--amber); }
.gf-out { background: var(--blue-soft); border-color: var(--blue); }
.gf-arrow { color: var(--muted); font-size: var(--fs-sm); }
.guide-steps { padding-left: var(--sp-5); display: grid; gap: var(--sp-3); margin: var(--sp-3) 0; }
.guide-steps > li > p { margin: var(--sp-1) 0 0; }
.guide-steps > li.done > b { color: var(--muted); text-decoration: line-through; }
.guide-sub { padding-left: var(--sp-5); display: grid; gap: var(--sp-1); margin: var(--sp-2) 0 0; }
.guide-tips { margin-top: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-lg); background: var(--surface-2); }
.guide-tips ul { margin: var(--sp-1) 0 0; padding-left: var(--sp-5); }
.guide-list { padding-left: var(--sp-5); display: grid; gap: var(--sp-2); }
.dm-guide .section { scroll-margin-top: 80px; }

/* The Campaign page (campaign.html, roadmap step 11; Brett 2026-10-22): World is the Reader, names down the left
   and one entry on the page (_peek.html with where="reader"); Quests is the board; Relationships has a tree per
   family, the other ties, and anyone's connections (_connections.html). */
.reader { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: var(--sp-5); align-items: start; }
.reader-nav { position: sticky; top: calc(var(--stick-top, 62px) + var(--tabs-h, 41px) + var(--sp-3)); max-height: calc(100vh - var(--stick-top, 62px) - var(--tabs-h, 41px) - var(--sp-5)); overflow-y: auto; background: var(--surface); border-radius: var(--r-lg); padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); }
.reader-nav > * { flex: none; }
.reader-nav-head { position: sticky; top: calc(-1 * var(--sp-3)); z-index: 1; display: flex; flex-direction: column; gap: var(--sp-2); margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) 0; padding: var(--sp-3); background: var(--surface); border-bottom: 1px solid var(--line-soft); }
.reader-nav .ls-views { display: flex; }
.reader-nav .ls-views button { flex: 1; }
.reader-filters { display: grid; gap: var(--sp-2); }
.reader-filters-head { display: flex; justify-content: space-between; align-items: baseline; font: 700 var(--fs-xs) var(--cond); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.reader-filters .ls-chip { font-size: var(--fs-xs); min-height: var(--control-h-sm); padding: 0 var(--sp-2); }
.reader-filters .note { margin: 0; }
/* The Campaign page's tabs: Story's underline tabs on the page itself, still sticky (Brett, 2026-10-23) */
.sheet.campaign-page .tabbar { background: var(--bg); margin: 0 0 var(--sp-4); padding: 0; }
.reader-rows { display: flex; flex-direction: column; }
.reader-rows .ls-grouphead { padding: var(--sp-3) var(--sp-2) var(--sp-1); }
.rd-item { display: flex; align-items: center; gap: var(--sp-2); padding: 5px var(--sp-2); border-radius: var(--r-sm); color: var(--text); text-decoration: none; font-size: var(--fs-sm); }
.rd-item:hover { background: var(--row-hover); }
.rd-item.sel { background: var(--selected-bg); color: var(--text-strong); box-shadow: inset 2px 0 0 var(--blue); }
.rd-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-dot { width: 7px; height: 7px; flex: none; border-radius: var(--r-pill); background: var(--blue); }
.rd-item:not(:has(.rd-dot)) { padding-left: calc(var(--sp-2) + 7px + var(--sp-2)); }
.reader-article { background: var(--surface); border-radius: var(--r-lg); padding: var(--sp-6) 40px; min-height: 60vh; scroll-margin-top: calc(var(--stick-top, 62px) + var(--tabs-h, 41px) + var(--sp-3)); }
/* One entry on the Reader (_entry_reader.html; World Entries by Kind, layout A, Brett 2026-11-05): the picture in its
   kind's shape beside the name and story, an at-a-glance panel, then key moments and what the table knows beside the
   connections. Its own width decides how it stacks (a container), since the list beside it takes room. */
.ep { container-type: inline-size; }
.ep h3:not(.divider) { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); margin: 24px 0 8px; }
.ep-top { display: grid; grid-template-columns: minmax(0, 1fr) 260px; grid-template-areas: "head glance"; gap: var(--sp-5) var(--sp-6); align-items: start; }
.ep-top.has-pic { grid-template-columns: 200px minmax(0, 1fr) 260px; grid-template-areas: "pic head glance"; }
.ep-side { grid-area: pic; }
.ep-head { grid-area: head; min-width: 0; }
.ep-glance { grid-area: glance; }
.ep-title { display: flex; gap: var(--sp-3); align-items: center; }
.ep-title h2 { font-size: var(--fs-xl); margin: 2px 0; }
.ep-head .story { margin: var(--sp-4) 0 0 -22px; padding: 16px 22px; }  /* its words line up with the name; the parchment reaches into the gap */
.ep-head > .muted { margin-top: var(--sp-4); }
.ep-pic { position: relative; margin: 0; }
.ep-pic img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-md); border: 1px solid var(--line); background: var(--surface-2); }
.ep[data-shape=portrait] .ep-side { aspect-ratio: 3 / 4; }
.ep[data-shape=square] .ep-side, .ep[data-shape=item] .ep-side { aspect-ratio: 1; }
.ep[data-shape=item] .ep-pic img { object-fit: contain; }
.ep-pic.ep-banner, .ep-slot.ep-banner { aspect-ratio: 16 / 5; min-height: 150px; margin-bottom: var(--sp-5); }
.ep-pic figcaption { margin-top: var(--sp-1); }
.ep-change { position: absolute; right: var(--sp-2); bottom: var(--sp-2); opacity: 0; transition: opacity .15s; }
.ep-pic:hover .ep-change, .ep-change:focus-visible { opacity: 1; }
@media (hover: none) { .ep-change { opacity: 1; } }
.ep-pic:has(figcaption) .ep-change { bottom: calc(var(--sp-2) + 1.6em); }
/* No picture yet: its place asks whoever can change the entry for one; everyone else gets a badge with its initials */
.ep-slot { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-2); padding: var(--sp-3); text-align: center; border: 1.5px dashed var(--line-strong); border-radius: var(--r-md); background: repeating-linear-gradient(135deg, var(--surface) 0 10px, var(--surface-2) 10px 20px); }
.ep-slot .note { margin: 0; }
.ep-slot-btns { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); }
.ep-outline { width: 48px; height: 48px; fill: none; stroke: var(--faint); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.ep-initials { flex: none; width: 52px; height: 52px; display: grid; place-items: center; border-radius: var(--r-pill); font-weight: 700; font-size: var(--fs-md); color: var(--blue); background: var(--blue-soft); border: 1px solid var(--blue-line); }
.ep-initials[data-kind=place] { color: var(--green); background: var(--green-soft); border-color: var(--green-line); }
.ep-initials[data-kind=item], .ep-initials[data-kind=quest] { color: var(--amber); background: var(--amber-soft); border-color: var(--amber-soft); }
.ep-initials[data-kind=faction] { color: var(--red); background: var(--red-soft); border-color: var(--red-line); }
.ep-initials[data-kind=creature] { color: var(--violet); background: var(--violet-soft); border-color: var(--violet-soft); }
.ep-mark { flex: none; width: 72px; height: 72px; object-fit: cover; border-radius: var(--r-md); border: 1px solid var(--line); }
.ep-mark-empty { display: grid; place-items: center; padding: 0; background: none; border: 1.5px dashed var(--line-strong); cursor: pointer; }
.ep-mark-empty .ep-outline { width: 36px; height: 36px; }
.ep-glance { margin: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2) var(--sp-3); padding: var(--sp-4); border: 1px solid var(--line); border-radius: var(--r-md); font-size: var(--fs-sm); }
.ep-glance dt { color: var(--muted); }
.ep-glance dd { margin: 0; min-width: 0; }
.ep-cols { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--sp-6); margin-top: var(--sp-3); }
.ep-links { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border: 1px solid var(--line); border-radius: var(--r-md); }
.ep-links ul { margin: 0; }
.ep-links li + li { margin-top: var(--sp-1); }
.ep-map-link { font-size: var(--fs-sm); }
@container (max-width: 820px) {
  .ep-top { grid-template-columns: minmax(0, 1fr); grid-template-areas: "head" "glance"; }
  .ep-top.has-pic { grid-template-columns: 160px minmax(0, 1fr); grid-template-areas: "pic head" "glance glance"; }
  .ep-cols { grid-template-columns: minmax(0, 1fr); gap: 0; }
}
@container (max-width: 520px) {
  .ep-top.has-pic { grid-template-columns: minmax(0, 1fr); grid-template-areas: "pic" "head" "glance"; }
  .ep-side { width: min(100%, 220px); }
}
.reader-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-6); }
.moment-line { list-style: none; padding: 0; margin: 0; border-left: 2px solid var(--story-line); }
.moment-line li { position: relative; padding: 0 0 var(--sp-3) var(--sp-4); }
.moment-line li::before { content: ""; position: absolute; left: -6px; top: 9px; width: 10px; height: 10px; border-radius: var(--r-pill); background: var(--story-line); }
.moment-line .note { display: block; font-size: var(--fs-xs); }
.reader-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-top: var(--sp-6); padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.reader-foot form { margin: 0; }
.reader-keys { margin-left: auto; font-size: var(--fs-xs); }
@media (max-width: 900px) {
  .reader { grid-template-columns: 1fr; }
  .reader-nav { position: static; max-height: 50vh; }
  .reader-cols { grid-template-columns: 1fr; gap: 0; }
  .reader-article { padding: var(--sp-5); }
  .reader-keys { display: none; }
}

/* Part of (an entry's quests) and other small link chips */
.part-of { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); }
.chip-link { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--chip-h); padding: 0 var(--sp-3); border-radius: var(--r-pill); background: var(--surface-2); border: 1px solid var(--line); color: var(--text); font-size: var(--fs-sm); text-decoration: none; }
.chip-link:hover { border-color: var(--line-strong); }
.head-link { margin-left: var(--sp-2); font-size: var(--fs-xs); font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--link); }

/* Quests: the board */
/* Quests: a quest log (docs/mockups/campaign-v2.html), on the Reader's layout */
.ql-item { display: grid; gap: 2px; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md); border-left: 3px solid var(--amber); color: var(--text); text-decoration: none; }
.ql-item[hidden] { display: none; }
.ql-item:hover { background: var(--row-hover); }
.ql-item.done { border-left-color: var(--line-strong); }
.ql-item.sel { background: var(--selected-bg); }
.ql-item b { color: var(--text-strong); font-size: var(--fs-sm); }
.ql-item .note { font-size: var(--fs-xs); }
.ql-new { align-self: flex-start; }
.ql-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-1); }
.ql-head h2 { margin: 0; font-size: var(--fs-xl); flex: 0 1 auto; }
.ql-head .row { margin-left: auto; }
.ql-head form { margin: 0; }
.quest-journal .story { margin: var(--sp-4) 0; }
.quest-journal h3 { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); margin: 24px 0 8px; }
.ql-steps { list-style: none; margin: 0; padding: 0 0 0 var(--sp-4); border-left: 2px solid var(--line); display: grid; gap: var(--sp-3); }
.ql-steps li { position: relative; font-size: var(--fs-sm); }
.ql-steps li::before { content: ""; position: absolute; left: calc(-1 * var(--sp-4) - 6px); top: 6px; width: 10px; height: 10px; border-radius: 50%; background: var(--surface); border: 2px solid var(--amber); }
.ql-steps li.now::before { background: var(--amber); }

/* Relationships: a tree per family, the other ties */
.ft-wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: var(--sp-4); }
.ft h2 { margin: 0 0 var(--sp-4); font-size: var(--fs-lg); }
.ft-gen { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-4) var(--sp-5); }
.ft-gen + .ft-gen { margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px dashed var(--line-strong); }
.ft-group { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); }
.ft-group > .note { font-size: var(--fs-xs); }
.ft-people { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); }
.ft-p { display: flex; flex-direction: column; align-items: center; min-width: 130px; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md); background: var(--surface-2); border: 1px solid var(--line); color: var(--text); text-decoration: none; text-align: center; }
.ft-p:hover { border-color: var(--line-strong); }
.ft-p b { font-size: var(--fs-sm); }
.ft-p .note { font-size: var(--fs-xs); }
.ties { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--sp-4); margin-top: var(--sp-4); }
.ties .section { margin: 0; padding: var(--sp-5); }
.ties h3.divider { margin-top: 0; }
.tie-row { margin: 0; padding: var(--sp-1) 0; font-size: var(--fs-sm); }
.tie-name { color: var(--text); text-decoration: underline; text-decoration-color: var(--red-line); text-underline-offset: 3px; }
.tie-name:hover { color: var(--text-strong); text-decoration-color: var(--red); }

/* Connections: one entry in the middle, everyone tied to it around it */
.cx-diagram { position: relative; margin: 0 auto; }  /* its width and shape come from the layout (campaign_views.connections) */
.cx-diagram > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.cx-node { position: absolute; transform: translate(-50%, -50%); width: 150px; max-width: 30%; display: flex; flex-direction: column; align-items: center; padding: var(--sp-1) var(--sp-2); border-radius: var(--r-md); background: var(--surface-2); border: 1px solid var(--line-strong); color: var(--text); text-decoration: none; text-align: center; line-height: 1.3; }
a.cx-node:hover { border-color: var(--text); }
.cx-node b { font-size: var(--fs-sm); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cx-node small { font-size: var(--fs-xs); color: var(--muted); }
.cx-node.faction, .cx-node.quest { background: var(--story-bg); border-color: var(--story-line); }
.cx-hub { width: 170px; background: var(--selected-bg); border: 2px solid var(--blue); }
.cx-hub b { font-size: var(--fs-md); }
.cx-label { position: absolute; transform: translate(-50%, -50%); font-size: var(--fs-xs); color: var(--muted); background: var(--surface); padding: 0 var(--sp-1); border-radius: var(--r-sm); white-space: nowrap; }
.peek .cx-label { background: var(--surface); }
.cx-head { position: absolute; transform: translate(-50%, -50%); font-size: var(--fs-xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--amber); white-space: nowrap; }
.cx line { stroke-width: 2; }
.e-family { stroke: var(--line-strong); }
.e-allegiance { stroke: var(--blue-line); stroke-dasharray: 6 4; }
.e-ally { stroke: var(--green-line); }
.e-enemy { stroke: var(--red-line); stroke-dasharray: 2 4; }
.e-quest { stroke: var(--story-line); stroke-dasharray: 8 3; }
.cx-legend { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-2); font-size: var(--fs-xs); }
.cx-legend svg { vertical-align: middle; margin-right: var(--sp-1); }
.cx .cx-list { display: none; }
@media (max-width: 900px) {
  .ft-wrap { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .cx-diagram, .cx-legend { display: none; }
  .cx .cx-list { display: block; }
}


/* View as (app/view_as.py): always on top while an admin sees askdnd as someone else */
.view-as-bar { position: sticky; top: 0; z-index: 60; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--sp-2) var(--sp-3); padding: var(--sp-2) var(--sp-4); background: var(--bg-glass); border-bottom: 2px solid var(--amber); color: var(--amber); font-size: var(--fs-sm); }

/* ------------------------------------------------------------------ Manage components (Brett, 2026-10-23)
   Every Manage page is built from these, with no one-off spacing (tests/test_design_system.py checks):
   records (any list of people, characters, campaigns, rules…), form rows, narrow forms, inline edits, folds.
   Shown on Manage › Design system › Manage. */
/* Records: a list of things, one row each: what it is, optional fields, then its buttons. The list sets the columns
   and every row shares them (subgrid), so columns line up however long a row's buttons are. */
.records { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--sp-4); }
.records.cols-3 { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) auto; }
.records.cols-3-even { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1.2fr); }
.records.cols-4 { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1.2fr) auto; }
.record, .records > .records-head { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; row-gap: var(--sp-2); align-items: start; padding: var(--sp-3) 0; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-sm); }
.record > * { min-width: 0; overflow-wrap: anywhere; }
.record.muted > :first-child, .record.muted:not(:has(> *)) { grid-column: 1 / -1; }
.record details.fold { border-top: 0; padding-top: 0; margin-top: var(--sp-1); }
.record-main b { color: var(--text-strong); font-size: var(--fs-md); }
.record-main .note, .record-main .tag + .note { display: block; margin-top: 2px; }
.record-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: flex-end; align-items: center; }
.record.faded .record-main b { color: var(--muted); }
.records > .records-head { color: var(--muted); font: 700 var(--fs-xs) var(--cond); letter-spacing: .06em; text-transform: uppercase; padding: 0 0 var(--sp-2); border-bottom: 1px solid var(--line); }
.records:has(> .records-head) { border-top: 0; }
.record .pick { display: flex; gap: var(--sp-2); align-items: center; }
.record .pick select { flex: 0 1 200px; min-width: 0; }
.record .pick > .btn, .record-actions .btn { flex: none; white-space: nowrap; }
.record select { width: 100%; }
.record .pick select, .record-actions select { width: auto; }
@media (max-width: 640px) {
  .records.cols-3, .records.cols-3-even, .records.cols-4 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .record > .record-main, .record > .pick { grid-column: 1 / -1; }
  .record .pick select { flex: 1 1 auto; }
  .record-actions { justify-content: flex-start; }
  .records > .records-head { display: none; }
}
@media (max-width: 1099px) {  /* four columns squeeze the buttons into the names below this (feedback 10) */
  .records.cols-4 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; }
  .records.cols-4 > .records-head > :last-child { display: none; }
  .records.cols-4 > .record > .record-actions { grid-column: 1 / -1; justify-content: flex-start; }
}
/* Form row: fields side by side, bottom-aligned with their button */
.form-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; }
.form-row .field { flex: 0 1 180px; min-width: 0; }
.form-row .field.grow { flex: 1 1 220px; }
.form-row > input:not([type="hidden"]):not([type="checkbox"]), .form-row > select { flex: 1 1 220px; min-width: 0; }
.row > .grow { flex: 1 1 240px; min-width: 0; }
table.list td.end, table.list th.end { text-align: right; white-space: nowrap; }
.manage-page details.section > summary { cursor: pointer; }
.manage-page details.section[open] > :not(summary) { margin-top: var(--sp-3); }
/* Narrow form: stacked fields at a readable width */
.form-narrow { display: grid; gap: var(--sp-3); max-width: 520px; }
/* Sections on Manage pages (main.manage-page): a heading, its hint, then content, always the same gaps */
.manage-page .section > h2 { margin: 0; }
.manage-page .section > h2 + .hint, .manage-page .section > .section-head + .hint { margin: var(--sp-1) 0 var(--sp-4); }
.manage-page .section > h2 + :not(.hint) { margin-top: var(--sp-4); }
.manage-page .section > h3 { margin: var(--sp-5) 0 var(--sp-1); }
.manage-page .section > h3:first-child { margin-top: 0; }
.manage-page .section > h3 + .hint { margin: 0 0 var(--sp-3); }
.form-row + .note, .form-narrow + .note { margin-top: var(--sp-2); }
.section-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); align-items: stretch; margin-bottom: var(--sp-5); }
.section-pair > .section { margin: 0; }
@media (max-width: 900px) { .section-pair { grid-template-columns: 1fr; } }
/* Folds ("+ Add a character"): the same gaps inside every one */
.manage-page details.fold[open] > :not(summary) { margin-top: var(--sp-3); }
.records + details.fold, .records + .note, .records + p, .records + form { margin-top: var(--sp-3); }
/* Inline edit (macros in _manage.html): text with Edit, or the field with Save and Cancel */
.inline-edit-form { display: flex; gap: var(--sp-2); align-items: center; }
.inline-edit-form input { flex: 1 1 auto; min-width: 0; }
/* A closing note under a page's content, and the title row's own button */
.page-note { margin-top: var(--sp-5); }
.title-row .title-action { margin-left: auto; flex: none; }
/* A page's own action stays at the right end of its title row at every width; the title wraps beside it instead
   (the UI pass, 2026-11-02: on phones it dropped to a line of its own, left, centred or right) */
.title-row:has(> .title-action) { flex-wrap: nowrap; align-items: flex-start; }
.title-row:has(> .title-action) > h1 { flex: 1 1 auto; min-width: 0; }
.all-clear + .records { margin-top: var(--sp-4); }

/* The new-player tour (app.js "the tour", app/tour.py): everything else dims, one thing stays lit, a card says what
   it is and why. The page underneath still works; the tour only reads. Phones: the card sits at the bottom. */
.tour-spot { position: fixed; z-index: 190; border-radius: var(--r-lg); box-shadow: 0 0 0 9999px var(--backdrop-strong), 0 0 0 2px var(--amber); pointer-events: none; transition: top .2s ease, left .2s ease, width .2s ease, height .2s ease; }
.tour-card { position: fixed; z-index: 191; width: min(380px, calc(100vw - 24px)); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); padding: var(--sp-4); box-shadow: var(--lift); display: grid; gap: var(--sp-2); }
.tour-card.alone { top: 50% !important; left: 50% !important; transform: translate(-50%, -50%); box-shadow: 0 0 0 9999px var(--backdrop-strong), var(--lift); }
.tour-card h3 { margin: 0; font-size: var(--fs-lg); }
.tour-card p { margin: 0; font-size: var(--fs-sm); }
.tour-card .why { color: var(--muted); }
.tour-card .why b { color: var(--text); }
.tour-progress { height: 3px; border-radius: var(--r-xs); background: var(--line); overflow: hidden; }
.tour-progress i { display: block; height: 100%; background: var(--amber); transition: width .2s ease; }
.tour-foot { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-1); }
.tour-foot .count { margin-right: auto; color: var(--faint); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
@media (max-width: 640px) {
  .tour-card, .tour-card.alone { left: 8px !important; right: 8px; top: auto !important; bottom: calc(8px + env(safe-area-inset-bottom, 0px)); width: auto; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .tour-spot { transition: none; } }

/* Help › What askdnd adds (help.html): the tour's groups to read, each with Show me */
.adds > .hint { margin: var(--sp-3) 0 var(--sp-4); }
.adds .section > h2 { font-size: var(--fs-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--amber); margin: 0 0 var(--sp-2); }
.add { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px var(--sp-4); align-items: baseline; padding: var(--sp-3) 0; border-bottom: 1px solid var(--line-soft); }
.add:last-child { border-bottom: 0; }
.add b { color: var(--text-strong); }
.add p { margin: 0; grid-column: 1 / -1; font-size: var(--fs-sm); color: var(--muted); max-width: 70ch; }

/* Party (party.html; Brett, 2026-10-26, docs/mockups/party.html): a card per character with Battle's number tiles,
   then the party's spells and items in the shared list, who has each by portrait */
.pt-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
.pt-card { margin: 0; padding: 20px 22px; display: grid; gap: var(--sp-3); align-content: start; }
.pt-card[data-modal-url] { cursor: pointer; }
.pt-card[data-modal-url]:hover { background: var(--surface-2); }
.pt-head { display: flex; align-items: center; gap: var(--sp-3); }
.pt-head .roll-av { width: 48px; height: 48px; font-size: var(--fs-md); }
.pt-id { min-width: 0; }
/* Gold across from the name, right-justified; the total wraps under the coins when the card is narrow (Brett, 2026-11-02) */
.pt-purse { flex: 0 1 auto; min-width: 0; align-self: flex-start; margin-left: auto; }  /* (the name and its conditions stay together on the left) */
.pt-purse .purse { justify-content: flex-end; }
.pt-card .pt-purse-foot { display: none; }
@media (max-width: 640px) { .pt-card .pt-purse { display: none; } .pt-card .pt-purse-foot { display: flex; } }  /* (on a phone, at the card's foot: beside the name it squeezed it) */

.pt-name { margin: 0; font-size: var(--fs-lg); }
.pt-head .char-name > .pt-name { flex: 0 1 auto; }  /* (as wide as the name, so its conditions sit right after it, Brett 2026-11-02) */
.pt-head .note { margin: 0; }
.strip.compact.pt-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0; }
.pt-tiles > div { background: var(--surface-2); }
.pt-card[data-modal-url]:hover .pt-tiles > div { background: var(--surface-3); }
.pt-line { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.pt-res { display: flex; flex-wrap: wrap; gap: 6px 16px; }
.pt-res > span, .pt-group > span { white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }
.pt-res > .pt-group { white-space: normal; flex-wrap: wrap; min-width: 0; column-gap: 12px; }  /* a group's rows wrap, each staying whole, however many a sheet has */
.pt-list { padding: 22px 24px; }
.pt-row { display: grid; gap: var(--sp-3); align-items: center; padding: 8px 6px; border-top: 1px solid var(--line-soft); }
.pt-spells .pt-row { grid-template-columns: minmax(0, 2fr) 64px 72px minmax(0, 1.4fr) minmax(0, 1.3fr); }
.pt-items .pt-row { grid-template-columns: minmax(0, 1.4fr) minmax(0, 2fr) minmax(0, 1.2fr); }
.pt-row[data-panel-url] { cursor: pointer; border-radius: var(--r-sm); }
.pt-row[data-panel-url]:hover, .pt-row[data-panel-url]:focus-visible { background: var(--row-hover); }
.pt-headrow { border-top: 0; color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; }
.pt-nm { min-width: 0; }
.pt-sum { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.pt-who { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pt-w { display: inline-flex; align-items: center; gap: 4px; border-radius: var(--r-pill); cursor: pointer; }
.pt-w .roll-av { width: 28px; height: 28px; font-size: var(--fs-xs); }
.pt-w:hover .roll-av, .pt-w:focus-visible .roll-av { box-shadow: 0 0 0 2px var(--blue); }
.pt-coins { margin: var(--sp-3) 6px 0; }
@media (max-width: 900px) { .pt-cards { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 640px) {
  /* The name and who has it side by side while both fit, the portraits under the name once they don't (the UI pass,
     2026-11-02: six portraits squeezed a name to one letter per line) */
  .pt-spells .pt-row, .pt-items .pt-row { display: flex; flex-wrap: wrap; column-gap: var(--sp-3); row-gap: var(--sp-1); }
  .pt-spells .pt-row > :first-child, .pt-items .pt-row > .pt-nm { flex: 1 1 10em; min-width: 0; }
  .pt-spells .pt-row > .pt-who, .pt-items .pt-row > .pt-who { margin-left: auto; }
  .pt-spells .pt-row > :nth-child(2), .pt-spells .pt-row > :nth-child(3), .pt-spells .pt-row > :nth-child(4), .pt-items .pt-sum { display: none; }
  .pt-list .pt-headrow { display: none; }
  .pt-list { padding: 16px; }
}

/* The D&D Beyond bridge's states (app/ddb_bridge.py `progress`; docs/artifacts/bridge-review.html), the same words
   everywhere: On D&D Beyond, On its way (Trying again), Didn't go through, Changed on D&D Beyond */
.br-st { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); font-weight: 600; border-radius: var(--r-pill); padding: var(--sp-1) var(--sp-3); white-space: nowrap; }
.br-st::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.br-st.br-live { background: var(--green-soft); color: var(--green); }
.br-st.br-wait { background: var(--amber-soft); color: var(--amber); }
.br-st.title-action { white-space: normal; max-width: 100%; text-align: left; }  /* (a long status wraps on a phone instead of pushing the page wider: sweep, Nov 5) */
.br-st.br-off { background: var(--red-soft); color: var(--link); }
.br-st.br-seen { background: var(--blue-soft); color: var(--blue); }
.br-st.br-asleep { background: var(--surface-2); color: var(--muted); }  /* between games: no calls to D&D Beyond */
.br-log { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.br-log li { padding: var(--sp-2) 0; border-top: 1px solid var(--line-soft); display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; }
/* the bridge's health, bottom right on every page (bottom left is Table rolls' pop-ups and tray); phones read only */
button.br-st { border: 0; font-family: inherit; cursor: pointer; }  /* the bridge's health on Manage › askdnd › D&D Beyond bridge: a click runs the check */
.br-phone { display: none; }
@media (max-width: 640px) { .br-phone { display: block; } }
.br-now { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 18px; margin: 0; }
.br-now dt { color: var(--muted); }
.br-now dd { margin: 0; }
/* What each connection can change (Manage › askdnd › D&D Beyond bridge; Brett, 2026-11-04): how each state comes about,
   in the Live pill's colours, then a matrix of marks (a tick, a clock for partly, a dash for no) */
.br-states { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-4); }
.br-state { border: 1px solid var(--line); border-top-width: 3px; border-radius: var(--r-md); padding: var(--sp-3); display: grid; gap: var(--sp-2); align-content: start; }
.br-state p { margin: 0; }
.br-state h3 { margin: 0; font-size: var(--fs-md); color: var(--text-strong); }
.br-state h3 .note { font-weight: 400; font-size: var(--fs-xs); margin-left: 4px; }
.br-state-own { border-top-color: var(--green); } .br-state-dm { border-top-color: var(--amber); } .br-state-none { border-top-color: var(--red); }
.br-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; }
.br-dot-own { background: var(--green); } .br-dot-dm { background: var(--amber); } .br-dot-none { background: var(--red); }
.br-mx th { white-space: nowrap; }
.br-mx th:not(:first-child), .br-mx td:not(:first-child) { text-align: center; width: 22%; }
.br-mx td { padding-top: 10px; padding-bottom: 10px; vertical-align: middle; }
.br-mx td:first-child { color: var(--text-strong); }
.br-mx td small { display: block; color: var(--muted); font-size: var(--fs-xs); }
.br-mx-other td { border-top: 2px solid var(--line-strong); }
.br-mk { display: inline-grid; width: 22px; height: 22px; }
.br-mk svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.br-mk-yes { color: var(--green); } .br-mk-some { color: var(--amber); } .br-mk-no { color: var(--faint); }

/* ---- Hit points window (app/hit_points.py, docs/mockups/hp-window.html): from Table rolls' Apply to … or Hit points */
/* Your creatures (app/creatures.py; docs/mockups/your-creatures/ Option B): who Battle shows, by the name (Vi · Homunculus),
   links styled as the segmented switch; the creature's band is Wild Shape's form band, in the creature's own words */
.who-switch a { display: inline-flex; align-items: center; padding: 0 var(--sp-3); min-height: calc(var(--control-h) - 6px); border-radius: var(--r-sm);
  font-size: var(--fs-sm); font-weight: 500; color: var(--muted); text-decoration: none; white-space: nowrap; }
.who-switch a:hover { color: var(--text-strong); }
.who-switch a[aria-current] { background: var(--selected-bg); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--blue); }
/* A character with creatures on Party: one card in parts on a row of its own, theirs then each creature's, a dashed line
   between and the creature's half tinted (Brett, Nov 4, docs/mockups/creatures-as-characters/ board 8, C); the others fill in
   around it. The master's portrait carries the creature's picture; the creature's whole half opens its Battle. */
.pt-cards { grid-auto-flow: row dense; }
.pt-pair { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(var(--parts, 2), minmax(0, 1fr)); border: 1px solid color-mix(in srgb, var(--green) 45%, var(--line)); border-radius: var(--r-lg); overflow: hidden; background: var(--surface); }
.pt-pair > .pt-card { margin: 0; border-radius: 0; }
.pt-creature-card { position: relative; background: color-mix(in srgb, var(--green) 7%, var(--surface)); border-left: 1px dashed color-mix(in srgb, var(--green) 60%, var(--line)); }
.pt-creature-card .pt-cr-av { box-shadow: 0 0 0 2px var(--green); background: var(--surface-3); color: var(--text-strong); }
.pt-creature-card:hover { background: color-mix(in srgb, var(--green) 12%, var(--surface)); }
.pt-av { position: relative; flex: none; display: inline-grid; }
.pt-badge.roll-av { position: absolute; right: -6px; bottom: -6px; width: 24px; height: 24px; font-size: var(--fs-xs); background: var(--surface-3); color: var(--text-strong); box-shadow: 0 0 0 2px var(--green), 0 0 0 4px var(--surface); }
.pt-whose { font: 700 var(--fs-xs)/1 var(--cond); letter-spacing: .08em; text-transform: uppercase; color: var(--green); }
.pt-cr-link { color: inherit; text-decoration: none; }
.pt-cr-link::after { content: ""; position: absolute; inset: 0; }
.pt-cr-link:focus-visible { outline: none; }
.pt-creature-card:has(.pt-cr-link:focus-visible) { outline: 2px solid var(--red); outline-offset: -2px; }
@media (max-width: 900px) {
  .pt-pair { grid-template-columns: minmax(0, 1fr); }
  .pt-creature-card { border-left: 0; border-top: 1px dashed color-mix(in srgb, var(--green) 60%, var(--line)); }
}
/* A familiar in play (app/companions.py; Creatures as Characters boards 2 and 3): where it is, the same pill everywhere */
.cr-where { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line-strong); border-radius: var(--r-pill); padding: 2px 10px; font-size: var(--fs-xs); font-weight: 600; color: var(--text); white-space: nowrap; }
.cr-where::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--green); }
.cr-where-away::before { background: var(--blue); } .cr-where-gone::before { background: var(--faint); }
.cr-not-here, .pt-not-here .pt-tiles, .pt-not-here .pt-line { opacity: .5; }
.cr-where-note { margin: var(--sp-1) 0 0; }
/* A creature's band (Battle as one of the character's creatures): its buttons wrap under its name when there's no room */
.cr-band { flex-wrap: wrap; }
.cr-band .ws-form-main { flex: 1 1 260px; }
.cr-band .ws-form-acts { flex: 0 1 auto; flex-wrap: wrap; align-items: center; justify-content: flex-end; margin-left: auto; }
.cr-forever { position: relative; display: inline-block; }
.cr-forever-pop { position: absolute; right: 0; top: calc(100% + 6px); z-index: 5; width: 280px; max-width: 80vw; background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: var(--sp-3); display: grid; gap: var(--sp-2); box-shadow: var(--lift); }
.ff-pick { border: 0; margin: 8px 0; padding: 0; display: grid; gap: 6px; }
.ff-pick legend { margin-bottom: 4px; padding: 0; width: auto; }
.ff-opt { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px; align-items: center; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; }
.ff-opt .cr-where { grid-column: 2; justify-self: start; }  /* where it is, under its name: a long one never squeezes the name (feedback 20) */
.ff-opt:has(input:checked) { border-color: var(--green); background: var(--green-soft); }
.ff-kinds { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; }
.ff-kinds legend { width: 100%; }
.ff-new { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.ff-new-head { margin-top: var(--sp-1); }
.ff-kind { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; }
.pt-with { border-color: color-mix(in srgb, var(--green) 55%, var(--line)); color: var(--text-strong); }
.cr-fix { margin: 0; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--amber); background: var(--amber-soft); border-radius: var(--r-md); font-size: var(--fs-sm); line-height: 1.9; }
.cr-fix .btn { margin: 0 var(--sp-1); }
.cr-hp-pop { position: relative; }
.cr-hp-pop > summary { list-style: none; cursor: pointer; }
.cr-hp-pop > summary::-webkit-details-marker { display: none; }
.cr-hp-form { position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; display: flex; gap: var(--sp-1); padding: var(--sp-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow); }
.cr-hp-form input { width: 5em; }
.cr-facts { gap: var(--sp-1) var(--sp-2); }
.cr-facts .roll-chip { cursor: pointer; }
.cr-text > summary { cursor: pointer; list-style: none; margin-top: 2px; }
.cr-text > summary::-webkit-details-marker { display: none; }
.seg label { display: inline-flex; }
.seg label input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; clip-path: inset(50%); pointer-events: none; }
.seg label span { display: inline-flex; align-items: center; padding: 0 var(--sp-3); min-height: calc(var(--control-h) - 6px); border-radius: var(--r-sm); font-size: var(--fs-sm); font-weight: 500; color: var(--muted); cursor: pointer; }
.seg label input:checked + span { background: var(--selected-bg); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--blue); border-radius: var(--r-sm); }
.seg label input:focus-visible + span { outline: 2px solid var(--blue); outline-offset: -2px; }
.hp-win .hp-form, .hp-fields { display: grid; gap: var(--sp-3); }
.hp-source { display: flex; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; align-items: baseline; background: var(--surface-2); border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); }
.hp-source b { color: var(--text-strong); }
.hp-part { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: end; }
.hp-type-from { flex-basis: 100%; margin: 0; }
.hp-amount, .hp-type { display: grid; gap: var(--sp-1); }
.hp-amount input { width: 6.5em; font-size: var(--fs-lg); font-weight: 600; }
.hp-form:has(.hp-modes):not(:has(.hp-modes input[value=damage]:checked)) :is(.hp-type, .hp-crit) { display: none; }
.hp-crit { display: flex; gap: var(--sp-2); align-items: baseline; font-size: var(--fs-sm); }
.hp-working { display: grid; gap: var(--sp-3); }
.hp-steps { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.hp-step { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px var(--sp-3); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); align-items: baseline; }
.hp-step + .hp-step { border-top: 1px solid var(--line); }
.hp-step label { display: inline-flex; gap: var(--sp-2); align-items: baseline; cursor: pointer; }
.hp-step label input { accent-color: var(--red); }
.hp-step b { color: var(--text-strong); font-variant-numeric: tabular-nums; }
.hp-step .why { grid-column: 1 / -1; color: var(--muted); font-size: var(--fs-xs); }
.hp-step.off .hp-st-title, .hp-step.off b { text-decoration: line-through; color: var(--muted); }
.hp-step.mass { background: var(--red-soft); border-radius: var(--r-md); }  /* massive damage: for the DM's ruling */
.hp-step .seg { grid-column: 1 / -1; justify-self: start; }
.hp-blocked { display: grid; gap: var(--sp-1); border: 1px solid var(--red); background: var(--red-soft); border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); margin: 0; }
.hp-go { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
/* Hit points' panel: its buttons as one group, the same gap between the rows as below them (Brett, 2026-11-02) */
.hp-acts { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.hp-acts > .row { margin: 0; }
.hp-btns { display: flex; gap: var(--sp-1); flex: none; }  /* − and + in Battle's Hit Points tile's label row (Brett, 2026-11-01, docs/mockups/hp-flow/ Option B; ntile 2026-11-02) */
.hp-btn { display: grid; place-items: center; width: var(--chip-h); height: var(--chip-h); padding: 0; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface-2); color: var(--text-strong); font: 700 var(--fs-md)/1 var(--sans); cursor: pointer; }
.hp-btn:hover, .hp-btn:focus-visible { color: var(--amber); border-color: var(--amber); }
.roll-win .hp-win .peek-close, .roll-win .hp-working > .note { display: none; }
.roll-win .hp-win { margin-top: var(--sp-2); }
/* In Table rolls: who a hit was applied to, the Concentration save it asks for, Apply to … */
.roll-applied { display: block; margin-top: 6px; color: var(--green); font-size: var(--fs-xs); }
.roll-conc { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 6px; padding: 6px 8px; border: 1px solid var(--amber); background: var(--amber-soft); border-radius: var(--r-md); font-size: var(--fs-xs); color: var(--text); }
.roll-apply-to summary { list-style: none; cursor: pointer; }
.roll-apply-to summary::-webkit-details-marker { display: none; }
.roll-apply-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }

/* Battle on a phone (Brett, 2026-10-31, docs/mockups/usability/): Hit Points first, whole and large, and Resources one line
   you open. The choice is one per device (app.js "Resources on a phone"). */
@media (max-width: 640px) {
  .battlebar { display: flex; flex-direction: column; }
  .battlebar > * { order: 1; }
  .battle-head { display: block; }
  .battle-head > .strip.compact { display: flex; flex-wrap: wrap; margin-bottom: 6px; }
  .battle-head > .save-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; padding: 0; border: none; }
  .battle-head .st { padding: 3px 6px; gap: 4px; }  /* (a proficient save with Adv fits its third of a phone) */
  .status-phone { display: contents; }
  .status-phone .res-insp { margin-left: 0; }
  .battlebar > .res-more { order: 2; display: flex; justify-content: space-between; align-items: center; width: 100%; min-height: var(--control-h-sm); margin-top: var(--sp-2); padding: 0 var(--sp-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); color: var(--text); font: inherit; cursor: pointer; }
  .battlebar > .res-row { order: 3; display: none; margin: var(--sp-2) 0 0; }
  body.res-open .battlebar > .res-row { display: flex; }
  .battlebar > .res-more + .res-row { margin: var(--sp-2) 0 0; }
  .battlebar > .res-row .res-label { display: none; }
  .res-group { padding: 0; border-left: none; }
  /* One row (Brett, 2026-11-02: a whole row for Hit Points wasted room): Hit Points first and a little wider, its − and +
     beside its label, short labels; the Dying tile keeps a row of its own for its marks */
  .battle-head .strip.compact > div { flex: 1 1 0; min-width: 0; padding: 6px; }
  .battle-head .strip.compact > div b { font-size: var(--fs-md); }
  .battle-head .strip.compact > div:first-child { flex-grow: 2.2; }
  .battle-head .strip.compact > div:first-child > .nt-lab > small { min-width: 1.6em; }  /* "HP" whole beside ↻, − and + */
  .battle-head .strip.compact > div:first-child b { font-size: var(--fs-lg); }
  .battle-head .strip.compact > div.dying { flex-basis: 100%; }
  .nt .hp-btns { gap: 2px; }
  .nt .hp-btn { width: 24px; height: 24px; font-size: var(--fs-sm); }
  .battle-head .nt > .nt-lab { min-height: 24px; }  /* (any tile on a phone: its buttons fit beside the label) */
  .battle-head .strip.compact small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ph-hide { display: none; }
}
@container battle-head (max-width: 330px) { .strip.compact > div:first-child { flex-basis: 100%; } }  /* the narrowest phones: five don't fit, Hit Points takes its own row */

/* Tap targets on a phone (DESIGN.md › Principles 8): what can grow, grows; what sits among words keeps its size and takes
   a larger area around it */
@media (max-width: 640px) {
  .roll-chip, .roll-hit { min-height: var(--control-h-sm); min-width: var(--control-h-sm); }
  .res-go, .rd-item, .btn.link, .btn.link.small, summary:not(.btn) { min-height: var(--control-h-sm); }
  .rs-toggle { width: var(--control-h-sm); height: var(--control-h-sm); }
  button.pick, .head-add, .sheet-open { position: relative; }
  button.pick::after, .head-add::after, .sheet-open::after { content: ""; position: absolute; inset: -10px 0; }
}
.ds-field { max-width: 240px; }

/* Prepare spells (app/preparing.py, _prepare_spells.html; Brett 2026-11-02, docs/mockups/prepare-spells/): a large
   modal like the Spell Book. The count first and big, the filters as chips, the list beside your changes, Make at the
   foot. On a phone the changes go under the list. */
.peek-body.prep { display: grid; gap: var(--sp-3); align-content: start; }
.prep-head { align-items: center; flex-wrap: wrap; }
.prep-top { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-2) var(--sp-3); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
.prep-big { font-size: var(--fs-xl); line-height: 1; color: var(--text-strong); white-space: nowrap; }
.prep-big b { font-weight: 800; }
.prep-big b.over { color: var(--link); }
.prep-of { color: var(--muted); font-weight: 600; }
.prep-tally-words { color: var(--muted); white-space: nowrap; }
.prep-tally-words > b { color: var(--text-strong); }
.prep-boxes { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.prep-boxes i { width: 18px; height: 18px; border-radius: var(--r-sm); border: 1.5px solid var(--line-strong); background: var(--surface); }
.prep-boxes i.on { background: var(--green); border-color: var(--green); }
.prep-boxes.over i.on { background: var(--red); border-color: var(--red); }
.prep-extra { color: var(--link); font-weight: 700; font-size: var(--fs-sm); margin-left: var(--sp-1); }
.prep-warn { margin: 0; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--amber); background: var(--amber-soft); border-radius: var(--r-md); font-size: var(--fs-sm); }
.prep-filters { display: grid; gap: var(--sp-1); align-content: start; }
.prep-fold { position: sticky; top: 0; align-self: start; min-width: 0; }
.prep-filters .chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-bottom: var(--sp-2); }
.prep-body { display: grid; grid-template-columns: 150px minmax(0, 1fr) 200px; gap: var(--sp-4); align-items: start; }
.prep-list { display: grid; gap: 2px; min-width: 0; }
.prep-level { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--amber); padding: var(--sp-3) var(--sp-2) var(--sp-1); }
.prep-row { display: grid; grid-template-columns: 32px minmax(0, 1fr) 80px 70px 20px; gap: var(--sp-2); align-items: center; padding: 6px var(--sp-2); border: 1px solid transparent; border-radius: var(--r-md); }
.prep-row:hover { background: var(--surface-2); }
/* A change (Brett, 2026-11-02, the UI pass): a green bar for a spell you're preparing, a grey bar and its name struck
   through for one you're dropping; + and − in Your changes. No amber. */
.prep-row.add { box-shadow: inset 3px 0 0 var(--green); }
.prep-row.drop { box-shadow: inset 3px 0 0 var(--muted); }
.prep-row.drop .prep-name .link-plain { text-decoration: line-through; color: var(--muted); }
.prep-tick { width: 28px; height: 28px; display: grid; place-items: center; padding: 0; border-radius: var(--r-sm); border: 1.5px solid var(--line-strong); background: var(--bg); color: transparent; font-weight: 700; cursor: pointer; }
.prep-tick[aria-pressed="true"] { background: var(--green); border-color: var(--green); color: var(--bg); }
.prep-tick.always { color: var(--cond-concentration); border-color: var(--cond-concentration); background: var(--surface-2); cursor: default; }
.prep-name { min-width: 0; }
/* A name that opens its side panel, plain (no link colour): Prepare spells, a character's card from Party */
.link-plain { background: none; border: 0; padding: 0; font: inherit; color: var(--text-strong); cursor: pointer; text-align: left; }
.link-plain:hover, .link-plain:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.prep-name .link-plain { display: inline-block; max-width: 100%; vertical-align: bottom; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.prep-name small { display: block; color: var(--muted); font-size: var(--fs-xs); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.prep-col { color: var(--muted); font-size: var(--fs-sm); }
.prep-fav { text-align: center; }  /* the shortlist's bookmark, as on every list (Brett, Nov 3: it was a star) */
.prep-side { display: grid; gap: var(--sp-2); position: sticky; top: 0; }
.prep-changes { display: grid; gap: var(--sp-1); }
.prep-change { display: grid; grid-template-columns: 14px minmax(0, 1fr) auto; column-gap: var(--sp-2); align-items: baseline; padding: 2px 0; }
.prep-change .prep-k { font-weight: 800; text-align: center; }
.prep-change.add .prep-k { color: var(--green); }
.prep-change.drop .prep-k, .prep-change.drop b { color: var(--muted); }
.prep-change b { color: var(--text-strong); font-weight: 600; overflow-wrap: anywhere; }
.prep-change small { color: var(--muted); font-size: var(--fs-xs); }
.prep-fold > summary { display: none; }

/* The item browser (_item_browser.html, app.js "Item browser"; Brett, 2026-11-03, docs/mockups/item-browser/): filters,
   the list and what the picked item is, side by side; Adding along the bottom. Narrower than 900px the item shows in
   place of the list, with Back to the list. */
.ib-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr) 300px; gap: var(--sp-4); align-items: start; }
.ib-cols { display: grid; grid-template-columns: 20px minmax(12rem, 1fr) max-content max-content max-content 32px; column-gap: 14px; }
.ib-cols > .ls-head, .ib-cols > .ls-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.ib-cols > .ls-empty { grid-column: 1 / -1; }
.ib-row.sel { background: var(--selected-bg); }
/* Can the character use it (app/item_fit.py): a tick in a circle, or a triangle for a catch; the details say the rest */
.ib-fit { display: grid; place-items: center; width: 20px; height: 20px; }
.ib-fit.ok { color: var(--green); }
.ib-fit.warn { color: var(--amber); }
.ib-for { display: grid; gap: 6px; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); }
.ib-for-h { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 var(--sp-2); }
.ib-for-h .note { font-size: var(--fs-xs); }
.ib-for-h b { color: var(--text-strong); }
.ib-for-l { display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: 6px; font-size: var(--fs-sm); line-height: 1.45; }
.ib-for-k { font-weight: 700; text-align: center; color: var(--muted); }
.ib-for-k.ok { color: var(--green); }
.ib-for-k.warn { color: var(--amber); }
.ib-for-k.change { color: var(--blue); }
/* Now and with it (Brett chose A and B, Nov 4, docs/mockups/gear-compare/): ▲ and ▼ differ in lightness too, not hue alone */
.ib-place select { width: auto; }
.ib-cmp { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.ib-cmp th { font-size: var(--fs-xs); font-weight: 400; letter-spacing: .04em; color: var(--muted); text-align: left; padding: 2px 8px 6px 0; }
.ib-cmp td { padding: 6px 8px 6px 0; border-top: 1px solid var(--line-soft); vertical-align: top; white-space: nowrap; }
.ib-cmp td.k { color: var(--muted); white-space: normal; }
.ib-cmp td.then { color: var(--text-strong); font-weight: 600; }
.ib-cmp tr.why td { border-top: 0; padding-top: 0; color: var(--muted); font-size: var(--fs-xs); white-space: normal; }
.tag.ib-d.up { color: var(--green); background: var(--green-soft); }
.tag.ib-d.down, .tag.ib-d.lost { color: var(--amber); background: var(--amber-soft); }
.ib-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: 2px; }
.ib-row.in-basket { box-shadow: inset 3px 0 0 var(--green); }
.ib-cost { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ib-plus { width: 28px; height: 28px; display: grid; place-items: center; padding: 0; border-radius: var(--r-sm); border: 1.5px solid var(--line-strong); background: var(--bg); color: var(--text-strong); font-weight: 700; cursor: pointer; }
.ib-plus:hover, .ib-plus:focus-visible { border-color: var(--green); color: var(--green); }
/* Rarity in D&D Beyond's own order of colours, from the page's tokens */
.ib-rar { font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; color: var(--muted); }
.ib-rar[data-rarity="uncommon"] { color: var(--green); }
.ib-rar[data-rarity="rare"] { color: var(--blue); }
.ib-rar[data-rarity="very-rare"] { color: var(--violet); }
.ib-rar[data-rarity="legendary"] { color: var(--amber); }
.ib-rar[data-rarity="artifact"] { color: var(--red); }
.ib-detail { position: sticky; top: 0; max-height: calc(90vh - 220px); overflow-y: auto; display: grid; gap: var(--sp-2); align-content: start; min-width: 0; }
.ib-card { display: grid; gap: var(--sp-2); }
.ib-card h3 { margin: 0; color: var(--text-strong); }
.ib-card-x { justify-self: start; display: none; }
.ib-pic { width: 100%; max-height: 180px; object-fit: contain; border-radius: var(--r-md); background: var(--surface-2); }
.ib-tags { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); align-items: center; }
.ib-attune { color: var(--amber); white-space: normal; }
.ib-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px var(--sp-3); margin: 0; font-size: var(--fs-sm); }
.ib-facts dt { color: var(--muted); }
.ib-facts dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
.ib-text { font-size: var(--fs-sm); color: var(--text); }
.ib-text p { margin: 0 0 var(--sp-2); }
.ib-card > .btn { justify-self: start; }
.ib-foot { position: sticky; bottom: calc(-1 * var(--sp-4)); display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); margin-top: var(--sp-3); padding: var(--sp-3) 0; background: var(--surface); border-top: 1px solid var(--line); }
.ib-basket { display: flex; flex-wrap: wrap; gap: var(--sp-1); min-width: 0; }
.ib-basket:empty { display: none; }
.ib-chip { display: inline-flex; align-items: center; gap: var(--sp-1); padding: 2px 4px 2px var(--sp-2); border-radius: var(--r-pill); background: var(--surface-2); font-size: var(--fs-sm); }
.ib-chip b { color: var(--text-strong); font-weight: 600; }
.ib-chip button { width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%; background: var(--surface-3); color: var(--text-strong); font-weight: 700; cursor: pointer; }
.ib-foot-end { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); justify-content: flex-end; }
.ib-foot select { width: auto; max-width: 14em; }
.ib-foot input.ib-pay { width: 8em; }
.ib-foot input.ib-amount { width: 6em; }
.ib-price-note, .ib-foot > .use-said { flex-basis: 100%; margin: 0; }
@container list (max-width: 560px) {
  .ib-cols { grid-template-columns: 20px minmax(0, 1fr) max-content 44px; }
  .ib-cols > .ls-head, .ib-cols > .ls-row { grid-template-columns: subgrid; }
  .ib-cols .ls-head > :nth-child(3), .ib-cols .ls-head > :nth-child(5), .ib-row > .ib-rar, .ib-row > .ib-cost + .note { display: none; }
  .ib-plus { width: 40px; height: 40px; }
}
@media (max-width: 900px) {
  .ib-layout { grid-template-columns: minmax(0, 1fr); }
  .ib-detail { position: static; max-height: none; }
  .ib:not(.ib-showing) .ib-detail, .ib.ib-showing .ib-main, .ib.ib-showing .ls-fold { display: none; }
  .ib-card-x { display: inline-flex; }
  .ib-foot-end { margin-left: 0; justify-content: flex-start; }
}

/* Coins as a purse (_char.html purse(); Brett, 2026-11-02, the UI pass): each coin in its metal, the total in gold. Small:
   at the right end of Sheet › Items' toolbar, after Full notes, and across from the name on a Party card. */
.ls-end { margin-left: auto; display: inline-flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: flex-end; }
.purse { display: inline-flex; align-items: center; gap: var(--sp-1) var(--sp-2); flex-wrap: wrap; padding: 0; background: none; border: 0; font: inherit; color: var(--text); }
button.purse { cursor: pointer; border-radius: var(--r-pill); }
button.purse:hover .coin, button.purse:focus-visible .coin { background: var(--surface-3); }
.coin { display: inline-flex; align-items: center; gap: var(--sp-1); padding: 1px var(--sp-2) 1px 2px; border-radius: var(--r-pill); background: var(--surface-2); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; white-space: nowrap; }
.coin i { width: 14px; height: 14px; border-radius: 50%; display: inline-block; box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--coin-ink) 25%, transparent); }
.coin b { color: var(--text-strong); font-weight: 700; }
.coin small { color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; }
.coin-pp { background: var(--coin-pp); } .coin-gp { background: var(--coin-gp); } .coin-ep { background: var(--coin-ep); } .coin-sp { background: var(--coin-sp); } .coin-cp { background: var(--coin-cp); }
.purse-sum { white-space: nowrap; }
.prep-foot { position: sticky; bottom: calc(-1 * var(--sp-4)); display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-3) 0; background: var(--surface); border-top: 1px solid var(--line); }
.prep-foot-note { flex: none; white-space: nowrap; }  /* ("Prepare 2, drop 1" on one line; a longer sentence beside it wraps instead) */
.prep-foot > .use-said { flex: 1 1 16em; min-width: 0; margin: 0; }
@media (max-width: 900px) {
  .prep-body { grid-template-columns: minmax(0, 1fr); }
  .prep-side, .prep-fold { position: static; }
  /* The count on its own line under the title, ✕ beside the title; the filters one Filters line that opens */
  .prep-head > .prep-top { order: 3; width: 100%; }
  .prep-fold { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-1) var(--sp-3); }
  .prep-fold > summary { display: block; cursor: pointer; font-weight: 600; padding: var(--sp-1) 0; }
  .prep-fold[open] > summary { margin-bottom: var(--sp-1); }
  .prep-row { grid-template-columns: 32px minmax(0, 1fr) 20px; }
  .prep-row .prep-col { display: none; }
}


/* ---- Feedback for Claude (app/feedback.py, app.js "feedback for Claude"; docs/mockups/feedback-queue/) */
kbd { display: inline-block; border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: var(--r-xs); padding: 0 5px; font: 600 var(--fs-xs)/1.5 var(--sans); color: var(--text-strong); background: var(--surface); }
.fb-win { width: min(560px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow: auto; padding: 0; border: 1px solid var(--line-strong); border-radius: var(--r-lg); background: var(--surface-2); color: var(--text); box-shadow: var(--lift); }
.fb-win::backdrop { background: var(--backdrop-soft); }
.fb-form { display: grid; gap: var(--sp-3); padding: var(--sp-4); }
.fb-form h2 { margin: 0; font-size: var(--fs-lg); color: var(--text-strong); }
.fb-form textarea { width: 100%; }
.fb-shots { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
@media (max-width: 640px) { .fb-shots { grid-template-columns: minmax(0, 1fr); } }
.fb-shot { margin: 0; display: grid; gap: var(--sp-1); align-content: start; min-width: 0; }
.fb-shot img, .fb-drop img { width: 100%; max-height: 140px; object-fit: cover; object-position: top left; border: 1px solid var(--line-strong); border-radius: var(--r-md); display: block; }
.fb-drop { display: grid; place-items: center; text-align: center; min-height: 110px; padding: var(--sp-2); border: 1px dashed var(--line-strong); border-radius: var(--r-md); color: var(--muted); font-size: var(--fs-xs); cursor: pointer; }
.fb-drop.over { border-color: var(--amber); background: var(--amber-soft); }
.fb-drop b { color: var(--text); }
.fb-ctx { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--sp-1) var(--sp-3); margin: var(--sp-2) 0 0; font-size: var(--fs-xs); }
.fb-ctx dt { color: var(--faint); }
.fb-ctx dd { margin: 0; overflow-wrap: anywhere; }
body.fb-pointing, body.fb-pointing * { cursor: crosshair !important; }
.fb-pointed { outline: 2px solid var(--amber) !important; outline-offset: 3px; }
body[data-feedback] .brand { -webkit-touch-callout: none; user-select: none; }  /* (pressing and holding it opens the window on a phone) */
.fb-list { list-style: none; margin: var(--sp-4) 0 0; padding: 0; display: grid; border-top: 1px solid var(--line); }
.fb-item { border-bottom: 1px solid var(--line); }
.fb-row { display: grid; grid-template-columns: 72px minmax(0, 1fr) auto; gap: var(--sp-3); align-items: center; padding: var(--sp-3) var(--sp-2); cursor: pointer; list-style: none; }
.fb-row::-webkit-details-marker { display: none; }
.fb-item details[open] > .fb-row { background: var(--surface); }
.fb-thumb { width: 72px; height: 46px; border-radius: var(--r-sm); border: 1px solid var(--line-strong); background: var(--chrome); object-fit: cover; object-position: top left; display: block; }
.fb-main { display: grid; gap: 2px; min-width: 0; }
.fb-main b { color: var(--text-strong); font-weight: 600; }
.fb-st { border-radius: var(--r-pill); padding: 2px var(--sp-2); font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; }
.fb-st.new { background: var(--red-soft); color: var(--link); }
.fb-st.question { background: var(--amber-soft); color: var(--amber); }
.fb-st.planned { background: var(--blue-soft); color: var(--blue); }
.fb-st.done { background: var(--green-soft); color: var(--green); }
.fb-st.deleted { background: var(--surface-3); color: var(--muted); }
.fb-open { display: grid; grid-template-columns: minmax(0, 320px) minmax(0, 1fr); gap: var(--sp-4); padding: var(--sp-3) var(--sp-2) var(--sp-4) calc(72px + var(--sp-5)); background: var(--surface); }
@media (max-width: 900px) { .fb-open { grid-template-columns: minmax(0, 1fr); padding-left: var(--sp-2); } }
.fb-pics { display: grid; gap: var(--sp-2); align-content: start; }
.fb-pics img { width: 100%; border: 1px solid var(--line-strong); border-radius: var(--r-md); display: block; }
.fb-side { display: grid; gap: var(--sp-3); align-content: start; min-width: 0; }
.fb-text { margin: 0; white-space: pre-wrap; }
.fb-msg { border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); background: var(--surface-3); white-space: pre-wrap; }
.fb-msg.you { background: var(--red-soft); }
.fb-msg small { display: block; color: var(--faint); font-size: var(--fs-xs); }
.fb-answer { display: grid; gap: var(--sp-2); justify-items: start; }
.fb-answer .field, .fb-answer textarea { width: 100%; }
/* The one Picture window (app/picture_window.py; Brett, 2026-11-04, docs/mockups/pictures/ board 2) */
.pw { display: grid; gap: var(--sp-3); }
.pw-form { display: grid; gap: var(--sp-3); }
.pw-drop { display: grid; place-items: center; gap: var(--sp-2); text-align: center; min-height: 160px; padding: var(--sp-3); border: 2px dashed var(--line-strong); border-radius: var(--r-lg); color: var(--muted); cursor: pointer; }
.pw-drop.over { border-color: var(--amber); background: var(--amber-soft); }
.pw-drop b { color: var(--text-strong); }
.pw-drop img { max-width: 100%; max-height: 240px; border-radius: var(--r-md); }
.pw-drop input[type=file] { max-width: 100%; }
.pw-foot { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.pw-style { margin: 0; display: flex; gap: var(--sp-2); align-items: baseline; flex-wrap: wrap; }
.pw-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--sp-3); }
.pw-option { display: grid; gap: 4px; padding: 6px; border: 2px solid var(--line); border-radius: var(--r-lg); cursor: pointer; }
.pw-option:has(input:checked) { border-color: var(--green); background: var(--green-soft); }
.pw-option input { position: absolute; opacity: 0; pointer-events: none; }
.pw-option img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--r-md); }
/* Drawing (feedback 22): shown only while the Describe it form is out; the last pictures stand aside meanwhile */
.pw-drawing { display: none; gap: var(--sp-2); }
.pw-draw-form.htmx-request ~ .pw-drawing { display: grid; }
.pw-draw-form.htmx-request ~ #pw-options { display: none; }
.pw-ghost { aspect-ratio: 1; border-radius: var(--r-md); border: 2px solid var(--line);
  background: linear-gradient(110deg, var(--surface-2) 30%, var(--amber-soft) 50%, var(--surface-2) 70%) 0 0 / 300% 100%;
  animation: pw-shimmer 1.6s linear infinite; }
.pw-ghost:nth-child(2) { animation-delay: .2s; } .pw-ghost:nth-child(3) { animation-delay: .4s; } .pw-ghost:nth-child(4) { animation-delay: .6s; }
.pw-bar { height: 6px; border-radius: var(--r-xs); background: var(--line); overflow: hidden; }
.pw-bar span { display: block; height: 100%; width: 0; background: var(--amber); border-radius: var(--r-xs); }
.pw-draw-form.htmx-request ~ .pw-drawing .pw-bar span { animation: pw-fill 60s cubic-bezier(.2, .6, .4, 1) forwards; }
@keyframes pw-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
@keyframes pw-fill { from { width: 0; } to { width: 95%; } }
@media (prefers-reduced-motion: reduce) { .pw-ghost { animation: none; } }
.pw-ddb { display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap; }
.pw-ddb img { width: 180px; max-width: 100%; border-radius: var(--r-md); }
.pw-now { display: flex; align-items: center; gap: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line); flex-wrap: wrap; }
.pw-now img { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; }
.pw-now .note { flex: 1; }
.ws-pic-btn { padding: 0; border: 0; background: none; cursor: pointer; border-radius: var(--r-md); flex: none; }
.ws-pic-btn:hover .ws-pic { outline: 2px solid var(--green); }
.pic-slot { display: grid; place-items: center; width: 48px; height: 48px; border: 2px dashed var(--line-strong); border-radius: var(--r-md); color: var(--muted); font-size: var(--fs-xs); line-height: 1.1; text-align: center; }
.art-refs { border: 0; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.art-refs legend { width: 100%; margin-bottom: var(--sp-1); color: var(--muted); font-size: var(--fs-sm); }
.art-ref { position: relative; cursor: pointer; }
.art-ref input { position: absolute; top: 4px; left: 4px; }
.art-ref img { width: 72px; height: 72px; object-fit: cover; border-radius: var(--r-md); border: 2px solid var(--line); display: block; }
.art-ref:has(input:checked) img { border-color: var(--green); }
/* One of a character's creatures in the fight (app/fight.py with_creatures; Your creatures, slice 3): its row under its
   master's when it shares their Initiative, its token ringed green in the band */
.pt-track tr.ft-creature.pet td:nth-child(2) { padding-left: var(--sp-6); }
.pt-track .ft-creature a.ft-who { color: inherit; text-decoration: none; }
.pt-track .ft-creature a.ft-who:hover b { text-decoration: underline; text-underline-offset: 3px; }
.ft-whose { border-color: color-mix(in srgb, var(--green) 55%, var(--line)); }
.fb-who.fb-creature .fb-pic { box-shadow: 0 0 0 2px var(--green); }
.fb-who.sub { transform: scale(.85); }

/* A test copy of your character (app/twins.py, twin.html; temporary until the twins are claimed): numbered steps, each
   green with Done once askdnd sees it; on a phone, a note to do it on a computer */
.tw-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-5); }
.tw-step { display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: var(--sp-3); align-items: start; }
.tw-n { width: 28px; height: 28px; border-radius: 50%; border: 1.5px solid var(--line-strong); display: grid; place-items: center; font-weight: 600; font-size: var(--fs-sm); color: var(--muted); }
.tw-step.done .tw-n { background: var(--green-soft); border-color: transparent; color: var(--green); }
.tw-head { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; min-height: 28px; }
.tw-head h3 { margin: 0; font-size: var(--fs-md); }
.tw-body p { margin: var(--sp-1) 0 0; max-width: 70ch; }
.tw-body .tw-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-top: var(--sp-3); }
.tw-done, .tw-ext-ok { display: none; }
.tw-step.done .tw-done { display: inline-block; }
.tw-step.done .tw-ext-ok { display: inline; }
.tw-phone { display: none; margin: 0 0 var(--sp-5); }
@media (max-width: 900px) { .tw-phone { display: block; } }
.tw-read { margin: 0 0 var(--sp-5); }
.tw-all { width: 100%; }
/* (the twin page's D&D Beyond cards: drawn plainly, the button to press lit) */
.tw-card { display: inline-grid; gap: var(--sp-1); margin-top: var(--sp-3); padding: var(--sp-3) var(--sp-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); max-width: 100%; }
.tw-card b { color: var(--text-strong); }
.tw-card small { color: var(--muted); }
.tw-card-btns { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin-top: var(--sp-1); font: 600 var(--fs-xs) var(--cond); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.tw-card-btns .press { color: var(--text-strong); outline: 2px solid var(--amber); outline-offset: 3px; border-radius: var(--r-xs); }

/* Frame it (app/templates/_picture_frame.html; feedback 12): the picture under a fixed square, a wide band beside it */
.pw-frame { display: grid; gap: var(--sp-4); }
.pw-frame-row { display: grid; grid-template-columns: minmax(0, 320px) minmax(0, 1fr); gap: var(--sp-5); align-items: start; }
@media (max-width: 640px) { .pw-frame-row { grid-template-columns: minmax(0, 1fr); } }
.pw-stage { position: relative; width: 100%; max-width: 320px; aspect-ratio: 1; overflow: hidden; border-radius: var(--r-lg); border: 2px solid var(--line-strong); background: var(--surface-2); cursor: grab; touch-action: none; }
.pw-stage:active { cursor: grabbing; }
.pw-stage img { position: absolute; max-width: none; user-select: none; -webkit-user-drag: none; }
.pw-frame-side { display: grid; gap: var(--sp-4); min-width: 0; }
.pw-frame-side input[type=range] { width: 100%; }
.pw-band { width: 100%; max-width: 360px; aspect-ratio: 360 / 206; object-fit: cover; border-radius: var(--r-md); border: 1px solid var(--line); display: block; }
/* Hide (app/hiding.py): an attack while hidden offers to end it */
.rw-hidden { display: flex; gap: var(--sp-2); align-items: center; font-size: var(--fs-sm); }
/* Readied (app/readying.py; feedback 14): the top of Battle's Reaction column, in the Readied colour */
.readied { border: 1.5px solid var(--amber); background: var(--amber-soft); border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); display: grid; gap: var(--sp-1); margin-bottom: var(--sp-3); }
.readied-k { font: 700 var(--fs-xs)/1 var(--cond); letter-spacing: .1em; text-transform: uppercase; color: var(--amber); }
.readied-when { margin: 0; font-size: var(--fs-sm); }
.ready-what { display: grid; gap: var(--sp-1); max-height: 240px; overflow-y: auto; }
.ready-opt { display: flex; gap: var(--sp-2); align-items: center; font-size: var(--fs-sm); }
.ready-opt small { color: var(--muted); margin-left: auto; }
/* A change for D&D Beyond that can't reach the sheet now (Brett, Nov 5): said where it starts (templating unreached_says) */
.unreached { margin: 0 0 var(--sp-2); padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md); background: var(--amber-soft); color: var(--text); font-size: var(--fs-sm); }
.ls-chip:disabled { opacity: .45; cursor: not-allowed; }
