/* The Magus Circle Wiki - theme v2, "THE OPEN GRIMOIRE".
   ===========================================================================
   Owner ruling 2026-08-19 00:55 (pipeline/REQUESTS.md): "can we have a light mode? The
   dark mode is hardly readable... Redesign the full UI please, it doesn't have the
   spirit of the game now". The design direction is the lead's /design canvas
   (design/canvas/make_artboards.py + design/canvas/shots/*.png), which is itself read
   off the game's own screens: raw/theme/steam/shots/ss08.jpg (the open grimoire),
   ss09.jpg (spell crafting: navy list panel + parchment spell card), ss04.jpg (tarot),
   ss12.jpg (credits panel).

   The idea in one line: the site IS the grimoire. A dark desk with a damask cloth holds
   an open book; the reading surface is parchment with brown ink; the chrome around it
   (topbar strip, sidebar panel, tooltips, buttons) is the game's brass-on-navy UI.

   TWO THEMES, ONE STRUCTURE. Light (default) = parchment pages. Dark
   (`html.theme-dark`) = the same layout with the pages on the game's navy PopUpPanel_5
   surface, cream ink and gold links (the DarkSpell artboard). Dark mode changes ONLY the
   page-surface token family and the desk; the navy chrome is identical in both, exactly
   like the artboards. Both themes must pass pipeline/check_contrast.py (run it twice,
   the second time with --dark).

   WHAT IS LOAD-BEARING AND MUST NOT BE RENAMED: every structural class/id hook
   app.js/search.js depends on (see pipeline/assets/README.md's DOM contract) - #mctip,
   .mctip-pin, #mclb, .mcsel, .mcw-*, #dbtable/table.db/.chip/.filters/#flt/#fcount,
   .secnav/.secnav-l, #sidebar/#topbar/#brand/#pagename/#main/#navtoggle/#navbackdrop/
   #searchwrap/#searchbox/#searchresults/#langsel, .card/.cb/.mini/.detailhead/.kv,
   .navgroup/.navgt/.group-body, .sb-*, .colcust-*, .tablewrap/.twshell, .hamb-i, .skip.
   This rewrite changes what they LOOK like, never what they are called.

   No em dashes and no exotic glyphs inside pixel-font contexts anywhere in this file. */

/* ---- Fonts ----------------------------------------------------------------
   The game's own faces (pipeline/extract_theme.py exports them out of the asset bundle;
   docs/recon/04_ui_theme.md section 1) carry every UI role: VeniceClassic for headings,
   table cells, kv rows, nav rows, badges and tooltips; Pixellari for the two plaque
   surfaces; runescape_uf for tiny pixel labels.

   NEW in v2: Alegreya (SIL Open Font License 1.1, licence text shipped next to the file
   at fonts/Alegreya-OFL.txt) for LONG PROSE only - .lead, .md paragraphs, entity
   descriptions, dialogue. VeniceClassic is a display face: it is beautiful in a heading
   and legible in a table cell, and it is tiring over a 76-character paragraph, which is
   most of what the owner reads on /mechanics and /story. Alegreya is a book face with
   the same warm, slightly calligraphic colour, so the two sit together like a real
   printed grimoire (display type on the plates, a text face in the body).
   It is SELF-HOSTED, never a Google Fonts <link>: a stylesheet link would tell
   fonts.googleapis.com the IP of every reader of this wiki. The files are the variable
   (wght 400..900) upright and italic, subset to Latin + Latin Extended-A + punctuation
   (about 62 KB each). Other faces stay declared so nothing 404s if a role changes. */
@font-face {
  font-family: "Alegreya";
  src: url("fonts/Alegreya.woff2") format("woff2-variations"),
       url("fonts/Alegreya.woff2") format("woff2");
  font-weight: 400 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Alegreya";
  src: url("fonts/Alegreya-Italic.woff2") format("woff2-variations"),
       url("fonts/Alegreya-Italic.woff2") format("woff2");
  font-weight: 400 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "VeniceClassic";
  src: url("fonts/VeniceClassic.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "Pixellari";
  src: url("fonts/Pixellari.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "RunescapeUF";
  src: url("fonts/runescape_uf.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "Adventurer";
  src: url("fonts/Adventurer.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "DOSGothic";
  src: url("fonts/DOSGothic.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "NotInter";
  src: url("fonts/NotInter-Regular.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "LiberationSans";
  src: url("fonts/LiberationSans.ttf") format("truetype");
  font-display: swap;
}
/* Declared, not a live default face anywhere below: a CP437 pixel font. Kept restricted
   to printable ASCII, as DS found this exact font family claims accented/box-drawing
   codepoints with junk glyphs - anything outside U+0020-007E falls through. */
@font-face {
  font-family: "PerfectDOSVGA437";
  src: url("fonts/PerfectDOSVGA437.ttf") format("truetype");
  font-display: swap;
  unicode-range: U+0020-007E;
}

/* ---- Tokens ---------------------------------------------------------------
   TOKEN CONTRACT (unchanged from v1 on purpose - page modules write inline styles
   against these names, e.g. pages_tarot.py's .mcw-tt-* and pages_characters.py's
   .abox*): the ":root" set is the DARK NAVY CHROME - --text is cream, --link is gold,
   --panel/--panel-alt are the navy fills. The PAGE SURFACE (parchment in light mode,
   navy panel in dark mode) redeclares --text/--muted/--link/--pos/--neg to their ink
   versions on #book/.card/.detailhead/.mini, so anything nested inside a page that
   reads a token retints itself. A component that hardcodes a hex does not - use a
   token, or add it to the override list at the tail of this file.

   Values come from design/canvas/make_artboards.py's :root, with four foreground
   colours darkened after measuring them against the zebra row (--parchment-shade):
   --muted-ink #6a4a2a -> #5c3d1c (4.89 -> 6.01), --ink-light -> #5c3d1c,
   --pos-ink #2e6b2e -> #1c5c22 (3.97 -> 4.97), --neg-ink #9c2f2f -> #8f2626
   (4.44 -> 5.17). Ratios are WCAG relative luminance, computed, and re-checked by
   pipeline/check_contrast.py on the composed DOM in BOTH themes. */
:root {
  color-scheme: light;

  /* --- the desk the book sits on (dark in both themes: the artboards' "--desk") --- */
  --bg: #17131f;             /* dark plum desk */
  --desk-glow: #2a1d33;      /* soft lamp glow behind the book */

  /* --- navy chrome (topbar, sidebar, tooltips, buttons, plaques) --- */
  --panel: #1c1b33;          /* navy panel interior */
  --panel-alt: #201020;      /* dark plum HUD variant (plaque interior, sigil insets) */
  --panel2: #14111f;         /* one step darker: table head / chip rest state */
  --panel3: #26254a;         /* one step lighter: hover/active fills on navy */
  --border: #b07040;         /* brass frame */
  --border-light: #f0c070;   /* brass highlight, gold text on navy */
  --border-dark: #6a3d1c;    /* dark brass, frame shadow side */
  --gold: var(--border-light);
  --text: #f3e6c8;           /* cream text on navy */
  --muted: #b9ad97;          /* muted cream on navy (7.9:1 on --panel) */
  --label-muted: #c2a06a;    /* small-caps group/facet labels on navy (6.4:1 on --panel) */
  --link: #f0c070;           /* gold link on navy */
  --link-hover: #fff6dd;     /* link hover on navy */
  --pos: #8fe08a;            /* positive delta on navy */
  --neg: #ff9a9a;            /* negative delta on navy */
  --white-outline: #f2ede0;  /* notched box: white pixel outline + its text */

  /* --- the page surface (LIGHT = parchment) --- */
  --parchment: #e9d7b3;
  --chrome-paper: #e2c39f;     /* the page ART's paper (emptyPage.png centre); light-theme topbar + sidebar */
  --parchment-shade: #dcc79c;  /* insets, table hover, card header band */
  --parchment-zebra: #e2d0a8;  /* alternate table rows (between page and shade) */
  --paper-white: #f4e8cc;      /* lightest paper: sheets, kv value strip */
  --text-dark: #3a2816;        /* body ink (9.9:1 on parchment) */
  --ink: var(--text-dark);
  --ink-light: #5c3d1c;        /* secondary ink (6.97:1 on parchment) */
  --muted-ink: #5c3d1c;        /* muted on parchment (6.01:1 on the zebra row) */
  --link-dark: #6b3f14;        /* link on parchment (6.36:1) */
  --link-dark-hover: #3a2816;  /* link hover on parchment: darkens to ink, never to gold */
  --pos-ink: #1c5c22;          /* positive delta on parchment (5.77:1) */
  --neg-ink: #8f2626;          /* negative delta on parchment (6.00:1) */
  --rule: #7c5a34;             /* hairline on parchment */
  --rule-soft: rgba(124, 90, 52, .38);
  --spine: #3a2816;            /* the book's gutter seam */
  --page-shadow: rgba(0, 0, 0, .45);

  /* Colours that sit on ART and therefore never flip with the theme: text printed on a
     brass fill, on a card sprite's baked nameplate, on the skip link. */
  --ink-fixed: #3a2816;
  --cream-fixed: #f3e6c8;
  /* The game's signature "selected" plate: a CREAM tile with ink text (ss09's chosen
     spell row). It is cream in BOTH themes - the DarkSpell artboard keeps the sidebar's
     active row and the section pill cream on the navy panel - so it cannot read
     var(--parchment), which is the dark page fill once html.theme-dark is on. Using the
     themed token there made the Dark segmented button dark-brown-on-dark-navy. */
  --plate: #e9d7b3;
  --plate-hi: #f4e8cc;

  /* decorative only, never used as a foreground colour */
  --accent-purple: #7a3ab0; --accent-indigo: #201040; --accent-blue: #203050;
  --mana-pink: #d95fc0; --mana-magenta: #d040b0; --mana-cyan: #59d0c8;
  --common: #b0aeb3; --uncommon: #6cae58; --rare: #5f8fd6; --legendary: #d9a13e;

  /* --- fonts --- */
  --font-title: "VeniceClassic", Georgia, serif;
  /* --font-ui is an ALIAS for --font-title, not a second face: dozens of rules below
     (table headers, chips, badges, tooltip pills, the column-customize menu, sidebar
     labels) read var(--font-ui) for "the game's UI face". */
  --font-ui: var(--font-title);
  --font-plaque: "Pixellari", "Courier New", monospace;
  /* RunescapeUF is DECLARED but is not the face of anything high-traffic: Chrome
     rejects the shipped TTF ("OTS parsing error: cmap: Range glyph reference too high
     (97 > 96)") and falls back, so every rule that names it logs a warning and renders
     Pixellari anyway. It stays on the one surface v1 already used it for
     (.tarot-card-tier) and is flagged for the theme-extraction lane; badges and pills
     use Pixellari directly. */
  --font-tiny: "RunescapeUF", "Pixellari", monospace;
  /* PROSE ONLY. body itself is set in --font-ui below, so a table cell or a nav row is
     VeniceClassic by default and only long-form text opts into the book face. */
  --font-body: "Alegreya", Georgia, "Times New Roman", serif;

  /* --- 9-slice sprites exported from the game (pipeline/extract_theme.py; the measured
     slice for each is in site/assets/theme.json). Every rule that uses one also sets a
     plain border-color + background-color, so a 404 degrades to a solid frame. The page
     sprite and its slice are tokens because the DARK theme swaps both. --- */
  --img-plaque: url("ui/Title.png");
  --img-panel-frame: url("ui/PopUpPanel_10.png");
  /* SLICE 58, not the 38/40 the first draft used. Both sprites carry a printed inner
     rule with a corner BRACKET that reaches 56 source px in from each edge (mapped
     pixel by pixel off the PNGs). A slice of 38 cut through that bracket, so the
     leftover 38..56 band belonged to the "fill" centre and got stretched across the
     whole page - it showed up as faint diagonal smears in the middle of every page. 58
     puts the whole ornament in the corner slices. */
  --img-page: url("ui/emptyPage.png");        /* the grimoire page */
  --img-page-slice: 58;
  --img-sheet: url("ui/descriptionBox.png");  /* the pinned note / callout sheet */
  --img-sheet-slice: 58;
  --img-parchment: var(--img-page);           /* v1 name, kept: page modules may read it */
  --img-sigilbox: url("ui/gemBackground.png");
  --img-damask: url("ui/damask_tile.png");
  --img-button: url("ui/thickButton.png");
  --img-cardframe-gold: url("ui/GoldCard.png");
  --img-cardframe-silver: url("ui/SilverCard.png");
  --img-tarot-frame: url("ui/TarotCard_Frame.png");
  --img-speaker: url("ui/dialogueboxname.png");
}

/* ---- DARK THEME (html.theme-dark) -----------------------------------------
   The DarkSpell artboard: identical layout and identical navy chrome, with the book's
   pages rendered on the game's own PopUpPanel_5 panel (brass frame, deep indigo fill)
   in cream ink with gold links. Only the page-surface family and the desk move, which
   is why every component below stays theme-agnostic: it reads --parchment/--text-dark/
   --rule and gets the right thing in either mode.
   The stamp is set by common.py's head preamble (pre-paint, so there is no flash) from
   localStorage 'mcw-theme'; app.js's Settings > Display > Theme writes it. Default is
   LIGHT: no class, no stamp. */
html.theme-dark {
  color-scheme: dark;
  --bg: #100d18;
  --desk-glow: #1d1730;

  --parchment: #221a30;
  --parchment-shade: #2a2140;
  --parchment-zebra: #261e39;
  --paper-white: #2f2648;
  --text-dark: #efe2c4;        /* 13.0:1 on the dark page */
  --ink-light: #cdbc9c;        /* 9.2:1 */
  --muted-ink: #c8b89a;        /* 8.6:1 on the page, 7.7:1 on the zebra row */
  --link-dark: #f0c070;        /* 9.9:1 */
  --link-dark-hover: #fff6dd;
  --pos-ink: #8fe08a;          /* 10.5:1 */
  --neg-ink: #ff9a9a;          /* 8.2:1 */
  --rule: #a07848;
  --rule-soft: rgba(160, 120, 72, .45);
  --spine: #0d0a14;
  --page-shadow: rgba(0, 0, 0, .7);

  /* PopUpPanel_5's brass band is a solid 39px wide / 34px tall in the source (mapped,
     not guessed); 30 sliced into it and stretched brass into the panel interior. */
  --img-page: url("ui/PopUpPanel_5.png");
  --img-page-slice: 40;
  --img-sheet: url("ui/PopUpPanel_5.png");
  --img-sheet-slice: 40;
  --img-parchment: var(--img-page);
}

/* ---- Base ---------------------------------------------------------------- */
* { box-sizing: border-box; }
html { scrollbar-color: #4a3a30 var(--bg); }
body {
  margin: 0; color: var(--text);
  background-color: var(--bg);
  background-image:
    radial-gradient(ellipse at 50% -8%, var(--desk-glow) 0%, transparent 58%),
    var(--img-damask);
  background-repeat: no-repeat, repeat;
  background-size: auto, 300px 300px;
  background-blend-mode: normal, soft-light;
  background-attachment: fixed, fixed;
  font: 15px/1.6 var(--font-ui);
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
/* Links are token-driven and NOTHING here raises the specificity above one type
   selector: a page module's own `.cstile { color: ... }` on a navy tile printed on the
   parchment page has to be able to win. An `#book a { color: ... }` rule was tried and
   caused exactly that bug - ID beats class, so the character tiles rendered ink-brown on
   navy at 2.03:1. The retint block on #book/.card/.detailhead/.mini moves --link and
   --link-hover to their ink values, which is all that is needed. */
a { color: var(--link); text-decoration: none; }
a:hover { color: var(--link-hover); text-decoration: underline; }
/* a plain prose link on parchment is ink-brown next to ink-brown text: invisible as a link
   unless it carries its own cue (night QA 2026-08-19). Entity refs (.refx, .ref) keep their
   icon + hover card and stay clean. */
.bookin p a:not(.refx):not(.ref):not(.btn):not(.tlink), .bookin li a:not(.refx):not(.ref):not(.btn):not(.tlink),
.bookin .lead a:not(.refx):not(.ref):not(.btn) { border-bottom: 1px dotted currentColor; }
code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; color: #d8ccb8; }
img { image-rendering: pixelated; object-fit: contain; }
/* Spell thumbnails are ink drawings of the card sigil, not pixel sprites. */
img[src*="/icons/spells/"] { image-rendering: auto; }
.refx .sigil-svg,
.entcell .sigil-svg,
.detailhead .sigil-svg {
  display: inline-block;
  vertical-align: middle;
  color: var(--text-dark);
  flex: 0 0 auto;
}
.refx .sigil-svg { width: 18px; height: 18px; margin-right: .3em; vertical-align: -3px; }
.entcell .sigil-svg { width: 32px; height: 32px; margin-right: 8px; }
.detailhead .sigil-svg { width: 88px; height: 88px; }
.mcw-ix-pair .mcw-ix-spell .sigil-svg { width: 36px; height: 36px; }
.mcw-ix.lead .mcw-ix-pair .mcw-ix-spell .sigil-svg { width: 52px; height: 52px; }
::selection { background: #b0704066; }
h1, h2, h3, h4, h3.pagetitle { font-family: var(--font-title); }
/* Long-form prose is the ONLY place the book face is used. */
.lead, .md p, .md li, .md dd, .detailhead .desc, .detailhead .flavor,
.card .cb > p, .mini .m, .fxr-d, .dialogue-line .speech, .mcw-tt-lead {
  font-family: var(--font-body);
}
.lead { color: var(--muted-ink); font-size: 15.5px; line-height: 1.6; max-width: 74ch; margin: 4px 0 14px; }
.lead b { color: var(--text-dark); font-weight: 700; }

/* handbook/mechanics: a long topic card flows its body in two sub-columns on a wide
   single page, so it reads as two short columns instead of one tall one. Never inside
   the two-page spread (columns in columns is unreadable). */
@media (min-width: 1000px) {
  .book-single .hb-long .cb { column-count: 2; column-gap: 34px; }
  .book-single .hb-long .cb > p { break-inside: avoid; margin-top: 0; }
}

/* ---- Layout -------------------------------------------------------------- */
#layout { display: flex; min-height: 100vh; align-items: flex-start; }
#main-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; }
#main { flex: 1; min-width: 0; padding: 0 22px 80px; }

/* ---- Topbar: the brass strip ---------------------------------------------
   ss09/ss12's screen header: a dark strip closed by a brass rule, carrying the brand as
   a title plaque, the page name in the game's face, the four NAV groups as book tabs,
   and the search field as a dark inset box with a brass edge. */
#topbar {
  display: flex; align-items: flex-end; gap: 12px; padding: 9px 20px 0;
  border-bottom: 3px solid var(--border);
  box-shadow: 0 2px 0 var(--border-dark), 0 6px 16px rgba(0,0,0,.45);
  margin-bottom: 20px;
  position: sticky; top: 0; z-index: 150;
  background:
    linear-gradient(180deg, #2a1c2ef2, #1a1320f2);
  backdrop-filter: blur(3px);
}
/* auto-hiding topbar: scrolling DOWN slides the bar out; scrolling UP brings it back.
   app.js owns the state (body.mcw-tbhide) and measures --topbar-h; the offset is derived
   HERE so it can never disagree with the bar. */
:root { --topbar-h: 60px; --topbar-off: 0px; }
body.mcw-tbhide { --topbar-off: calc(-1 * var(--topbar-h)); }
#topbar { transition: transform .22s ease; }
body.mcw-tbhide #topbar { transform: translateY(-100%); }
nav.secnav { transition: top .22s ease, transform .22s ease; }
body.mcw-shot { --topbar-off: 0px; }
@media (prefers-reduced-motion: reduce) {
  #topbar, nav.secnav { transition: none; }
}
#topbar h1 {
  font-family: var(--font-title); font-size: 21px; font-weight: 400; margin: 0 0 11px;
  white-space: nowrap; overflow: hidden; text-overflow: clip; color: var(--text);
  letter-spacing: .2px;
}
/* .plaque: the game's title-plaque motif (ui/Title.png: navy interior, brass frame with
   small diamond finials) - ss09's "Spell Crafting", ss12's "Credits". Reusable by any
   page module that wants a section title on a plate. */
.plaque {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-plaque); font-weight: 400; letter-spacing: .04em;
  color: var(--border-light); background: var(--panel-alt);
  border-image: var(--img-plaque) 40 55 40 55 fill stretch;
  border-style: solid; border-width: 13px 17px; border-color: var(--border);
  text-shadow: 0 1px 2px #000000aa;
  white-space: nowrap;
}
/* common.py emits <a id="brand" href="/"> with no class, so the plaque look is applied
   to the ID directly and the shell markup contract does not have to carry a class. */
#topbar #brand {
  flex: 0 0 auto; font-size: 15px; padding: 0 4px; margin-bottom: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-plaque); font-weight: 400; letter-spacing: .04em;
  color: var(--border-light); background: var(--panel-alt);
  border-image: var(--img-plaque) 40 55 40 55 fill stretch;
  border-style: solid; border-width: 11px 15px; border-color: var(--border);
  text-shadow: 0 1px 2px #000000aa;
  white-space: nowrap;
}
#topbar #brand:hover { color: #fff0cc; text-decoration: none; }
#topbar #brandsep { display: none; }
/* the page name never shrinks (it was being squeezed to "Damage over" / "Milk: Tar" by
   the tab strip and the search box); it clips at a share of the viewport instead, and
   the full name is always readable in the crumb and in the book's own heading. */
#topbar #pagename { text-align: left; flex: 0 0 auto; max-width: 26vw; min-width: 0; }
#main .crumb {
  color: var(--muted); font-size: 12.5px; font-family: var(--font-ui);
  margin: 0 0 10px; padding: 0 2px;
}
#main .crumb a { color: var(--link); }
#main .crumb .sep { margin: 0 6px; color: var(--border); }

/* Section tabs: the four NAV groups as the book's index tabs (ss08's tab strip along the
   top of the grimoire). Parchment tabs with the top corners rounded, sitting on the
   brass rule; the active one is brighter and joins the strip. */
#sectabs { display: flex; gap: 5px; margin-left: auto; align-items: flex-end; flex: 0 0 auto; }
/* below this width the strip and the search box together starve the page name; the
   sidebar carries the same navigation, so the tabs are the thing that goes. */
@media (max-width: 1180px) { #sectabs { display: none; } }
#sectabs .tab {
  display: inline-flex; align-items: center; justify-content: center;
  height: 34px; padding: 0 13px;
  font-family: var(--font-title); font-size: 15px; color: var(--ink-fixed);
  background-color: #cbb183; background-image: linear-gradient(180deg, #d5bd8f, #c2a878);
  border: 2px solid var(--border); border-bottom: none;
  border-radius: 9px 9px 0 0; text-decoration: none;
  box-shadow: inset 0 1px 0 #fff6dd88;
}
#sectabs .tab:hover { background-color: #e2cda1; background-image: linear-gradient(180deg, #ecd8ae, #d8c08f); text-decoration: none; color: var(--ink-fixed); }
#sectabs .tab.active {
  background-color: var(--plate-hi); background-image: linear-gradient(180deg, #f4e8cc, #e4d0a4);
  border-color: var(--border-light); font-weight: 700; height: 38px;
  box-shadow: inset 0 1px 0 #fffaf0, 0 3px 0 #f4e8cc;
}
#sectabs .tab:focus-visible { outline: 2px solid var(--border-light); outline-offset: 2px; }

#navtoggle {
  display: none; background: var(--panel2); color: var(--border);
  border: 2px solid var(--border); border-radius: 5px;
  font-family: var(--font-ui); font-size: 18px; padding: 6px 11px; cursor: pointer; flex: 0 0 auto;
  margin-bottom: 8px;
}
/* mobile-drawer hamburger: drawn with box-shadow bars, never a unicode glyph (#navtoggle
   uses --font-ui and no pixel font here is guaranteed to carry U+2630). */
.hamb-i {
  display: inline-block; width: 16px; height: 2px; background: currentColor;
  box-shadow: 0 -5px 0 currentColor, 0 5px 0 currentColor;
}
#navbackdrop { display: none; }

/* ---- Sidebar: the brass-framed navy panel (surface #1, ss09's spell list, ss12) ----
   A real 9-slice frame (PopUpPanel_10) around a damask navy interior. It is exactly one
   viewport tall and sticky, so the frame's corner ornaments always sit at the top and
   bottom of what you can see instead of being stretched down a whole document. */
#sidebar {
  width: 246px; flex: 0 0 246px;
  position: sticky; top: 0; height: 100vh; z-index: 200;
  margin: 0; padding: 8px 0 28px;
  overflow-y: auto; overscroll-behavior: contain;
  background-color: var(--panel);
  background-image: var(--img-damask);
  background-repeat: repeat; background-size: 240px 240px;
  background-blend-mode: soft-light;
  border-image: var(--img-panel-frame) 70 110 70 110 fill stretch;
  border-style: solid; border-width: 14px 18px; border-color: var(--border);
}
#sidebar .logo {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  margin: 22px 10px 10px; text-decoration: none;
}
#sidebar .logo .plaque { font-size: 14px; padding: 0 4px; border-width: 11px 15px; }
#sidebar .logo span { color: inherit; }
#sidebar .logo small {
  display: block; color: var(--label-muted); font-size: 11px;
  font-family: var(--font-plaque); letter-spacing: .06em;
}
#sidebar .logo:hover .plaque { color: #fff0cc; }
#sidebar .group { margin-top: 10px; }
#sidebar .group > .gt {
  padding: 6px 12px 3px; font-family: var(--font-plaque); font-size: 11px;
  text-transform: uppercase; letter-spacing: .12em; color: var(--label-muted);
}
/* List row (surface #2, ss09): a navy box with a thin brass hairline; the SELECTED row
   flips to parchment with ink text, which is the game's own signature move. */
:is(#sidebar, .navlist) a.nav {
  /* The nav row is a NAVY tile, and .navlist puts it on the parchment page (the topic
     list on /mechanics). Inside #book the retint makes var(--text) ink, which rendered
     those rows dark-brown on midnight blue. The row restates its own foreground tokens,
     like every other navy surface printed on the page. */
  --text: var(--cream-fixed); --muted: #c4b9a6;
  --link: var(--border-light); --link-hover: #fff6dd;
  display: flex; align-items: center; gap: 8px; margin: 3px 8px; padding: 7px 10px;
  color: var(--cream-fixed); font-family: var(--font-title); font-size: 14px;
  background: #1a1a30;
  border: 1px solid rgba(176, 112, 64, .5); border-radius: 3px;
  box-shadow: inset 0 1px 0 #ffffff0d;
}
:is(#sidebar, .navlist) a.nav:hover {
  background: var(--panel3); text-decoration: none; color: var(--border-light);
  border-color: var(--border);
}
:is(#sidebar, .navlist) a.nav.active {
  background: var(--plate); color: var(--ink-fixed); font-weight: 700;
  --text: var(--ink-fixed); --muted: #5c3d1c; --link: var(--link-dark); --link-hover: #3a2816;
  border-color: var(--border-light);
  box-shadow: inset 0 0 0 1px #ffffff66, 0 1px 3px #00000066;
}
:is(#sidebar, .navlist) a.nav.active .cnt { color: #5c3d1c; }
:is(#sidebar, .navlist) a.nav .cnt { margin-left: auto; color: #c4b9a6; font-size: 11px; font-family: var(--font-ui); }
#sidebar a.nav .mcw-navmark { width: 10px; height: 10px; flex: 0 0 auto; margin-left: 6px; opacity: .6; }

/* foldable nav categories (app.js). Each .navgt header collapses its .group-body. */
#sidebar .navgt { cursor: pointer; position: relative; padding-right: 28px; user-select: none; }
#sidebar .navgt:hover { color: var(--border-light); }
#sidebar .navgt:focus-visible { outline: 2px solid var(--border-light); outline-offset: -2px; }
#sidebar .navgt::after {
  content: ""; position: absolute; right: 12px; top: 50%; width: 7px; height: 7px; margin-top: -6px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .18s ease; opacity: .7;
}
#sidebar .navgroup.collapsed .navgt::after { transform: rotate(-45deg); margin-top: -3px; }
#sidebar .navgroup.collapsed .group-body { display: none; }
@media (prefers-reduced-motion: reduce) { #sidebar .navgt::after { transition: none; } }
#sidebar .sb-foldctl { display: flex; gap: 6px; padding: 2px 10px 8px; }
#sidebar .sb-foldbtn {
  flex: 1 1 auto; background: var(--panel2); color: var(--muted);
  border: 1px solid var(--border); border-radius: 4px;
  font-family: var(--font-ui); font-size: 11px; padding: 3px 6px; cursor: pointer; letter-spacing: .04em;
}
#sidebar .sb-foldbtn:hover { color: var(--border-light); border-color: var(--border-light); }
#sidebar .sb-foldbtn:focus-visible { outline: 2px solid var(--border-light); outline-offset: -1px; }

/* Settings group (injected by app.js): same chrome as any other sidebar group. */
#sidebar .sb-set { margin-top: 2px; padding-bottom: 8px; border-bottom: 1px solid #3a2c46; margin-bottom: 4px; }
#sidebar .sb-set-gt { cursor: pointer; position: relative; padding-right: 28px; user-select: none; }
#sidebar .sb-set-gt:hover { color: var(--border-light); }
#sidebar .sb-set-gt:focus-visible { outline: 2px solid var(--border-light); outline-offset: -2px; }
#sidebar .sb-set-gt::after {
  content: ""; position: absolute; right: 12px; top: 50%; width: 7px; height: 7px; margin-top: -6px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .18s ease; opacity: .85;
}
#sidebar .sb-set.collapsed .sb-set-gt::after { transform: rotate(-45deg); margin-top: -3px; }
#sidebar .sb-set.collapsed .sb-set-body { display: none; }
@media (prefers-reduced-motion: reduce) { #sidebar .sb-set-gt::after { transition: none; } }
#sidebar .sb-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 12px 5px 16px;
  color: var(--text); font-size: 13px;
}
#sidebar .sb-row.sb-check { cursor: pointer; }
#sidebar .sb-row.sb-check:hover { background: #ffffff12; color: var(--border-light); }
#sidebar .sb-lbl { color: var(--muted); font-size: 12.5px; }
#sidebar .sb-hint { margin-left: auto; color: var(--muted); font-size: 10.5px; text-align: right; }
#sidebar .sb-check input { width: 16px; height: 16px; accent-color: var(--border); flex: 0 0 auto; }
#sidebar .sb-seg {
  display: flex; gap: 2px; margin-left: auto; padding: 2px;
  background: var(--panel2); border: 1px solid var(--border); border-radius: 5px;
}
#sidebar .sb-segb {
  cursor: pointer; background: none; border: none; border-radius: 3px; padding: 3px 9px;
  color: var(--muted); font-family: var(--font-ui); font-size: 11.5px; line-height: 1.5;
}
#sidebar .sb-segb:hover { color: var(--border-light); }
#sidebar .sb-segb.on {
  background: var(--plate); color: var(--ink-fixed); font-weight: 700;
  box-shadow: inset 0 0 0 1px var(--border-light);
}
#sidebar .sb-segb:focus-visible, #sidebar .sb-check:focus-within { outline: 2px solid var(--border-light); outline-offset: -2px; }
#sidebar .sb-touch { display: none; }
@media (max-width: 900px) { #sidebar .sb-touch { display: flex; } }
#sidebar .sb-shotbtn {
  background: var(--panel3); color: var(--text); border: 1px solid var(--border);
  border-radius: 4px; font-family: var(--font-ui); font-size: 12px; padding: 5px 12px; cursor: pointer;
}
#sidebar .sb-shotbtn:hover { color: var(--border-light); border-color: var(--border-light); }
.sb-subhead {
  font-family: var(--font-plaque); font-size: 10px; text-transform: uppercase;
  letter-spacing: .1em; color: var(--label-muted); margin: 10px 10px 2px;
  padding: 0 2px 3px; border-bottom: 1px solid #3a2c46;
}
.sb-set-body .sb-subhead:first-child { margin-top: 4px; }
.sb-foldhead { cursor: pointer; display: flex; align-items: center; justify-content: space-between; }
.sb-foldhead:hover { color: var(--border-light); }
.sb-foldchev { font-size: 11px; opacity: .8; }

/* desktop sidebar collapse: the chevron lives in the sidebar's top-right corner and the
   way back (#sbshow) sits at the head of the topbar. */
#sbtoggle, #sbshow {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  cursor: pointer; padding: 0;
}
#sbtoggle {
  /* in the panel frame's own top band, ABOVE the brand plaque: the plaque is centred and
     fills the panel width, so a toggle placed level with it sat on top of the brass. */
  position: absolute; top: 4px; right: 8px; width: 24px; height: 24px;
  background: none; border: 0; color: var(--label-muted); z-index: 2;
}
#sbtoggle:hover { color: var(--border-light); }
#sbshow {
  display: none; width: 30px; height: 30px; border-radius: 4px; margin-bottom: 8px;
  background: var(--panel2); color: var(--border); border: 2px solid var(--border);
}
body.sb-collapsed #sbshow { display: inline-flex; }
#sbshow:hover { color: var(--border-light); border-color: var(--border-light); }
#sbtoggle:focus-visible, #sbshow:focus-visible { outline: 2px solid var(--border-light); outline-offset: 2px; }
#sbtoggle .sbt-i, #sbshow .sbt-i {
  width: 14px; height: 2px; background: currentColor;
  box-shadow: 0 -4px 0 currentColor, 0 4px 0 currentColor;
}
body.sb-collapsed #sidebar { display: none; }
@media (max-width: 900px) {
  #sbtoggle, #sbshow { display: none !important; }
  body.sb-collapsed #sidebar { display: block; }
}

/* ---- Search + language ---------------------------------------------------- */
#searchwrap { position: relative; flex: 0 1 240px; width: 240px; min-width: 140px; margin: 0 0 8px 12px; }
#searchbox, #langsel {
  width: 100%; padding: 7px 11px; background: #12101c; color: var(--text);
  border: 2px solid var(--border); border-radius: 3px; font-size: 13.5px; outline: none;
  font-family: var(--font-title);
}
#searchbox::placeholder { color: var(--label-muted); opacity: 1; }
#searchbox:focus { border-color: var(--border-light); box-shadow: 0 0 0 2px #b0704055; }
#searchresults {
  position: absolute; top: 40px; left: 0; right: 0;
  background-color: var(--panel);
  background-image: var(--img-damask); background-repeat: repeat; background-size: 200px 200px;
  background-blend-mode: soft-light;
  border: 2px solid var(--border); border-radius: 3px;
  max-height: min(420px, calc(100dvh - 140px)); overflow-y: auto;
  display: none; z-index: 300; box-shadow: 0 12px 32px rgba(0,0,0,.7);
}
#searchresults a {
  display: flex; align-items: center; gap: 10px; padding: 7px 12px; color: var(--text); font-size: 13.5px;
}
#searchresults a:hover, #searchresults a.sel { background: var(--panel3); text-decoration: none; color: var(--border-light); }
#searchresults .stype { margin-left: auto; color: var(--muted); font-size: 11px; font-family: var(--font-ui); }
#searchresults img { width: 24px; height: 24px; object-fit: contain; }
#searchresults .snone { padding: 10px 12px; color: var(--muted); font-size: 13px; }

/* ---- custom dropdown (replaces native <select> popups) ---- */
.mcsel { position: relative; display: inline-block; flex: 0 0 auto; margin-bottom: 8px; }
#topbar .mcsel .mcsel-btn { min-width: 92px; }
.mcsel select { display: none !important; }
.mcsel-btn {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 7px 10px; min-width: 116px; font-size: 12.5px; color: var(--text);
  background: #12101c;
  border: 2px solid var(--border); border-radius: 3px;
  font-family: var(--font-title); text-align: left; width: 100%;
}
.mcsel-btn:hover { border-color: var(--border-light); color: var(--border-light); }
.mcsel-btn .mcsel-arrow { margin-left: auto; color: var(--border); font-size: 10px; }
#sidebar .mcsel.langsel-side { display: block; width: auto; margin: 2px 10px 12px; }
#sidebar .mcsel.langsel-side .mcsel-btn { width: 100%; min-width: 0; }
.mcsel-menu {
  display: none; position: absolute; top: calc(100% + 5px); right: 0; min-width: 100%;
  max-height: 340px; overflow-y: auto; z-index: 600; white-space: nowrap;
  background: var(--panel); border: 2px solid var(--border-light); border-radius: 3px;
  box-shadow: 0 10px 28px #000000cc; padding: 4px;
}
.mcsel.open .mcsel-menu { display: block; }
.mcsel-opt { padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: 13px; color: var(--text); }
.mcsel-opt:hover, .mcsel-opt.hl { background: var(--panel3); color: var(--border-light); }
.mcsel-btn:focus-visible { outline: 2px solid var(--border-light); outline-offset: 1px; }
.mcsel-opt.on { background: var(--plate); color: var(--ink-fixed); font-weight: 700; }

/* ---- scrollbars ---- */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: #14111f; }
::-webkit-scrollbar-thumb { background: #4a3a30; border: 2px solid #14111f; border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: #6b5240; }

/* ---- THE BOOK -------------------------------------------------------------
   #book is the open grimoire: one parchment surface (ui/emptyPage.png as a 9-slice, so
   the deckled edge frames it at any size) laid on the desk, with a real drop shadow.

   On a detail page (`.book-spread`, wide screens only) the content flows through TWO
   columns and a gutter seam is painted down the middle: the .detailhead and the first
   cards land on the left page, the rest on the right, exactly like ss08. The seam is a
   background gradient rather than an element because the page modules emit one flat
   stream of cards and this lane does not edit them - CSS columns are what turns that
   stream into two pages without touching a single page module.

   Index pages and narrow screens get `.book-single`: one wide page, the table on the
   parchment. `column-fill: balance` keeps the two pages the same height, and
   break-inside on the blocks keeps a card from being sliced by the gutter. */
#book {
  position: relative;
  /* flex column so .bookin STRETCHES to the book's min-height: the gutter is painted on
     .bookin, and on a short page (e.g. /cards/quasar) a content-height .bookin ended the
     fold halfway down the page with a hard edge. flex-basis stays auto and shrink is off,
     so a tall page is still content-height and the two columns never get a height they
     have to overflow. */
  display: flex; flex-direction: column;
  background-color: var(--parchment);
  border-image: var(--img-page) var(--img-page-slice) fill stretch;
  border-style: solid; border-width: 36px; border-color: var(--border-dark);
  box-shadow: 0 6px 26px var(--page-shadow);
  color: var(--text-dark);
  padding: 4px 10px 18px;
  min-height: 60vh;
  /* the book never grows past the main column: a wide index table scrolls INSIDE its
     .tablewrap instead of pushing the page frame off the viewport (seen live 2026-08-19) */
  max-width: 100%; min-width: 0; box-sizing: border-box;
}
.bookin { position: relative; flex: 1 0 auto; min-width: 0; max-width: 100%; }
.tablewrap, .twshell { max-width: 100%; }
.bookin > :first-child { margin-top: 0 !important; }
@media (min-width: 1000px) {
  .book-spread .bookin {
    column-count: 2; column-gap: 64px; column-fill: balance;
    background-image: linear-gradient(90deg,
      rgba(58, 40, 22, 0) 0%,
      rgba(255, 246, 221, .16) 34%,          /* the leaf lifting away from the fold */
      rgba(58, 40, 22, .06) 40%,
      rgba(58, 40, 22, .34) 47.6%,
      rgba(58, 40, 22, .58) 49.2%,
      var(--spine) 49.8%, var(--spine) 50.2%,
      rgba(58, 40, 22, .58) 50.8%,
      rgba(58, 40, 22, .34) 52.4%,
      rgba(58, 40, 22, .06) 60%,
      rgba(255, 246, 221, .16) 66%,
      rgba(58, 40, 22, 0) 100%);
    background-size: 170px 100%;
    background-position: center top;
    background-repeat: no-repeat;
  }
  html.theme-dark .book-spread .bookin {
    background-image: linear-gradient(90deg,
      rgba(0, 0, 0, 0) 0%,
      rgba(160, 120, 72, .10) 34%,
      rgba(0, 0, 0, .18) 40%,
      rgba(0, 0, 0, .48) 47.6%,
      rgba(0, 0, 0, .70) 49.2%,
      var(--spine) 49.8%, var(--spine) 50.2%,
      rgba(0, 0, 0, .70) 50.8%,
      rgba(0, 0, 0, .48) 52.4%,
      rgba(0, 0, 0, .18) 60%,
      rgba(160, 120, 72, .10) 66%,
      rgba(0, 0, 0, 0) 100%);
  }
  .book-spread .bookin > * { break-inside: avoid; }
  .book-spread { padding: 4px 16px 18px; }
  /* A big list table is handled at BUILD time, not here: common.py gives a page that
     carries a 5-column-or-wider table the single-page layout instead of the spread,
     because such a table cannot read in a half-page column. `column-span: all` was tried
     first and rejected - a spanner nested inside a .card makes Chrome stop balancing the
     fragment before it, which left a whole blank right-hand page on /tarot/Milk. */
}
/* A page-number-ish footer line on the book, the way the artboards close each page. */
#book .bookfoot {
  margin-top: 18px; padding-top: 8px; border-top: 1px solid var(--rule-soft);
  display: flex; justify-content: space-between; align-items: center;
  font-family: var(--font-plaque); font-size: 10.5px; letter-spacing: .06em;
  color: var(--muted-ink);
}

/* THE PAGE-SURFACE RETINT. #book and the three sheet components redeclare the five
   foreground tokens to their ink versions, so every rule below that reads var(--text) /
   var(--muted) / var(--link) / var(--pos) / var(--neg) automatically renders the right
   colour for the surface it is actually on, in EITHER theme, with no second copy of
   the rule. --border/--border-light stay brass (a brass hairline reads as a brown/gold
   rule on parchment too). A component that hardcodes a hex will NOT retint - see the
   override list at the tail of this file. */
#book, .card, .detailhead, .mini {
  --text: var(--text-dark);
  --muted: var(--muted-ink);
  --link: var(--link-dark);
  --link-hover: var(--link-dark-hover);
  --pos: var(--pos-ink);
  --neg: var(--neg-ink);
}
.bookin code, .bookin .mono { color: var(--ink-light); background: var(--parchment-shade);
  padding: 1px 5px; border-radius: 3px; }

/* ---- Detail head: the page's title block ---------------------------------
   ss08's plate: the sigil in its brass gem frame, the name in the display face, the
   badges, the description - closed by a brown rule across the page. NOT a box any more:
   it is printed ON the page, like the artboards. */
.detailhead {
  display: flex; gap: 18px; align-items: flex-start;
  margin: 0 0 16px; padding: 4px 2px 14px;
  border-bottom: 2px solid var(--rule);
  color: var(--text-dark); background: transparent;
}
/* framed sigil box (surface #3b/#10): a brass frame around a dark inset, matching the
   game's spell-icon frame in ss09 even though the sheet around it is parchment. */
.detailhead .icon {
  width: 92px; height: 92px; background: var(--panel-alt);
  border-image: var(--img-sigilbox) 48 fill stretch; border-style: solid; border-width: 13px;
  border-color: var(--border);
  display: flex; align-items: center; justify-content: center; flex: 0 0 92px;
  box-shadow: inset 0 0 12px #00000066;
}
.detailhead .icon img { width: 70px; height: 70px; object-fit: contain; }
.detailhead h1, .detailhead h2 {
  margin: 0 0 6px; font-family: var(--font-title); font-size: 32px; font-weight: 700;
  line-height: 1.08; color: var(--text-dark); letter-spacing: .01em;
}
.detailhead .key { color: var(--muted-ink); font-size: 12px; font-family: var(--font-ui); }
.detailhead .desc { margin-top: 8px; max-width: 68ch; font-size: 16px; line-height: 1.55; }
.detailhead .flavor {
  font-style: italic; color: var(--ink-light); margin-top: 6px; max-width: 68ch; font-size: 15.5px;
}
.detailhead .dh-main { flex: 1 1 auto; min-width: 0; }
.detailhead .dh-variants {
  flex: 0 0 auto; align-self: stretch; padding-left: 16px;
  border-left: 1px solid var(--rule-soft);
}
.dh-vhead { font-family: var(--font-plaque); font-size: 10px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted-ink); margin-bottom: 6px; }
.dh-vrow { display: flex; gap: 12px; }
.dh-vbox { text-align: center; color: inherit; text-decoration: none; display: block; }
.dh-vthumb {
  width: 54px; height: 54px; display: flex; align-items: center; justify-content: center;
  background: var(--panel-alt); border: 2px solid var(--border); border-radius: 3px;
}
.dh-vthumb.cur { border-color: var(--border-light); box-shadow: 0 0 0 2px var(--plate); }
.dh-vthumb img { max-width: 46px; max-height: 46px; object-fit: contain; image-rendering: pixelated; }
.dh-vname { font-size: 12px; margin-top: 3px; white-space: nowrap; color: var(--text-dark); }
.dh-vlbl { font-size: 10px; color: var(--muted-ink); }
@media (max-width: 760px) {
  .detailhead { flex-wrap: wrap; }
  .detailhead .dh-variants { margin-left: 0; border-left: 0; padding-left: 0; margin-top: 8px; width: 100%; }
}

/* ---- Cards = sections of the grimoire page --------------------------------
   A .card is no longer a framed box floating on a dark page: it is a SECTION printed on
   the parchment, the way "Numbers" / "Affinity" / "Prestige options" / "Upgrade cards"
   read in the SpellDetail artboard. Heading in the display face over a brown rule, body
   underneath. Keeping the .card/.cb/h3 markup untouched means every page module, the
   collapse behaviour and the spoiler gate all still work. */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 26px; align-items: start; }
.card { background: transparent; color: var(--text-dark); margin: 0; }
.card > h3 {
  margin: 0 0 10px; padding: 0 0 6px;
  font-family: var(--font-title); font-size: 21px; font-weight: 700;
  color: var(--text-dark); letter-spacing: .01em;
  border-bottom: 2px solid var(--rule);
}
.card .cb { padding: 0; }
.card.wide { grid-column: 1 / -1; }
.cards + .cards { margin-top: 26px; }
.bookin > .card + .card,
.bookin > .card + .cards,
.bookin > .cards + .card,
.bookin > .card + .tablewrap,
.bookin > .tablewrap + .card { margin-top: 30px; }
.card .cb > :first-child { margin-top: 0 !important; }
.card .cb.md p { margin: 8px 0 16px; }
.card .cb a[data-lightbox] { margin-top: 16px !important; }
.card .kv td { padding: 7px 2px; font-size: 14px; }

/* Per-entity History (changelog.json, Palworld-style: version then what moved). */
.mcw-hist-builds { list-style: none; margin: 0; padding: 0; }
.mcw-hist-builds > li { margin: 0 0 14px; }
.mcw-hist-ver {
  font-family: var(--font-title); font-size: 16px; font-weight: 700;
  color: var(--text-dark);
}
.mcw-hist-lines { margin: 6px 0 0 1.2em; padding: 0; }
.mcw-hist-lines li { margin: 0 0 4px; color: var(--text-dark); }

/* Special interactions: one card per pairing. Not a link. No hover. */
.mcw-ix-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 14px; }
.mcw-ix-grid .card { margin: 0; cursor: default; }
.mcw-ix-grid .card.lead { grid-column: 1 / -1; }
.mcw-ix { cursor: default; }
.mcw-ix-pair { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mcw-ix-amp { color: var(--text-dark); font-family: var(--font-body); font-size: 15px; font-weight: 700; }
.mcw-ix.lead .mcw-ix-amp { font-size: 17px; }
.mcw-ix-spell {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--text-dark); font-family: var(--font-title); font-weight: 700;
}
.mcw-ix-term { font-weight: 700; color: var(--text-dark); }
.mcw-ix-pair .mcw-ix-spell .sigil-svg { width: 36px; height: 36px; }
.mcw-ix.lead .mcw-ix-pair .mcw-ix-spell .sigil-svg { width: 52px; height: 52px; }
.mcw-ix-explain {
  margin: 8px 0 0 0.2em; padding: 6px 10px 6px 12px;
  border-left: 3px solid var(--rule); color: var(--text-dark);
  font-family: var(--font-body); font-size: 14.5px; line-height: 1.45; max-width: 68ch;
}
.mcw-ix-explain + .mcw-ix-explain { margin-top: 6px; }
h2.mcw-gh {
  margin: 18px 0 10px; color: var(--text-dark);
  font-family: var(--font-title); font-size: 20px;
}
.card .cb h4, .card .cb .mcw-gh {
  margin: 18px 0 6px; color: var(--text-dark); font-family: var(--font-title); font-size: 16px;
}
.card .cb ul { margin: 8px 0 14px; }

/* .kv = the grimoire stats list (ss08's "Spell Power: +25%" over a thin brown rule) */
.kv { width: 100%; border-collapse: collapse; }
.kv td {
  padding: 6px 2px; border-bottom: 1px solid var(--rule-soft);
  font-family: var(--font-title); font-size: 14.5px; color: var(--text-dark);
}
.kv tr:last-child td { border-bottom: none; }
.kv td:first-child { color: var(--muted-ink); width: 46%; padding-right: 10px; }
.kv td.num { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }

/* .mini = the pinned note sheet (ui/descriptionBox.png in light mode): the callout the
   game uses for a description panel. Prestige options, gesture blocks, region tiles. */
.grid-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 14px; }
.mini {
  background-color: var(--paper-white); color: var(--text-dark);
  border-image: var(--img-sheet) var(--img-sheet-slice) fill stretch;
  border-style: solid; border-width: 22px; border-color: var(--border-dark);
  padding: 0 2px; margin: 0;
}
.mini:hover { filter: brightness(1.03); }
.mini h4 { margin: 0 0 5px; font-size: 15.5px; font-family: var(--font-title); color: var(--text-dark); }
.mini .m { color: var(--muted-ink); font-size: 13px; line-height: 1.5; }
.mini .cnt { color: var(--ink-light); font-family: var(--font-ui); }
/* the big drawn sigil on a spell page: the game shows it in a dark gem frame (ss09), so
   a .sigil-svg that is a direct child of a note sheet gets that frame. Elsewhere (table
   cells) it stays a plain ink drawing on the paper, which is what a grimoire looks like. */
.sigil-svg { color: var(--text-dark); display: inline-block; vertical-align: middle; }
.mini > .sigil-svg {
  display: block; margin: 8px auto 10px; color: var(--mana-pink);
  background: var(--panel-alt); border: 2px solid var(--border); padding: 6px;
  box-shadow: inset 0 0 10px #00000077;
}

/* effect/keyword rows and inline refs read correctly on the page surface */
.bookin .fxr-n { color: var(--text-dark); }
.bookin a.fxr-hd:hover .fxr-n { color: var(--link-dark); }

/* ---- Tables ---------------------------------------------------------------
   The list table is printed on the page, not boxed in a navy panel: brown hairlines, a
   soft zebra shade, a deeper tan on hover, and the navy "selected row" treatment on the
   anchor-targeted row (ss09's highlighted list entry, inverted). */
.tablewrap {
  overflow-x: auto; margin: 12px 0 18px;
  background: transparent; border: 0; box-shadow: none;
}
.tablesec { margin: 12px 0 18px; }
table.db { border-collapse: collapse; width: 100%; background: transparent; }
table.db th {
  text-align: left; padding: 9px 10px;
  font-family: var(--font-plaque); font-size: 11px; font-weight: 400;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-light);
  border-bottom: 2px solid var(--rule); white-space: nowrap; cursor: default; user-select: none;
  position: sticky; top: 0; z-index: 10; background: var(--parchment);
}
table.db th[data-sort] { cursor: pointer; }
table.db th[data-sort]:hover { color: var(--link-dark); }
table.db th[data-sort]:active { color: var(--link-dark); }
table.db th[data-sort]:focus-visible { outline: 2px solid var(--border); outline-offset: -2px; }
/* ASCII sort cues only - a pixel font that maps a different glyph set would garble a
   unicode triangle. */
table.db th[data-sort]:not(.sorted-asc):not(.sorted-desc)::after {
  content: " v"; color: var(--muted-ink); font-size: 10px; opacity: .65;
}
table.db th.sorted-asc::after { content: " ^"; color: var(--link-dark); font-size: 10px; }
table.db th.sorted-desc::after { content: " v"; color: var(--link-dark); font-size: 10px; }
table.db.constbl { table-layout: fixed; min-width: 760px; }
table.db.constbl th:nth-child(1) { width: 27%; }
table.db.constbl th:nth-child(2) { width: 12%; }
table.db.constbl th:nth-child(3) { width: 21%; }
table.db.constbl th:nth-child(4) { width: 40%; }
table.db.constbl td { overflow-wrap: break-word; }
table.db th { position: relative; }
table.db th .colresize {
  position: absolute; top: 0; right: -4px; width: 9px; height: 100%; z-index: 15;
  cursor: col-resize; touch-action: none; user-select: none;
}
table.db th .colresize::before {
  content: ""; position: absolute; top: 25%; bottom: 25%; left: 4px; width: 2px;
  background: var(--rule); opacity: 0; transition: opacity .12s;
}
table.db th .colresize:hover::before, table.db th.resizing .colresize::before { opacity: .8; }
table.db td {
  padding: 7px 10px; border-bottom: 1px solid var(--rule-soft); vertical-align: middle;
  font-family: var(--font-title); font-size: 14.5px; color: var(--text-dark);
}
table.db tr:last-child td { border-bottom: none; }
table.db tbody tr:nth-child(even) td { background: var(--parchment-zebra); }
/* hover = a deeper tan wash, ink text kept. Every token-driven colour is restated so a
   green stat value cannot end up at 1.4:1 on the tan. */
table.db tbody tr:hover td {
  background: var(--parchment-shade); color: var(--text-dark);
  --text: var(--text-dark); --muted: var(--ink-light); --link: var(--link-dark);
  --pos: var(--pos-ink); --neg: var(--neg-ink);
}
table.db tbody tr:hover td a, table.db tbody tr:hover td .refx { color: var(--link-dark); }
table.db tbody tr:hover td .sub, table.db tbody tr:hover td .muted { color: var(--ink-light); }
table.db td .cell-desc {
  display: block; max-width: 44ch; overflow: hidden; white-space: nowrap; text-overflow: clip;
  -webkit-mask-image: linear-gradient(90deg, #000 85%, transparent); mask-image: linear-gradient(90deg, #000 85%, transparent);
}
/* injected 'no matches' row */
table.db tbody tr.mcempty td, table.db tbody tr.mcempty:hover td {
  background: var(--parchment-shade); color: var(--ink-light); text-align: center;
  padding: 16px 12px; font-size: 13.5px; cursor: default;
  box-shadow: inset 0 0 0 1px var(--rule);
}
table.db td.num, table.db th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.db.chgtable { table-layout: fixed; }
table.db.chgtable th, table.db.chgtable td { white-space: normal; overflow-wrap: anywhere;
  word-break: break-word; vertical-align: top; }
table.db.chgtable th:first-child, table.db.chgtable td:first-child { width: 42%; }
table.db.chgtable code { white-space: normal; overflow-wrap: anywhere; }
@media (max-width: 900px) { table.db.chgtable th:first-child, table.db.chgtable td:first-child { width: 46%; } }
.entcell { display: flex; align-items: center; gap: 10px; min-width: 180px; }
.entcell.mcw-fit { min-width: 0; width: fit-content; max-width: 100%; }
.entcell img { width: 32px; height: 32px; object-fit: contain; flex: 0 0 32px; }
/* the img + name sit INSIDE the <a>, so the cell's flex centring never reached them and the
   name overlapped the icon's bottom edge (night QA 2026-08-19) */
.entcell a { display: inline-flex; align-items: center; gap: 8px; }
.entcell .sub { display: block; color: var(--muted-ink); font-size: 11.5px; }

/* Applied-effects chip: an icon + linked name for an effect referenced in a table cell. */
.effchip-wrap { display: inline-flex; flex-wrap: wrap; gap: 3px 10px; align-items: center; max-width: 340px; }
.effchip { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; color: var(--link-dark);
  text-decoration: none; white-space: nowrap; }
.effchip:hover { color: var(--text-dark); text-decoration: underline; }
.effchip-ic { width: 16px; height: 16px; object-fit: contain; image-rendering: pixelated; flex: 0 0 16px; }

/* Column-customization menu: app.js mounts this above any table[data-colcustom]. */
.colcust { display: flex; justify-content: flex-end; position: relative; margin: 0 0 8px; }
.colcust-btn { cursor: pointer; background: var(--parchment-shade); border: 1px solid var(--rule);
  border-radius: 3px; color: var(--ink-light); font-family: var(--font-ui); font-size: 12.5px;
  padding: 4px 10px; display: inline-flex; align-items: center; gap: 6px; }
.colcust-btn:hover, .colcust-btn[aria-expanded="true"] { color: var(--link-dark); border-color: var(--border); }
.colcust-panel { position: absolute; top: 100%; right: 0; z-index: 60; margin-top: 4px;
  background: var(--paper-white); border: 2px solid var(--border); border-radius: 3px;
  box-shadow: 0 8px 22px #00000077; padding: 8px; min-width: 190px; max-height: 60vh; overflow-y: auto; }
.colcust-panel[hidden] { display: none; }
.colcust-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; font-size: 12.5px;
  color: var(--text-dark); cursor: pointer; border-radius: 3px; }
.colcust-row:hover { background: var(--parchment-shade); }
.colcust-row input { cursor: pointer; accent-color: var(--border-dark); }
.colcust-row.locked { color: var(--muted-ink); cursor: default; }
.colcust-ctl { display: inline-flex; flex-direction: column; gap: 1px; min-width: 20px; }
.colcust-lab { display: flex; align-items: center; gap: 8px; flex: 1; cursor: pointer; }
.colcust-row.locked .colcust-lab { cursor: default; }
.colcust-arrow { font-family: var(--font-ui); font-size: 9px; line-height: 1; padding: 2px 5px;
  cursor: pointer; background: var(--parchment); border: 1px solid var(--rule); color: inherit;
  border-radius: 2px; }
.colcust-arrow:disabled { opacity: .35; cursor: default; }
.colcust-foot { border-top: 1px solid var(--rule); margin-top: 6px; padding-top: 6px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.colcust-saved { color: var(--muted-ink); font-size: 11px; opacity: 0; transition: opacity .18s; }
.colcust-saved.show { opacity: 1; }
.colcust-footbtns { display: inline-flex; align-items: center; gap: 4px; }
.colcust-foot button { background: none; border: none; color: var(--link-dark); font-size: 11.5px; cursor: pointer; padding: 2px 4px; }
.colcust-foot button:hover { color: var(--text-dark); text-decoration: underline; }

/* wide tables: right-edge fade cue while more columns hide off-screen. */
.twshell { position: relative; }
.twshell.has-more::after {
  content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 34px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--parchment));
}

/* ---- Filters (parchment chips on the page) -------------------------------- */
.filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; align-items: center; }
.filters input[type=text] {
  padding: 7px 10px; background: var(--paper-white); color: var(--text-dark);
  border: 1px solid var(--rule); border-radius: 3px;
  font-family: var(--font-title); font-size: 13.5px; width: 210px; outline: none;
}
.filters input[type=text]::placeholder { color: var(--muted-ink); opacity: 1; }
.filters input[type=text]:focus { border-color: var(--border); box-shadow: 0 0 0 2px #b0704044; }
.chip {
  padding: 4px 11px; border: 1px solid var(--rule); border-radius: 3px;
  font-family: var(--font-title); font-size: 13px;
  color: var(--ink-light); cursor: pointer; user-select: none;
  background: var(--parchment-shade); min-height: 26px;
  box-shadow: inset 0 1px 0 #ffffff55;
}
.chip.b-common, .chip.b-uncommon, .chip.b-rare, .chip.b-legendary { font-family: var(--font-ui); letter-spacing: .03em; }
.chip:hover { border-color: var(--border-dark); color: var(--text-dark); background: var(--paper-white); }
.chip.on {
  background: var(--panel); color: var(--cream-fixed);
  border-color: var(--border-light); font-weight: 700;
  box-shadow: inset 0 1px 0 #ffffff22;
}
.chip:focus-visible { outline: 2px solid var(--border); outline-offset: 1px; }
.chip:active, .btn:active, .mcsel-btn:active, .mclb-close:active {
  transform: translateY(1px);
}
.filters .fcount { margin-left: auto; color: var(--muted-ink); font-size: 12.5px; font-family: var(--font-ui); }
/* facet-group labels (bare "Type" / "Rarity" words): classless spans with inline styles
   from the page generator, hence the !important. */
.filters > span:not([class]) {
  font-family: var(--font-plaque); font-size: 10px !important; text-transform: uppercase;
  letter-spacing: .09em; color: var(--muted-ink) !important;
  margin-left: 10px !important; flex: 0 0 auto;
}
.filters > span:not([class]):first-of-type { margin-left: 2px !important; }
.filters[data-reflowed] { flex-direction: column; align-items: stretch; gap: 7px; }
.filters[data-reflowed] .fgroup { display: flex; align-items: center; gap: 8px; min-width: 0; }
.filters[data-reflowed] .fgroup.ftop { gap: 10px; }
.filters[data-reflowed] .fglabel {
  flex: 0 0 auto; width: 86px; text-align: right; color: var(--muted-ink);
  font-family: var(--font-plaque); font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
}
.filters[data-reflowed] .fgchips { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
.filters[data-reflowed] .fgchips .chip { flex: 0 0 auto; }
@media (max-width: 900px) { .filters[data-reflowed] .fglabel { width: 64px; font-size: 9.5px; } }
.filters > .chip { flex: 0 0 auto; }
.fcount .fclear, .filters .fclear { margin-left: 6px; color: var(--link-dark); cursor: pointer; text-decoration: none; }
.fcount .fclear:hover { color: var(--text-dark); text-decoration: underline; }

/* ---- Badges: small pixel-labelled tags on the page ------------------------ */
.badge {
  display: inline-block; padding: 2px 8px 1px; border-radius: 3px; font-size: 11.5px;
  font-family: var(--font-plaque); font-weight: 400; letter-spacing: .02em;
  border: 1px solid var(--rule); background: var(--parchment-shade); color: var(--ink-light);
}
.r-common { color: var(--common); } .r-uncommon { color: var(--uncommon); }
.r-rare { color: var(--rare); } .r-legendary { color: var(--legendary); }
/* rarity/kind badges keep their hue but are darkened for ink-on-paper legibility; each
   pair below was measured against its own fill, not against the page. */
.b-common { background: #ddd3c2; color: #4a4340; border-color: #9b9083; }
.b-uncommon { background: #d5e0c4; color: #2c5220; border-color: #7d9463; }
.b-rare { background: #cbd8ea; color: #23406e; border-color: #6f8bb4; }
.b-legendary { background: #f0dcaa; color: #6b4a08; border-color: #b08b34; }
.b-gated { background: #eccfda; color: #7a2450; border-color: #b57193; }
.b-ok { background: #cfe3d6; color: #1c5c22; border-color: #6f9d80; }
.b-type { background: #dcd5e6; color: #46356b; border-color: #9186ab; }
.b-spoiler { background: #e5d3ea; color: #5c2c64; border-color: #a583ac; }
html.theme-dark .b-common { background: #34302e; color: #d3ccc4; border-color: #6a635c; }
html.theme-dark .b-uncommon { background: #23331d; color: #9cd48a; border-color: #4d6b40; }
html.theme-dark .b-rare { background: #1e2c44; color: #9dbde8; border-color: #43608e; }
html.theme-dark .b-legendary { background: #3d2f14; color: #e8c274; border-color: #8a6a26; }
html.theme-dark .b-gated { background: #3a1e2c; color: #e6a0bf; border-color: #8a4f6b; }
html.theme-dark .b-ok { background: #1c3326; color: #8fe08a; border-color: #46714f; }
html.theme-dark .b-type { background: #2a2440; color: #c3b4e2; border-color: #5c4f86; }
html.theme-dark .b-spoiler { background: #33203a; color: #dcae70; border-color: #6b4a76; }

/* ---- Sigil frame / card frames / tarot / dialogue (surfaces #6/#7/#8/#10) --- */
.sigilbox {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  background: var(--panel-alt);
  border-image: var(--img-sigilbox) 48 fill stretch; border-style: solid; border-width: 12px;
  border-color: var(--border);
  width: 72px; height: 72px;
  box-shadow: inset 0 0 10px #00000066;
}
.sigilbox img,
.sigilbox .sigil-svg { width: 68%; height: 68%; object-fit: contain; }
.sigilbox img { image-rendering: pixelated; }
.sigilbox.lg { width: 110px; height: 110px; border-width: 18px; }
.sigilbox.sm { width: 44px; height: 44px; border-width: 8px; }

/* level-up card frames (ui/GoldCard.png / ui/SilverCard.png, 304x630 native, border
   [80,35,100,35] measured in site/assets/theme.json).
   NOTE on the offsets: an absolutely-positioned child's top/right/bottom/left is measured
   from its containing block's PADDING edge, which already excludes the frame's own border
   - so "flush with the border" is offset 0, and reaching INTO the border band (to sit on
   the nameplate art baked into the bottom slice) takes a NEGATIVE offset of (desired
   distance from the true outer edge) minus (that side's border-width). Getting this
   backwards floats the nameplate text a border-width too high. */
.cardframe {
  position: relative; width: 152px; height: 315px; margin: 0 auto;
  border-style: solid; border-color: var(--border); border-width: 40px 17px 50px 17px;
  box-shadow: 0 3px 10px #00000055;
}
.cardframe.gold { border-image: var(--img-cardframe-gold) 80 35 100 35 fill stretch; }
.cardframe.silver { border-image: var(--img-cardframe-silver) 80 35 100 35 fill stretch; }
.cardframe-art {
  position: absolute; inset: 0;
  border-radius: 45px 45px 0 0; /* approximates the card's lancet-arch top corners */
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.cardframe-art img { max-width: 100%; max-height: 100%; object-fit: contain; image-rendering: pixelated; }
.cardframe-name {
  position: absolute; left: 0; right: 0; bottom: -40px; height: 30px;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  text-align: center; font-family: var(--font-title); font-weight: 700; font-size: 12.5px;
  /* printed on the sprite's own light nameplate: never flips with the theme */
  color: var(--ink-fixed); line-height: 1.15;
}
.cardframe.lg { width: 304px; height: 630px; border-width: 80px 35px 100px 35px; }
.cardframe.lg .cardframe-art { border-radius: 90px 90px 0 0; }
.cardframe.lg .cardframe-name { bottom: -78px; height: 56px; font-size: 21px; }

.tarot-card {
  position: relative; width: 145px; height: 315px; margin: 0 auto;
  background: var(--panel-alt); border-style: solid; border-color: var(--border);
  border-width: 7px 7px 35px 7px; border-image: var(--img-tarot-frame) 14 14 70 14 fill stretch;
  box-shadow: 0 3px 10px #00000055;
}
.tarot-card-art {
  position: absolute; inset: 0; overflow: hidden;
  border-radius: 40px 40px 0 0;
  display: flex; align-items: center; justify-content: center;
}
.tarot-card-art img { width: 100%; height: 100%; object-fit: cover; }
.tarot-card-name {
  position: absolute; left: 0; right: 0; bottom: -29px; height: 24px;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  text-align: center; font-family: var(--font-title); font-weight: 700; font-size: 11.5px;
  color: var(--ink-fixed);
}
.tarot-card-tier {
  position: absolute; top: -5px; left: -5px; font-family: var(--font-tiny); font-size: 11px;
  color: var(--border-light); background: var(--panel-alt); border: 1px solid var(--border);
  border-radius: 2px; padding: 1px 5px; letter-spacing: .02em;
}
.tarot-card.lg { width: 290px; height: 629px; border-width: 14px 14px 70px 14px; }
.tarot-card.lg .tarot-card-art { border-radius: 80px 80px 0 0; }
.tarot-card.lg .tarot-card-name { bottom: -58px; height: 46px; font-size: 18px; }
/* the "Upright / Reversed" readout under a tarot card (ss04): the notched navy box. */
.tarot-info .ti-row { margin: 6px 0; }
.tarot-info .ti-label { font-weight: 700; color: var(--white-outline); margin-right: 6px; }
.tarot-info .ti-row.reversed .ti-label, .tarot-info .ti-row.reversed { color: #c9c0e0; }

/* dialogue line: a gold-trimmed speaker plaque overlapping a cream speech box (ss02/ss06).
   ui/SpeechBubble2.png is the real sprite but its left/right edges are diagonal cuts a
   9-slice cannot stretch, so the box is a clip-path in the same silhouette family,
   coloured from that sprite. */
.dialogue-line { display: flex; flex-direction: column; align-items: flex-start; margin: 20px 0; max-width: 660px; }
.dialogue-line .speaker {
  position: relative; z-index: 2; margin: 0 0 -10px 18px; padding: 2px 14px;
  font-family: var(--font-title); font-weight: 700; font-size: 14px; color: var(--border-light);
  background: var(--panel-alt);
  border-image: var(--img-speaker) 26 5 5 5 fill stretch; border-style: solid; border-width: 9px 5px;
  border-color: var(--border);
  white-space: nowrap;
}
.dialogue-line .speaker img { width: 22px; height: 22px; object-fit: contain; image-rendering: pixelated; vertical-align: -5px; margin-right: 6px; }
.dialogue-line .speech {
  background: var(--paper-white); color: var(--text-dark); border: 3px solid var(--rule);
  clip-path: polygon(0 50%, 22px 0, 100% 0, 100% 100%, 22px 100%);
  padding: 18px 22px 16px 34px; font-size: 16.5px; line-height: 1.55;
  box-shadow: 0 3px 10px #00000044;
}
.dialogue-line .speech a.refx, .dialogue-line .speech span.refx { color: var(--link-dark); }

/* ---- Anchor landing: the targeted row/section gets the game's "selected" navy fill --- */
:target { background: var(--parchment-shade); }
tr:target td {
  background: var(--panel) !important; color: var(--cream-fixed);
  --text: var(--cream-fixed); --muted: #c8bca8; --link: var(--border-light);
  --pos: #8fe08a; --neg: #ff9a9a;
}
tr:target td a, tr:target td .refx { color: var(--border-light); }
tr:target td .sub, tr:target td .muted { color: #c8bca8; }
[id] { scroll-margin-top: 84px; }

/* ---- Misc: inline stat/effect chips --------------------------------------- */
.statmods.fx-inline { display: inline; }
.statmods.fx-inline span { margin: 0 0 0 6px; }
.statmods span {
  display: inline-block; margin: 2px 6px 2px 0; padding: 2px 8px;
  background: var(--parchment-shade); border: 1px solid var(--rule); border-radius: 3px;
  font-family: var(--font-title); font-size: 13px;
}
.pos { color: var(--pos); font-weight: 700; } .neg { color: var(--neg); font-weight: 700; }
a.stat { color: inherit; }
a.stat:hover { border-color: var(--border); text-decoration: none; }
a.stat:hover .v { color: var(--link-dark); }
.statlink { color: var(--link-dark); }
.muted, .mut, .sub { color: var(--muted); }
.missing, .miss { color: var(--neg); }
/* inline keyword refs (linkify() output in the tooltip engine) */
/* token-driven, one type+class of specificity: ink on the page, gold inside a tooltip */
.refx { color: var(--link); border-bottom: 1px dotted currentColor; cursor: help; text-decoration: none; opacity: 1; }
a.refx { cursor: pointer; }
a.refx:hover { color: var(--link-hover); text-decoration: none; border-bottom-style: solid; }
/* ctx.ref() emits <a class="refx"><img src="...">name</a> for every entity mention with
   NO html size attribute - native icon sizes run from 17x16 to 175x195, so an
   unconstrained mention rendered a full picture frame mid-sentence. THE inline entity
   icon is 18x18 flat, in pixels not em: an em box made the same icon four sizes on one
   page and pixel art at a fractional size shimmers. */
.refx img {
  width: 18px; height: 18px; max-width: 18px; object-fit: contain;
  vertical-align: -3px; margin-right: .3em; image-rendering: pixelated;
}
.entcell .refx img { width: 32px; height: 32px; max-width: none; margin-right: 8px; vertical-align: middle; }

/* ---- inline "Effects & keywords" definitions ------------------------------
   THE unified effect row (fxr): icon + bold linked name + coloured (scope) + colon +
   definition. Card rows get .fxr; tooltip rows get .fxr + .tfx-row. */
.fxr { display: flex; align-items: flex-start; gap: 7px; line-height: 1.45; }
.fxr-ic { width: 20px; height: 20px; object-fit: contain; image-rendering: pixelated; flex: 0 0 auto; margin-top: 2px; }
.fxr-t { flex: 1 1 auto; }
.fxr-hd { text-decoration: none; }
.fxr-n { font-weight: 700; color: var(--border-light); font-family: var(--font-title); }
a.fxr-hd:hover .fxr-n { text-decoration: underline; }
.fxr-sc { white-space: nowrap; }
.fxr-sc.tag-self { color: var(--pos); }
.fxr-sc.tag-enemy { color: var(--neg); }
#mctip .tb .fxr-sc.tag-self, .mctip-pin .tb .fxr-sc.tag-self { color: #8fe08a; }
#mctip .tb .fxr-sc.tag-enemy, .mctip-pin .tb .fxr-sc.tag-enemy { color: #ff9a9a; }
.fxr-d { color: var(--muted); line-height: 1.5; }
.fxr-t img { vertical-align: -3px; }
.fxdefs { margin: 0 0 16px; }
.fxdefs-l { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.fxdefs-l .fxr { font-size: 13.5px; }
.fxdefs-note { margin: 11px 0 0; font-size: 11.5px; color: var(--muted); }
.fx-sep { border-top: 1px solid var(--rule-soft); padding-top: 7px; }
.reflist { margin: 6px 0; }
.pill-list a { display: inline-block; margin: 2px 6px 2px 0; }
.famlist { margin: 6px 0; }

/* ---- instant tooltip: the white-outlined notched box (surface #4) ----------
   ss04's ability-description panel: dark navy fill, crisp white pixel outline, small
   clipped notch corners. UNCHANGED between themes on purpose - it is the game's own
   hover window and the artboards keep it identical in light and dark. */
#mctip {
  position: fixed; display: none; z-index: 1000; max-width: min(340px, calc(100vw - 16px)); pointer-events: none;
  background: var(--panel); color: var(--cream-fixed);
  border: 2px solid var(--white-outline);
  clip-path: polygon(7px 0, calc(100% - 7px) 0, 100% 7px, 100% calc(100% - 7px), calc(100% - 7px) 100%,
    7px 100%, 0 calc(100% - 7px), 0 7px);
  box-shadow: 0 8px 22px #000000cc;
  padding: 9px 12px; font-family: var(--font-title); font-size: 13.5px; line-height: 1.45;
  --text: var(--cream-fixed); --muted: #c4b9a6; --link: var(--border-light);
  --link-hover: #fff6dd; --pos: #8fe08a; --neg: #ff9a9a;
}
#mctip .th { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
#mctip .th img { width: 26px; height: 26px; object-fit: contain; }
#mctip .th b { font-family: var(--font-title); font-weight: 700; font-size: 15px; color: var(--white-outline); margin-right: auto; }
#mctip .th a.tnav { pointer-events: auto; text-decoration: none; }
#mctip .th a.tnav:hover b, .mctip-pin .th a.tnav:hover b { text-decoration: underline; }
.mctip-pin .th a.tnav { text-decoration: none; }
#mctip .tb { color: #e4dac6; white-space: pre-wrap; max-height: calc(100vh - 96px); overflow-y: auto; overflow-x: hidden; }
#mctip .tf { margin-top: 6px; padding-top: 5px; border-top: 1px solid #ffffff26; color: #c4b9a6; font-size: 10.5px; }
.itip .itr { display: flex; justify-content: space-between; gap: 20px; line-height: 1.55; white-space: nowrap; }
.itip .itl { color: var(--muted); }
.itip .itv { font-variant-numeric: tabular-nums; }
.itip .itv.pos { color: var(--pos); }
.itip .itv.neg { color: var(--neg); }
.itip .itd { margin-top: 6px; color: var(--muted); white-space: normal; }
#mctip .tb .tref, .mctip-pin .tb .tref { white-space: nowrap; color: var(--cream-fixed); }
#mctip .tb a.tref:hover, .mctip-pin .tb a.tref:hover { color: var(--border-light); }
#mctip .tb .tref img, .mctip-pin .tb .tref img {
  width: 16px; height: 16px; object-fit: contain; vertical-align: -3px; margin-right: 3px; }
#mctip .tb .tfx-d, .mctip-pin .tb .tfx-d { margin-bottom: 4px; }
#mctip .tb .tfx, .mctip-pin .tb .tfx {
  margin-top: 6px; padding-top: 6px; border-top: 1px solid #ffffff26;
  display: flex; flex-direction: column; gap: 6px; white-space: normal; }
#mctip .tb .tfx-row, .mctip-pin .tb .tfx-row { display: flex; align-items: flex-start; gap: 7px; line-height: 1.4; }
#mctip .tb .tfx-ic, .mctip-pin .tb .tfx-ic {
  width: 20px; height: 20px; object-fit: contain; image-rendering: pixelated; flex: 0 0 auto; margin-top: 1px; }
#mctip .tb .tfx-t b, .mctip-pin .tb .tfx-t b { color: var(--border-light); }
#mctip .tb .tfx-h, .mctip-pin .tb .tfx-h {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: #c4b9a6; }
#mctip .tb .tfx-atk, .mctip-pin .tb .tfx-atk { margin-left: 16px; }
#mctip .tb .tfx-ic.tfx-sub, .mctip-pin .tb .tfx-ic.tfx-sub { width: 16px; height: 16px; }
#mctip .tb .tfx-hp, .mctip-pin .tb .tfx-hp { color: #c4b9a6; }
#mctip .tb .tfx-t img, .mctip-pin .tb .tfx-t img { vertical-align: -3px; }
#mctip .tb .tfx-dl, .mctip-pin .tb .tfx-dl { margin: 5px 0; color: #e8dfd0; font-size: 12.5px; line-height: 1.4; }
#mctip .tf .tpin { pointer-events: auto; background: var(--panel3); color: var(--border-light);
  border: 1px solid var(--border); border-radius: 3px; font-family: var(--font-ui);
  font-size: 11px; padding: 2px 9px; cursor: pointer; }
/* title-bar pills (data-tippl): a small badge in the tooltip title bar. */
.tpill { display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
  padding: 1px 7px; font-family: var(--font-plaque); font-size: 11px; line-height: 1.4;
  color: var(--cream-fixed); background: var(--panel3); border: 1px solid var(--border);
  border-radius: 3px; white-space: nowrap; flex: 0 0 auto; }
.tpill img { height: 9px; width: auto; image-rendering: pixelated; display: block;
  margin-left: 2px; vertical-align: middle; }
.tpill .tp-ec { color: #8fd8ff; display: inline-flex; align-items: center; }
.tpill .tp-cd, .tpill .tp-rg { color: #c4b9a6; display: inline-flex; align-items: center; }
#mctip .th .tpill, .mctip-pin .th .tpill { display: inline-flex; align-items: center;
  gap: 6px; margin-left: auto; padding: 1px 7px; font-family: var(--font-plaque); font-size: 11px;
  line-height: 1.4; color: var(--cream-fixed); background: var(--panel3);
  border: 1px solid var(--border); border-radius: 3px; white-space: nowrap; }
#mctip .th .tpill img, .mctip-pin .th .tpill img { height: 9px; width: auto;
  image-rendering: pixelated; display: block; margin-left: 2px; vertical-align: middle; }
#mctip .th .tpill .tp-ec, .mctip-pin .th .tpill .tp-ec { color: #8fd8ff; display: inline-flex; align-items: center; }
#mctip .th .tpill .tp-cd, .mctip-pin .th .tpill .tp-cd { color: #c4b9a6; display: inline-flex; align-items: center; }
#mctip .th .tpill .tp-rg, .mctip-pin .th .tpill .tp-rg { color: #c4b9a6; display: inline-flex; align-items: center; }
#mctip .tb.cut {
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 26px), transparent);
  mask-image: linear-gradient(180deg, #000 calc(100% - 26px), transparent);
}
/* Alt-pinned tooltip panels: interactive twins of #mctip that stay open. */
.mctip-pin {
  position: fixed; z-index: 990; max-width: 360px;
  background: var(--panel); color: var(--cream-fixed);
  border: 2px solid var(--white-outline);
  clip-path: polygon(7px 0, calc(100% - 7px) 0, 100% 7px, 100% calc(100% - 7px), calc(100% - 7px) 100%,
    7px 100%, 0 calc(100% - 7px), 0 7px);
  box-shadow: 0 10px 26px #000000cc;
  padding: 9px 12px; font-family: var(--font-title); font-size: 13.5px; line-height: 1.5;
  --text: var(--cream-fixed); --muted: #c4b9a6; --link: var(--border-light);
  --link-hover: #fff6dd; --pos: #8fe08a; --neg: #ff9a9a;
}
.mctip-pin .th { display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
  cursor: grab; user-select: none; touch-action: none; }
.mctip-pin .th:active { cursor: grabbing; }
.mctip-pin .th img { width: 26px; height: 26px; object-fit: contain; }
.mctip-pin .th b { font-family: var(--font-title); font-weight: 700; font-size: 15px; color: var(--white-outline); margin-right: auto; }
.mctip-pin .th .tgo { font-size: 11px; color: var(--border-light); white-space: nowrap; }
.mctip-pin .th .tx { background: none; border: 0; color: #c4b9a6; font-size: 16px;
  line-height: 1; cursor: pointer; padding: 0 2px; }
.mctip-pin .th .tx:hover { color: var(--border-light); }
.mctip-pin .tb { color: #e4dac6; white-space: pre-wrap; max-height: 46vh; overflow-y: auto; overflow-x: hidden; }
.mctip-pin .tb a.refx, .mctip-pin .tb span.refx { color: var(--border-light); border-bottom: 1px dotted var(--border); cursor: help; }

/* ---- screenshot mode: chrome-free capture with a small site watermark ------ */
body.mcw-shot #sidebar, body.mcw-shot #topbar,
body.mcw-shot #navtoggle, body.mcw-shot #sbshow,
body.mcw-shot #mctip, body.mcw-shot .mctip-pin, body.mcw-shot .mcw-rehide,
body.mcw-shot footer { display: none !important; }
body.mcw-shot #main { padding-top: 20px; }
#mcw-shot-wm {
  position: fixed; right: 14px; bottom: 10px; z-index: 900; pointer-events: none;
  font-family: var(--font-plaque); font-size: 12px; color: var(--border-light); opacity: .85;
  text-shadow: 0 1px 3px #000;
}
#mcw-shot-toast {
  position: fixed; left: 50%; top: 18px; transform: translateX(-50%); z-index: 950;
  background: var(--panel); border: 2px solid var(--border); border-radius: 3px;
  padding: 8px 16px; font-family: var(--font-ui); font-size: 13px; color: var(--border-light);
  transition: opacity .7s ease; pointer-events: none;
}
#mcw-shot-toast.gone { opacity: 0; }

/* ---- spoiler covers ------------------------------------------------------- */
.card.mcw-spoiled > .cb { display: none; }
/* Hiding a gated surface: '> :not(.mcw-spoilcover)' hides ELEMENT children only, so
   gated text that is a bare TEXT NODE stayed readable behind the cover. The host itself
   is therefore hidden too: visibility+colour kill any text node it owns directly,
   font-size:0 stops that dead text from reserving a line box, user-select:none keeps it
   out of a copy/paste. The cover is a child, so it turns all four back on. */
.mcw-spoiled:not(.card) > :not(.mcw-spoilcover) { display: none !important; }
.mcw-spoiled:not(.card) {
  visibility: hidden; color: transparent; font-size: 0; line-height: 0;
  user-select: none; -webkit-user-select: none; text-shadow: none;
}
.mcw-spoiled:not(.card) > .mcw-spoilcover {
  /* --white-outline, not --text: this rule outranks the page retint, and a gated span
     inside the book would otherwise inherit ink onto the cover's navy fill. */
  visibility: visible; color: var(--white-outline); font-size: 13.5px; line-height: 1.35;
  user-select: auto; -webkit-user-select: auto;
}
.mcw-spoiled:not(.card) > .mcw-spoilcover:hover { color: var(--border-light); }
/* dense small spoiler elements (a table row, a list item): blur-to-reveal. */
.mcw-blur { filter: blur(5px); cursor: pointer; user-select: none; }
.mcw-blur:hover { filter: blur(3.5px); }
/* pre-paint guard: while a spoiler mode is ON and app.js has not installed the real
   gates, blanket-blur every tagged surface so nothing flashes readable during load.
   Lifted when app.js stamps body.mcw-applied. */
html.mcw-mode-unreleased body:not(.mcw-applied) [data-spoiler="unreleased"],
html.mcw-mode-story body:not(.mcw-applied) [data-spoiler="story"],
html.mcw-mode-story body:not(.mcw-applied) [data-spoiler="progression"],
html.mcw-mode-story body:not(.mcw-applied) [data-spoiler="boss"] { filter: blur(6px); pointer-events: none; user-select: none; }
.mcw-unveilcue { position: absolute; z-index: 400; display: none; pointer-events: none;
  padding: 3px 10px; font-family: var(--font-ui); font-size: 11px; letter-spacing: .4px;
  color: var(--border-light); background: var(--panel); border: 1px solid var(--border-light);
  border-radius: 3px; box-shadow: 0 2px 8px rgba(0,0,0,.55); white-space: nowrap; }
.mcw-navtag { margin-left: 7px; vertical-align: 1px; }
.mcw-blur:focus-visible { outline: 2px solid var(--border); outline-offset: 1px; }
/* spoiler cover = the game's notched navy box (surface #4). */
.mcw-spoilcover {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 16px 14px; cursor: pointer; border: 2px solid var(--white-outline);
  clip-path: polygon(6px 0, calc(100% - 6px) 0, 100% 6px, 100% calc(100% - 6px), calc(100% - 6px) 100%,
    6px 100%, 0 calc(100% - 6px), 0 6px);
  background: var(--panel);
  color: var(--white-outline); font-family: var(--font-title); font-weight: 700; font-size: 13.5px;
}
.mcw-spoilcover:hover { color: var(--border-light); }
.mcw-spoilcover:focus-visible { outline: 2px solid var(--border-light); outline-offset: -3px; }
.mcw-spoil-i {
  flex: 0 0 auto; width: 20px; height: 13px; border-radius: 2px; opacity: .85;
  background: repeating-linear-gradient(0deg, var(--white-outline) 0 2px, transparent 2px 4px);
}
.mcw-rehide {
  float: right; margin: 0 0 8px 10px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--panel); color: var(--cream-fixed); border: 1px solid var(--border);
  border-radius: 3px; font-family: var(--font-ui); font-size: 11.5px; padding: 3px 9px;
}
.mcw-rehide:hover { color: var(--border-light); border-color: var(--border-light); }
.mcw-rehide-inline { float: none; display: inline-flex; margin: 0 0 0 8px; }
/* hide-unreleased detail page: covers the content area below the topbar */
body.mcw-unrel-page #main > *:not(#topbar):not(#mcw-unrel-cover):not(footer) { display: none; }
#mcw-unrel-cover { display: flex !important; }
.mcw-spoilcover-page {
  border: 2px solid var(--white-outline); margin: 0 0 16px;
  background: var(--panel);
  box-shadow: 0 2px 8px #00000060;
}

/* ---- first-visit spoiler prompt: the brass-framed navy panel (surface #1) ---- */
.mcw-spoilerask {
  position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center;
  padding: 5vh 5vw; background: #08060eaa;
}
.mcw-sa-box {
  max-width: 460px; width: 100%; padding: 20px 24px;
  background-color: var(--panel);
  background-image: var(--img-damask); background-repeat: repeat; background-size: 200px 200px;
  background-blend-mode: soft-light;
  border-image: var(--img-panel-frame) 70 110 70 110 fill stretch;
  border-style: solid; border-width: 16px 22px; border-color: var(--border);
  box-shadow: 0 14px 50px #000000dd;
  color: var(--cream-fixed);
}
.mcw-sa-t { font-family: var(--font-title); font-size: 23px; font-weight: 700; color: var(--border-light); margin-bottom: 8px; }
.mcw-sa-p { color: var(--cream-fixed); font-size: 14px; line-height: 1.55; margin: 0 0 16px; }
.mcw-sa-btns { display: flex; flex-wrap: wrap; gap: 10px; }
.mcw-sa-btns .btn { margin-top: 0; }

/* ---- Buttons (surface #5) -------------------------------------------------
   .btn = the game's navy thickButton 9-slice with cream text ("Back"/"Reverse" in
   ss04/ss09/ss12). .btn.primary = the parchment fill for the emphasised action. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 12px; padding: 0 18px; min-height: 42px; cursor: pointer;
  font-family: var(--font-title); font-weight: 700; font-size: 15px;
  color: var(--cream-fixed); background: var(--panel);
  border-image: var(--img-button) 26 fill stretch;
  border-style: solid; border-width: 9px; border-color: var(--border);
  text-decoration: none;
}
.btn:hover { color: var(--border-light); text-decoration: none; filter: brightness(1.12); }
.btn:focus-visible { outline: 2px solid var(--border-light); outline-offset: 2px; }
.btn.primary {
  /* background-COLOR as well as the gradient: the `background` shorthand resets
     background-color to transparent, so a gradient-only button leaves nothing behind the
     ink text if the gradient ever fails to paint - and check_contrast.py, which reads
     background-color, measured this as ink on the dark page (1.19:1) in dark mode. */
  color: var(--ink-fixed); background-color: var(--plate);
  background-image: linear-gradient(180deg, var(--plate-hi), #dcc79c);
  border-image: none; border: 3px solid var(--border); border-radius: 3px;
  box-shadow: inset 0 0 0 2px #fff6dd, 0 2px 5px #00000055;
}
.btn.primary:hover { color: var(--ink-fixed); filter: brightness(1.04); }
.btn.secondary { background: var(--panel3); color: var(--cream-fixed); }
.bookin .btn { color: var(--cream-fixed); }
.bookin .btn:hover { color: var(--border-light); text-decoration: none; }
.bookin .btn.primary, .bookin .btn.primary:hover { color: var(--ink-fixed); }

/* ---- lightbox ------------------------------------------------------------- */
#mclb {
  display: none; position: fixed; inset: 0; z-index: 900;
  background: #0b0812e8; padding: 4vh 4vw;
  align-items: center; justify-content: center;
}
#mclb.open { display: flex; }
#mclb .mclb-box {
  position: relative; max-width: 100%; max-height: 100%;
  background: var(--panel); border: 2px solid var(--border); border-radius: 3px;
  box-shadow: 0 14px 50px #000000dd; padding: 10px;
}
#mclb img {
  display: block; max-width: 88vw; max-height: 84vh; border-radius: 2px;
  image-rendering: auto; cursor: zoom-out;
}
#mclb .mclb-cap { font-family: var(--font-ui); font-size: 13px; color: var(--cream-fixed); padding: 8px 2px 0; }
#mclb .mclb-close {
  position: absolute; top: -12px; right: -12px; width: 28px; height: 28px; line-height: 24px;
  border-radius: 50%; border: 2px solid var(--border-light); background: var(--panel);
  color: var(--border-light); font-size: 17px; cursor: pointer; padding: 0;
  box-shadow: 0 2px 8px #000a;
}
#mclb .mclb-close:hover { background: var(--panel3); }

/* ---- section-nav scrollspy: the book's ribbon tabs -------------------------
   Hoisted by common.py out of the page body so it can pin under the topbar (a sticky
   element inside the two-column spread would not stick). */
nav.secnav {
  position: sticky; top: calc(var(--topbar-h) + var(--topbar-off, 0px)); z-index: 90;
  display: flex; gap: 6px; overflow-x: auto;
  padding: 8px 2px 9px; margin: 0 0 14px; scrollbar-width: thin;
  background: var(--bg);
}
.secnav-l {
  flex: 0 0 auto; white-space: nowrap; padding: 5px 13px; border-radius: 3px;
  font-family: var(--font-title); font-size: 13.5px; color: var(--text);
  background: #1a1a30; border: 1px solid var(--border);
}
.secnav-l:hover { color: var(--border-light); text-decoration: none; border-color: var(--border-light); }
/* active pill = the parchment-fill/ink-text move, same as an active sidebar row. */
.secnav-l.active { color: var(--ink-fixed); background: var(--plate); border-color: var(--border-light); font-weight: 700; }
.secnav-l.active:hover { color: var(--ink-fixed); background: var(--plate-hi); border-color: var(--border-light); }

/* ---- skip-to-content: off-screen until keyboard focus ---- */
.skip {
  position: absolute; left: 8px; top: -60px; z-index: 2000;
  background: var(--border-light); color: var(--ink-fixed); padding: 8px 14px;
  border-radius: 3px; font-family: var(--font-ui); font-size: 13px;
  text-decoration: none; transition: top .12s;
}
.skip:focus { top: 8px; outline: 2px solid var(--border-light); outline-offset: 2px; }

/* ---- "Show tips" gating ---------------------------------------------------
   THE YELLOW BOX IS A TIP BY DEFAULT: every .notice hides with tips off unless it
   carries .mcw-keep - the audited opt-out for boxes a reader is actively misled or
   stuck without. Do not reach for .mcw-keep to keep something merely useful. */
body.mcw-hidetips .mcw-tip { display: none; }
body.mcw-hidetips .notice:not(.mcw-keep):not(.mcw-x-open) { display: none; }
body.mcw-hidetips .mcw-explain:not(.mcw-x-open) { display: none; }
.mcw-qmark {
  display: none; width: 22px; height: 22px; border-radius: 50%;
  align-items: center; justify-content: center; cursor: pointer;
  background: var(--parchment-shade); border: 1px solid var(--rule); color: var(--ink-light);
  font-family: var(--font-ui); font-size: 13px; padding: 0; vertical-align: middle;
  margin: 2px 0;
}
.mcw-qmark:hover { border-color: var(--border); color: var(--link-dark); }
body.mcw-hidetips .mcw-qmark { display: none; }
/* .notice = the same notched navy box as #mctip (surface #4) */
.notice {
  background: var(--panel); border: 2px solid var(--white-outline); color: var(--cream-fixed);
  clip-path: polygon(6px 0, calc(100% - 6px) 0, 100% 6px, 100% calc(100% - 6px), calc(100% - 6px) 100%,
    6px 100%, 0 calc(100% - 6px), 0 6px);
  padding: 11px 15px; margin: 0 0 16px; font-family: var(--font-title); font-size: 14px;
  --text: var(--cream-fixed); --muted: #c4b9a6; --link: var(--border-light);
  --link-hover: #fff6dd; --pos: #8fe08a; --neg: #ff9a9a;
}
.notice a { color: var(--border-light); }
.notice a:hover { color: #fff0cc; }

/* ---- collapsible detail-page cards (click the heading; session-only) ---- */
.card > h3.collh { cursor: pointer; user-select: none; position: relative; padding-right: 30px; }
.card > h3.collh:hover { color: var(--link-dark); }
.card > h3.collh:focus-visible { outline: 2px solid var(--border); outline-offset: -2px; }
.card > h3.collh::after {
  content: ""; position: absolute; right: 8px; top: 42%; width: 8px; height: 8px; margin-top: -6px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .18s ease; opacity: .6;
}
.card.collapsed > h3.collh::after { transform: rotate(-45deg); margin-top: -4px; }
.card.collapsed > .cb { display: none; }
@media (prefers-reduced-motion: reduce) { .card > h3.collh::after { transition: none; } }

/* ---- typography, prepared for future mirrors ---- */
html[lang="ko"] .tnm, html[lang="ko"] h1, html[lang="ko"] h3, html[lang="ko"] .chip,
html[lang="ko"] .badge { word-break: keep-all; }
html[lang="de"] .md, html[lang="de"] td, html[lang="de"] .card { hyphens: auto; }
html[lang="de"] .constbl td { overflow-wrap: anywhere; }

h2.sec {
  font-family: var(--font-title); font-size: 24px; font-weight: 700; margin: 28px 0 10px;
  color: var(--text-dark); padding-bottom: 5px; border-bottom: 2px solid var(--rule);
}
.bookin > h2.sec:first-child { margin-top: 4px; }
h3.pagetitle { font-size: 26px; color: var(--text-dark); margin: 0 0 10px; }
/* Long-paragraph prose: the book face at 17px/1.65 with a measured line length. */
.md p { margin: 10px 0; font-size: 17px; line-height: 1.65; }
.md ul, .md ol { margin: 8px 0; padding-left: 22px; }
.md li { font-size: 16.5px; line-height: 1.6; margin: 4px 0; }
.card .cb.md p, .md p, .card .cb > p { max-width: 74ch; }
.md h3 { font-family: var(--font-title); font-size: 18px; font-weight: 700; color: var(--text-dark); margin: 18px 0 6px; }
.md table { border-collapse: collapse; margin: 10px 0; }
.md table td, .md table th { border: 1px solid var(--rule); padding: 5px 10px; font-size: 13.5px; }
.md code { background: var(--parchment-shade); color: var(--ink-light); padding: 1px 5px; border-radius: 3px; }
footer {
  margin: 40px 22px 0; padding: 14px 0 22px; border-top: 1px solid #3a2c46;
  color: var(--muted); font-size: 12.5px;
}
footer a { color: var(--link); }

/* ---- print: strip nav/chrome, ink-friendly, full-width content ---- */
@media print {
  #sidebar, #topbar, #navbackdrop, #mctip, .skip, #sectabs { display: none !important; }
  :root, body { --topbar-off: 0px !important; }
  nav.secnav { transform: none !important; visibility: visible !important; position: static; }
  #layout { display: block !important; }
  #main { margin: 0 !important; padding: 0 !important; }
  body, #main, #book, .card, .mini, .detailhead { background: #fff !important; color: #000 !important;
    box-shadow: none !important; border-image: none !important; border: 0 !important; }
  .book-spread .bookin { column-count: 1 !important; background-image: none !important; }
  a { color: #000 !important; text-decoration: underline; }
  .card, .mini, table, .detailhead { break-inside: avoid; page-break-inside: avoid; }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* ---- mobile ---- */
@media (max-width: 900px) {
  html, body { overflow-x: clip; }
  .cards, .grid-cards { grid-template-columns: 1fr; }
  .card, .mini, .stat { min-width: 0; }
  .card .cb { overflow-x: auto; }
  h2.sec { font-size: 20px; overflow-wrap: anywhere; }
  .md code, p code, .notice code { overflow-wrap: anywhere; }
  table.db td code { overflow-wrap: normal; white-space: nowrap; }
  .kv td:first-child { width: 42%; }
  .detailhead > div:last-child { min-width: 0; }
  #sidebar {
    position: fixed; left: 0; top: 0; bottom: 0; height: 100vh; width: 280px; flex-basis: 280px;
    transform: translateX(-105%); transition: transform .2s ease; box-shadow: 6px 0 28px #000000cc;
  }
  #sidebar.open { transform: translateX(0); }
  #navbackdrop.show { display: block; position: fixed; inset: 0; background: #00000099; z-index: 190; }
  #navtoggle { display: block; }
  #main { padding: 0 10px 80px; }
  footer { margin: 30px 10px 0; }
  /* >=16px inputs: stops iOS Safari focus-zoom */
  input[type=text], textarea, select,
  #searchbox, .filters input[type=text] { font-size: 16px; }
  /* sticky first column: keep the entity readable while the table pans */
  table.db th:first-child, table.db td:first-child {
    position: sticky; left: 0; z-index: 5;
    background: var(--parchment);
    box-shadow: 6px 0 8px -6px #00000055;
  }
  table.db th:first-child { z-index: 12; }
  table.db tbody tr:nth-child(even) td:first-child { background: var(--parchment-zebra); }
  table.db tbody tr:hover td:first-child { background: var(--parchment-shade); }
  .entcell { min-width: 108px; max-width: 46vw; }
  .entcell > span { min-width: 0; overflow: hidden; }
  .entcell > span > a, .entcell .sub { display: block; overflow: hidden; white-space: nowrap;
    text-overflow: clip;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 14px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 14px), transparent); }
  #topbar { flex-wrap: wrap; gap: 8px; padding: 8px 10px 0; align-items: center; }
  #topbar #brand { font-size: 12px; border-width: 8px 11px; margin-bottom: 8px; }
  #topbar #brandsep { display: none; }
  #topbar h1 { font-size: 17px; flex: 1 1 auto; min-width: 0; margin-bottom: 8px; }
  /* the desktop rule caps the title at 26vw so the tab strip cannot squeeze it; on a
     phone there is no tab strip and 26vw is 101px, which clipped "maguscircle.wiki"
     to "maguscircle.wi" on the home screen. */
  #topbar #pagename { flex: 1 1 auto; max-width: none; }
  #sectabs { display: none; }
  #topbar.home #brand, #topbar.home #brandsep { display: none; }
  #topbar.home #pagename { flex: 1 1 auto; text-align: center; margin: 0 0 8px; }
  #searchwrap { order: 5; width: 100%; max-width: none; margin: 0 0 8px; }
  /* the book keeps its deckled frame but thins it so it does not eat a narrow viewport */
  #book { border-width: 18px; padding: 2px 2px 12px; }
  .detailhead { flex-direction: row; gap: 12px; padding: 2px 0 12px; }
  /* keep the sigil and the name on ONE row, the way the PhoneSpell artboard has them.
     dh-main's flex-basis is `auto` (its content width) at desktop, which on a phone is
     wider than the row and made it wrap under the icon, leaving a tall empty band beside
     a 62px sigil. Basis 0 lets it share the row and shrink instead. */
  .detailhead .dh-main { flex: 1 1 0; min-width: 0; }
  .detailhead .icon { width: 62px; height: 62px; flex: 0 0 62px; border-width: 9px; }
  .detailhead .icon img { max-width: 46px; max-height: 46px; }
  .detailhead h1, .detailhead h2 { font-size: 23px; }
  .detailhead .desc { font-size: 15.5px; }
  .mini { border-width: 18px; }
  table.db th, table.db td { padding: 7px 8px; }
  .entcell { min-width: 140px; }
  /* touch targets >= 40px */
  .chip { min-height: 40px; display: inline-flex; align-items: center; }
  #sidebar a.nav { min-height: 40px; }
  .btn { min-height: 44px; }
  .filters .fcount { flex-basis: 100%; text-align: right; }
  nav.secnav { padding-left: 0; padding-right: 0; }
}
@media (pointer: coarse) {
  #sidebar a.nav { padding-top: 9px; padding-bottom: 9px; }
  #searchresults a { padding: 10px 12px; }
  .chip { min-height: 40px; display: inline-flex; align-items: center; }
  table.db th { padding-top: 11px; padding-bottom: 11px; }
  #navtoggle { padding: 8px 14px; font-size: 22px; }
  .mclb-close { width: 30px; height: 30px; line-height: 26px; font-size: 19px; }
}

/* ---- COMPOSED SURFACES: a navy box printed on the parchment page -----------
   The page retint above sets --text/--muted/--link to their INK values on everything
   inside #book. A component that paints itself with the NAVY fill and then reads
   var(--text) would therefore render ink on navy (measured 1.2:1 in the v1 equivalent of
   this trap, F-E in docs/recon). Every navy surface that can appear inside the book
   restates the five foreground tokens for itself. The first group is this file's own;
   the second is the page modules' (pages_tarot.py's .mcw-tt-* and pages_characters.py's
   tiles) - listed here rather than edited there because style.css owns the floor that
   makes an unreadable composition impossible, and because that lane is not this one.
   If you add a component with a dark fill, add it here. */
#mctip, .mctip-pin, .notice, .mcw-spoilcover, .mcw-spoilcover-page, .mcw-rehide,
.mcw-sa-box, .mcw-unveilcue, .tarot-info, .dialogue-line .speaker, .tpill,
.mcw-tt, .mcw-tt-info, .mcw-tt-tile, .mcw-tt-kind, .cstile, .aboxt, .secnav-l, .chip.on,
.btn:not(.primary), #mcw-shot-toast, .colcust-panel .mcsel-menu {
  --text: var(--cream-fixed);
  --muted: #c4b9a6;
  --link: var(--border-light);
  --link-hover: #fff6dd;
  --pos: #8fe08a;
  --neg: #ff9a9a;
}
.mcw-tt, .mcw-tt-info, .mcw-tt-tile, .mcw-tt-kind, .cstile, .aboxt { color: var(--cream-fixed); }
.mcw-tt a, .mcw-tt-info a, .mcw-tt-tile a, .mcw-tt-kind a, .cstile a { color: var(--border-light); }
/* .mcw-tt is pages_tarot.py's constellation panel: a navy gradient with the deck tiles
   on it. Its background is a GRADIENT, so check_contrast.py (which reads
   background-color) cannot see it and reported nothing while its caption rendered ink
   on midnight blue at about 1.3:1 in light mode. Caught by looking at the screenshot,
   which is why the visual pass is a gate and not a courtesy. */
.tarot-info { color: var(--cream-fixed); }

/* A table.db that lands INSIDE a .card or .mini is on the same parchment page as one at
   top level, so it needs no separate palette any more (v1 had to retint it because the
   table carried its own dark rows). The only thing left to restate is the sticky header
   fill, which must not go transparent over scrolling rows. */
:is(.card, .detailhead, .mini) table.db th { background: var(--parchment); }
:is(.card, .detailhead, .mini) table.db tbody tr:nth-child(even) td { background: var(--parchment-zebra); }
:is(.card, .detailhead, .mini) table.db tbody tr:hover td { background: var(--parchment-shade); }
.mini table.db th, .mini table.db td { padding-left: 4px; padding-right: 4px; }

/* ---- Tooltips follow the theme (owner 2026-08-19 10:50) ----------------------------
   LIGHT (default): the hover window and the pinned panel are a parchment note - paper
   fill, brown ink, brass outline, same notched shape. DARK (html.theme-dark): the game's
   navy notched box stays as it was. All token pairs re-measured by check_contrast. */
html:not(.theme-dark) #mctip, html:not(.theme-dark) .mctip-pin {
  background: var(--paper-white, #f5ecd6); color: var(--text-dark, #3a2816);
  border-color: var(--border, #b07040);
  box-shadow: 0 8px 22px #00000066;
  --text: var(--text-dark, #3a2816); --muted: var(--muted-ink, #6b5030); --link: var(--link-dark, #6b3f14);
  --link-hover: #2a1808; --pos: var(--pos-ink, #1f6b28); --neg: var(--neg-ink, #a02c2c);
}
html:not(.theme-dark) #mctip .th b, html:not(.theme-dark) .mctip-pin .th b { color: var(--text-dark, #3a2816); }
html:not(.theme-dark) #mctip .tb, html:not(.theme-dark) .mctip-pin .tb { color: var(--text-dark, #3a2816); }
html:not(.theme-dark) #mctip .tb .fxr-sc.tag-self, html:not(.theme-dark) .mctip-pin .tb .fxr-sc.tag-self { color: var(--pos-ink, #1f6b28); }
html:not(.theme-dark) #mctip .tb .fxr-sc.tag-enemy, html:not(.theme-dark) .mctip-pin .tb .fxr-sc.tag-enemy { color: var(--neg-ink, #a02c2c); }
html:not(.theme-dark) #mctip .tpill, html:not(.theme-dark) .mctip-pin .tpill { background: var(--parchment-shade, #dcc79c); color: var(--text-dark, #3a2816); border-color: var(--border, #b07040); }
html:not(.theme-dark) #mctip a, html:not(.theme-dark) .mctip-pin a { color: var(--link-dark, #6b3f14); }
html:not(.theme-dark) .mctip-pin .th .tx { color: var(--text-dark, #3a2816); }

/* ---- LIGHT CHROME: the menubar follows the theme (owner ask 2026-08-19) ------------
   LIGHT: the topbar and the sidebar are parchment surfaces too, with the brass frame and
   the navy brand plaque kept as art. Nav rows are cream tiles with ink text; the SELECTED
   row flips to the navy plate with cream text (the game's signature move, inverted for a
   light panel). DARK (html.theme-dark): the navy HUD look above, untouched. */
html:not(.theme-dark) #topbar {
  --text: var(--text-dark); --muted: var(--muted-ink); --link: var(--link-dark); --link-hover: var(--link-dark-hover);
  background: linear-gradient(180deg, #e8ccaaf5, #dcbc97f5);
  box-shadow: 0 2px 0 var(--border-dark), 0 6px 16px rgba(0,0,0,.35);
}
html:not(.theme-dark) #topbar h1 { color: var(--text-dark); }
html:not(.theme-dark) #sectabs .tab { background-color: #c8a97e; background-image: linear-gradient(180deg, #d1b389, #bd9d72); }
html:not(.theme-dark) #sectabs .tab:hover { background-color: #dcc394; background-image: linear-gradient(180deg, #e6d0a4, #d2b885); }
html:not(.theme-dark) #sectabs .tab.active { background-color: #faf2dc; background-image: linear-gradient(180deg, #fbf4e2, #f0e2c2); box-shadow: inset 0 1px 0 #fffaf0, 0 3px 0 #f6ecd4; }
html:not(.theme-dark) #searchbox, html:not(.theme-dark) #langsel, html:not(.theme-dark) .mcsel-btn {
  background: var(--paper-white); color: var(--text-dark);
}
html:not(.theme-dark) #searchbox::placeholder { color: var(--muted-ink); }
html:not(.theme-dark) .mcsel-btn:hover { color: var(--link-dark-hover); border-color: var(--border-dark); }
html:not(.theme-dark) .mcsel-btn .mcsel-arrow { color: var(--border-dark); }
html:not(.theme-dark) .mcsel-menu { background: var(--paper-white); border-color: var(--border); box-shadow: 0 10px 28px #00000088; }
html:not(.theme-dark) .mcsel-opt { color: var(--text-dark); }
html:not(.theme-dark) .mcsel-opt:hover, html:not(.theme-dark) .mcsel-opt.hl { background: var(--parchment-shade); color: var(--link-dark-hover); }
html:not(.theme-dark) .mcsel-opt.on { background: #fbf4e2; color: var(--text-dark); box-shadow: inset 0 0 0 1px var(--border-dark); }
html:not(.theme-dark) #searchresults {
  --text: var(--text-dark); --muted: var(--muted-ink);
  background-color: var(--paper-white); background-image: none; box-shadow: 0 12px 32px rgba(0,0,0,.45);
}
html:not(.theme-dark) #searchresults a { color: var(--text-dark); }
html:not(.theme-dark) #searchresults a:hover, html:not(.theme-dark) #searchresults a.sel { background: var(--parchment-shade); color: var(--link-dark-hover); }
html:not(.theme-dark) #searchresults .stype, html:not(.theme-dark) #searchresults .snone { color: var(--muted-ink); }
html:not(.theme-dark) #navtoggle, html:not(.theme-dark) #sbshow { background: var(--paper-white); color: var(--link-dark); }
html:not(.theme-dark) #sbshow:hover { color: var(--link-dark-hover); border-color: var(--border-dark); }

html:not(.theme-dark) #sidebar {
  --text: var(--text-dark); --muted: var(--muted-ink); --label-muted: var(--ink-light);
  --link: var(--link-dark); --link-hover: var(--link-dark-hover);
  background-color: var(--chrome-paper);
  background-image: none;
}
html:not(.theme-dark) #sidebar .logo small { color: var(--ink-light); }
html:not(.theme-dark) #sidebar .group > .gt { color: var(--ink-light); }
html:not(.theme-dark) #sidebar .navgt:hover, html:not(.theme-dark) #sidebar .sb-set-gt:hover,
html:not(.theme-dark) #sidebar .sb-foldhead:hover { color: var(--text-dark); }
html:not(.theme-dark) #sidebar .navgt:focus-visible, html:not(.theme-dark) #sidebar .sb-set-gt:focus-visible,
html:not(.theme-dark) #sidebar .sb-segb:focus-visible, html:not(.theme-dark) #sidebar .sb-check:focus-within,
html:not(.theme-dark) #sidebar .sb-foldbtn:focus-visible { outline-color: var(--border-dark); }
html:not(.theme-dark) #sidebar a.nav {
  --text: var(--text-dark); --muted: var(--muted-ink); --link: var(--link-dark); --link-hover: var(--link-dark-hover);
  color: var(--text-dark); background: var(--paper-white);
  border-color: rgba(176, 112, 64, .65); box-shadow: inset 0 1px 0 #ffffff80;
}
html:not(.theme-dark) #sidebar a.nav:hover { background: var(--parchment-shade); color: var(--link-dark-hover); border-color: var(--border); }
html:not(.theme-dark) #sidebar a.nav .cnt { color: var(--muted-ink); }
html:not(.theme-dark) #sidebar a.nav.active {
  --text: var(--text-dark); --muted: var(--muted-ink); --link: var(--link-dark); --link-hover: var(--link-dark-hover);
  background: #fbf4e2; color: var(--text-dark); border-color: var(--border-dark);
  box-shadow: inset 0 0 0 1px #fffaf0, inset 3px 0 0 var(--border), 0 1px 3px #00000055;
}
html:not(.theme-dark) #sidebar a.nav.active .cnt { color: var(--muted-ink); }
html:not(.theme-dark) #sidebar .sb-foldbtn { background: var(--paper-white); color: var(--muted-ink); }
html:not(.theme-dark) #sidebar .sb-foldbtn:hover { color: var(--text-dark); border-color: var(--border-dark); }
html:not(.theme-dark) #sidebar .sb-set, html:not(.theme-dark) .sb-subhead { border-bottom-color: var(--rule-soft); }
html:not(.theme-dark) .sb-subhead { color: var(--ink-light); }
html:not(.theme-dark) #sidebar .sb-row { color: var(--text-dark); }
html:not(.theme-dark) #sidebar .sb-row.sb-check:hover { background: #00000012; color: var(--text-dark); }
html:not(.theme-dark) #sidebar .sb-lbl, html:not(.theme-dark) #sidebar .sb-hint { color: var(--muted-ink); }
html:not(.theme-dark) #sidebar .sb-seg { background: var(--paper-white); }
html:not(.theme-dark) #sidebar .sb-segb { color: var(--muted-ink); }
html:not(.theme-dark) #sidebar .sb-segb:hover { color: var(--text-dark); }
html:not(.theme-dark) #sidebar .sb-segb.on { background: #fbf4e2; color: var(--text-dark); box-shadow: inset 0 0 0 1px var(--border-dark); }
html:not(.theme-dark) #sidebar .sb-shotbtn { background: var(--paper-white); color: var(--text-dark); }
html:not(.theme-dark) #sidebar .sb-shotbtn:hover { color: var(--link-dark-hover); border-color: var(--border-dark); }
html:not(.theme-dark) #sbtoggle { color: var(--ink-light); }
html:not(.theme-dark) #sbtoggle:hover { color: var(--text-dark); }
/* the panel frame's 9-slice carries the navy interior in its centre tile ("fill"); in the
   light theme the centre is dropped so the parchment background shows through the frame. */
html:not(.theme-dark) #sidebar { border-image: url("ui/PopUpPanel_10_light.png") 70 110 70 110 fill stretch; }
/* the brand plaque keeps its gold-on-navy art in both themes (the sidebar's link token is
   ink in the light theme, and #sidebar .logo span inherits it); the topbar carries a solid
   cream under its gradient so the composed background is the cream, not the desk. */
/* (the plaque's own light variant is defined below: "LIGHT PLAQUE") */
html:not(.theme-dark) #topbar { background-color: var(--chrome-paper); background-image: linear-gradient(180deg, #e8ccaaf5, #dcbc97f5); }
/* the section strip (nav.secnav) under the topbar followed the dark HUD in the light theme
   (navy pills on the dark desk, owner 2026-08-19 11:40). LIGHT: a tan band that continues
   the topbar, cream pills with ink, the active pill the bright sheet with a brass edge. */
html:not(.theme-dark) nav.secnav {
  --text: var(--text-dark); --muted: var(--muted-ink); --link: var(--link-dark); --link-hover: var(--link-dark-hover);
  background: var(--chrome-paper);
  border-bottom: 2px solid var(--border); box-shadow: 0 1px 0 var(--border-dark), 0 4px 10px rgba(0,0,0,.25);
  padding: 8px 10px 9px; margin: -20px 0 18px; border-radius: 0 0 4px 4px;
}
html:not(.theme-dark) .secnav-l { color: var(--text-dark); background: var(--paper-white); border-color: rgba(176, 112, 64, .65); }
html:not(.theme-dark) .secnav-l:hover { color: var(--link-dark-hover); background: var(--parchment-shade); border-color: var(--border); }
html:not(.theme-dark) .secnav-l.active { color: var(--text-dark); background: #fbf4e2; border-color: var(--border-dark); box-shadow: inset 0 0 0 1px #fffaf0; }
html:not(.theme-dark) .secnav-l.active:hover { color: var(--text-dark); background: #fbf4e2; border-color: var(--border-dark); }

/* ---- LIGHT PLAQUE (owner 2026-08-19 12:05: "a light & dark mode to the logo") -------
   LIGHT: the title plaque keeps the brass frame with its diamond finials (the 9-slice,
   centre tile dropped) over a cream plate with ink text. DARK: gold on navy, as the game
   draws it. Applies to every plaque: the brand in the topbar and the sidebar, story act
   headings, scene plaques. */
html:not(.theme-dark) .plaque, html:not(.theme-dark) #topbar #brand {
  border-image: url("ui/Title_light.png") 40 55 40 55 fill stretch;
  /* no background: the sprite's centre tile is the cream plate; a background colour also
     painted the box BEHIND the sprite's transparent outer band (owner 2026-08-19 18:29). */
  background: transparent; color: var(--text-dark); text-shadow: none;
}
html:not(.theme-dark) #topbar #brand:hover, html:not(.theme-dark) #sidebar .logo:hover .plaque { color: var(--link-dark-hover); }
/* one brand on a desktop: while the sidebar is on screen its plaque is the brand, and the
   topbar's copy is hidden; it returns when the sidebar is collapsed (#sbtoggle) and on
   phones, where the sidebar is a drawer. (owner 2026-08-19 12:05) */
@media (min-width: 901px) {
  body:not(.sb-collapsed) #topbar #brand { display: none; }
}
