/* ============================================================
   THE PARTS — every screen is built from these, and only these.

   A theme is values (tokens.css, a theme record). A screen is layout
   (theme.css, a package's own sheet). Between them sits this file: the
   handful of parts every page shares — the surface, the frame, the
   three kinds of action, the three kinds of "on", the three type
   voices, the drawer. A screen arranges parts; it does not restyle
   them. That is how the character page and the story page end up
   speaking one language, and how a new theme reaches every page by
   changing values rather than rules.

   THE PICTURE BEHIND ALL OF IT: a lit page on a dark desk. The chrome
   is the desk. The work surface is a page with light falling on it —
   grain, and a falloff toward the edges. Frames are printed. Actions
   are hardware. What each of those LOOKS like is the theme's choice
   (`data-texture`, `data-frame`, `data-ornament` on the root); that
   each exists, and what it means, is fixed here.

   SCOPE. `#stage` is the work surface; the bar and the nav sit outside
   it. Package sheets load after this one, so parts inside the stage
   are scoped by id to win without !important.
   ============================================================ */

/* ---- faces. Bundled, never fetched: see packages/ui/assets/fonts. */
@font-face { font-family: 'Crimson Pro'; font-style: normal; font-weight: 400; font-display: swap; src: url(../assets/fonts/crimson-pro-latin-400-normal.woff2) format('woff2'); }
@font-face { font-family: 'Crimson Pro'; font-style: italic; font-weight: 400; font-display: swap; src: url(../assets/fonts/crimson-pro-latin-400-italic.woff2) format('woff2'); }
@font-face { font-family: 'Crimson Pro'; font-style: normal; font-weight: 500 700; font-display: swap; src: url(../assets/fonts/crimson-pro-latin-600-normal.woff2) format('woff2'); }
@font-face { font-family: 'Crimson Pro'; font-style: italic; font-weight: 500 700; font-display: swap; src: url(../assets/fonts/crimson-pro-latin-600-italic.woff2) format('woff2'); }
@font-face { font-family: 'EB Garamond'; font-style: normal; font-weight: 400; font-display: swap; src: url(../assets/fonts/eb-garamond-latin-400-normal.woff2) format('woff2'); }
@font-face { font-family: 'EB Garamond'; font-style: italic; font-weight: 400; font-display: swap; src: url(../assets/fonts/eb-garamond-latin-400-italic.woff2) format('woff2'); }
@font-face { font-family: 'EB Garamond'; font-style: normal; font-weight: 500 700; font-display: swap; src: url(../assets/fonts/eb-garamond-latin-600-normal.woff2) format('woff2'); }
@font-face { font-family: 'UnifrakturMaguntia'; font-style: normal; font-weight: 400 700; font-display: swap; src: url(../assets/fonts/unifrakturmaguntia-latin-400-normal.woff2) format('woff2'); }
@font-face { font-family: 'Chakra Petch'; font-style: normal; font-weight: 400; font-display: swap; src: url(../assets/fonts/chakra-petch-latin-400-normal.woff2) format('woff2'); }
@font-face { font-family: 'Chakra Petch'; font-style: normal; font-weight: 500 700; font-display: swap; src: url(../assets/fonts/chakra-petch-latin-600-normal.woff2) format('woff2'); }
@font-face { font-family: 'Share Tech Mono'; font-style: normal; font-weight: 400 700; font-display: swap; src: url(../assets/fonts/share-tech-mono-latin-400-normal.woff2) format('woff2'); }

/* ------------------------------------------------------------
   0. SCALES — the only numbers a screen may use (tools/style-guide.html).
   A size, a gap, a height or a corner that is not one of these is the
   bug; `npm run smoke:style` reads the sheet and says so.
   ------------------------------------------------------------ */
:root {
  /* Type: ten steps. Serif from 16 up, sans below (§3). */
  --fs-label: 11px;    /* caps — the one label voice                */
  --fs-small: 13px;    /* facts lines, hints, chips, tags, switches  */
  --fs-body: 14px;     /* any sentence you read; fields; buttons     */
  --fs-nav: 16px;      /* the nav and the timeline's names; a phone's
                          fields (iOS zooms into anything smaller)     */
  --fs-lede: 17px;     /* subtitle, pane lede, section tabs          */
  --fs-name: 18px;     /* a record's name; an inline stat            */
  --fs-h3: 22px;       /* a section; a short tile's number           */
  --fs-stat: 27px;     /* a tall tile's number; the drawer's title   */
  --fs-title: 34px;    /* the tab's title                            */
  --track-caps: .1em;  /* the one letter-spacing for caps            */

  /* Space: a 4px scale. */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;

  /* Height: two. A line of controls is 36 — an inline tile's height, so
     a field, a button, a switch and a stat sit level. In a row, 28. */
  --h-line: 36px;
  --h-compact: 28px;

  /* Corners: frame (--panel-radius), control (--control-radius), tag, round. */
  --r-tag: 4px;
  --r-pill: 999px;
}
/* A button is not a font. Without this a bare label falls back to the
   browser's Arial 13.33px. */
#stage button { font: inherit; }

/* ------------------------------------------------------------
   1. SURFACES — the page, and the desk around it.

   Every texture is drawn in CSS (SVG noise, gradients), so it costs no
   download and scales to any screen. `--texture-strength` washes it
   back toward the plain board: 0 is a flat page, 1 is the full recipe.
   ------------------------------------------------------------ */
:root {
  --wash: linear-gradient(color-mix(in srgb, var(--board) calc(100% - var(--texture-strength) * 100%), transparent),
                          color-mix(in srgb, var(--board) calc(100% - var(--texture-strength) * 100%), transparent));
  --grain-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='320'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .2 0 0 0 0 .13 0 0 0 0 .05 .5 0 0 0 -.17'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --stains: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='900'%3E%3Cfilter id='s' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.0045' numOctaves='4' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .42 0 0 0 0 .28 0 0 0 0 .1 .75 0 0 0 -.3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23s)'/%3E%3C/svg%3E");
  --grain-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .92 0 0 0 0 .8 .16 0 0 0 -.05'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  --foxing: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='700' height='700'%3E%3Cfilter id='f' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.03' numOctaves='3' seed='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .45 0 0 0 0 .28 0 0 0 0 .15 1.9 0 0 0 -1.32'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)'/%3E%3C/svg%3E");
  --vignette: radial-gradient(130% 100% at 42% 30%, transparent 48%, color-mix(in srgb, var(--shadow) 15%, transparent) 100%);
  --surface: var(--board);
  --desk: var(--chrome);
  /* Words in the accent: on parchment a gold that reads as gold fails
     as text, so it is darkened toward the ink until it reads. */
  --accent-text: color-mix(in srgb, var(--accent) 50%, var(--ink));
}

/* Parchment — a lit page: big soft stains, fibre grain, darker edges. */
[data-texture='parchment'] {
  --surface: var(--wash), var(--vignette), var(--stains) 0 0 / 900px 900px, var(--grain-dark) 0 0 / 320px 320px, var(--board);
  --desk: var(--grain-light) 0 0 / 260px 260px, radial-gradient(120% 70% at 0 0, color-mix(in srgb, var(--chrome-ink) 4%, transparent), transparent 60%), var(--chrome);
}
/* Vellum — smoother skin, cooler stains, foxing, a heavier falloff. */
[data-texture='vellum'] {
  --surface: var(--wash), radial-gradient(120% 95% at 50% 35%, transparent 40%, color-mix(in srgb, var(--shadow) 22%, transparent) 100%),
             var(--foxing) 0 0 / 700px 700px, var(--stains) 0 0 / 900px 900px, var(--board);
  --desk: var(--grain-light) 0 0 / 260px 260px, var(--chrome);
}
/* Circuit — a terminal: faint grid, scanlines, a cold glow from above. */
[data-texture='circuit'] {
  --surface: var(--wash),
    radial-gradient(90% 60% at 70% -10%, color-mix(in srgb, var(--steel) 14%, transparent), transparent 70%),
    repeating-linear-gradient(0deg, color-mix(in srgb, var(--ink) 3%, transparent) 0 1px, transparent 1px 3px),
    linear-gradient(color-mix(in srgb, var(--rule) 45%, transparent) 1px, transparent 1px) 0 0 / 32px 32px,
    linear-gradient(90deg, color-mix(in srgb, var(--rule) 45%, transparent) 1px, transparent 1px) 0 0 / 32px 32px,
    var(--board);
  --desk: repeating-linear-gradient(0deg, color-mix(in srgb, var(--chrome-ink) 2.5%, transparent) 0 1px, transparent 1px 3px), var(--chrome);
}

#stage { background: var(--surface); }
/* Pages lie ON the surface; they do not repaint it. */
#stage :is(.cm-page, .cm-sheet-page, .sm, .wm-page, .universe-page, .list-mount, .mode-mount, .cm-body, .cm-version) {
  background: transparent;
}

/* ------------------------------------------------------------
   2. THE DESK — dark chrome. Anything here re-maps the page tokens,
   so a part placed on the desk (a button, a field, a frame) inverts
   by itself instead of needing a second rule.
   ------------------------------------------------------------ */
.sb-head, .world-nav, .menu-panel, .app-header, #stage .detail-pane {
  --board: var(--chrome); --card: color-mix(in srgb, var(--chrome-ink) 5%, var(--chrome));
  --ink: var(--chrome-ink); --ink-dim: color-mix(in srgb, var(--chrome-ink) 62%, var(--chrome));
  --rule: var(--chrome-rule);
  /* An accent dark enough to read on parchment can vanish on the desk
     (Gothic's crimson does), so words in the accent are lifted here. */
  --accent-text: color-mix(in srgb, var(--accent) 70%, var(--chrome-ink));
  --action-2: var(--accent-text);
  background: var(--desk); color: var(--chrome-ink);
}

/* ------------------------------------------------------------
   3. TYPE — serif is for size, sans is for everything small.

   THE VOICE (--serif, --display at title size) is for reading at 16px
   and up: titles, names, navigation, section tabs, prose. Below that a
   serif's small x-height turns to texture, so anything smaller — and
   anything on a control or in a column, whatever its size — is sans
   (--sans). THE LABEL (--caps) is a small tracked caption over a value
   or a column. Weight is for titles; the chrome is never bold. Numbers
   are lining, so a 13 does not look like a 1 and a 3 at different
   heights.
   ------------------------------------------------------------ */
#stage, .sb-head, .world-nav, .menu-panel { font-family: var(--serif); font-variant-numeric: lining-nums; }
#stage :is(h1, h2, h3) { font-family: var(--display); font-weight: 600; letter-spacing: -.02em; color: var(--ink); }
/* A HEADING ON A LINE WITH CONTROLS centres on its LETTERS. A display
   face carries more room above its capitals than below its baseline, so
   centring the line box leaves the word sitting low beside a button.
   Trimmed to cap height and baseline, the box is the letters, and
   `align-items: center` puts them dead level with the button's middle.
   Any new row of a heading and controls joins this list. */
#stage :is(.cm-sets-head, .cm-modal-head, .collection-head, .tile-line, .cm-listbar) > :is(h1, h2, h3, h4, h5, h6) {
  display: block; text-box: trim-both cap alphabetic;   /* a block: a flex box has no lines to trim */
}
.label, #stage .label {
  font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim);
}

/* ------------------------------------------------------------
   4. FRAMES — SQUARE IS THE FRAME. Anything that HOLDS something —
   a stat tile, a table, a panel, a figure — is one of these. The
   recipe decides the marks:

     rule     a hairline
     manual   a hairline, with brass corner ticks like a printed sidebar
     gilt     a double printer's rule
     bracket  a faint line with bright HUD corners
   ------------------------------------------------------------ */
:root { --mark: transparent; --mark-len: 0px; --mark-w: 0px; --gilt: 0px; --frame-line: 100%;
        --frame-fill: color-mix(in srgb, var(--card) 66%, transparent); }
[data-frame='manual']  { --mark: color-mix(in srgb, var(--accent) 50%, var(--rule)); --mark-len: 7px; --mark-w: 1.5px; }
[data-frame='gilt']    { --gilt: 3px; --frame-fill: var(--card); }
[data-frame='bracket'] { --mark: color-mix(in srgb, var(--ink) 50%, var(--rule)); --mark-len: 12px; --mark-w: 1.5px; --frame-line: 45%; }

#stage :is(.tile, .cm-spell-table, .detail-figure, .frame) {
  border: 1px solid color-mix(in srgb, var(--rule) var(--frame-line), transparent);
  border-radius: var(--panel-radius);
  background:
    linear-gradient(var(--mark), var(--mark)) left top / var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--mark), var(--mark)) left top / var(--mark-w) var(--mark-len) no-repeat,
    linear-gradient(var(--mark), var(--mark)) right top / var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--mark), var(--mark)) right top / var(--mark-w) var(--mark-len) no-repeat,
    linear-gradient(var(--mark), var(--mark)) left bottom / var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--mark), var(--mark)) left bottom / var(--mark-w) var(--mark-len) no-repeat,
    linear-gradient(var(--mark), var(--mark)) right bottom / var(--mark-len) var(--mark-w) no-repeat,
    linear-gradient(var(--mark), var(--mark)) right bottom / var(--mark-w) var(--mark-len) no-repeat,
    var(--frame-fill);
  box-shadow: inset 0 0 0 var(--gilt) var(--card),
              inset 0 0 0 calc(var(--gilt) + min(var(--gilt), 1px)) var(--rule),
              0 1px 2px color-mix(in srgb, var(--shadow) 9%, transparent);
}

/* ------------------------------------------------------------
   5. ACTIONS — ROUND IS THE ACTION, and the action colour is worn by
   actions alone. Three weights, and a screen uses them in that order:

     primary    filled, in the action colour, with a material finish —
                one per view, the thing this screen is for
     secondary  drawn in the action colour on the surface
     quiet      ink only, until hovered — close, ⋯, back
   ------------------------------------------------------------ */
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn) {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--h-line); padding: 0 var(--sp-4); box-sizing: border-box;
  font: 500 var(--fs-body)/1.1 var(--sans); letter-spacing: 0; text-transform: none;
  color: var(--action-2);
  background: color-mix(in srgb, var(--card) 72%, transparent);
  border: 1px solid color-mix(in srgb, var(--action-2) 55%, transparent);
  border-radius: var(--control-radius);
  box-shadow: 0 1px 1px color-mix(in srgb, var(--shadow) 8%, transparent);
  cursor: pointer; transition: background-color .12s, border-color .12s, box-shadow .12s, filter .12s;
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn):hover:not(:disabled) {
  border-color: var(--action-2);
  background: color-mix(in srgb, var(--action-2) 9%, var(--card));
}

:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn.primary, .sm-btn.primary, .cm-btn.primary) {
  color: var(--action-ink);
  border-color: color-mix(in srgb, var(--action) 60%, #000);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--action) 72%, #fff) 0%,
    var(--action) 46%,
    color-mix(in srgb, var(--action) 84%, #000) 100%);
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 50%, transparent),
              inset 0 -1px 0 color-mix(in srgb, #000 12%, transparent),
              0 1px 2px color-mix(in srgb, var(--shadow) 30%, transparent),
              0 0 var(--glow-strength) color-mix(in srgb, var(--action) 65%, transparent);
  text-shadow: 0 1px 0 color-mix(in srgb, #fff 25%, transparent);
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn.primary, .sm-btn.primary, .cm-btn.primary):hover:not(:disabled) {
  filter: brightness(1.06); background: linear-gradient(180deg,
    color-mix(in srgb, var(--action) 72%, #fff) 0%, var(--action) 46%, color-mix(in srgb, var(--action) 84%, #000) 100%);
  border-color: color-mix(in srgb, var(--action) 60%, #000);
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn):active:not(:disabled) {
  box-shadow: inset 0 1px 3px color-mix(in srgb, var(--shadow) 35%, transparent); filter: none;
}

/* QUIET, BUT STILL AN ACTION: no box, in the action colour, so "+ New
   set" reads as something to press rather than a label. */
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn).quiet {
  color: var(--action-2); background: transparent; border-color: transparent; box-shadow: none; padding: 0 var(--sp-2);
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn).quiet:hover:not(:disabled) {
  color: var(--action-2); background: color-mix(in srgb, var(--action-2) 10%, transparent); border-color: transparent;
}
/* Under a label, a quiet button's words line up with the label's: its
   padding hangs out to the left, where its hover fill still shows. */
#stage :is(.field-wrap, .cm-arc-body) > .btn.quiet { justify-self: start; margin-left: calc(-1 * var(--sp-2)); }
/* TOOL — gold DOES something, ink CHOOSES something. A tool opens a
   menu or changes the view (Filters, Sort, Body ▾); it is ink. */
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.tool {
  color: var(--ink); background: color-mix(in srgb, var(--card) 60%, transparent); border-color: var(--rule); box-shadow: none; padding: 0 var(--sp-3);
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.tool:hover:not(:disabled),
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) details[open] > .btn.tool {
  background: color-mix(in srgb, var(--card) 85%, transparent); border-color: color-mix(in srgb, var(--ink) 40%, var(--rule));
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.tool[aria-pressed='true'] { color: var(--board); background: var(--ink); border-color: var(--ink); }
/* COMPACT — inside a row or a table, everything is 28. */
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn).compact {
  min-height: var(--h-compact); padding: 0 var(--sp-3); font-size: var(--fs-small);
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn).compact.quiet { padding: 0 var(--sp-2); }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn.danger, .sm-btn.danger) { color: var(--oxblood); }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn).quiet.danger { border-color: transparent; }
/* A GLYPH BUTTON — ⋯ × + ←. Round, ink, no box until hovered; `.raised`
   carries its own page when it sits on a picture. */
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.icon {
  width: var(--h-line); min-width: var(--h-line); height: var(--h-line); padding: 0; border-radius: var(--r-pill);
  font: 400 18px/1 var(--sans); color: var(--ink-dim); background: transparent; border-color: transparent; box-shadow: none;
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.icon.compact { width: var(--h-compact); min-width: var(--h-compact); height: var(--h-compact); font-size: 16px; }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.icon:hover:not(:disabled) { color: var(--ink); background: color-mix(in srgb, var(--ink) 9%, transparent); border-color: transparent; }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn.icon.raised {
  color: var(--ink); background: color-mix(in srgb, var(--card) 85%, transparent); backdrop-filter: blur(4px);
  border-color: color-mix(in srgb, var(--rule) 80%, transparent); box-shadow: 0 2px 8px color-mix(in srgb, var(--shadow) 18%, transparent);
}
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) .btn svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) summary.btn { list-style: none; }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) summary.btn::-webkit-details-marker { display: none; }
/* A button that cannot act is inert ink, not faded gold: pale gold
   reads as a hint about the action rather than as "not now". */
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn):disabled { opacity: .45; cursor: not-allowed; }
:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(.btn, .sm-btn, .cm-btn).quiet:disabled { color: var(--ink-dim); }

/* A quiet round action that is only a glyph: ⋯, ×, +. */
#stage :is(.cm-row-actions > summary, .detail-x), .sb-tab-new, .sb-tab-x {
  display: inline-grid; place-items: center; border-radius: 999px; cursor: pointer; color: var(--ink-dim);
}
#stage :is(.cm-row-actions > summary, .detail-x):hover, .sb-tab-new:hover, .sb-tab-x:hover {
  color: var(--ink); background: color-mix(in srgb, var(--ink) 9%, transparent);
}

:is(#stage, .sb-head, .world-nav, .menu-panel, .account-settings, .gate) :is(button, a, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px;
}

/* ------------------------------------------------------------
   6. "ON" — three different states, never drawn the same way.

     here      where you are in a list: an accent ring, a faint tint,
               and on a row a bar down its leading edge
     section   which part of a page you are reading: an accent rule
               under the label, exactly as wide as the label
     toggle    a filter or a mode that is switched on: filled with
               ink, because it is a state you set, not a place you are
   ------------------------------------------------------------ */
.here, #stage .cm-spell[data-selected='true'], .world-nav .world-nav-link[aria-current='page'] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, var(--card));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}

#stage :is(.cm-spell-filter, .toggle) {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 34px; padding: 5px 8px 5px 16px; box-sizing: border-box;
  font: 400 14px/1 var(--sans); color: var(--ink);
  background: color-mix(in srgb, var(--card) 60%, transparent);
  border: 1px solid var(--rule); border-radius: 999px; cursor: pointer;
}
#stage :is(.cm-spell-filter, .toggle) > span {
  min-width: 22px; height: 22px; display: inline-grid; place-items: center; box-sizing: border-box; padding: 0 6px;
  border-radius: 999px; font: 600 11px/1 var(--sans); background: color-mix(in srgb, var(--ink) 8%, transparent);
}
#stage :is(.cm-spell-filter, .toggle):hover { border-color: color-mix(in srgb, var(--ink) 40%, var(--rule)); }
#stage :is(.cm-spell-filter, .toggle)[aria-pressed='true'] {
  color: var(--board); background: var(--ink); border-color: var(--ink);
}
#stage :is(.cm-spell-filter, .toggle)[aria-pressed='true'] > span { background: color-mix(in srgb, var(--board) 18%, transparent); }

/* ------------------------------------------------------------
   7. TAGS — a kind of thing, pointed at. Tinted, never filled with
   the action colour, never raised: they are words, not buttons.
   Each kind has its own ink (--tag-*); the story editor maps its
   mentions onto `.tag[data-tag]` or onto these variables directly.
   ------------------------------------------------------------ */
.tag {
  --tag: var(--tag-story);
  display: inline-flex; align-items: center; gap: 5px; padding: 1px 8px; border-radius: 4px;
  font: inherit; white-space: nowrap; color: color-mix(in srgb, var(--tag) var(--tag-ink-mix, 70%), var(--ink));
  background: color-mix(in srgb, var(--tag) 13%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tag) 22%, transparent);
}
.tag[data-tag='roll'] { --tag: var(--tag-roll); }
.tag[data-tag='treasure'] { --tag: var(--tag-treasure); }
.tag[data-tag='creature'] { --tag: var(--tag-creature); }
.tag[data-tag='character'] { --tag: var(--tag-character); }
.tag[data-tag='crew'] { --tag: var(--tag-crew); }
.tag[data-tag='place'] { --tag: var(--tag-place); }
.tag[data-tag='condition'] { --tag: var(--tag-condition); }

/* RULE TAGS (ui/record/ruletag.js) — the same tag on a sheet row, in
   the shape a figure has always had on the turn strip ("running 13 ft"):
   ink on a near-neutral ground with a hairline edge, the label dim and
   the value bold, slightly rounded. The KIND is a faint tint of ground
   and edge — a roll is blue-ish, a condition violet-ish — so the words
   stay as legible as the text around them and the row is not loud.
   A tag with no label is just its value, a touch lighter. */
#stage .rule-tags { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
#stage .tag.rule-tag {
  gap: 5px; padding: 0 6px; border-radius: var(--r-tag);
  font: 400 var(--fs-small)/1.55 var(--sans); letter-spacing: 0; text-transform: none;
  color: var(--ink-dim);   /* restated: a list's own span colour is more specific than .tag */
  background: color-mix(in srgb, var(--tag) 6%, color-mix(in srgb, var(--card) 70%, transparent));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tag) 16%, var(--rule));
}
#stage .tag.rule-tag[data-tag='fact'] { --tag: var(--ink); }
#stage .tag.rule-tag > :is(i, b) { font-family: var(--sans); letter-spacing: 0; text-transform: none; }
#stage .tag.rule-tag > i { font-style: normal; font-weight: 400; color: var(--ink-dim); }
#stage .tag.rule-tag > b { font-weight: 600; font-size: var(--fs-small); color: var(--ink); font-variant-numeric: lining-nums tabular-nums; }
#stage .tag.rule-tag[data-dry] { --tag: var(--oxblood); }
#stage .tag.rule-tag[data-dry] > b { color: var(--oxblood); }
/* Information in front of the tags is just words, in the row's own dim ink. */
#stage .rule-tags > .rule-words { font: var(--fs-small) var(--sans); color: var(--ink-dim); margin-right: 2px; }

/* ------------------------------------------------------------
   8. ORNAMENT — drawn only in dead space, never behind words, and
   faint. One per screen at most; the drawer's corner is where it
   lives. Masked, so it takes the accent colour of whatever theme.
   ------------------------------------------------------------ */
:root { --ornament: none; --ornament-alpha: .14; }
[data-ornament='arcane'] {
  --ornament: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' fill='none' stroke='%23000' stroke-width='.7'%3E%3Ccircle cx='100' cy='100' r='97'/%3E%3Ccircle cx='100' cy='100' r='88'/%3E%3Ccircle cx='100' cy='100' r='92.5' stroke-width='3' stroke-dasharray='.6 4.2'/%3E%3Cpath d='M100 12 176.2 144H23.8Z M100 188 23.8 56h152.4Z'/%3E%3Ccircle cx='100' cy='100' r='50.8'/%3E%3Ccircle cx='100' cy='100' r='44'/%3E%3Cpath d='M100 56v88 M56 100h88 M69 69l62 62 M131 69l-62 62' stroke-width='.4'/%3E%3Ccircle cx='100' cy='100' r='14'/%3E%3Ccircle cx='100' cy='12' r='4'/%3E%3Ccircle cx='176.2' cy='144' r='4'/%3E%3Ccircle cx='23.8' cy='144' r='4'/%3E%3Ccircle cx='100' cy='188' r='4'/%3E%3Ccircle cx='23.8' cy='56' r='4'/%3E%3Ccircle cx='176.2' cy='56' r='4'/%3E%3C/svg%3E");
}
[data-ornament='tracery'] {
  --ornament-alpha: .16;
  --ornament: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' fill='none' stroke='%23000' stroke-width='.9'%3E%3Ccircle cx='100' cy='100' r='97'/%3E%3Ccircle cx='100' cy='100' r='91'/%3E%3Cg%3E%3Ccircle cx='100' cy='55' r='45'/%3E%3Ccircle cx='139' cy='77.5' r='45'/%3E%3Ccircle cx='139' cy='122.5' r='45'/%3E%3Ccircle cx='100' cy='145' r='45'/%3E%3Ccircle cx='61' cy='122.5' r='45'/%3E%3Ccircle cx='61' cy='77.5' r='45'/%3E%3C/g%3E%3Ccircle cx='100' cy='100' r='20'/%3E%3Ccircle cx='100' cy='100' r='9'/%3E%3C/svg%3E");
}
[data-ornament='circuit'] {
  --ornament-alpha: .2;
  --ornament: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' fill='none' stroke='%23000' stroke-width='1'%3E%3Cpath d='M0 40h70l20 20h60l20-20h30 M0 80h40l20 20v40l20 20h120 M60 200v-30l20-20h40 M140 0v30l-20 20h-30 M200 110h-40l-15 15v40 M20 200v-60l30-30'/%3E%3Cg fill='%23000'%3E%3Ccircle cx='150' cy='60' r='3'/%3E%3Ccircle cx='90' cy='50' r='3'/%3E%3Ccircle cx='120' cy='150' r='3'/%3E%3Ccircle cx='145' cy='165' r='3'/%3E%3Ccircle cx='50' cy='110' r='3'/%3E%3C/g%3E%3C/svg%3E");
}

/* ------------------------------------------------------------
   9. THE DRAWER — supporting detail, on the desk. Dark so the page
   stays the brightest thing on screen; the figure is the one place a
   colour is allowed to glow.
   ------------------------------------------------------------ */
#stage .detail-pane { border-left: 1px solid var(--chrome-rule); isolation: isolate; }
#stage .detail-pane::after {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  right: -130px; bottom: -130px; width: 440px; height: 440px;
  background: var(--accent); opacity: var(--ornament-alpha);
  -webkit-mask: var(--ornament) center / contain no-repeat; mask: var(--ornament) center / contain no-repeat;
}
#stage .detail-figure {
  --frame-fill: radial-gradient(circle at 50% 35%, color-mix(in srgb, var(--accent) 30%, var(--chrome)), color-mix(in srgb, var(--accent) 8%, var(--chrome)));
  display: grid; place-items: center; width: 64px; height: 64px; flex: none; color: var(--accent);
  box-shadow: 0 0 calc(var(--glow-strength) + 14px) color-mix(in srgb, var(--accent) 22%, transparent),
              inset 0 0 18px color-mix(in srgb, var(--accent) 25%, transparent);
}
#stage .detail-figure[hidden] { display: none; }
#stage .detail-figure .cm-glyph { width: 36px; height: 36px; }
#stage .detail-figure svg { width: 100%; height: 100%; stroke-width: 1.3; }

/* ------------------------------------------------------------
   10. SCROLL AREAS keep their gutter. A scrollbar that appears only
   when the content overflows takes its width out of the content, so
   the same drawer laid out a second time with a longer explanation
   moved every line sideways. The gutter is reserved up front instead,
   and drawn thin and in the theme so the reservation does not read as
   a gap. (Overlay scrollbars take no room, and are unaffected.)
   ------------------------------------------------------------ */
#stage :is(#detailBody, .cm-version > .cm-panel, .cm-log, .list-mount, .mode-mount), .world-nav {
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--ink) 28%, transparent) transparent;
}
/* Except a list area holding a page that scrolls inside itself (the
   character sheet): it never scrolls, and its reserved gutter was an
   empty strip between the page's own scrollbar and the drawer. */
#stage #listMount:has(.cm-sheet-page) { scrollbar-gutter: auto; overflow: hidden; }   /* an id, to outrank the :is(#detailBody, …) above */

@media (prefers-reduced-motion: reduce) {
  :is(#stage, .sb-head, .world-nav) * { transition: none !important; }
}

/* ------------------------------------------------------------
   11. MODALS IN THE TOP LAYER (apps/web/src/modals.js). A popover
   brings the browser's own box with it — fit-content, centred, a
   border and Canvas colours — and a modal is none of those: it is the
   whole window, scrim and all.
   ------------------------------------------------------------ */
.cm-modal[popover] {
  inset: 0; width: auto; height: auto; max-width: none; max-height: none;
  margin: 0; border: 0; color: inherit;
}

/* THE DIALOG — one for every question the app stops to ask: a level, a
   moment, an action, a picture, a thing to pick. The page's own paper
   over a scrim, square because it holds a record being made:

     head   the title at the drawer's size (27), "1 of 3" if it has
            steps, a round ×; under them, one line on what it asks
     body   groups 16 apart, 24 from the sides
     foot   ruled; Back and a dim note on the left, the one action last

   `.cm-picker` is the wide one, with a list and the record beside it. */
#stage .cm-modal {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-5) var(--sp-4); overflow-y: auto; box-sizing: border-box;
  background: color-mix(in srgb, #0b0907 72%, transparent);
}
#stage .cm-modal:focus { outline: none; }
#stage .cm-modal-card {
  width: min(600px, 100%); max-height: calc(100vh - 48px); box-sizing: border-box;
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  color: var(--ink); background: var(--surface); border: 1px solid var(--rule); border-radius: 0;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}
#stage .cm-modal-head {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; column-gap: var(--sp-3); row-gap: var(--sp-1);
  padding: var(--sp-4) var(--sp-4) var(--sp-3) var(--sp-5); border: 0;
}
#stage .cm-modal-head > h3 { margin: 0; font: 600 var(--fs-stat)/1.1 var(--display); letter-spacing: -.01em; text-transform: none; color: var(--ink); }
#stage .cm-modal-step { margin: 0; font: 400 var(--fs-small)/1 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink-dim); white-space: nowrap; }
#stage .cm-modal-x {
  grid-column: 3; grid-row: 1; display: grid; place-items: center; width: var(--h-line); height: var(--h-line); min-height: 0; margin: 0; padding: 0; box-sizing: border-box; cursor: pointer;
  font: 400 22px/1 var(--sans); color: var(--ink-dim); background: transparent; border: 0; border-radius: var(--r-pill);
}
#stage .cm-modal-x:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 7%, transparent); }
#stage .cm-modal-x:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* What it asks: the page head's subtitle, under the title. */
#stage .cm-modal-sub { grid-column: 1 / -1; margin: 0; font: 400 var(--fs-lede)/1.4 var(--serif); color: var(--ink-dim); }
#stage .cm-modal-body {
  display: grid; gap: var(--sp-4); align-content: start; min-height: 0; overflow-y: auto;
  padding: var(--sp-2) var(--sp-5) var(--sp-5);
}
#stage .cm-modal-body > * { margin: 0; }
/* A hint sits 4 under what it explains, not a group away. */
#stage .cm-modal-body > .field-hint { margin-top: calc(var(--sp-1) - var(--sp-4)); }
#stage .cm-modal-body > .section-head { margin: 0; padding-top: var(--sp-4); }
#stage .cm-modal-body > .section-head:first-child { padding-top: 0; border-top: 0; }
#stage .cm-modal-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--rule);
}
#stage .cm-modal-foot > :last-child { margin-left: auto; }
#stage .cm-modal-note { flex: 1 1 180px; margin: 0; font: 400 var(--fs-small)/1.45 var(--sans); color: var(--ink-dim); }
@media (max-width: 560px) {
  #stage .cm-modal { padding: 0; }
  #stage .cm-modal-card { width: 100%; height: 100%; max-height: none; border: 0; }
  #stage .cm-modal-head { padding: var(--sp-3) var(--sp-3) var(--sp-2) var(--sp-4); }
  #stage .cm-modal-head > h3 { font-size: var(--fs-h3); }
  #stage .cm-modal-body { padding: var(--sp-2) var(--sp-4) var(--sp-4); }
  #stage .cm-modal-foot { padding: var(--sp-3) var(--sp-4); }
}

/* ------------------------------------------------------------
   12. THE LIST TOOLBAR (ui/record/listbar.js) — search, filters, sort,
   table or cards, Add. One row, one height, on top of any list. The
   buttons are actions, so they are rounded; the menus they open are
   the same paper as every other menu.
   ------------------------------------------------------------ */
/* ONE ROW AT ALMOST EVERY WIDTH. As the room goes, the search gives first,
   then the words on the buttons — Filters, the sort, Add — leaving
   their icons, a count and a +. The bar is its own container, so it
   answers to its own width and not the window's: a drawer narrows it
   the same way a phone does. */
#stage .cm-listbar {
  /* It wraps only as a last resort: the search grows from 80, so it
     gives all its room before the view switch and Add take a second
     line — never off the edge. */
  position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-3);
  container: listbar / inline-size;
}
/* Its parts are the shared ones — a .field, .btn.tool menus, a
   .seg.icons, a .btn Add — so this is only where they sit. */
#stage .cm-listbar > * { flex-shrink: 0; }
#stage .cm-listbar > .cm-lb-search { flex: 1 1 80px; min-width: 80px; max-width: 320px; }
#stage .cm-lb-space { flex: 1 1 0; min-width: 0; }
#stage .cm-lb-menu { position: relative; }
#stage .cm-lb-menu > .cm-lb-pop { position: absolute; top: calc(100% + 6px); left: 0; max-height: min(64vh, 520px); overflow-y: auto; }
/* The filters menu lays its groups side by side, so six questions are
   one glance and not a scroll — and it hangs from the whole bar rather
   than from its button, so it is never wider than the list it filters
   and never runs under a drawer. The columns reflow to fit. */
#stage .cm-lb-wide { position: static; }
#stage .cm-lb-wide > .cm-lb-pop {
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 4px 12px; align-items: start; right: 0; min-width: 0;
}
#stage .cm-lb-group { display: flex; flex-direction: column; }
#stage .cm-lb-clear { grid-column: 1 / -1; justify-content: center; margin-top: 4px; border-top: 1px solid var(--rule); border-radius: 0; color: var(--ink-dim); }
#stage .cm-lb-add { gap: 5px; }
#stage .cm-lb-plus { font-size: 16px; line-height: 1; }
@container listbar (max-width: 600px) {
  #stage .cm-listbar .btn.tool > span:not(.count), #stage .cm-lb-word { display: none; }
  /* A menu hangs from the bar's right edge, not its button's left, so
     it opens inside the column. */
  #stage .cm-listbar .cm-lb-menu { position: static; }
  #stage .cm-listbar .cm-lb-menu:not(.cm-lb-wide) > .cm-lb-pop { left: auto; right: 0; max-width: 100%; }
  #stage .cm-lb-add { width: var(--h-line); padding: 0; }
  #stage .cm-lb-plus { font-size: 18px; }
}
@container listbar (max-width: 380px) {
  #stage .cm-listbar .seg.icons > button { width: 30px; }
}

/* ------------------------------------------------------------
   13. TILES — a stat: a drawing, a label, a value. THREE SIZES, and a
   screen picks one; it never invents a fourth.

     tall     a headline number in a frame — Armour Class, Spell Save DC
     short    one of many in a frame — the eighteen skills
     inline   a one-line frame, label then value side by side, in a
              line led by its own label; with no value at all it is a
              fact in a frame — "trained in Arcana". In a `table` the
              label moves up to head a column — spell slots by level

   Tiles in a set share one column width on every section of a page, so
   they line up down the page instead of stretching to fill whatever row
   they landed on. Height never changes with what is in it: a label is
   allowed two lines from the start (tall) or one (short).
   ------------------------------------------------------------ */
/* ONE FIXED WIDTH. A tile that stretched to share the leftover room
   changed size whenever the drawer opened or shut, and a row of two
   was a different width from a row of four. Fixed, a tile is the same
   box on every tab and every width; on a phone the columns share the
   row instead, two to a line. */
#stage .tiles { display: grid; grid-template-columns: repeat(auto-fill, 176px); gap: var(--sp-3); }
@media (max-width: 480px) { #stage .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* A label in the grid: 16 below the group before it and 4 over its own
   tiles, as everywhere, net of the grid's 12. */
#stage .tiles > :is(h3, h4, .group-label) { grid-column: 1 / -1; margin: var(--sp-1) 0 calc(var(--sp-1) - var(--sp-3)); }
#stage .tiles > :is(h3, h4, .group-label):first-child { margin-top: 0; }
#stage .tile { display: flex; align-items: center; gap: 12px; box-sizing: border-box; min-height: 0; padding: 0 14px; cursor: pointer; }
#stage .tile:hover { --frame-line: 100%; border-color: color-mix(in srgb, var(--accent) 55%, var(--rule)); }
#stage .tile > .cm-glyph { flex: none; display: inline-grid; place-items: center; color: var(--accent); }
#stage .tile > .cm-glyph > svg { width: 100%; height: 100%; display: block; }
#stage .tile > .cm-glyph svg { stroke-width: 1.3; }
#stage .tile .cm-field-body { row-gap: 3px; align-content: center; }
#stage .tile .cm-field-name { font: 600 var(--fs-label)/1.25 var(--caps); letter-spacing: var(--track-caps); color: var(--ink-dim); }
/* The value is a button (it opens the why); the part owns its reset. */
#stage .tile .cm-field-value {
  margin: 0; padding: 0; border: 0; background: none; box-shadow: none; cursor: pointer;
  color: var(--ink); font-variant-numeric: lining-nums tabular-nums;
}

#stage .tile.tall { min-height: 88px; }
#stage .tile.tall > .cm-glyph { width: 30px; height: 30px; }
#stage .tile.tall .cm-field-name > span { white-space: normal; overflow: visible; }
#stage .tile.tall .cm-field-value { font: 600 var(--fs-stat)/1.1 var(--display); }

#stage .tile.short { min-height: 60px; gap: 10px; padding: 0 12px; }
#stage .tile.short > .cm-glyph { width: 22px; height: 22px; }
#stage .tile.short .cm-field-body { row-gap: 1px; }
#stage .tile.short .cm-field-value { font: 600 var(--fs-h3)/1.1 var(--display); }

#stage .tile.inline { display: inline-flex; min-height: var(--h-line); gap: 10px; padding: 0 12px; }
#stage .tile.inline > .cm-glyph { display: none; }
#stage .tile.inline .cm-field-body { display: flex; align-items: baseline; gap: 10px; }
#stage .tile.inline .cm-field-name > span { white-space: nowrap; }
#stage .tile.inline .cm-field-value { font: 600 var(--fs-name)/1 var(--display); }
/* PLAIN: an inline tile with no frame, for a count that reads as a
   status line beside controls that are already boxes. */
#stage .tile.inline.plain { min-height: 0; padding: 0; border: 0; background: none; box-shadow: none; }
#stage .tile-line:has(> .tile.plain) { column-gap: 28px; }
/* PAST WHAT THE RULES GIVE — a warning, in the colour that means short
   or wrong everywhere else (a missing row's tag). Never a block. */
#stage .tile[data-over] .cm-field-value { color: var(--oxblood); }
/* With no value, the label is the fact: ink, sentence case, readable. */
#stage button.tile.inline { min-height: 32px; font: 500 var(--fs-body) var(--sans); letter-spacing: 0; text-transform: none; color: var(--ink); min-height: 0; }

/* Six to a row with room to spare: an ability tile holds three letters
   and two numbers, so it is narrower than a stat tile. */
#stage .tiles.cm-abilities { grid-template-columns: repeat(auto-fill, 102px); }
@media (max-width: 480px) { #stage .tiles.cm-abilities { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* ABILITY — a tall tile with two doors: the score, and under a hairline
   the saving throw that comes off it. Each half lights on its own, so
   which pane a press will open is never a guess. */
#stage .tile.ability {
  height: auto; padding: 0; gap: 0; display: grid; grid-template-rows: 64px auto;
  align-items: stretch; overflow: hidden; cursor: default;
}
#stage .tile.ability > button {
  all: unset; box-sizing: border-box; display: flex; align-items: center; cursor: pointer; min-width: 0;
  transition: background-color .12s;
}
#stage .tile.ability > button:hover { background: var(--rec-hover); }
#stage .tile.ability > button:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
#stage .tile.ability > .ability-main { gap: 10px; padding: 0 12px; }
#stage .tile.ability > .ability-main > .cm-glyph { flex: none; width: 26px; height: 26px; color: var(--accent); }
#stage .tile.ability { justify-items: stretch; }
#stage .tile.ability .cm-field-body { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: start; row-gap: 2px; min-width: 0; }
#stage .tile.ability .cm-ability-line { justify-content: flex-start; }
#stage .tile.ability > .cm-ability-save { width: 100%; margin: 0; align-items: center; }
#stage .tile.ability .cm-ability-name { font: 600 var(--fs-label)/1.25 var(--caps); letter-spacing: var(--track-caps); color: var(--ink-dim); }
#stage .tile.ability .cm-ability-value { font: 600 var(--fs-stat)/1.1 var(--display); color: var(--ink); font-variant-numeric: lining-nums tabular-nums; }
#stage .tile.ability > .cm-ability-save {
  justify-content: space-between; gap: 8px; padding: 6px 12px;
  border-top: 1px solid color-mix(in srgb, var(--rule) 80%, transparent);
}
#stage .tile.ability > .cm-ability-save em { font: 600 var(--fs-label)/1.3 var(--caps); font-style: normal; letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim); }
#stage .tile.ability > .cm-ability-save .ability-save-n { display: inline-flex; align-items: center; gap: 5px; }
#stage .tile.ability > .cm-ability-save b { font: 600 14px/1.3 var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
#stage .tile.ability > .cm-ability-save[data-trained='true'] em { color: var(--accent-text, var(--accent)); }

/* A LINE OF INLINE TILES, its own label first. */
#stage .tile-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
#stage .tile-line > :is(h4, h5) { margin: 0 6px 0 0; font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim); }
#stage .tile-line > .cm-prof-chips { display: contents; }

/* A SET OF INLINE TILES UNDER ITS OWN HEADING — the heading on a row of
   its own, the tiles wrapping beneath it. For groups whose names are
   long or uneven ("Saving throws", "Tool"), where a heading to the left
   leaves a ragged column of gaps. */
#stage .tile-set { display: grid; gap: 8px; }
/* A step under the section's own small-caps heading, so "Trained in"
   reads as the title and "Skills" as one of its groups. */
#stage .tile-set > :is(h4, h5) { margin: 0; font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim); }
#stage .tile-set > .cm-prof-chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* A TABLE OF INLINE TILES — one row of columns, a heading over each, the
   value centred in its tile. A column at zero is still there, faint, so
   the row reads as the whole ladder; the tile's own label is for a
   screen reader only, since the heading above already says it. */
#stage .tile-cols { display: flex; flex-wrap: wrap; gap: 8px; }
#stage .tile-col { flex: 1 1 56px; min-width: 56px; max-width: 96px; display: grid; gap: var(--sp-1); justify-items: stretch; }
#stage .tile-head { font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim); text-align: center; white-space: nowrap; }
#stage .tile-col > .tile.inline { display: flex; justify-content: center; padding: 0 8px; }
#stage .tile-col > .tile.inline .cm-field-body { justify-content: center; }
#stage .tile-col > .tile.inline .cm-field-name {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
#stage .tile-col > .tile.inline[data-zero='true'] .cm-field-value { color: var(--ink-dim); font-weight: 400; }

/* ------------------------------------------------------------
   14. RECORD ROWS — one look for a spell, an item, an action, in a
   table, a list, a card or a picker. The NAME is the headline: serif,
   bold, a size above the facts beside it. The row sits on one fill,
   warms on hover, and is lit when it is the one open in the drawer.
   ------------------------------------------------------------ */
#stage {
  /* ONE NAME SIZE in a table, a list, a card and a picker (decided
     2026-10-06). --rec-name-lg is the old name for the same thing. */
  --rec-name: 600 var(--fs-name)/1.25 var(--serif);
  --rec-name-lg: var(--rec-name);
  --rec-fill: color-mix(in srgb, var(--card) 60%, transparent);
  --rec-hover: color-mix(in srgb, var(--accent) 7%, var(--card));
  --rec-on: color-mix(in srgb, var(--accent) 12%, var(--card));
  --rec-edge: color-mix(in srgb, var(--accent) 55%, var(--rule));

  /* ONE GEOMETRY FOR EVERY LIST — Actions, Abilities' features, Spells,
     Items. A row and a card are padded, spaced and drawn the same on
     every tab; theme.css "ONE ROW, ONE CARD" applies these. */
  --rec-pad: 10px 14px;                       /* inside a list row */
  --rec-card-pad: 14px 16px;                  /* inside a card */
  --rec-fig: 24px;                            /* the drawing in the margin */
  --rec-fig-gap: 12px;                        /* drawing to name */
  --rec-line-gap: 3px;                        /* name to the lines under it */
  --rec-gap: 6px;                             /* row to row */
  --rec-card-gap: 12px;                       /* card to card */
  --rec-card-min: 260px;                      /* narrowest card */
  --rec-list-end: 16px;                       /* after a band's last row */
  --rec-meta: 400 13px/1.45 var(--sans);      /* the facts line, dim */
  --rec-text: 400 var(--fs-body)/1.5 var(--sans);   /* a row's sentence: a sentence you read */
}

/* ------------------------------------------------------------
   15. THE DRAWER — one type scale for everything in it.

   The side pane had grown a size for every author: 10px caps over
   12px serif over 11px blue mono over 17px serif at a line height of
   1.6. It is one surface, so it gets five voices and no others:

     lede     what the thing is — serif, a line height you can read
              without the paragraph falling apart (1.45, not 1.6)
     text     everything else that is words — sans
     small    the kicker under the title, provenance — sans, dim
     heading  a section's name — the small caps every list uses
     formula  any number or expression the rules worked out — the one
              formula face (--mono), one size, in ink

   And no deep indents: a formula runs the width of the pane under the
   line it explains, instead of starting a column in.
   ------------------------------------------------------------ */
#stage .detail-pane {
  --pane-lede: 400 var(--fs-lede)/1.45 var(--serif);
  --pane-text: 400 var(--fs-body)/1.5 var(--sans);
  --pane-small: 400 var(--fs-small)/1.4 var(--sans);
  --pane-formula: 400 var(--fs-small)/1.5 var(--mono);
}
/* The word under the title — "Rest", "Equipped · Archon" — is a label,
   so it is the sans every label is. */
#stage .detail-pane #detailKicker { font: var(--pane-small); color: var(--ink-dim); }

/* Headings: every kind any pane builds, one look. */
#stage .detail-pane :is(h4, h5, .cm-about-h, .pane-h, .cm-condpick-h) {
  margin: 0; font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim);
}

/* A control's own label is a heading too — never under 11px. */
#stage .detail-pane :is(.cm-story-qty) > span:first-child {
  font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim);
}
/* Chips and switches inside the drawer: the 12px sans every chip uses. */

/* A record section's h3 in the drawer is the drawer's heading: caps 11. */
#stage .detail-pane .section-head > h3 { font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim); }
#stage .detail-pane .section-head { min-height: 0; margin-top: var(--sp-4); padding-top: var(--sp-3); border-top-color: var(--chrome-rule, var(--rule)); }
#stage .detail-pane .cm-pane-moment strong { font: 600 14px/1.4 var(--sans); }
/* An attack in "What it does": its name, then its roll. */
#stage .detail-pane .cm-story-rule { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }

/* Words. */
#stage .detail-pane :is(p, li, .pane-text, .cm-ask-intro, .cm-note, .cm-cond-doing li, .cm-calc-what > span,
                        .cm-why-steps em, .cm-about-label) {
  font: var(--pane-text); font-style: normal; color: var(--ink);
}
#stage .detail-pane :is(.cm-ask-intro, .cm-note, .pane-text) { color: var(--ink-dim); }
#stage .detail-pane .cm-note { border-left: 0; padding-left: 0; margin: 0; }
#stage .detail-pane p.cm-about-text { font: var(--pane-lede); color: var(--ink); padding: 16px 0 18px; }   /* p.: outranks the words rule */
#stage .detail-pane p.cm-about-source { font: var(--pane-small); color: var(--ink-dim); border-top: 0; margin-top: 0; padding-top: 14px; }   /* the rule above already ends the text */
/* The record's slug in the pack: the formula face, as a reference. */
#stage .detail-pane .cm-about-source .pane-ref { font: 400 var(--fs-small)/1.4 var(--mono); color: var(--ink-dim); }

/* Numbers and expressions: the formula face, everywhere. */
#stage .detail-pane :is(pre, code, .cm-calc-formula, .cm-calc-reads, .cm-calc-gate, .cm-about-formula,
                        .cm-why-input, .pane-num, .cm-cond-level-h, .cm-why-steps li > strong) {
  font: var(--pane-formula); color: var(--ink); letter-spacing: 0;
}
#stage .detail-pane .cm-calc-reads { color: var(--ink-dim); }

/* THE BREAKDOWN: the number, then what it is, then — across the whole
   pane — the formula that made it. */
/* One column of numbers for the whole list, as wide as its widest
   ("at least 13"), so they line up without a fixed width cutting one. */
#stage .detail-pane .cm-why-steps { display: grid; grid-template-columns: minmax(36px, max-content) minmax(0, 1fr); column-gap: 12px; }
#stage .detail-pane .cm-why-steps > li {
  grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; row-gap: 6px; align-items: baseline; padding: 8px 0;
}
#stage .detail-pane .cm-why-steps li > strong { font-size: 14px; text-align: right; white-space: nowrap; }
#stage .detail-pane .cm-why-state { margin-top: 8px; padding-top: 16px; display: grid; gap: 8px; }
#stage .detail-pane .cm-calc-what { display: contents; }
#stage .detail-pane .cm-calc-what > * { grid-column: 2; }
/* THE FORMULA IS A REFERENCE, NOT THE ANSWER: small, grey, unboxed,
   under the words it explains and level with them. The number on the
   left is what somebody came for. */
#stage .detail-pane .cm-calc-code { display: grid; gap: 2px; padding: 0; border: 0; background: none; }
#stage .detail-pane .cm-calc-code :is(.cm-calc-formula, .cm-calc-reads) {
  font-size: var(--fs-small); line-height: 1.45; color: var(--ink-dim); padding: 0; border: 0;
}

/* A BOX TO TYPE IN LOOKS LIKE ONE: a lit edge and a ground a step off
   the pane's, ink when there is something in it. */
#stage .detail-pane :is(input:not([type='checkbox'], [type='radio']), select, textarea) {
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  border: 1px solid var(--edge); color: var(--ink);
}
/* A PLACEHOLDER IS NOT AN ANSWER — dimmer than any typed text, and in
   italic, so a box with a suggestion in it never reads as filled in. */
#stage :is(input, textarea)::placeholder { color: var(--placeholder); font-style: italic; opacity: 1; }
#stage .detail-pane :is(input, textarea)::placeholder { color: var(--placeholder); font-family: var(--sans); }
#stage .detail-pane :is(input, select, textarea):hover { border-color: color-mix(in srgb, var(--ink) 45%, transparent); }
#stage .detail-pane :is(input, select, textarea):focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
#stage .detail-pane .cm-why-steps li[data-part='total'] > strong,
#stage .detail-pane .cm-why-steps li[data-part='stated'] > strong { color: var(--brass); font-weight: 600; }

/* A pane's own sections (a rest, an action): a heading, then its body. */
#stage .detail-pane .pane-secs { display: grid; gap: 0; }
#stage .detail-pane .pane-sec { display: grid; gap: 10px; padding: 16px 0; border-top: 1px solid var(--chrome-rule, var(--rule)); }
#stage .detail-pane .pane-sec:first-child { border-top: 0; }
#stage .detail-pane .pane-sec > p { margin: 0; }
#stage .detail-pane .pane-list { margin: 0; padding: 0 0 0 18px; display: grid; gap: 4px; }
#stage .detail-pane .pane-list > li:has(> .pane-num) { list-style: none; margin-left: -18px; display: flex; justify-content: space-between; gap: 12px; }
#stage .detail-pane .pane-item { display: grid; gap: 4px; }
#stage .detail-pane .pane-item + .pane-item { margin-top: 8px; }
#stage .detail-pane .pane-item-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
#stage .detail-pane .pane-item-head > strong { font: 600 14px/1.4 var(--sans); color: var(--ink); }
#stage .detail-pane .pane-item > .pane-text { margin: 0; }
#stage .detail-pane .cm-about-facts dd .rule-tag { vertical-align: baseline; }

/* A FOLDING SECTION OF THE PANE (character view `paneFold`): its heading
   is the control, with a chevron that turns. */
#stage .detail-pane .pane-fold { border-top: 1px solid var(--chrome-rule, var(--rule)); padding: 4px 0 8px; }
#stage .detail-pane .pane-fold-head {
  display: flex; align-items: center; gap: 8px; padding: 12px 0; cursor: pointer; list-style: none;
}
#stage .detail-pane .pane-fold-head::-webkit-details-marker { display: none; }
#stage .detail-pane .pane-fold-head::before {
  content: ''; width: 6px; height: 6px; margin: 0 2px; flex: none;
  border-right: 1.5px solid var(--ink-dim); border-bottom: 1.5px solid var(--ink-dim);
  transform: rotate(-45deg); transition: transform .12s;
}
#stage .detail-pane .pane-fold[open] > .pane-fold-head::before { transform: rotate(45deg); }
#stage .detail-pane .pane-fold-head:hover .pane-h { color: var(--ink); }
#stage .detail-pane .pane-fold-body { display: grid; gap: 8px; }

/* ------------------------------------------------------------
   16. HEADINGS — a title, a section, a group. Three levels under the
   banner; a page that needs a fourth is too deep. h2 → h3 → h4.
   ------------------------------------------------------------ */
#stage .page-head { display: grid; gap: var(--sp-1); margin: 0 0 var(--sp-5); }
#stage .page-head > .head-row { display: flex; align-items: center; gap: var(--sp-2); }
#stage .page-title { margin: 0; font: 600 var(--fs-title)/1.1 var(--display); letter-spacing: -.02em; color: var(--ink); }
#stage .page-sub { margin: 0; font: 400 var(--fs-lede)/1.4 var(--serif); color: var(--ink-dim); }

/* A section: 24 above, a rule, 16, then its heading. The first one
   under the page head has no rule — the subtitle already ends it. */
#stage .section { margin: var(--sp-5) 0 0; padding-top: var(--sp-4); border-top: 1px solid var(--rule); }
#stage .page-head + .section, #stage .section.first { margin-top: 0; padding-top: 0; border-top: 0; }
#stage .section-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); min-height: var(--h-line); margin: 0 0 var(--sp-3); }
#stage .section-head > h3 { display: block; margin: 0; font: 600 var(--fs-h3)/1.2 var(--display); letter-spacing: -.01em; text-transform: none; color: var(--ink); text-box: trim-both cap alphabetic; }
#stage .section-head > .grow { flex: 1 1 0; }
/* In a stack that spaces its children with a gap, the gap is the 12
   under the heading; the heading adds none of its own. */
#stage :is(.cm-arc-block:not(.cm-creation), .cm-startgear, .cm-inv-sec, .cm-abilitygen) > .section-head { margin-bottom: 0; }
/* A note on the heading's line: what this section is about, in small. */
#stage .section-head > .head-note { display: inline-flex; align-items: center; gap: 6px; font: 400 var(--fs-small)/1.3 var(--sans); color: var(--ink-dim); }
/* Every section is ruled above, except the first under the page head. */
#stage .section-head { margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--rule); }
/* A field that belongs to the page head (a moment's name) already
   leaves 24 under it; the section after it does not add its own. */
#stage .page-head + .field + * > .section-head:first-child { margin-top: 0; }
#stage :is(.section-head.first, .page-head + .section-head, .page-head + * > .section-head:first-child, .section > .section-head) { margin-top: 0; padding-top: 0; border-top: 0; }
/* A section whose first content is another section (Level 1, then its
   Ability scores) is a heading over a cluster: one rule, not two. */
#stage .section-head + * > .section-head:first-child { margin-top: var(--sp-4); padding-top: 0; border-top: 0; }
/* A SUB-SECTION: a part of the section above (Level 1 → its ability
   scores, its choices, its gear). An h4 at the record-name size, so it
   reads as under the h3; the first one sits 12 under its section's
   heading like any content does, the rest are ruled like sections. */
#stage .section-head.sub { min-height: var(--h-compact); }
#stage .section-head.sub > h4 { display: block; margin: 0; font: 600 var(--fs-name)/1.25 var(--display); letter-spacing: 0; text-transform: none; color: var(--ink); }
#stage .section-head + * > .section-head.sub:first-child { margin-top: 0; }
/* A SECTION THAT FOLDS (a record's sections): the h3 is the button, the
   band's chevron before the words. Same size, same rule above. */
#stage .section-head.fold > h3 > button {
  all: unset; box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer;
  font: inherit; color: inherit;
}
#stage .section-head.fold > h3 > button::before { content: ''; flex: none; width: 7px; height: 7px; margin: -4px 2px 0; border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); transition: transform .12s; }
#stage .section-head.fold > h3 > button[aria-expanded='false']::before { transform: rotate(-45deg); margin-top: 0; }
#stage .section-head.fold > h3 > button:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
#stage .section-head.fold > h3 > button:hover { color: var(--ink); }
/* A record's sections are siblings in a column: the first is unruled. */
#stage .cm-pane-sec:first-child > .section-head { margin-top: 0; padding-top: 0; border-top: 0; }
/* A hint sits 4 under the field it explains, not a group away. */
#stage .cm-pane-body > .field-hint { margin-top: calc(var(--sp-1) - var(--sp-4)); }
#stage .cm-pane-sec[data-shut='true'] > .section-head { margin-bottom: 0; }

/* A group: the caps label over tiles, rows, a field, a column. */
#stage .group-label, .menu-panel .group-label {
  /* 16 from the group before, 4 to its own content: compact, and
     still plainly nearer what it labels than what came before. */
  display: block; margin: var(--sp-4) 0 var(--sp-1);
  font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim);
}
#stage :is(.page-head, .section-head) + .group-label, #stage .group-label:first-child, #stage .group-label.flush { margin-top: 0; }
/* In a stack that spaces its children 4 apart, the gap is the 4. */
#stage :is(.cm-arc-body, .cm-prompt) > .group-label { margin-bottom: 0; }

/* The ? after a heading: a round glyph button, 24 so it is a target. */
#stage .help {
  flex: none; display: inline-grid; place-items: center; width: 24px; height: 24px; min-height: 0; padding: 0; margin: 0; box-sizing: border-box;
  font: 600 12px/1 var(--sans); color: var(--ink-dim); cursor: pointer;
  background: transparent; border: 1px solid var(--rule); border-radius: var(--r-pill);
}
#stage .help:hover { color: var(--action-2); border-color: var(--action-2); }
#stage .help[aria-pressed='true'] { color: var(--board); background: var(--ink); border-color: var(--ink); }

/* ------------------------------------------------------------
   17. FIELD — one box to type in: text, number, select, search, a long
   answer, the rich-text editor's frame. Sans 14, 36 tall.
   ------------------------------------------------------------ */
:is(#stage, .gate) .field {
  height: var(--h-line); width: 100%; min-width: 0; box-sizing: border-box; margin: 0; padding: 0 var(--sp-3);
  font: 400 var(--fs-body)/1.3 var(--sans); color: var(--ink);
  background: color-mix(in srgb, var(--card) 80%, transparent);
  border: 1px solid var(--edge); border-radius: var(--control-radius); box-shadow: none;
}
:is(#stage, .gate) .field:hover { border-color: color-mix(in srgb, var(--ink) 35%, var(--edge)); }
:is(#stage, .gate) :is(.field:focus-visible, .field:focus-within, .field.focused) { outline: 2px solid var(--focus); outline-offset: 1px; }
#stage .field.compact { height: var(--h-compact); padding: 0 var(--sp-2); font-size: var(--fs-small); }
#stage .field.number { text-align: right; font-variant-numeric: tabular-nums; }
#stage .field.number.centre { text-align: center; }
#stage :is(textarea.field, .field.area) { height: auto; min-height: 88px; padding: var(--sp-2) var(--sp-3); resize: vertical; line-height: 1.5; }
#stage select.field {
  appearance: none; padding-right: 32px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-dim) 50%), linear-gradient(135deg, var(--ink-dim) 50%, transparent 50%);
  background-position: calc(100% - 17px) 55%, calc(100% - 12px) 55%; background-size: 5px 5px; background-repeat: no-repeat;
  background-color: color-mix(in srgb, var(--card) 80%, transparent);
}
/* A BOX THAT TAKES PROSE (ui/src/richtext.js): the field's edge, fill
   and ring, and its toolbar only while somebody is writing in it — or
   while one of its menus is open (they hang off the page, not the box). */
#stage .field-rich {
  /* clip, not hidden: hidden makes this a scroll box, and the toolbar
     would stick to it instead of to the page being scrolled. */
  display: grid; box-sizing: border-box; min-width: 0; overflow: clip;
  /* Opaque: a live editor repaints on every caret blink, and seen-through
     each repaint redrew the page's noise under it (Safari). */
  background: var(--card);
  border: 1px solid var(--edge); border-radius: var(--control-radius);
}
#stage .field-rich:hover { border-color: color-mix(in srgb, var(--ink) 35%, var(--edge)); }
#stage .field-rich:focus-within { outline: 2px solid var(--focus); outline-offset: 1px; }
/* The toolbar stays at the top of whatever is scrolling, so a change
   three screens down the text is still one press away. Opaque, so the
   text scrolls under it rather than through it. */
#stage .field-rich > .rt-bar {
  position: sticky; top: calc(-1 * var(--pane-top, 0px)); z-index: 3;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1); padding: var(--sp-1) var(--sp-2);
  border-bottom: 1px solid var(--rule); background: var(--card);
}
#stage .field-rich:not(:focus-within):not(:has([aria-expanded='true'])) > .rt-bar { display: none; }
#stage .field-rich :is(.ProseMirror, .rt-doc) { min-height: 64px; padding: var(--sp-2) var(--sp-3); font: 400 var(--fs-lede)/1.5 var(--serif); color: var(--ink); outline: none; }
#stage .field-rich .rt-tool { min-width: var(--h-compact); padding: 0 var(--sp-2); }
#stage .field-rich .rt-tool[aria-pressed='true'] { color: var(--board); background: var(--ink); border-color: var(--ink); }
#stage .field-rich .rt-sep { width: 1px; height: 18px; margin: 0 var(--sp-1); background: var(--rule); }
/* A PRIVATE PART (ui/visibility.js, core/audience.js): a band round
   its paragraphs, told apart by its edge and its words, never by
   colour alone — Player only a dark rule, GM only the private box's
   dashed frame. Revealed (public since a moment) has no band at all.
   Its switch on its top edge. */
#stage .rt-vis { margin: var(--sp-2) 0; padding: var(--sp-1) var(--sp-3) var(--sp-2); border-left: 3px solid var(--rule); }
/* REVEALED READS AS ORDINARY WRITING: no band, no inset, and its
   switch only while the field is being written in, or the part is
   pointed at in read-only writing — so it can be hidden again. */
#stage .rt-vis[data-now='public'] { border: 0; padding: 0; background: none; }
#stage .field-rich:not(:focus-within) .rt-vis[data-now='public'] > .rt-vis-head,
#stage .rt-doc .rt-vis[data-now='public']:not(:hover, :focus-within) > .rt-vis-head,
#stage .rt-vis[data-now='public']:not(:has(.rt-vis-btn)) > .rt-vis-head { display: none; }
#stage .rt-vis[data-now='player'] { border-left-color: var(--ink-dim); }
#stage .rt-vis[data-now='gm'] {
  border: 1px dashed color-mix(in srgb, var(--ink) 35%, var(--rule)); border-left-width: 3px;
  background: color-mix(in srgb, var(--ink) 3%, transparent);
}
#stage .rt-vis-head { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); user-select: none; }
#stage .rt-vis-seg { display: inline-flex; border: 1px solid var(--edge); border-radius: var(--control-radius); overflow: hidden; }
#stage .rt-vis-btn {
  all: unset; box-sizing: border-box; height: 24px; padding: 0 var(--sp-2); cursor: pointer;
  font: 600 var(--fs-label)/24px var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim);
}
#stage .rt-vis-btn + .rt-vis-btn { border-left: 1px solid var(--edge); }
#stage .rt-vis-btn:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }
#stage .rt-vis-btn[aria-checked='true'] { color: var(--board); background: var(--ink); }
#stage .rt-vis-btn:focus-visible, #stage .rt-vis-x:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
#stage .rt-vis-x { all: unset; display: grid; place-items: center; width: 24px; height: 24px; border-radius: var(--r-pill); cursor: pointer; font: 400 16px/1 var(--sans); color: var(--ink-dim); }
#stage .rt-vis-x:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 8%, transparent); }
/* Until the field is being written in, a section says only who reads it. */
/* Quiet until wanted: in a field until you write in it, and in
   read-only writing (an earlier moment's, read from a later one,
   where a reveal is made) until you point at or tab into the part. */
#stage .field-rich:not(:focus-within) .rt-vis-btn[aria-checked='false'],
#stage .field-rich:not(:focus-within) .rt-vis-x,
#stage .rt-doc .rt-vis:not(:hover, :focus-within) .rt-vis-btn[aria-checked='false'],
#stage .rt-doc .rt-vis:not(:hover, :focus-within) .rt-vis-x { display: none; }
#stage :is(.field-rich:not(:focus-within), .rt-doc .rt-vis:not(:hover, :focus-within)) .rt-vis-seg { border-color: transparent; }
#stage :is(.field-rich:not(:focus-within), .rt-doc .rt-vis:not(:hover, :focus-within)) .rt-vis-btn[aria-checked='true'] { color: var(--ink-dim); background: transparent; padding: 0; }
#stage .rt-vis-label { font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim); }
#stage .rt-vis > :is(p, ul, ol, blockquote, h1, h2, h3):last-child { margin-bottom: 0; }
/* A FIELD THAT TAKES SECTIONS says, while it is being written in, who
   reads what is outside them. */
#stage .field-rich[data-audience]:focus-within::after {
  display: block; padding: 0 var(--sp-3) var(--sp-2); font: 400 var(--fs-small)/1.4 var(--sans); color: var(--ink-dim);
}
#stage .field-rich[data-audience='public']:focus-within::after { content: 'The table can read this. Private keeps a part to the player and you, or to you.'; }
#stage .field-rich[data-audience='gm']:focus-within::after { content: 'Only you read this.'; }
#stage .field-wrap { display: grid; gap: var(--sp-1); min-width: 0; }   /* a label sits 4 over its value */
#stage .field-wrap > .group-label { margin: 0; }
/* A FIELD THAT OPENS A PICKER (who has it, which place): the field's
   box with its value on the left and an ellipsis — there is more
   behind it than a dropdown holds. */
#stage button.field.field-choose { display: flex; align-items: center; gap: var(--sp-2); text-align: left; cursor: pointer; }
#stage button.field.field-choose::after { content: '\2026'; margin-left: auto; color: var(--ink-dim); }
#stage button.field.field-choose:disabled { color: var(--ink-dim); cursor: default; }
/* A control with its unit or size beside it ('lb', '5 by 5 ft.'). */
#stage .field-line { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
#stage .field-line > .field { flex: 1 1 auto; min-width: 0; }
#stage .field-line > .field-hint { flex: none; }
#stage .field-hint[data-warn='true'] { color: var(--oxblood); }
/* A VALUE UNDER A LABEL that is read, not typed (what a level brings):
   the field's words without the box — ink, not the hint's grey, so it
   does not read as an explanation. */
#stage .field-value { margin: 0; font: 400 var(--fs-body)/1.45 var(--sans); color: var(--ink); }
#stage .field-hint { margin: 0; font: 400 var(--fs-small)/1.45 var(--sans); color: var(--ink-dim); }
/* THE SIX ABILITIES — one grid for every way of setting them (point
   buy's steppers, the array's and a roll's selects, typed boxes, an
   improvement's +1s). A label over each, the control under it, and the
   number the sheet uses beside the label. It measures its own column,
   not the window: six across, then three, then two. */
#stage .ability-grid { container: abilities / inline-size; }
#stage .ability-entry { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--sp-3) var(--sp-2); }
/* The steps are where six, then three, smallest steppers stop fitting. */
@container abilities (max-width: 700px) { #stage .ability-entry { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@container abilities (max-width: 345px) { #stage .ability-entry { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
#stage .ability-cell-head { display: flex; align-items: baseline; gap: var(--sp-2); min-width: 0; }
#stage .ability-cell-head > .group-label { margin: 0; }
#stage .ability-cell-head > .cm-ability-final { font: 600 var(--fs-label)/1.3 var(--sans); font-style: normal; color: var(--ink); font-variant-numeric: tabular-nums; }
/* A STEPPER: a number that moves one at a time inside a legal range
   (point buy). The field's frame, split three ways: − value +. */
#stage .stepper {
  /* The number gives up its room first; at three equal segments the
     stepper stops shrinking (108 + its edge). */
  display: grid; grid-template-columns: var(--h-line) minmax(var(--h-line), 1fr) var(--h-line); height: var(--h-line); min-width: calc(3 * var(--h-line) + 2px); box-sizing: border-box;
  background: color-mix(in srgb, var(--card) 80%, transparent); border: 1px solid var(--edge); border-radius: var(--control-radius); overflow: hidden;
}
#stage .stepper > button {
  all: unset; box-sizing: border-box; display: grid; place-items: center; cursor: pointer;
  font: 400 var(--fs-nav)/1 var(--sans); color: var(--ink-dim);
}
#stage .stepper > button:hover:not(:disabled) { color: var(--ink); background: color-mix(in srgb, var(--ink) 7%, transparent); }
#stage .stepper > button:disabled { opacity: .3; cursor: not-allowed; }
#stage .stepper > button:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
#stage .stepper > output {
  display: grid; place-items: center; border-inline: 1px solid color-mix(in srgb, var(--edge) 60%, transparent);
  font: 600 var(--fs-body)/1 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink);
}

/* ------------------------------------------------------------
   18. SEGMENTED CONTROL — one answer from a few: one rounded frame,
   hairlines between, the chosen one filled with ink. Variants: compact
   (in a row), icons (a view switch), scale (equal columns), grid (rows).
   Buttons carry aria-pressed, or role=radio with aria-checked.
   ------------------------------------------------------------ */
#stage .seg {
  display: inline-flex; justify-self: start; width: fit-content; height: var(--h-line); max-width: 100%; box-sizing: border-box; margin: 0; padding: 0; gap: 0;
  border: 1px solid var(--edge); border-radius: var(--control-radius); overflow: hidden;
  background: color-mix(in srgb, var(--card) 60%, transparent);
}
#stage .seg > :is(button, summary, details > summary) {
  all: unset; box-sizing: border-box; display: inline-grid; grid-auto-flow: column; align-items: center; justify-content: center; gap: 4px;
  height: 100%; padding: 0 var(--sp-3); cursor: pointer;
  font: 400 var(--fs-small)/1.2 var(--sans); color: var(--ink-dim); text-align: center; white-space: nowrap;
}
#stage .seg > details { display: flex; position: relative; }
#stage .seg > details > summary {
  all: unset; box-sizing: border-box; display: inline-flex; align-items: center; gap: 4px; height: 100%; padding: 0 var(--sp-3); cursor: pointer;
  font: 400 var(--fs-small)/1.2 var(--sans); color: var(--ink-dim);
}
#stage .seg > details > summary::-webkit-details-marker { display: none; }
#stage .seg > :is(button, summary, details) + :is(button, summary, details) { border-left: 1px solid var(--rule); }
#stage .seg > :is(button, details > summary):hover:not([aria-pressed='true'], [aria-checked='true']),
#stage .seg > details > summary:hover:not([aria-pressed='true']) { color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, transparent); }
#stage .seg > :is([aria-pressed='true'], [aria-checked='true']),
#stage .seg > details > summary[aria-pressed='true'] { color: var(--board); background: var(--ink); }
#stage .seg > :is([aria-pressed='true'], [aria-checked='true']) + :is(button, summary, details) { border-left-color: var(--ink); }
#stage .seg > :is(button, summary):focus-visible, #stage .seg > details > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; }
#stage .seg svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
#stage .seg:has(> details[open]) { overflow: visible; }
#stage .seg.compact { height: var(--h-compact); }
#stage .seg.compact > :is(button, summary), #stage .seg.compact > details > summary { font-weight: 500; }
/* NARROWER THAN ITS WORDS: the choices share the width and wrap to a
   second line rather than running off the edge. At full width nothing
   changes — they only shrink when the column makes them. */
#stage .seg:not(.icons, .scale, .grid) { height: auto; min-height: var(--h-line); }
#stage .seg.compact:not(.icons, .scale, .grid) { min-height: var(--h-compact); }
#stage .seg:not(.icons, .scale, .grid) > button { flex: 0 1 auto; min-width: 0; height: auto; padding-block: var(--sp-1); white-space: normal; }
#stage .seg.icons > :is(button, summary) { width: var(--h-line); padding: 0; }
/* A scale: equal columns, so the middle is where the middle is. */
#stage .seg.scale { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); width: 100%; }
#stage .seg.scale > button { min-width: 0; padding: 0 var(--sp-1); white-space: normal; line-height: 1.1; hyphens: auto; -webkit-hyphens: auto; }   /* narrow: "Exact-ing", not "Exa" */
/* A grid: the same control in rows (an alignment). */
#stage .seg.grid { display: grid; grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); height: auto; width: 100%; }
#stage .seg.grid > :is(button, summary, details) + :is(button, summary, details) { border-left: 0; }
#stage .seg.grid > button { height: var(--h-line); position: relative; }
#stage .seg.grid > button:not(:nth-child(3n)) { border-right: 1px solid var(--rule); }
#stage .seg.grid > button:not(:nth-last-child(-n + 3)) { border-bottom: 1px solid var(--rule); }
/* Where something usually sits: a dot in the corner — a mark, never a fence. */
#stage .seg.grid > [data-fits='true']::after { content: ''; position: absolute; top: 6px; right: 6px; width: 6px; height: 6px; border-radius: var(--r-pill); background: var(--accent); }
#stage .seg.grid > [data-fits='true'][aria-pressed='true']::after { background: var(--board); }
@media (max-width: 560px) {
  #stage .seg.scale { height: auto; min-height: 40px; }
  #stage .seg.scale > button { font-size: 12px; padding: 4px 2px; }
}

/* ------------------------------------------------------------
   19. CHOICE — one of many, picked or not: a set, a skill to take, a
   filter, Prepared. Always 28, always round, ink when on.
   ------------------------------------------------------------ */
#stage .choices { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
#stage .choice {
  display: inline-flex; align-items: center; gap: 6px; height: var(--h-compact); margin: 0; padding: 0 var(--sp-3); box-sizing: border-box; cursor: pointer;
  font: 400 var(--fs-small)/1 var(--sans); letter-spacing: 0; text-transform: none; color: var(--ink); white-space: nowrap;
  background: color-mix(in srgb, var(--card) 60%, transparent); border: 1px solid var(--rule); border-radius: var(--r-pill); box-shadow: none;
}
#stage .choice:hover:not(:disabled, [aria-pressed='true'], [aria-checked='true']) { border-color: color-mix(in srgb, var(--ink) 40%, var(--rule)); }
/* A SWITCH: one yes/no about a record ("The table may know"). A track
   that fills with ink when on, and its words in the body voice beside
   it — ink, never gold, because it chooses; it does not do. */
#stage .switch {
  all: unset; box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--h-compact); cursor: pointer;
  font: 400 var(--fs-body)/1.3 var(--sans); color: var(--ink);
}
#stage .switch-track { position: relative; flex: none; width: 32px; height: 18px; border-radius: var(--r-pill); border: 1px solid var(--edge); background: color-mix(in srgb, var(--card) 80%, transparent); box-sizing: border-box; transition: background .12s; }
#stage .switch-dot { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--ink-dim); transition: transform .12s; }
#stage .switch[aria-checked='true'] .switch-track { background: var(--ink); border-color: var(--ink); }
#stage .switch[aria-checked='true'] .switch-dot { transform: translateX(14px); background: var(--board); }
#stage .switch:focus-visible .switch-track { outline: 2px solid var(--focus); outline-offset: 2px; }
#stage .switch:disabled { opacity: .45; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) { #stage :is(.switch-track, .switch-dot) { transition: none; } }
/* A rarity chip carries its rung's colour on its word until it is on. */
#stage .choice[data-rank]:not([aria-pressed='true']) > span:first-child { color: var(--rarity); }
#stage .choice:is([aria-pressed='true'], [aria-checked='true']) { color: var(--board); background: var(--ink); border-color: var(--ink); }
#stage .choice:is([aria-pressed='true'], [aria-checked='true']) > .count { color: var(--board); background: color-mix(in srgb, var(--board) 18%, transparent); }
#stage .choice:disabled { opacity: .45; cursor: not-allowed; }
#stage .choice[data-changed='true'] { border-style: dashed; }
/* A CHOICE THAT NEEDS A SENTENCE (a class to take a level in, a kind of
   moment): the same part as a card — its name, what is true of it, one
   line on what it is. Rounded, because it is pressed; ink when on. */
#stage .choice-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
#stage .choice-card {
  display: grid; align-content: start; gap: var(--sp-1); margin: 0; padding: var(--sp-3) var(--sp-4); box-sizing: border-box; cursor: pointer; text-align: left;
  font: 400 var(--fs-body)/1.4 var(--sans); color: var(--ink);
  background: color-mix(in srgb, var(--card) 60%, transparent); border: 1px solid var(--rule); border-radius: var(--control-radius); box-shadow: none;
}
#stage .choice-card > strong { font: 600 var(--fs-body)/1.3 var(--sans); color: var(--ink); }
#stage .choice-card > :is(span, em) { font: 400 var(--fs-small)/1.45 var(--sans); font-style: normal; color: var(--ink-dim); }
#stage .choice-card:hover:not(:disabled, [aria-pressed='true']) { border-color: color-mix(in srgb, var(--ink) 40%, var(--rule)); }
#stage .choice-card:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
#stage .choice-card[aria-pressed='true'] { background: var(--ink); border-color: var(--ink); }
#stage .choice-card[aria-pressed='true'] > * { color: var(--board); }
#stage .choice-card:disabled { opacity: .45; cursor: not-allowed; }

/* ------------------------------------------------------------
   20. COUNT — a number beside a label. One shape. `.alert` is owed.
   ------------------------------------------------------------ */
#stage .count, .sb-head .count, .world-nav .count {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  font: 600 var(--fs-label)/1 var(--sans); font-style: normal; letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums;
  color: var(--ink); background: color-mix(in srgb, var(--ink) 9%, transparent); border-radius: var(--r-pill);
}
#stage .count.alert { color: var(--alert-ink); background: var(--oxblood); }
/* A METER: how much of a budget is used — points, a pool. The words
   carry the numbers; the bar is the glance. Ink, because it is a
   reading; oxblood, with the words saying why, when it is over. */
#stage .meter { display: grid; gap: 6px; margin: 0; }
#stage .meter-text { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-2); font: 400 var(--fs-body)/1.3 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink); }
#stage .meter-text > strong { font-weight: 600; }
#stage .meter-bar { display: block; height: 4px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--ink) 12%, transparent); overflow: hidden; }
#stage .meter-fill { display: block; height: 100%; border-radius: inherit; background: var(--ink); }
#stage .meter[data-over='true'] .meter-text { color: var(--oxblood); }
#stage .meter[data-over='true'] .meter-fill { background: var(--oxblood); }

/* ------------------------------------------------------------
   21. BAND — the folding heading over a group of rows: CANTRIPS 3,
   ACTION 20, EQUIPPED 1. A button (aria-expanded); filled in a table.
   ------------------------------------------------------------ */
#stage .band {
  all: unset; box-sizing: border-box; display: flex; align-items: center; gap: var(--sp-2); width: 100%; min-height: 32px;
  padding: var(--sp-1) 0; margin: var(--sp-4) 0 var(--sp-2); cursor: pointer; border-bottom: 1px solid var(--rule);
  font: 600 var(--fs-label)/1.2 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink);
}
#stage .band::before { content: ''; flex: none; width: 6px; height: 6px; margin: -3px 2px 0; border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); transition: transform .12s; }
#stage .band[aria-expanded='false']::before { transform: rotate(-45deg); margin-top: 0; }
#stage .band-h { margin: 0; padding: 0; font: inherit; }
/* The group's total at the far end — '26 lb', '7 creatures': a fact,
   in the facts voice, not the band's caps. */
#stage .band > .band-name { min-width: 0; overflow-wrap: anywhere; }
#stage .band > .band-note { margin-left: auto; padding-left: var(--sp-3); font: 400 var(--fs-small)/1.2 var(--sans); letter-spacing: 0; text-transform: none; color: var(--ink-dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A rarity heading is on the ladder: colour is data. */
#stage .band[data-rank] > .band-name { color: var(--rarity); }
#stage :is(.band:first-child, .band-h:first-child > .band) { margin-top: 0; }
/* A shut fold draws no rule: there is nothing under it to divide off,
   and the next section's own rule would make two. */
#stage details:not([open]) > summary.band { border-bottom-color: transparent; }
#stage .band:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
#stage .frame > .band, #stage .in-frame > .band, #stage .band.in-frame { margin: 0; padding: var(--sp-2) var(--sp-4); background: color-mix(in srgb, var(--ink) 5%, transparent); border-bottom-color: color-mix(in srgb, var(--rule) 70%, transparent); }

/* ------------------------------------------------------------
   22. PANEL — a box of content that is not a stat: an offer, a note,
   a line to read aloud, what only the GM sees. A frame with one padding.
   ------------------------------------------------------------ */
#stage .panel { display: grid; gap: var(--sp-2); padding: var(--sp-4); align-content: start; }
#stage .panel > .group-label { margin: 0; }
/* What the panel is about, as a figure: ink, the body size. */
#stage .panel-value { font: 600 var(--fs-body)/1.3 var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
#stage .panel.private { border-style: dashed; border-color: color-mix(in srgb, var(--ink) 35%, var(--rule)); background: color-mix(in srgb, var(--ink) 3%, transparent); box-shadow: none; }
#stage .private-mark {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 var(--fs-label)/1.3 var(--caps); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--ink-dim);
}
#stage .private-mark svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.6; }
#stage .panel.cue { border-left: 3px solid var(--accent); }
#stage .panel.cue p { margin: 0; font: 500 19px/1.4 var(--display); color: var(--ink); }

/* ------------------------------------------------------------
   23. MENU — what ⋯, Filters, Stow and the banner open. One paper.
   ------------------------------------------------------------ */
#stage .menu {
  display: grid; min-width: 200px; padding: 6px; box-sizing: border-box; z-index: 8;
  background: var(--card); border: 1px solid var(--rule); box-shadow: 0 10px 26px color-mix(in srgb, var(--shadow) 20%, transparent);
}
#stage .menu > .group-label { margin: var(--sp-1) 10px 6px; }
/* A menu off a ⋯ or a switch hangs from it. */
#stage details:not(.cm-lb-wide, .cm-lb-menu):has(> .menu) { position: relative; }
#stage details:not(.cm-lb-menu) > .menu { position: absolute; top: calc(100% + 4px); right: 0; white-space: nowrap; }
:is(#stage, .menu-panel) .menu-item {
  all: unset; box-sizing: border-box; display: flex; align-items: center; gap: 10px; min-height: var(--h-line); padding: 0 10px; cursor: pointer;
  font: 400 var(--fs-body)/1.2 var(--sans); color: var(--ink); border-radius: calc(var(--control-radius) - 2px);
}
:is(#stage, .menu-panel) .menu-item:hover , :is(#stage, .menu-panel) .menu-item:focus-visible { background: color-mix(in srgb, var(--ink) 7%, transparent); }
:is(#stage, .menu-panel) .menu-item:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
:is(#stage, .menu-panel) .menu-item > em { margin-left: auto; padding-left: var(--sp-3); font: 600 var(--fs-label) var(--sans); font-style: normal; color: var(--ink-dim); }
:is(#stage, .menu-panel) .menu-item.danger { color: var(--oxblood); margin-top: var(--sp-1); border-top: 1px solid var(--rule); border-radius: 0; }
:is(#stage, .menu-panel) .menu-item:is([role='menuitemcheckbox'] , [role='menuitemradio'])::before {
  content: ''; flex: none; width: 14px; height: 14px; box-sizing: border-box; border-radius: 3px;
  border: 1.5px solid var(--edge-check);
}
:is(#stage, .menu-panel) .menu-item[role='menuitemradio']::before { border-radius: 50%; }
:is(#stage, .menu-panel) .menu-item[role='menuitemcheckbox'][aria-checked='true']::before {
  border-color: var(--ink);
  background: var(--ink) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3 7.2l2.6 2.6L11 4.4' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
}
:is(#stage, .menu-panel) .menu-item[role='menuitemradio'][aria-checked='true']::before { border-color: var(--ink); background: radial-gradient(circle, var(--board) 0 3px, var(--ink) 3.5px); }

/* ------------------------------------------------------------
   24. EMPTY NOTE — nothing here yet. A state, not an instruction.
   ------------------------------------------------------------ */
#stage .empty-note { margin: 0; font: italic 400 var(--fs-lede)/1.45 var(--serif); color: var(--ink-dim); }

/* ------------------------------------------------------------
   25. SKIP LINK — the first thing Tab reaches (2.4.1, Level A).
   ------------------------------------------------------------ */
.skip-link {
  position: fixed; left: 12px; top: -64px; z-index: 1000; padding: 10px 16px;
  font: 500 14px/1 var(--sans); color: var(--action-ink); background: var(--action); border-radius: var(--control-radius); text-decoration: none;
}
.skip-link:focus { top: 12px; outline: 2px solid var(--chrome-ink); outline-offset: 2px; }
/* Where it lands: focusable so the next Tab starts there, never ringed. */
#stageMain:focus { outline: none; }

/* ------------------------------------------------------------
   26. CONTRAST — Level A by default, AA as a choice.

   The default look is the theme's: low-contrast edges, a bright gold
   ring, meaning carried by colour where that is the prettier answer.
   `data-contrast="aa"` on the root (Settings → Theme → High contrast,
   kept per person, not per universe) turns on WCAG 2.2 AA: every value
   below is DERIVED from the theme's own ink and page, so any theme
   passes by construction. tools/style-guide-check.js measures it.

   Two kinds of rule live here and nowhere else:
     tokens  --edge --edge-check --focus --placeholder --alert-ink
             --tag-ink-mix --mark-moss (default = the aesthetic value)
     cues    information that is colour alone by default gets a
             second channel (a word, a shape) under AA. Search
             "AA CUE" to find every one. NOTE: WCAG 1.4.1 (use of
             colour) is Level A, so a cue here is strictly an A gap in
             the default — move it above this section to close it.
   ------------------------------------------------------------ */
#stage, .sb-head, .world-nav, .menu-panel, .app-header, .gate {
  --edge: var(--rule);
  --edge-check: color-mix(in srgb, var(--ink) 45%, var(--rule));
  --focus: var(--accent);
  --placeholder: color-mix(in srgb, var(--ink-dim) 70%, transparent);
  --alert-ink: #fff;
  --mark-moss: var(--moss);
}
/* The drawer's boxes keep their lit edge by default. */
.sb-head, .world-nav, .menu-panel, .app-header, .gate, #stage .detail-pane {
  --edge: color-mix(in srgb, var(--ink) 28%, transparent);
}

:root[data-contrast='aa'] :is(#stage, .sb-head, .world-nav, .menu-panel, .app-header, .gate, #stage .detail-pane) {
  --edge: color-mix(in srgb, var(--ink) 54%, var(--board));     /* 3:1 on page and card */
  --edge-check: var(--edge);
  --focus: var(--accent-text);                                  /* the gold, until it is 3:1 */
  --placeholder: var(--ink-dim);
  --alert-ink: var(--board);
  --tag-ink-mix: 55%;                                           /* tag words 4.5:1 in every theme */
  --mark-moss: color-mix(in srgb, var(--moss) 72%, var(--ink)); /* 3:1 as a mark */
}
/* On the desk the gold words lift further, for the darkest chrome. */
:root[data-contrast='aa'] :is(.sb-head, .world-nav, .menu-panel, .app-header, #stage .detail-pane) {
  --accent-text: color-mix(in srgb, var(--accent) 55%, var(--chrome-ink));
}
/* "You are here": the bar on the open row is a state mark, so 3:1. */
:root[data-contrast='aa'] #stage .cm-spells .cm-spell[data-selected='true']::before { background: var(--focus); }
/* AA CUE — a value past what the rules give says so in words. */
:root[data-contrast='aa'] #stage .tile[data-over] .cm-field-value::after {
  content: 'over'; margin-left: 6px; vertical-align: middle;
  font: 600 var(--fs-label)/1 var(--sans); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--oxblood);
}
/* AA CUE — a prepared spell says so beside its name, not only a dot. */
:root[data-contrast='aa'] #stage .cm-spell[data-prepared='true'] strong > span::after { content: ' ● prepared'; font: 600 var(--fs-label) var(--sans); letter-spacing: var(--track-caps); text-transform: uppercase; }
