/* Noto is self-hosted and its cmap is tested for the full Polish alphabet.
   Keep the reader and UI families explicit: no product-specific aliases can
   silently point at a partial font subset again. */
@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/noto-serif-400.65a09f4eb5.ttf") format('truetype')}
@font-face{font-family:'Noto Serif';font-style:normal;font-weight:600 700;font-display:swap;
  src:url("fonts/noto-serif-700.3614f8029f.ttf") format('truetype')}
@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;font-display:swap;
  src:url("fonts/noto-serif-400-italic.ef624da0bf.ttf") format('truetype')}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/noto-sans-400.c59994685c.ttf") format('truetype')}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:600 700;font-display:swap;
  src:url("fonts/noto-sans-700.042f891db7.ttf") format('truetype')}
@font-face{font-family:'Noto Sans';font-style:italic;font-weight:400;font-display:swap;
  src:url("fonts/noto-sans-400-italic.b01c149d81.ttf") format('truetype')}

/* Display headline fonts (blackletter/gothic). Two @font-face blocks per
   family with Google's own unicode-range split -- the browser auto-picks
   latin-ext for Polish diacritics (ą ć ę ł ń ś ź ż) and latin for the rest
   (ó included, U+00F3 is in Latin-1). */
@font-face{font-family:'Gwyn Display';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/new-rocker-400.latin-ext.0062fe6968.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Gwyn Display';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/new-rocker-400.latin.051f98221f.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Gwyn Display Sub';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/pirata-one-400.latin-ext.227f037b23.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Gwyn Display Sub';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/pirata-one-400.latin.c374e15f38.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
/* Homepage-only headline pair (base.css .home-hero h1 / .section-title):
   Cinzel Decorative for the hero H1, its calmer sibling Cinzel for the
   library section titles -- same two-block latin/latin-ext split as above. */
@font-face{font-family:'Cinzel Decorative';font-style:normal;font-weight:700;font-display:swap;
  src:url("fonts/cinzel-decorative-700.latin-ext.fba82a4be1.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Cinzel Decorative';font-style:normal;font-weight:700;font-display:swap;
  src:url("fonts/cinzel-decorative-700.latin.1a21ed4ee2.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Cinzel';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/cinzel-400.latin-ext.50b0f43409.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Cinzel';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/cinzel-400.latin.0be240fb63.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

/* Gwyn -- base.css: palette, shell chrome, landing, library, footer.
   Shared by every page (index/podrecznik/postacie/biblioteka). */

:root{
  --obelisk-void:#070c09;
  --obelisk-stone:#151c18;
  --obelisk-green:#3fa866;
  --shard-glow:#91f29a;
  --parchment:#f1f0e8;
  --mist:#9aa79f;
  --bg:var(--obelisk-void); --panel:var(--obelisk-stone);
  --ink:var(--parchment); --muted:var(--mist);
  --accent:var(--obelisk-green); --accent-bar:var(--obelisk-green);
  --line:#304138; --tblhead:#1d2922;
  --badge:#5a3a1a;
  /* Layering scale (GWYN CHANGE, mobile nav redesign): formalizes the
     informal 6/7/8/20/30/40 scheme the floating chrome already used --
     collisions between same-layer fixed elements (e.g. the old search FAB
     vs. to-top) used to be avoided only by hand-tuned `bottom` offsets, the
     same fragility that let the pin panel's dead breakpoint go unnoticed. */
  --z-scrim:6; --z-drawer:7; --z-float-chrome:8;
  --z-modal:20; --z-toast:30; --z-lightbox:40;
  /* ONE reading measure for prose, shared by .book--flat + md.css. clamp()
     not a fixed px: 840px read as "too wide" (2026-07-14), 760px still felt
     wide on very large monitors (2026-07-15 AM) -- scales with viewport
     instead of picking one number that's only right at one width. 620px
     floor then read as too narrow (2026-07-15 PM) -> bumped to 680px; same
     round asked for the reading column a bit wider still overall -> ceiling
     760->820px (tables now match this width too, see .tbl grid-column). */
  --measure:clamp(680px, 44vw, 820px);
}
*{box-sizing:border-box}
/* No html{scroll-behavior:smooth}: with content-visibility:auto on hundreds
   of .page-flow blocks (book.css), smooth scroll forces sequential layout of
   everything it passes over and the landing point drifts. Instant jumps are
   deterministic. */
/* Noto Serif is bundled first, not last. Its character map is regression
   tested for the complete Polish alphabet, so every reader sees the same
   diacritics instead of falling back per glyph to a system font. */
body{margin:0;background:var(--bg);color:var(--ink);
  font:400 18px/1.5 "Noto Serif",Georgia,serif}
a{color:inherit}
.sr-only{position:absolute!important;width:1px!important;height:1px!important;
  padding:0!important;margin:-1px!important;overflow:hidden!important;
  clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}

.brand-seal{display:grid;place-items:center;flex:0 0 48px;width:48px;height:48px;
  color:var(--shard-glow);transition:transform .32s ease}
.brand-seal-svg{display:block;width:100%;height:100%;overflow:visible;
  filter:drop-shadow(0 0 1px rgba(145,242,154,.08));transition:filter .55s ease}
.seal-interactive:hover .brand-seal-svg,
.seal-interactive:focus-visible .brand-seal-svg,
.seal-interactive.seal--awake .brand-seal-svg{
  filter:drop-shadow(0 0 4px rgba(145,242,154,.34))
    drop-shadow(0 0 10px rgba(63,168,102,.15))}
.brand-seal-arc{fill:none;stroke-linecap:round;vector-effect:non-scaling-stroke}
.brand-seal-arc--soft{stroke:rgba(145,242,154,.24);stroke-width:.8}
.brand-seal-arc--mid{stroke:rgba(145,242,154,.48);stroke-width:.9}
.brand-seal-arc--bright{stroke:rgba(169,255,162,.74);stroke-width:1.05;
  filter:drop-shadow(0 0 2px rgba(145,242,154,.3))}
.brand-seal-arc-a,.brand-seal-arc-b{transform-box:view-box;transform-origin:60px 65px}
.brand-seal-arc-a{animation:brand-seal-arc-a 112s linear infinite}
.brand-seal-arc-b{animation:brand-seal-arc-b 137s linear infinite}
@keyframes brand-seal-arc-a{
  0%{transform:rotate(0)}6%{transform:rotate(42deg)}12%{transform:rotate(87deg)}
  18%{transform:rotate(126deg)}23%{transform:rotate(119deg)}30%{transform:rotate(191deg)}
  36%{transform:rotate(235deg)}41%{transform:rotate(230deg)}49%{transform:rotate(314deg)}
  55%{transform:rotate(359deg)}61%{transform:rotate(354deg)}69%{transform:rotate(444deg)}
  76%{transform:rotate(494deg)}82%{transform:rotate(487deg)}90%{transform:rotate(585deg)}
  96%{transform:rotate(654deg)}100%{transform:rotate(720deg)}
}
@keyframes brand-seal-arc-b{
  0%{transform:rotate(0)}6%{transform:rotate(-37deg)}12%{transform:rotate(-82deg)}
  18%{transform:rotate(-126deg)}23%{transform:rotate(-120deg)}31%{transform:rotate(-201deg)}
  37%{transform:rotate(-241deg)}43%{transform:rotate(-236deg)}51%{transform:rotate(-320deg)}
  58%{transform:rotate(-367deg)}64%{transform:rotate(-361deg)}72%{transform:rotate(-447deg)}
  79%{transform:rotate(-495deg)}84%{transform:rotate(-489deg)}91%{transform:rotate(-582deg)}
  96%{transform:rotate(-648deg)}100%{transform:rotate(-720deg)}
}
.brand-seal-rune-reveal{opacity:.075;transition:opacity .65s ease}
.brand-seal-rune-ink{fill:none;stroke:rgba(131,255,145,.98);stroke-width:.68;
  stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke;
  animation:brand-seal-rune-whisper 73s ease-in-out infinite}
.brand-seal-rune-slot.w .brand-seal-rune-ink{animation-delay:-17s}
.brand-seal-rune-slot.y .brand-seal-rune-ink{animation-delay:-34s}
.brand-seal-rune-slot.n .brand-seal-rune-ink{animation-delay:-52s}
.seal-interactive:hover .brand-seal-rune-reveal,
.seal-interactive:focus-visible .brand-seal-rune-reveal,
.seal-interactive.seal--awake .brand-seal-rune-reveal{opacity:.42}
.seal-interactive:hover .brand-seal-rune-ink,
.seal-interactive:focus-visible .brand-seal-rune-ink,
.seal-interactive.seal--awake .brand-seal-rune-ink{
  filter:drop-shadow(0 0 2.2px rgba(145,242,154,.42))}
@keyframes brand-seal-rune-whisper{
  0%,100%{opacity:.34}7%{opacity:.65}13%{opacity:.42}21%{opacity:.88}
  28%{opacity:.38}36%{opacity:.72}43%{opacity:.32}52%{opacity:.82}
  60%{opacity:.45}71%{opacity:.76}80%{opacity:.35}90%{opacity:.58}
}
.brand-seal-runner{transform-box:view-box;transform-origin:0 0;
  animation:brand-seal-runner 41s linear infinite}
.brand-seal-runner--echo1{animation-delay:-.28s}
.brand-seal-runner--echo2{animation-delay:-.56s}
@keyframes brand-seal-runner{
  0%{transform:translate(23px,40px) rotate(22deg);opacity:0}3%{opacity:1}
  7%{transform:translate(35px,22px) rotate(38deg)}13%{transform:translate(58px,10px) rotate(78deg)}
  19%{transform:translate(82px,14px) rotate(112deg)}24%{transform:translate(78px,13px) rotate(108deg)}
  31%{transform:translate(101px,35px) rotate(148deg)}38%{transform:translate(108px,61px) rotate(184deg)}
  44%{transform:translate(104px,58px) rotate(180deg)}52%{transform:translate(94px,91px) rotate(222deg)}
  59%{transform:translate(70px,114px) rotate(262deg)}65%{transform:translate(67px,112px) rotate(258deg)}
  73%{transform:translate(42px,111px) rotate(302deg)}79%{transform:translate(20px,93px) rotate(336deg)}
  84%{transform:translate(12px,70px) rotate(360deg);opacity:1}88%,96%{opacity:0}
  100%{transform:translate(23px,40px) rotate(382deg);opacity:0}
}
.brand-seal-satellite{fill:var(--shard-glow);stroke:#e6ffdf;stroke-width:.55;
  vector-effect:non-scaling-stroke;filter:drop-shadow(0 0 4px rgba(145,242,154,.8))}
.brand-seal-runner--echo1 .brand-seal-satellite{opacity:.15}
.brand-seal-runner--echo2 .brand-seal-satellite{opacity:.06}
.brand-seal-facet--1{fill:#73d78b}.brand-seal-facet--2{fill:#59bf75}
.brand-seal-facet--3{fill:#419d5f}.brand-seal-facet--4{fill:#307b4b}
.brand-seal-facet--5{fill:#245e3b}.brand-seal-facet--6{fill:#1c4d31}
.brand-seal-outline{fill:none;stroke:var(--shard-glow);stroke-width:1.25;
  stroke-linejoin:round;vector-effect:non-scaling-stroke}
.brand-seal-edge{fill:none;stroke:rgba(236,255,229,.52);stroke-width:.82;
  stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.brand-seal-core{transform-box:fill-box;transform-origin:center;
  animation:brand-seal-core 37s ease-in-out infinite;
  filter:drop-shadow(0 0 3px rgba(83,206,116,.15))}
@keyframes brand-seal-core{
  0%,100%{transform:translate(-.15px,.7px) rotate(-.2deg)}
  11%{transform:translate(.15px,.25px) rotate(-.05deg)}
  23%{transform:translate(.35px,-.55px) rotate(.2deg)}
  34%{transform:translate(.25px,-.72px) rotate(.26deg)}
  43%{transform:translate(.2px,-.68px) rotate(.23deg)}
  56%{transform:translate(-.2px,-.15px) rotate(.04deg)}
  68%{transform:translate(-.38px,.45px) rotate(-.17deg)}
  76%{transform:translate(-.34px,.52px) rotate(-.2deg)}
  88%{transform:translate(-.2px,.1px) rotate(-.08deg)}
}
.brand-seal-glint{fill:#e2ffdd;filter:drop-shadow(0 0 3px rgba(190,255,182,.8));
  transform-box:fill-box;transform-origin:center;opacity:0;
  animation:brand-seal-glints 53s ease-in-out infinite}
.brand-seal-glint--2{animation-delay:-17.7s}.brand-seal-glint--3{animation-delay:-35.3s}
@keyframes brand-seal-glints{
  0%,8%{opacity:0;transform:scale(.55)}10%{opacity:.22;transform:scale(.72)}
  12%{opacity:.9;transform:scale(1)}14%{opacity:.55;transform:scale(.86)}
  17%,61%{opacity:0;transform:scale(.6)}64%{opacity:.12;transform:scale(.68)}
  66%{opacity:.42;transform:scale(.82)}68%,100%{opacity:0;transform:scale(.6)}
}
/* Edge tabs: discoverable drawer/column triggers pinned to the viewport
   edge. GWYN CHANGE (mobile nav redesign, round 3 -- round 2's mistake was
   the opposite of round 1's: making the visible shape and the tap target
   the SAME 20x72px pill fixed "trudno zauważyć" (hard to notice) but read
   as heavy/generic. This round splits them again, correctly this time --
   .edge-tab is the tap target (wide, mostly invisible padding), and
   .edge-tab-shard is a small but solid, on-brand faceted sliver (the same
   crystal-facet silhouette family as .home-search-shard/.panel-search-
   shard, not a generic chevron pill) rather than round 1's near-invisible
   hairline. Tall and parked near the top (200px tall, top:5%) instead of
   vertically centered, per operator reference. */
.edge-tab{position:fixed;top:5%;height:224px;z-index:var(--z-float-chrome);
  width:40px;padding:0;border:0;background:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  transition:left .22s ease,right .22s ease}
.edge-tab[hidden]{display:none}
.edge-tab--left{left:var(--gwyn-left-edge,0px);transform:translateX(-50%)}
.edge-tab--right{right:var(--gwyn-right-edge,0px);transform:translateX(50%)}
.edge-tab-shard{position:relative;flex:0 0 16px;width:16px;height:150px;
  display:flex;align-items:center;justify-content:center;
  clip-path:polygon(50% 0,100% 5%,100% 95%,50% 100%,0 95%,0 5%);
  background:linear-gradient(180deg,rgba(83,206,116,.55) 0%,
    rgba(20,52,34,.92) 50%,rgba(83,206,116,.55) 100%);
  box-shadow:0 0 10px rgba(83,206,116,.28);opacity:.82;
  transition:box-shadow .16s ease,filter .16s ease,opacity .16s ease}
.edge-tab:hover .edge-tab-shard,.edge-tab:focus-visible .edge-tab-shard{
  box-shadow:0 0 16px rgba(105,236,126,.55);filter:brightness(1.12);opacity:1}
.edge-tab-mark{display:block;width:9px;height:9px;flex:0 0 auto;
  color:#f2fff5;filter:drop-shadow(0 1px 1px rgba(0,0,0,.65));
  transition:transform .16s ease}
.edge-tab--right .edge-tab-mark{transform:scaleX(-1)}
.edge-tab--left[aria-expanded="true"] .edge-tab-mark{transform:rotate(180deg)}
.edge-tab--right[aria-expanded="true"] .edge-tab-mark{transform:scaleX(-1) rotate(180deg)}
.edge-tab:focus-visible{outline:2px solid rgba(145,242,154,.9);outline-offset:2px}
@media(prefers-reduced-motion:reduce){.edge-tab,.edge-tab-shard{transition:none}}
/* Follows the open drawer's own edge instead of staying stranded at the
   true viewport edge (GWYN CHANGE round 3 -- operator: round 2's tab
   stayed put at the screen edge once the drawer slid open, ending up
   floating awkwardly under/beside it instead of reading as its handle).
   :has() lets plain CSS react to a state change on a element that's
   neither an ancestor nor a sibling of .edge-tab (the panel is a CHILD of
   <main>, the tab is a SIBLING of <main> -- no combinator reaches
   sideways-then-down without it). Browsers without :has() just keep
   round 2's placement (tab stays at the true edge) -- a real fallback
   position, not a broken one. These selectors cover overlay mode;
   book.css adds the matching persistent-column positions at desktop.
   min() mirrors .side-panel--right's own width:360px;max-width:86vw so the tab
   lands exactly at the drawer's edge even on very narrow phones. */
body:has(.side-panel--left.open){--gwyn-left-edge:var(--gwyn-left-overlay-width,300px)}
body:has(.side-panel--right.open){--gwyn-right-edge:var(--gwyn-right-overlay-width,360px)}
/* GWYN CHANGE (mobile nav redesign, round 2): no longer force-hidden at
   desktop widths -- postacie's panels stay true overlays at every width
   now (they never grow persistent desktop columns), so the scrim needs to
   be available there too. nav.js only shows it while an overlay-mode
   drawer is actually open (never for a persistent desktop column), so
   there's nothing left for a width-based CSS rule to add. */
.nav-scrim{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:var(--z-scrim)}
.nav-scrim[hidden]{display:none}

/* Side panels: off-canvas drawers, opened by the matching .edge-tab. GWYN
   CHANGE (mobile nav redesign, round 2): left and right now share one
   shape (previously the left drawer lived only in book.css as
   .reader .sidebar / .sidebar--drawer, and the right one was an unrelated
   "wide panel with a 2120px cliff" in chrome.css). This overlay shape is
   the default at EVERY width -- postacie has neither a TOC nor pins, so it
   keeps a closed-by-default tap-to-reveal drawer at every width instead of
   ever becoming a persistent column (a permanently-open column with almost
   nothing in it would just be empty space). Panels marked data-persistent
   (podrecznik, biblioteka -- see book.css) override this default with a
   real desktop column at >=901px instead. position:fixed already removes
   the panel from normal flow, so there's no need for a display:none
   default -- transform alone keeps it off-screen until opened, and stays
   transitionable (display can't be). */
.side-panel{position:fixed;top:0;bottom:0;z-index:var(--z-drawer);
  width:300px;max-width:86vw;overflow-y:auto;transition:transform .22s ease;
  background:rgba(15,28,20,.97);padding:1rem .85rem 1.1rem;
  font-family:"Noto Sans",system-ui,-apple-system,'Segoe UI',sans-serif;
  scrollbar-width:thin;scrollbar-color:#44403a transparent}
.side-panel--left{left:0;width:var(--gwyn-left-overlay-width,300px);transform:translateX(-102%);box-shadow:2px 0 16px rgba(0,0,0,.5)}
.side-panel--right{right:0;width:var(--gwyn-right-overlay-width,360px);transform:translateX(102%);box-shadow:-2px 0 16px rgba(0,0,0,.5)}
.side-panel.open{transform:translateX(0)}
/* A phone needs a genuinely visible reading column while the search field
   still keeps a usable result measure. Desktop keeps the established 360px
   rail. */
@media(max-width:900px){
  body{--gwyn-left-overlay-width:min(360px,86vw);--gwyn-right-overlay-width:min(336px,68vw)}
  .side-panel--left{max-width:86vw}
  .side-panel--right{max-width:68vw}
}
/* Character pages use the same left overlay as the reader.  Its home lockup
   must remain visible there too; otherwise the opened drawer is empty on
   desktop because only persistent reader panels override the default hide. */
.side-panel--left:not([data-persistent]) .reader-drawer-home{display:flex}
@media(prefers-reduced-motion:reduce){.side-panel{transition:none}}

/* Global search field embedded at the top of the right panel (GWYN CHANGE,
   round 2: replaces a click-to-open modal). Narrower sibling of the
   homepage's .home-search-field -- same idea (crystal-shard icon + plain
   input), sized for a ~300px column instead of a centered hero. */
.panel-search{margin:0 0 1rem}
.panel-search-field{position:relative;display:flex;align-items:center;gap:.55rem;
  min-height:44px;padding:.5rem .6rem;background:rgba(7,16,11,.85);
  border:1px solid rgba(145,242,154,.32);border-radius:7px;
  transition:border-color .2s ease,background .2s ease}
.panel-search-field:focus-within{background:rgba(7,16,11,.97);
  border-color:rgba(184,255,192,.65)}
.panel-search-shard{flex:0 0 22px;width:22px;height:22px}
.panel-search .panel-search-input{min-width:0;flex:1;width:auto;padding:0;border:0;
  background:transparent;color:#edf5ec;outline:0;font-size:.88rem;
  -webkit-appearance:none;appearance:none}
.panel-search-input::placeholder{color:#9aa79f;opacity:.85}
.panel-search-input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}
.panel-search-input::-ms-clear{display:none}
/* Fixed (not max-) height, GWYN CHANGE round 3: operator complaint --
   typing changes the result count, which changed this list's natural
   height, which shifted .pin-section below it up/down on every keystroke.
   A max-height alone doesn't fix that (it only caps the tall end; 1 result
   is still shorter than 6). Reserving a constant height the moment the
   list becomes visible -- enough for ~3 rows, .search-item's own
   min-height:3.45rem (chrome.css) -- means .pin-section moves at most
   once (when the field is first focused and the hidden list appears),
   never again while typing; overflow:auto (chrome.css .search-results)
   already scrolls past 3 without growing the box. The empty-query
   placeholder (.search-empty) renders inside the same fixed box instead
   of a shorter one, which is the deliberate trade: some idle whitespace
   under 1-2 results, in exchange for a list that never jumps under you. */
.panel-search-results{margin-top:.4rem;height:min(11.5rem,45vh)}

.pin-section{margin-top:.5rem;padding-top:.85rem;border-top:1px solid rgba(145,242,154,.18)}
.pin-section[hidden]{display:none}
.pin-section-title{font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);margin:0 0 .5rem;padding:0 .2rem}

/* GWYN CHANGE (mobile nav redesign, round 2): the crystal lens used to sit
   on a <button> that opened a modal search overlay -- now the search field
   itself is always directly editable (.panel-search-input), so the lens is
   purely decorative next to it (no cursor:pointer -- nothing to click). */
.search-lens-trigger{--lens-ring-stroke:2.2;--lens-handle-stroke:2.7;
  --lens-energy-primary:97s;--lens-energy-secondary:131s;
  position:relative;display:grid;place-items:center;padding:0;border:0;
  background:transparent;color:#91f29a;isolation:isolate;
  transition:transform .32s ease}
.search-lens-svg{display:block;width:100%;height:100%;overflow:visible;
  filter:drop-shadow(0 0 2px rgba(145,242,154,.14));
  transition:filter .42s ease}
.search-lens-frame{pointer-events:none;opacity:.82;transition:opacity .34s ease}
.search-lens-ring,.search-lens-bevel,.search-lens-handle,.search-lens-energy-track,
.search-lens-edge,.search-lens-eye-shard,
.search-lens-blink-flare{fill:none;stroke-linecap:round;stroke-linejoin:round;
  vector-effect:non-scaling-stroke}
.search-lens-ring{stroke:#a6eab0;stroke-width:var(--lens-ring-stroke)}
.search-lens-handle{stroke:#b7efbd;stroke-width:var(--lens-handle-stroke)}
.search-lens-bevel{stroke:rgba(105,236,126,.48);stroke-width:.72}
.search-lens-energy-track{stroke:#91f29a;stroke-width:.66;opacity:.58;
  transform-box:view-box;transform-origin:32px 28px}
.search-lens-energy--primary{
  animation:search-lens-energy var(--lens-energy-primary) linear infinite}
.search-lens-energy--secondary{
  animation:search-lens-energy var(--lens-energy-secondary) linear infinite reverse}
.search-lens-facet--1{fill:#72d78a}.search-lens-facet--2{fill:#58bd74}
.search-lens-facet--3{fill:#378d55}.search-lens-facet--4{fill:#225d3a}
.search-lens-edge{stroke:#9cf5a5;stroke-width:.8}
.search-lens-edge--inner{opacity:.52;stroke-width:.48}
.search-lens-eye-shard{stroke:rgba(211,255,214,.42);stroke-width:.62}
.search-lens-gaze{transform:translate(var(--lens-gaze-x,0),var(--lens-gaze-y,0));
  transform-box:view-box;transform-origin:center;
  transition:transform .16s cubic-bezier(.2,.75,.25,1);
  animation:search-lens-gaze 29s ease-in-out infinite}
.search-lens-trigger[data-pointer-gaze="true"] .search-lens-gaze{animation:none}
.search-lens-pupil-diamond{fill:#f2fff1;filter:drop-shadow(0 0 2px #91f29a)}
.search-lens-blink-flare{stroke:#efffee;stroke-width:.55;opacity:0}
.search-lens-pupil{transform-box:fill-box;transform-origin:center;
  animation:search-lens-blink 31s ease-in-out infinite}
@keyframes search-lens-energy{to{transform:rotate(360deg)}}
@keyframes search-lens-gaze{
  0%,12%,100%{transform:translate(0,0)}18%,25%{transform:translate(1.4px,-.8px)}
  32%,58%{transform:translate(0,0)}64%,70%{transform:translate(-1.1px,.65px)}
  76%,91%{transform:translate(0,0)}
}
@keyframes search-lens-blink{
  0%,11.7%,12.5%,64.2%,65%,100%{transform:scaleY(1)}
  12.05%,64.55%{transform:scaleY(.22)}
}
@keyframes search-lens-flare{
  0%,11.75%,12.38%,64.25%,64.88%,100%{opacity:0;transform:scaleX(.25)}
  12.05%,64.55%{opacity:.9;transform:scaleX(1)}
}
.search-lens-blink-flare{transform-box:fill-box;transform-origin:center;
  animation:search-lens-flare 31s ease-in-out infinite}
.search-lens-trigger:hover,.search-lens-trigger:focus-visible,
.search-lens-trigger.search-lens--awake{--lens-energy-primary:38s;--lens-energy-secondary:53s;
  transform:translateY(-1px) scale(1.055)}
.search-lens-trigger:hover .search-lens-svg,
.search-lens-trigger:focus-visible .search-lens-svg,
.search-lens-trigger.search-lens--awake .search-lens-svg{
  filter:drop-shadow(0 0 3px rgba(181,255,188,.4))
    drop-shadow(0 0 8px rgba(83,206,116,.18))}
.search-lens-trigger:hover .search-lens-frame,
.search-lens-trigger:focus-visible .search-lens-frame,
.search-lens-trigger.search-lens--awake .search-lens-frame{opacity:1}
.search-lens-trigger:hover .search-lens-ring,
.search-lens-trigger:focus-visible .search-lens-ring{stroke:#ddffe0}
.search-lens-trigger:hover .search-lens-handle,
.search-lens-trigger:focus-visible .search-lens-handle{stroke:#effff0}

/* --topbar-clear: top breathing room for text-only surfaces that don't
   supply their own hero art. GWYN CHANGE (mobile nav redesign, round 2):
   used to also compensate for a floating topbar/nav-cluster overlapping
   the first line of content -- that chrome is gone entirely now (brand
   moved into the left panel, search into the right panel), but the
   plain-text surfaces below still want some clear air above their first
   heading, so the variable stays, just for that simpler reason. */
:root{--topbar-clear:5.2rem}
main.app{padding:0}
/* Text-only surfaces reserve room INSIDE themselves. Hero surfaces and the
   PHB opener deliberately begin behind the floating chrome at y=0. */
.view-critique{padding-top:var(--topbar-clear)}
.view-chars{padding-top:0}
.view-char:not(.view-char--hero):not(.view-char--codex){padding-top:var(--topbar-clear)}
.view-char--hero{position:relative}
.view-char--codex{position:relative;padding-top:0}

/* Whole-homepage backdrop (operator request: the party/goblin-vista art
   should carry the page, not just the hero band). Lives on .view-home (the
   section wrapping hero + roster + both libraries, pages.py build_landing)
   rather than .home-hero alone -- covers/home-bg.jpg is anchored top-right
   (that's where the goblin cluster sits in the source art) and sized to
   the viewport HEIGHT, not `cover`: `cover` fits both axes of the
   *element's own box*, and .view-home is many screens tall, so `cover`
   would blow the image up to page-height instead of screen-height. `auto
   100vh` + no-repeat means the art only actually appears for the first
   viewport or so; .view-home's background-color is the fallback for
   everything below that, and the ::before gradient (vh-based stops, not %,
   for the same reason) settles into flat var(--bg) by ~115vh so the two
   meet seamlessly instead of the page trailing off into a paler flat tone.
   Direct children get position:relative + z-index so they sit above the
   gradient layer instead of behind it.

   `fixed` (operator: tło "nie ma być przesuwalne"): anchors the image to
   the VIEWPORT, not to .view-home's own (very tall) box -- classic CSS
   parallax. This is safe on .view-home specifically (not body): no
   ancestor of .view-home sets transform/filter/contain/perspective, any
   of which would silently break fixed's viewport-relative positioning.
   ::before deliberately stays background-attachment:scroll (the default)
   -- its gradient is meant to travel WITH the content (that's how the
   page gets progressively darker toward the footer), only the photo
   itself should stay pinned. */
.view-home{position:relative;
  background-image:url("covers/home-bg.jpg");
  background-repeat:no-repeat;
  background-attachment:fixed;
  background-position:center right;
  background-size:cover;
  background-color:var(--bg)}
/* Plain darkening scrim, no color washes. A flat vertical darken keeps text
   legible over the
   photo, easing to var(--bg) by ~115vh so it meets the flat page below
   seamlessly. */
.view-home::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(10,9,8,.36) 0,rgba(10,9,8,.50) 45vh,
    rgba(10,9,8,.80) 85vh,var(--bg) 115vh)}
.view-home>*{position:relative;z-index:1}
@media(max-width:640px){
  /* Narrower viewport -> auto-width shrinks with it (aspect ratio locked),
     which crops from the LEFT first (background-position:right) -- the
     goblin cluster on the right stays in frame without extra rules. Mobile
     stays noticeably darker than desktop at every stop (less clean
     background around the text at this crop). */
  .view-home::before{background:linear-gradient(180deg,rgba(10,9,8,.46) 0,
    rgba(10,9,8,.62) 35vh,rgba(10,9,8,.85) 70vh,var(--bg) 95vh)}
}
.home-hero{position:relative;overflow:visible;min-height:clamp(340px,50svh,560px);
  display:flex;align-items:center;justify-content:center}
.home-intro{min-height:100svh;display:flex;flex-direction:column;justify-content:center;
  padding-bottom:clamp(1rem,2.2vh,2.2rem)}
.home-hero-inner{position:relative;width:100%;max-width:1080px;margin:0 auto;
  padding:calc(var(--topbar-clear) + 1rem) 1.5rem 2.2rem;text-align:center}
.home-hero-title{margin:0 0 .55rem;line-height:1;text-transform:uppercase;
  text-shadow:0 2px 14px rgba(0,0,0,.7)}
.home-hero-kicker{display:flex;align-items:center;justify-content:center;gap:1rem;
  margin:0 0 .55rem;font-family:'Cinzel',Georgia,serif;font-weight:400;
  font-size:clamp(.85rem,1.8vw,1.15rem);letter-spacing:.34em;color:#e8d0a0}
.home-hero-kicker::before,.home-hero-kicker::after{content:"";height:1px;
  flex:1 1 7rem;max-width:10rem;background:linear-gradient(90deg,transparent,#fff)}
.home-hero-kicker::after{background:linear-gradient(90deg,#fff,transparent)}
.home-hero-name{display:block;font-family:'Cinzel Decorative',Georgia,serif;
  font-weight:700;font-size:clamp(2.65rem,7.5vw,5.35rem);line-height:1.04;
  letter-spacing:.055em;
  white-space:nowrap;
  color:#fffdf7;
  background:linear-gradient(180deg,#fffdf7 0%,#f5ead3 58%,#e6d0a6 100%);
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  text-shadow:none;filter:drop-shadow(0 2px 12px rgba(0,0,0,.86))
    drop-shadow(0 0 2px rgba(255,248,228,.34))}
.home-hero-flourish{margin:0 0 1.3rem;color:#fffdf7;opacity:.92}
.home-hero-flourish-svg{width:min(360px,68vw);height:auto;display:block;margin:0 auto}
@media(max-width:560px){.home-hero-flourish{margin-bottom:1rem}}
.home-search{position:relative;z-index:4;width:min(560px,calc(100vw - 2rem));margin:0 auto}
.home-search-field{position:relative;display:flex;align-items:center;gap:.72rem;
  min-height:58px;padding:.78rem 4.6rem .78rem .9rem;background:rgba(7,16,11,.92);
  border:1px solid rgba(145,242,154,.44);border-radius:7px;
  box-shadow:0 10px 30px rgba(0,0,0,.38);
  transition:border-color .22s ease,box-shadow .3s ease,background .22s ease}
.home-search-field:hover,.home-search-field:focus-within{background:rgba(7,16,11,.96);
  border-color:rgba(184,255,192,.7);
  box-shadow:0 12px 34px rgba(0,0,0,.46),0 0 18px rgba(145,242,154,.12)}
.home-search-shard{position:relative;flex:0 0 16px;width:16px;height:27px;
  clip-path:polygon(50% 0,91% 34%,80% 82%,50% 100%,18% 82%,8% 34%);
  background:linear-gradient(105deg,#2f7b4b 0 42%,#72d78a 43% 67%,#3fa866 68%);
  filter:drop-shadow(0 0 4px rgba(145,242,154,.5))}
.home-search-shard::after{content:"";position:absolute;left:50%;top:4px;bottom:4px;
  width:1px;background:rgba(226,255,221,.56);transform:translateX(-50%)}
.home-search .home-search-input{min-width:0;flex:1;width:auto;padding:0;border:0;
  background:transparent;color:#edf5ec;outline:0;font-size:1rem;
  -webkit-appearance:none;appearance:none}
.home-search-input::placeholder{color:#9aa79f;opacity:.92}
.home-search-input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}
.home-search-input::-ms-clear{display:none}
.home-search-shortcut{pointer-events:none;position:absolute;right:.72rem;top:50%;
  transform:translateY(-50%);width:max-content;padding:.24rem .42rem;
  border:1px solid rgba(145,242,154,.22);border-radius:3px;
  background:rgba(13,25,17,.8);color:#aeeeb5;
  font:600 .64rem/1.2 system-ui,sans-serif;letter-spacing:.045em;
  box-shadow:inset 0 0 8px rgba(145,242,154,.035)}
@media(hover:none),(pointer:coarse){.home-search-shortcut{display:none}
  .home-search-field{padding-right:.9rem}}
.home-hero .tagline{color:#d7d1c6;font-size:1.05rem;max-width:34rem;
  margin:.5rem auto 0;text-wrap:balance;text-shadow:0 1px 8px rgba(0,0,0,.6)}
.home-hero-cta{display:inline-block;margin-top:1.1rem;color:var(--accent);
  font-family:system-ui,sans-serif;font-size:.88rem;font-weight:600;
  text-decoration:none;border-bottom:1px solid transparent;
  transition:border-color .12s ease}
.home-hero-cta:hover{border-color:var(--accent)}
@media(max-width:560px){
  .home-intro{min-height:0;display:block;padding-bottom:2rem}
  .home-hero{min-height:390px}
  .home-hero-kicker{letter-spacing:.24em}
  .home-hero-name{white-space:normal}
  .home-search{width:100%}
  .home-search-field{padding-left:.78rem}
}
@media(max-width:900px){
  .search-lens-trigger{--lens-ring-stroke:2;--lens-handle-stroke:2.45}
}
@media(max-width:640px){
  .brand-seal{width:44px;height:44px;flex-basis:44px}
}
@media(prefers-reduced-motion:reduce){
  .brand-seal{transition:none}
  .brand-seal-svg,.brand-seal-rune-reveal{transition:none}
  .brand-seal-arc-a,.brand-seal-arc-b,.brand-seal-runner,.brand-seal-core,
  .brand-seal-glint,.brand-seal-rune-ink{animation:none!important}
  .brand-seal-runner{display:none}
  .brand-seal-glint--1{opacity:.55}
  .brand-seal-rune-ink{opacity:.55}
  .search-lens-energy--primary,.search-lens-energy--secondary,
  .search-lens-pupil{animation:none!important}
  .search-lens-blink-flare,.search-lens-gaze{animation:none!important}
  .search-lens-gaze{transform:none!important;transition:none}
  .search-lens-trigger{transition:none}
}

/* Party roster section (homepage): "PATERA", big and centered, over a row
   of coin-flip tokens (gwynbuild.chars.roster_tokens_html). Replaces the
   old plain .section-title + rectangular .libcard grid the shelf still
   uses on postacie.html -- this section deliberately reads as more epic
   than a plain library listing (operator: "epicka strona-wizytówka"). */
.roster-section{max-width:1100px;margin:.2rem auto 1rem;padding:0 1.5rem;text-align:center}
.roster-section>.section-title{padding-left:0;padding-right:0;text-align:left}
.roster-section-link{display:block;text-decoration:none;transition:color .14s ease,text-shadow .14s ease}
.roster-section-link:hover,.roster-section-link:focus-visible{color:#fff;outline:none;
  text-shadow:0 0 18px rgba(145,242,154,.3)}
.patera-title{display:flex;align-items:center;justify-content:center;gap:.9rem;
  margin:0;font-family:'Gwyn Display',Georgia,serif;font-weight:400;
  font-size:clamp(2.6rem,7vw,4.2rem);letter-spacing:.03em;color:#f3e7d0;
  text-shadow:0 2px 18px rgba(63,168,102,.34),0 2px 14px rgba(0,0,0,.75)}
.patera-ornament{flex:0 0 auto;width:2.6rem;height:.7rem;color:var(--accent)}
.patera-ornament--left{transform:scaleX(-1)}
@media(max-width:640px){.patera-ornament{width:1.6rem}}
.roster-intro{color:var(--muted);font-size:1rem;font-weight:500;max-width:64ch;
  margin:.7rem auto 0;font-family:"Noto Serif",Georgia,serif;
  text-wrap:pretty}
/* Coin-flip tokens have two explicit variants: curated character art with a
   real token reverse, or a static graphite monogram. Shared campaign scenery
   is never presented as substitute character art. */
.patera-tokens{display:flex;flex-wrap:wrap;justify-content:center;gap:1.8rem 1.3rem;
  margin:1.35rem auto 0}
.patera-token{display:flex;flex-direction:column;align-items:center;gap:.55rem;
  width:7rem;text-decoration:none;color:var(--ink);transition:transform .12s ease}
.patera-token:hover,.patera-token:focus-visible{transform:translateY(-3px)}
.patera-token-coin{position:relative;width:7rem;height:7rem;perspective:900px}
/* display:block is load-bearing, not decorative: this is a <span> (inline
   by default), and width/height are simply IGNORED on inline elements per
   spec -- without this, the flip wrapper collapses to zero size and the
   whole coin disappears. Art tokens use this wrapper; monograms do not. */
.patera-token-flip{display:block;position:relative;width:100%;height:100%;
  transform-style:preserve-3d;transition:transform .6s cubic-bezier(.4,.1,.2,1)}
.patera-token-face{position:absolute;inset:0;width:100%;height:100%;border-radius:50%;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  border:2px solid var(--line);box-shadow:0 3px 10px rgba(0,0,0,.5);
  transition:border-color .12s ease}
.patera-token-face--front{position:relative;background:#101713;color:#f1f0e8}
.patera-token-front-art{position:absolute;inset:0;width:100%;height:100%;display:block;
  object-fit:cover;transform:scale(1.035)}
.patera-token-face--monogram{position:relative;background:
  radial-gradient(circle at 36% 28%,#424743 0,#292d2a 42%,#151817 100%);
  border-color:#505652;box-shadow:inset 0 0 0 1px rgba(255,255,255,.035),
  0 3px 12px rgba(0,0,0,.58)}
.patera-token-letter{font:400 clamp(3rem,4.5vw,4.5rem)/1 'Gwyn Display',Georgia,serif;
  color:#d4d2cb;text-shadow:0 2px 10px #000}
/* A lone face in the coin remains positioned so the circular clipping and
   hit area stay identical to the flipping art variant. */
.patera-token-coin>.patera-token-face--front{position:relative;width:100%;height:100%}
.patera-token-face--back{width:100%;height:100%;max-width:100%;max-height:100%;
  transform:rotateY(180deg);object-fit:cover;background:var(--panel)}
@media(hover:hover){
  .patera-token--flip:hover .patera-token-flip,
  .patera-token--flip:focus-visible .patera-token-flip{transform:rotateY(180deg)}
}
.patera-token:hover .patera-token-face,
.patera-token:focus-visible .patera-token-face{border-color:var(--accent)}
.patera-token-name{font-family:system-ui,sans-serif;font-size:1.02rem;font-weight:750;
  letter-spacing:.035em;color:#ece4d1;text-shadow:0 2px 8px rgba(0,0,0,.8);
  text-align:center;line-height:1.25}
.patera-token:hover .patera-token-name,
.patera-token:focus-visible .patera-token-name{color:var(--ink)}
.patera-token--dead .patera-token-face--front,
.patera-token--dead .patera-token-face--back,
.patera-token--dead .patera-token-face--monogram{filter:grayscale(1) brightness(.78)}
.home-obelisk-link{display:flex;align-items:center;justify-content:center;
  gap:clamp(1rem,3vw,2.4rem);width:min(100%,760px);margin:0 auto .7rem;
  text-decoration:none;color:var(--shard-glow);outline-offset:.4rem}
.home-obelisk-link .brand-seal-svg{width:260px;height:auto;flex:0 0 auto}
.home-obelisk-label{position:relative;max-width:22rem;padding-top:1rem;
  font:700 clamp(1rem,1.9vw,1.42rem)/1.35 'Cinzel',Georgia,serif;
  letter-spacing:.17em;text-align:left;text-transform:uppercase;color:#f0dfb8;
  text-shadow:0 2px 12px rgba(0,0,0,.88)}
.home-obelisk-label::before{content:"";position:absolute;left:0;top:0;width:100%;height:1px;
  background:linear-gradient(90deg,#91f29a,rgba(145,242,154,.28),transparent)}
.home-obelisk-link:hover .home-obelisk-label,
.home-obelisk-link:focus-visible .home-obelisk-label{color:#fffdf7}
@media(max-width:560px){
  .home-obelisk-link{flex-direction:column;gap:.2rem;margin-bottom:1rem}
  .home-obelisk-link .brand-seal-svg{width:196px}
  .home-obelisk-label{padding-top:.75rem;text-align:center;font-size:.86rem}
  .home-obelisk-label::before{background:linear-gradient(90deg,transparent,#91f29a,transparent)}
}
@media(prefers-reduced-motion:reduce){.patera-token-flip{transition:none}}

/* The campaign is often shown on 21:9 and 5K2K displays. The image remains
   viewport-covering while the ceremonial sections gain breathing room;
   reading measures stay unchanged elsewhere. */
@media(min-width:2200px) and (min-height:1200px){
  .home-hero-inner,.roster-section{max-width:1800px}
  .home-hero{min-height:clamp(520px,30vh,720px)}
  .home-hero-name{font-size:clamp(5rem,5.4vw,8.5rem)}
  .patera-tokens{gap:clamp(1.5rem,1.5vw,3.2rem)}
  .patera-token{width:clamp(7rem,5vw,10rem)}
  .patera-token-coin{width:clamp(7rem,5vw,10rem);height:clamp(7rem,5vw,10rem)}
}

/* PL covers: angled 3D product-shot renders with real alpha -- the flat
   card frame (border/shadow/panel bg) would box the floating perspective
   render awkwardly, so it comes off and the render sits directly on the
   page background; contain (not cover) so nothing gets cropped. */
.libcard--render3d .libcard-cover{background:transparent;border:0;
  box-shadow:none;border-radius:0;overflow:visible}
.libcard--render3d .libcard-img{object-fit:contain;
  aspect-ratio:1401/1966;
  filter:drop-shadow(0 6px 14px rgba(0,0,0,.5))}
.libcard--render3d:hover .libcard-cover{border:0;box-shadow:none}
/* DMG/MM PL placeholder cards ("w przygotowaniu"): same .libcard shape as a
   real book, but a <div> not a link -- nothing to open yet -- so hover/lift
   is switched off and the REAL Polish cover shows heavily washed out
   (grayscale, not just dimmed -- operator: "bardziej wyszarzone") under the
   badge. */
.libcard--pending{cursor:default}
.libcard--pending:hover{transform:none}
.libcard--pending .libcard-img{filter:grayscale(.85) opacity(.55)
  drop-shadow(0 6px 14px rgba(0,0,0,.5))}
.libcard--pending:hover .libcard-cover{border-color:var(--line);
  box-shadow:0 3px 10px rgba(0,0,0,.45)}

/* Section titles */
.section-title{max-width:1100px;margin:.2rem auto .6rem;padding:0 1.5rem;
  color:#e8d9b5;border-bottom:2px solid var(--accent)}
/* Scoped to .view-home (not a plain .section-title override): this class
   is shared with postacie.html and design-critique.html, and the operator
   wants the new display font on the homepage only this round -- a
   descendant selector reaches just the two "Biblioteka..." h2s here
   without touching those other pages' headings or their Python/HTML. */
.view-home .section-title{font-family:'Cinzel',Georgia,serif;font-weight:400}
.library-section{max-width:1120px;margin:0 auto;padding:clamp(5rem,9vh,8rem) 0 1rem}
.library-section+.library-section{padding-top:clamp(3rem,6vh,5rem)}
.library-section>.section-title{max-width:900px;margin:0 auto 1rem;padding:0 1.5rem 1rem;
  text-align:center;border:0;position:relative;text-wrap:balance}
.library-section>.section-title::after{content:"";position:absolute;left:50%;bottom:0;
  width:min(540px,72%);height:1px;transform:translateX(-50%);
  background:linear-gradient(90deg,transparent,var(--accent),transparent)}

/* Library: cover wall (cover on top, title below). */
.library{max-width:1160px;margin:0 auto;padding:1rem 1.5rem 3.5rem;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,180px));
  justify-content:center;gap:1.6rem 1.4rem;align-items:start}
.libcard{display:flex;flex-direction:column;gap:.5rem;text-decoration:none;
  color:var(--ink);transition:transform .14s ease;position:relative}
.libcard:hover{transform:translateY(-4px)}
.libcard-cover{position:relative;display:block;border-radius:6px;overflow:hidden;
  border:1px solid var(--line);box-shadow:0 3px 10px rgba(0,0,0,.45);
  transition:border-color .14s ease,box-shadow .14s ease}
.libcard:hover .libcard-cover{border-color:var(--accent);
  box-shadow:0 6px 18px rgba(0,0,0,.6)}
.libcard-img{display:block;width:100%;aspect-ratio:766/1000;object-fit:cover}
.libcard-img--missing{display:flex;align-items:center;justify-content:center;
  text-align:center;padding:.6rem;background:#332a29;color:#e8d9b5;font-weight:700;
  font-size:.9rem}
.libcard-title{font-weight:700;font-size:1.02rem;color:#e8d9b5;line-height:1.2}
.libcard-sub{color:var(--muted);font-size:.8rem;font-family:system-ui,sans-serif;
  line-height:1.3}
.libcard-size{color:var(--muted);font-size:.72rem;font-family:system-ui,sans-serif;
  margin-top:auto}
.pill{display:inline-block;font-family:system-ui,sans-serif;font-size:.68rem;
  padding:.1rem .45rem;border-radius:10px;vertical-align:middle}
.pending-pill{background:var(--badge);color:#e8c9a0}
.libcard-badge{position:absolute;top:.4rem;left:.4rem;box-shadow:0 1px 4px rgba(0,0,0,.5)}

/* Character-index campaign feature. The complete supplied cover stays
   contained; atmosphere comes from gradients behind it, never cropping. */
.campaign-feature{position:relative;overflow:hidden;min-height:clamp(700px,88svh,1120px);
  padding-top:var(--topbar-clear);background:
  radial-gradient(circle at 74% 42%,rgba(63,168,102,.23),transparent 31%),
  linear-gradient(115deg,#070c09 0%,#101a13 52%,#070c09 100%)}
.campaign-feature::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(124deg,transparent 0 44px,rgba(145,242,154,.018) 45px 46px)}
.campaign-feature-inner{position:relative;width:min(calc(100% - 3rem),1800px);min-height:inherit;
  margin:0 auto;padding:clamp(3rem,7vw,7rem) 0;display:grid;
  grid-template-columns:minmax(20rem,.82fr) minmax(24rem,1.18fr);
  align-items:center;gap:clamp(2rem,6vw,8rem)}
.campaign-feature-copy{position:relative;z-index:1}
.campaign-feature-kicker{margin:0;color:#91f29a;font:600 .78rem/1.2 system-ui,sans-serif;
  letter-spacing:.28em;text-transform:uppercase}
.campaign-feature-copy h1{margin:.7rem 0 0;color:#f1f0e8;font:400 clamp(4rem,8vw,8rem)/.86 'Gwyn Display',Georgia,serif;
  text-shadow:0 6px 36px #000}
.campaign-feature-copy p:not(.campaign-feature-kicker){max-width:58ch;color:#c7d0c9;
  font-size:clamp(1rem,1.25vw,1.3rem);line-height:1.72}
.campaign-feature-ornament{display:block;width:min(26rem,82%);height:2px;margin:1.8rem 0;
  background:linear-gradient(90deg,#3fa866,#91f29a 62%,transparent)}
.campaign-feature-link{display:inline-flex;gap:.7rem;align-items:center;margin-top:1rem;
  min-height:44px;color:#f1f0e8;text-decoration:none;font:650 .9rem/1 system-ui,sans-serif;
  letter-spacing:.08em;text-transform:uppercase;border-bottom:1px solid #3fa866}
.campaign-feature-link:hover,.campaign-feature-link:focus-visible{color:#91f29a;outline:none;border-color:#91f29a}
.campaign-feature-art{display:flex;justify-content:center;align-items:center;min-width:0}
.campaign-feature-cover{display:block;width:auto;max-width:100%;height:auto;max-height:min(78svh,940px);
  object-fit:contain;filter:drop-shadow(0 30px 42px rgba(0,0,0,.7))}
@media(min-width:2200px){
  .campaign-feature-inner{width:min(calc(100% - 8rem),2800px);grid-template-columns:minmax(34rem,.8fr) minmax(48rem,1.2fr)}
}
@media(max-width:700px){
  .campaign-feature{min-height:auto}
  .campaign-feature-inner{width:min(calc(100% - 2rem),42rem);min-height:auto;
    padding:5rem 0 3rem;grid-template-columns:1fr;gap:2rem}
  .campaign-feature-copy h1{font-size:clamp(3.7rem,18vw,6rem)}
  .campaign-feature-art{grid-row:1}
  .campaign-feature-cover{max-height:62svh}
}

/* Character index: a deliberate Patera archive rather than a second book
   shelf. Living characters form the illustrated procession; fallen party
   members have a separate, quieter memorial register. */
.chronicle-roster{position:relative;isolation:isolate;scroll-margin-top:82px;
  padding:clamp(4rem,8vw,7rem) 1.5rem clamp(5rem,9vw,8rem);overflow:hidden;
  background:
    radial-gradient(ellipse at 50% 0,rgba(63,168,102,.13),transparent 52%),
    linear-gradient(180deg,#060b08 0%,#09110c 48%,#050806 100%)}
.chronicle-roster::before{content:"";position:absolute;z-index:-1;inset:0;pointer-events:none;
  background:
    repeating-linear-gradient(122deg,transparent 0 68px,rgba(145,242,154,.018) 69px 70px),
    linear-gradient(90deg,transparent,rgba(145,242,154,.025),transparent)}
.chronicle-roster-heading{max-width:58rem;margin:0 auto clamp(2rem,4vw,3.6rem);
  text-align:center}
.chronicle-roster-kicker{margin:0 0 .55rem;color:#91f29a;
  font:650 .72rem/1.2 system-ui,sans-serif;letter-spacing:.32em;text-transform:uppercase}
.chronicle-roster-heading h2{margin:0;color:#f1f0e8;
  font:400 clamp(3.4rem,7vw,6.5rem)/.92 'Gwyn Display',Georgia,serif;
  text-shadow:0 8px 34px rgba(0,0,0,.75)}
.chronicle-roster-heading>p:last-child{margin:.8rem auto 0;color:#9aa79f;
  font-size:clamp(.95rem,1.4vw,1.15rem)}
.chronicle-living-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  gap:clamp(.7rem,1.4vw,1.35rem);max-width:1500px;margin:0 auto}
.chronicle-card{position:relative;display:block;min-width:0;aspect-ratio:4/5.15;
  color:#f1f0e8;text-decoration:none;outline-offset:4px;
  border:1px solid rgba(145,242,154,.2);background:#0b130e;
  box-shadow:0 16px 36px rgba(0,0,0,.46);
  transition:transform .28s ease,border-color .28s ease,box-shadow .28s ease}
.chronicle-card::before{content:"";position:absolute;z-index:2;left:10%;right:10%;top:0;
  height:1px;background:linear-gradient(90deg,transparent,#91f29a,transparent);
  opacity:.45;transition:opacity .28s ease}
.chronicle-card-visual{position:absolute;inset:0;display:block;overflow:hidden}
.chronicle-card-art{width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(.86) contrast(1.04) brightness(.88);
  transform:scale(1.01);transition:filter .45s ease,transform .7s ease}
.chronicle-card-plate{position:absolute;z-index:1;left:0;right:0;bottom:0;
  display:block;padding:5.2rem 1rem 1rem;
  background:linear-gradient(180deg,transparent,rgba(4,8,6,.76) 48%,rgba(4,8,6,.97))}
.chronicle-card-name{display:block;color:#f3e7d0;
  font:400 clamp(2rem,3vw,3.2rem)/.92 'Gwyn Display',Georgia,serif;
  text-shadow:0 3px 14px #000}
.chronicle-card-role{display:block;margin-top:.52rem;color:#b8c4bb;
  font:600 clamp(.62rem,.82vw,.76rem)/1.35 system-ui,sans-serif;
  letter-spacing:.035em}
.chronicle-card-sigil{position:absolute;inset:0;display:grid;place-items:center;
  background:
    radial-gradient(circle at 50% 39%,rgba(80,188,106,.19),transparent 24%),
    repeating-linear-gradient(135deg,transparent 0 31px,rgba(145,242,154,.025) 32px 33px),
    linear-gradient(155deg,#111d15,#070c09)}
.chronicle-card-sigil::before,.chronicle-card-sigil::after{content:"";position:absolute;
  left:50%;top:39%;transform:translate(-50%,-50%) rotate(45deg);
  border:1px solid rgba(145,242,154,.32)}
.chronicle-card-sigil::before{width:44%;aspect-ratio:1}
.chronicle-card-sigil::after{width:27%;aspect-ratio:1;border-color:rgba(145,242,154,.17)}
.chronicle-card-rune{position:relative;z-index:1;color:#dcebdc;
  font:400 clamp(4rem,8vw,7.4rem)/1 'Gwyn Display',Georgia,serif;
  text-shadow:0 0 24px rgba(145,242,154,.25)}
.chronicle-memory{max-width:980px;margin:clamp(3.5rem,7vw,6rem) auto 0}
.chronicle-memory-heading{display:grid;grid-template-columns:1fr auto 1fr;
  align-items:center;gap:1.2rem;margin-bottom:1.25rem}
.chronicle-memory-heading span{height:1px;background:linear-gradient(90deg,transparent,#59635d)}
.chronicle-memory-heading span:last-child{background:linear-gradient(90deg,#59635d,transparent)}
.chronicle-memory-heading h3{margin:0;color:#aeb5b0;font:500 .75rem/1.2 system-ui,sans-serif;
  letter-spacing:.24em;text-transform:uppercase}
.chronicle-memory-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem}
.chronicle-memory-card{display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:1rem;min-height:94px;padding:1rem 1.15rem;
  color:#c7cbc8;text-decoration:none;border:1px solid #29312c;
  background:linear-gradient(115deg,rgba(28,33,30,.92),rgba(10,13,11,.94));
  transition:border-color .24s ease,background .24s ease,transform .24s ease}
.chronicle-memory-mark{display:grid;place-items:center;width:2.5rem;height:2.5rem;
  color:#8d9690;border:1px solid #414943;transform:rotate(45deg);
  font:400 1.2rem/1 Georgia,serif}
.chronicle-memory-mark>span{display:block;transform:rotate(-45deg)}
.chronicle-memory-copy{display:block;min-width:0}
.chronicle-memory-name{display:block;color:#d8d6cf;
  font:400 1.7rem/1 'Gwyn Display',Georgia,serif}
.chronicle-memory-role{display:block;margin-top:.3rem;color:#858e88;
  font:500 .68rem/1.3 system-ui,sans-serif}
.chronicle-memory-open{color:#727d76;font:650 .62rem/1.2 system-ui,sans-serif;
  letter-spacing:.08em;text-transform:uppercase}
@media(hover:hover){
  .chronicle-card:hover,.chronicle-card:focus-visible{transform:translateY(-7px);
    border-color:rgba(145,242,154,.66);
    box-shadow:0 24px 48px rgba(0,0,0,.58),0 0 22px rgba(63,168,102,.1)}
  .chronicle-card:hover::before,.chronicle-card:focus-visible::before{opacity:1}
  .chronicle-card:hover .chronicle-card-art,.chronicle-card:focus-visible .chronicle-card-art{
    filter:saturate(1) contrast(1.04) brightness(.98);transform:scale(1.045)}
  .chronicle-memory-card:hover,.chronicle-memory-card:focus-visible{
    transform:translateY(-2px);border-color:#59675f;
    background:linear-gradient(115deg,rgba(34,42,37,.96),rgba(12,16,13,.96))}
}
@media(max-width:1100px){
  .chronicle-living-grid{grid-template-columns:repeat(3,minmax(0,1fr));max-width:900px}
}
@media(max-width:700px){
  .chronicle-roster{padding:3.5rem .85rem 5rem}
  .chronicle-roster-heading{margin-bottom:2rem}
  .chronicle-living-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:.7rem}
  .chronicle-card:last-child:nth-child(odd){grid-column:1/-1;
    width:calc((100% - .7rem)/2);justify-self:center}
  .chronicle-card-plate{padding:4rem .7rem .7rem}
  .chronicle-card-name{font-size:clamp(1.75rem,11vw,2.5rem)}
  .chronicle-card-role{font-size:.61rem}
  .chronicle-memory-grid{grid-template-columns:1fr}
  .chronicle-memory-card{grid-template-columns:auto minmax(0,1fr)}
  .chronicle-memory-open{display:none}
}

/* Footer -- quiet signature + two small easter eggs (Kręcik, Boblin). */
.site-footer{max-width:1100px;margin:2rem auto 0;padding:1.2rem 1.5rem 3rem;
  color:var(--muted);font-family:system-ui,sans-serif;font-size:.78rem;
  border-top:1px solid var(--line);display:flex;align-items:center;
  justify-content:space-between;gap:1rem;flex-wrap:wrap}
.footer-signature{font-style:italic}
.footer-krecik{display:inline-block;cursor:default;transition:transform .5s ease;
  opacity:.55}
.footer-krecik:hover{transform:translateX(180px) scale(1.15);opacity:1}
@media(max-width:640px){.footer-krecik:hover{transform:translateX(70px) scale(1.15)}}

@media(max-width:900px){.library{padding:1rem}}
@media print{.edge-tab,.side-panel,.site-footer{display:none}}

/* Gwyn -- book.css: the PHB reader (spread cards + prose typography).
   Used only by podrecznik.html. */

/* min-width:0: a flex item's default min-width is auto (= its content's
   min-content size), the same trap as grid's bare 1fr -- without this, one
   unbreakable-wide descendant overflows the whole reader horizontally on
   narrow viewports. */
.reader{width:100%;max-width:2040px;margin:0 auto;min-width:0}
body[data-page="podrecznik"]{--measure:clamp(760px,calc(100vw - 500px),920px);
  font-size:17px;line-height:1.58}
/* Fragment links need only a quiet breathing space on desktop -- neither
   side panel overlaps reading content at any width now (GWYN CHANGE,
   mobile nav redesign round 2: they're either off-canvas or real layout
   columns, never floating over text). On mobile the fixed navigation
   cluster spans the viewport, so its height plus a small gap is reserved.
   The !important override replaces legacy inline 64px margins emitted by
   several PHB block renderers with one responsive contract. */
body[data-page="podrecznik"]{--reader-anchor-clearance:16px}
body[data-page="podrecznik"] .reader-main [id]{
  scroll-margin-top:var(--reader-anchor-clearance)!important}

/* Desktop reader shell: the TOC is a genuine left column, while the
   search/pins panel is a fixed right overlay.  This distinction is deliberate:
   opening search must not resize the reader, crop the opening art, or cause a
   visible reflow.  The right panel may cover the far edge of the page, and its
   crystal handle stays attached to that overlay's inner edge. */
@media(min-width:901px){
  body[data-page="podrecznik"] main.app,
  body[data-page^="biblioteka-"] main.app{
    display:grid;grid-template-columns:auto minmax(0,1680px);
    width:100%;max-width:2040px;margin:0 auto;align-items:start;justify-content:center}
  body[data-page="podrecznik"] .reader,
  body[data-page^="biblioteka-"] .reader{display:contents}
  body[data-page="podrecznik"] .side-panel--left[data-persistent],
  body[data-page^="biblioteka-"] .side-panel--left[data-persistent]{
    display:block;position:sticky;top:0;width:300px;height:100vh;max-height:100vh;
    overflow-y:auto;overflow-x:hidden;padding:1.2rem 1.05rem 1.1rem;
    background:var(--panel);font-family:"Noto Sans",system-ui,-apple-system,'Segoe UI',sans-serif;
    scrollbar-width:thin;scrollbar-color:#44403a var(--panel);
    /* Reset the off-canvas transform from base.css's unconditional
       .side-panel--left/--right rules -- position:sticky still honors a
       leftover transform, so without this the column was correctly
       "sticky" but shoved fully off-screen (caught in browser
       verification: left/right came back negative-width-sized offscreen
       despite position:sticky reading correctly). */
    transform:none;transition:width .18s ease,padding .18s ease}
  body[data-page="podrecznik"] .side-panel--left[data-persistent]{width:390px}
  /* Mobile-only by default (see the max-width:900px block for the drawer
     variant) -- podrecznik/biblioteka's persistent left column has no
     other brand mark now that .nav-cluster is gone, so it needs the same
     home-link row visible here too. */
  .side-panel--left[data-persistent] .reader-drawer-home{display:flex}
  body[data-page="podrecznik"] .side-panel--right[data-persistent],
  body[data-page^="biblioteka-"] .side-panel--right[data-persistent]{
    display:block;position:fixed;top:0;right:0;width:360px;height:100vh;max-height:100vh;
    overflow-y:auto;padding:1.2rem 1rem 1.1rem;background:var(--panel);
    font-family:system-ui,-apple-system,'Segoe UI',sans-serif;
    scrollbar-width:thin;scrollbar-color:#44403a var(--panel);
    transform:translateX(0);transition:transform .18s ease}
  /* The left column genuinely collapses; the fixed right overlay only slides
     beyond the viewport, so it never participates in reader geometry. */
  body[data-page="podrecznik"] .side-panel--left[data-persistent].collapsed,
  body[data-page^="biblioteka-"] .side-panel--left[data-persistent].collapsed{
    width:0;min-width:0;padding-left:0;padding-right:0;overflow:hidden;border:0}
  body[data-page="podrecznik"] .side-panel--right[data-persistent].collapsed,
  body[data-page^="biblioteka-"] .side-panel--right[data-persistent].collapsed{
    transform:translateX(102%);pointer-events:none}

  /* The tall crystal handles remain visible and travel with the outer edge
     of each persistent column.  The reader itself is centered in a 2040px
     shell, so the left coordinate includes that shell's responsive gutter;
     a fixed 360px value would land inside the menu on ultrawide displays. */
  /* Keep each fixed tab on the actual grid edge.  The rail lives inside a
     centered 2040px shell, so a raw 390px/300px offset would drift inward
     on wide screens.  This is the same geometry that places the grid. */
  body[data-page="podrecznik"]:has(.side-panel--left[data-persistent]:not(.collapsed)){
    --gwyn-left-edge:calc(50% - min(50%,1020px) + 390px)}
  body[data-page^="biblioteka-"]:has(.side-panel--left[data-persistent]:not(.collapsed)){
    --gwyn-left-edge:calc(50% - min(50%,1020px) + 300px)}
  body[data-page="podrecznik"]:has(.side-panel--right[data-persistent]:not(.collapsed)),
  body[data-page^="biblioteka-"]:has(.side-panel--right[data-persistent]:not(.collapsed)){
    --gwyn-right-edge:360px}
  body[data-page="podrecznik"] .edge-tab,
  body[data-page^="biblioteka-"] .edge-tab{transition-duration:.18s}
}
/* Brand/home lockup: the same structured identity is used on desktop and
   mobile. Visibility still depends on the drawer/column mode, but the type
   system must never fall back to an underlined, concatenated inline link. */
.reader-drawer-home{display:none;align-items:center;gap:.72rem;min-height:64px;
  margin:0 0 1rem;padding:.28rem .44rem .75rem;color:#f1f0e8;
  text-decoration:none;border-bottom:1px solid rgba(145,242,154,.24)}
.reader-drawer-home .brand-seal{width:50px;height:54px;flex:0 0 50px}
.reader-drawer-home-copy{display:flex;flex-direction:column;min-width:0}
.reader-drawer-home-kicker{font:700 .6rem/1.1 "Noto Sans",system-ui,sans-serif;
  letter-spacing:.18em;text-transform:uppercase;color:#91f29a}
.reader-drawer-home-copy strong{margin-top:.16rem;font:750 1.08rem/1.08
  "Noto Sans",system-ui,sans-serif;letter-spacing:.01em;color:#f7f0df;
  white-space:normal;text-shadow:0 2px 10px rgba(0,0,0,.55)}
.reader-drawer-home-subtitle{margin-top:.24rem;font:500 .54rem/1.1 "Noto Sans",system-ui,sans-serif;
  letter-spacing:.14em;text-transform:uppercase;color:#aaa69c}
.side-panel--left[data-persistent]::-webkit-scrollbar,
.side-panel--right[data-persistent]::-webkit-scrollbar{width:9px}
.side-panel--left[data-persistent]::-webkit-scrollbar-thumb,
.side-panel--right[data-persistent]::-webkit-scrollbar-thumb{background:#44403a;
  border-radius:5px;border:2px solid var(--panel)}
.side-panel--left[data-persistent]::-webkit-scrollbar-thumb:hover,
.side-panel--right[data-persistent]::-webkit-scrollbar-thumb:hover{background:#5a554c}
.reader-nav{font-family:"Noto Sans",system-ui,-apple-system,'Segoe UI',sans-serif}
.reader-toc-header{margin:0 .5rem .72rem;padding:.08rem 0 .72rem;
  border-bottom:1px solid rgba(145,242,154,.18)}
.reader-toc-kicker{display:block;color:var(--accent);font:800 .58rem/1.2
  "Noto Sans",system-ui,sans-serif;letter-spacing:.16em;text-transform:uppercase}
.toc-title{display:block;margin:.22rem 0 0;color:#f4ead7;text-decoration:none;
  font:750 1.08rem/1.18 "Noto Sans",system-ui,sans-serif;
  letter-spacing:.015em;text-shadow:0 2px 10px rgba(0,0,0,.6)}
.reader-toc-subtitle{display:block;margin-top:.25rem;color:var(--muted);
  font:500 .62rem/1.25 "Noto Sans",system-ui,sans-serif;letter-spacing:.04em}
.toc-title:hover,.toc-title:focus-visible{color:#fff;outline:none}
.toc{list-style:none;margin:0;padding:0}
.toc li{margin:0}
.toc a{color:var(--ink);text-decoration:none;display:block;padding:.2rem .5rem;
  border-radius:5px;font-size:.89rem;line-height:1.25;
  transition:background .1s ease,color .1s ease}
body[data-page="podrecznik"] .toc>li:not(.toc-part)>a,
body[data-page="podrecznik"] .toc>li summary>a{white-space:nowrap}
.toc>li:not(.toc-part)>a:hover,.toc-part>a:hover,.toc-sub a:hover{
  background:rgba(80,179,105,.16);color:#f4fff5;
  box-shadow:inset 2px 0 0 rgba(145,242,154,.42)}
/* Stronger active state: solid accent-tinted fill + full-height left bar,
   not just a thin inset shadow -- easy to lose track of "where am I" in a
   42-item list on a long scroll (operator feedback 2026-07-15). */
.toc a.active{background:rgba(63,168,102,.22);color:#fff;
  box-shadow:inset 3px 0 0 var(--accent);font-weight:700}
.toc-sub{list-style:none;margin:0 0 .3rem;padding:0 0 0 .7rem;
  margin-left:.55rem;border-left:1px solid var(--line)}
.toc-sub a{min-height:30px;display:flex;align-items:center;font-size:.8rem;
  color:var(--muted);padding:.18rem .5rem}
.toc-sub a:hover{color:#f4fff5;background:rgba(80,179,105,.16)}
.toc-sub a.active{color:#fff;font-weight:700;background:rgba(63,168,102,.18)}
/* Direct spell index beneath Chapter 11's class links.  There is deliberately
   no "Czary A-Z" caption: the letters are a compact navigation continuation,
   not a second heading in the sidebar. */
.toc-alpha{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:.12rem;
  margin:.24rem .5rem .42rem .55rem;padding:0 .1rem}
.toc-alpha a{display:grid;place-items:center;min-width:0;padding:.18rem .08rem;
  min-height:28px;color:#d9d3c4;font:750 .78rem/1 system-ui,sans-serif;border-radius:3px}
.toc-alpha a:hover,.toc-alpha a:focus-visible{color:#fff;background:rgba(63,168,102,.2);outline:none}
/* PHB part label in the nav: a quiet uppercase divider grouping chapters.
   padding-left:0 (vs. .toc a's ~.55rem) so it sticks out to the LEFT of the
   chapter links below it -- reads as a section break, not just another row. */
/* Part labels ("CZĘŚĆ 1: ..."): clear air above (separating from the
   previous part's chapters), tight to their own first chapter below --
   the old .3rem bottom + the chapter link's own padding read as a weird
   dangling gap (operator report). Left padding aligns the label with the
   chapter titles' text edge instead of hugging the sidebar edge. */
.toc-part{list-style:none;margin:.42rem 0 .05rem;padding:0}
.toc-part>a{min-height:30px;display:flex;align-items:center;padding:.2rem .5rem;
  color:var(--accent);font-size:.72rem;line-height:1.15;font-weight:800;
  letter-spacing:.06em;text-transform:uppercase;border-radius:4px}
.toc-part>a:hover,.toc-part>a:focus-visible{color:#fff;background:rgba(63,168,102,.16);outline:none}
.toc-part>a.active{color:#fff;background:rgba(63,168,102,.22)}
.toc-part:first-child{margin-top:.2rem}
/* Accordion groups (Rasy/Klasy -- the only chapters with .toc-sub children):
   native <details>/<summary>, closed by default. The <a> stays a normal
   block-level link (existing .toc a rule); the caret is a sibling so a click
   on the link text navigates while a click anywhere else in the row toggles. */
.toc>li:not(.toc-part)>a,.toc summary{min-height:34px;display:flex;align-items:center}
.toc>li:not(.toc-part)>a,.toc summary>a,.toc summary>.toc-summary-label{
  font-size:.92rem;font-weight:600;line-height:1.25}
.toc summary{position:relative;gap:0;list-style:none;
  cursor:pointer;border-radius:5px}
.toc summary::before{content:"";position:absolute;inset:0;z-index:0;border-radius:inherit;
  background:rgba(80,179,105,.16);box-shadow:inset 2px 0 0 rgba(145,242,154,.42);
  opacity:0;transition:opacity .12s ease;pointer-events:none}
.toc summary:hover::before,.toc summary:focus-within::before{opacity:1}
.toc summary:focus-visible{outline:1px solid rgba(145,242,154,.68);outline-offset:-1px}
.toc a:focus-visible{background:rgba(80,179,105,.16);color:#f4fff5;
  box-shadow:inset 2px 0 0 rgba(145,242,154,.42);outline:1px solid rgba(145,242,154,.68);
  outline-offset:-1px}
.toc summary::-webkit-details-marker{display:none}
.toc summary::marker{content:""}
.toc summary a,.toc-summary-label{position:relative;z-index:1;flex:0 1 auto;min-width:0}
.toc summary>.toc-summary-label{display:flex;align-items:center;flex:1 1 auto;padding:.2rem .5rem;color:var(--ink)}
.toc-flat-title{display:flex;align-items:center;min-height:34px;padding:.2rem .5rem;
  color:var(--ink);font-size:.92rem;font-weight:600;line-height:1.25}
.toc-sub-flat{margin-bottom:.3rem}
.toc-caret{flex:0 0 30px;width:30px;height:30px;padding:8px;box-sizing:border-box;
  position:relative;z-index:1;color:var(--muted);border-radius:50%;transition:transform .15s ease,color .15s ease,
  background .15s ease,box-shadow .15s ease}
.toc summary:hover .toc-caret{color:var(--ink)}
.toc-caret:hover{color:var(--shard-glow);background:transparent;box-shadow:none}
.toc-group details[open]>summary .toc-caret{transform:rotate(90deg)}
.reader-main{flex:1 1 auto;min-width:0;max-width:none;margin:0;
  padding:var(--topbar-clear) 2.4rem 6rem}
body[data-page="podrecznik"] .reader-main{padding:0;overflow:hidden}

.qnote{background:#2c2622;border:1px solid var(--line);border-left:3px solid var(--accent);
  padding:.6rem .8rem;margin:0 0 1.4rem;color:var(--muted);font-size:.85rem;
  font-family:system-ui,sans-serif;border-radius:4px}
.pending{background:#3a2c18;border:1px solid #5a3a1a;border-left:4px solid #c8912e;
  padding:.8rem 1rem;margin:0 0 1rem;border-radius:6px;color:#f0dcae;
  font-family:system-ui,sans-serif;font-size:.9rem;line-height:1.5}
.pending code{background:#241c10;padding:.05rem .3rem;border-radius:3px}

/* Utility appendix sections (usable cards, missing/supplied pages) -- NOT
   part of the numbered page-flow (siblings of .book in reader-main, see
   gwynbuild.phb.cards_html/missing_page_html), so a plain bordered card is
   enough; there are only 1-3 of these, no content-visibility need. */
.page-card{background:var(--panel);border:1px solid var(--line);
  border-radius:8px;padding:1.2rem 1.6rem;margin:2rem 0}
.page-card>h2.chapter{margin-top:0}

/* --- Continuous flow: the whole book is ONE break-out grid (prose in a left
   measure column, tables/statblocks/figures span full width, reads like a
   web page) with per-PAGE content-visibility containment so the browser can
   defer layout/paint for pages outside the viewport -- the whole book is
   still in the DOM at once (Ctrl+F, window.find(), screen readers all see
   everything; nothing is virtualized away). --- */
/* minmax(0,1fr) not 1fr: a bare 1fr floors at the item's min-content width, so
   one unbreakable-wide child (a table, a long token) can blow a track out to
   thousands of px. minmax(0,...) lets tracks shrink to the available space
   and pushes overflow-handling down to the content instead (.tbl{overflow-x:
   auto}, hyphens, break-word). */
.book{margin:0}
.content{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,var(--measure)) minmax(0,1fr);
  align-content:start;position:relative;max-width:1680px;margin:0 auto;
  padding:0 2.4rem}
.content>*{grid-column:2;min-width:0}
/* figure:not(.tbl) -- a plain <figure> (illustration, OCR quarantine box)
   still always goes full-width, but every table is ALSO a <figure> (see
   blocks.py render_table), so without the :not() this rule silently
   overrode the dedicated .tbl.tbl--wide-only rule below for every table
   regardless of width (same specificity as the old blanket .content>.tbl
   rule it was meant to replace -- .content>figure at (0,1,1) still beat
   .content>*{grid-column:1} at (0,1,0), so plain tables stayed full-width
   even after that rule was scoped to .tbl--wide only). */
.content>.statblock-grid,.content>figure:not(.tbl),.content>.badge,
.content>.page-body--statblocks{grid-column:1/-1}
/* Only genuinely-wide tables (>=5 columns, .tbl--wide, see blocks.py
   render_table) break out to the full width -- a plain .tbl (most tables in
   the book: ability scores, short lookups) stays in the measure column so it
   reads the same width as the prose around it instead of visually jumping
   wider (operator: "cały kontent... jednej szerokości"). */
.content>.tbl{grid-column:2;justify-self:stretch;width:100%}
.content>.tbl.tbl--wide{grid-column:1/-1;justify-self:start;width:min(100%,1320px)}
/* spell cards stay in the measure column (comfortable ~780px reading width) */
.content>.page-body--cards{grid-column:2}

/* GWYN: per-page content-visibility container. Re-declares the SAME 2-track
   formula as .content (not grid-template-columns:subgrid -- content-
   visibility:auto implies contain:layout, and layout containment forbids a
   subgrid from depending on an ancestor's tracks, so the browser silently
   drops the subgrid declaration and .page-flow falls back to ONE implicit
   full-width column; every paragraph in the book rendered at full width,
   not --measure, until this was caught -- verified via getComputedStyle
   showing grid-template-columns:"none" on every .page-flow). Because
   .page-flow spans exactly .content's two tracks (grid-column:1/-1, zero
   gap), re-resolving the identical minmax() formula against .page-flow's
   own width lands on the SAME pixel split as the parent -- no subgrid
   needed, and it works fine alongside content-visibility. */
.page-flow{grid-column:1/-1;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,var(--measure)) minmax(0,1fr);
  min-width:0;content-visibility:auto;
  contain-intrinsic-size:auto var(--cis,1400px)}
.page-flow>*{grid-column:2;min-width:0}
/* figure:not(.tbl) -- see the .content> version above for why: every table
   is ALSO a <figure>, so without this a plain (non-wide) table still got
   caught by the blanket figure rule and stayed full-width regardless of
   the dedicated .tbl.tbl--wide rule below. */
.page-flow>.statblock-grid,.page-flow>figure:not(.tbl),.page-flow>.badge,
.page-flow>.page-body--statblocks{grid-column:1/-1}
/* .page-flow, not .content, is what almost all real content actually lands
   in (see the comment above on why this rule is duplicated instead of
   inherited) -- the .content>.tbl.tbl--wide rule alone had NO visible effect
   because this parallel rule was the one actually in force. */
.page-flow>.tbl{grid-column:2;justify-self:stretch;width:100%}
.page-flow>.tbl.tbl--wide{grid-column:1/-1;justify-self:start;width:min(100%,1320px)}
.page-flow>.page-body--cards{grid-column:2}
/* D&D 2024 class art supplied by the operator is plain reading content, not
   a figure: no caption, border, shadow, card, button, or lightbox chrome.
   Preserve the complete portrait source -- including its transparent and
   decorative edges -- at the full reading-measure width. */
.class-art,.race-art{grid-column:2;width:100%;max-width:var(--measure);margin:1.05rem 0 1.8rem}
.class-art img,.race-art img{display:block;width:100%;height:auto;object-fit:contain}
.race-art--rail{width:min(100%,300px);justify-self:end}
.race-art--rail img{width:auto;max-width:100%;max-height:480px;margin-left:auto}
/* The book opener is an independent viewport stage before .content. It fills
   the entire pane to the right of the TOC, while the image remains complete
   and uncropped at its native ratio. A translucent top fade belongs to the
   image itself and only keeps the floating brand/search legible. */
.book-header-art{position:relative;width:100%;max-width:none;margin:0 0 3.2rem;
  overflow:hidden;background:#151517}
.book-header-art::after{content:"";position:absolute;inset:0 0 auto;height:7rem;
  pointer-events:none;background:linear-gradient(180deg,rgba(12,11,11,.62),transparent)}
.book-header-art img{display:block;width:100%;height:auto;object-fit:contain;
  filter:saturate(1.1) contrast(1.045) brightness(1.025)}
.book-hero-title{position:absolute;z-index:2;left:50%;bottom:clamp(1.2rem,5vw,4.5rem);
  transform:translateX(-50%);display:block;width:min(90%,1050px);text-align:center;
  text-decoration:none;text-transform:uppercase;text-shadow:0 3px 20px rgba(0,0,0,.92)}
.book-hero-kicker{display:block;margin:0 0 .14rem;color:#fff2dc;
  transform:translateY(-.34rem);
  font:500 clamp(.82rem,1.5vw,1.18rem)/1.2 'Cinzel',Georgia,serif;
  letter-spacing:.3em}
.book-hero-name{display:block;color:#fffefb;
  font:700 clamp(2.15rem,5.9vw,5.5rem)/1.02 'Cinzel Decorative',Georgia,serif;
  letter-spacing:.015em}
.book-hero-ornament{display:flex;align-items:center;justify-content:center;
  width:min(430px,60%);margin:.8rem auto 0;color:#fffefb}
.book-hero-ornament i{display:block;flex:1;height:2px;background:currentColor}
.book-hero-ornament b{display:block;width:.72rem;height:.72rem;margin:0 .16rem;
  background:currentColor;transform:rotate(45deg)}
.book-hero-title:hover .book-hero-name,.book-hero-title:focus-visible .book-hero-name{color:#fff}
.book-hero-title:focus-visible{outline:2px solid var(--accent);outline-offset:6px}
.reader-extras{max-width:1680px;margin:0 auto;padding:0 2.4rem 6rem}
@media(max-width:640px){.class-art,.race-art{margin:.9rem 0 1.45rem}}
/* Print: Chrome renders content-visibility:auto elements as empty unless
   forced visible -- without this override, printing/PDF-export would show
   blank pages for every .page-flow the user never scrolled past. */
@media print{.page-flow{content-visibility:visible;contain-intrinsic-size:none}}

/* ------------------------------------------------------------------------- */
/* Xanathar PL reader (kept separate from PHB-specific rules above)           */
/* ------------------------------------------------------------------------- */
body[data-page="xanathar"]{--measure:clamp(760px,calc(100vw - 500px),980px);
  --reader-anchor-clearance:16px;font-size:17px;line-height:1.58}
body[data-page="xanathar"] .reader-main{padding:0;overflow:hidden}
body[data-page="xanathar"] .reader-main [id]{scroll-margin-top:var(--reader-anchor-clearance)!important}
body[data-page="xanathar"] .toc>li:not(.toc-part)>a{white-space:normal}
@media(min-width:901px){
  body[data-page="xanathar"] main.app{
    display:grid;grid-template-columns:auto minmax(0,1680px);width:100%;
    max-width:2040px;margin:0 auto;align-items:start;justify-content:center}
  body[data-page="xanathar"] .reader{display:contents}
  body[data-page="xanathar"] .side-panel--left[data-persistent]{
    display:block;position:sticky;top:0;width:390px;height:100vh;max-height:100vh;
    overflow-y:auto;overflow-x:hidden;padding:1.2rem 1.05rem 1.1rem;
    background:var(--panel);font-family:"Noto Sans",system-ui,-apple-system,'Segoe UI',sans-serif;
    scrollbar-width:thin;scrollbar-color:#44403a var(--panel);transform:none;
    transition:width .18s ease,padding .18s ease}
  body[data-page="xanathar"] .side-panel--right[data-persistent]{
    display:block;position:fixed;top:0;right:0;width:360px;height:100vh;max-height:100vh;
    overflow-y:auto;padding:1.2rem 1rem 1.1rem;background:var(--panel);
    font-family:system-ui,-apple-system,'Segoe UI',sans-serif;
    scrollbar-width:thin;scrollbar-color:#44403a var(--panel);
    transform:translateX(0);transition:transform .18s ease}
  /* The right drawer is fixed over the shell.  Reserve its width in the
     XGtE reader while it is open so the continuous measure never hides the
     title or prose underneath the panel at tablet/desktop widths. */
  body[data-page="xanathar"]:has(.side-panel--right[data-persistent]:not(.collapsed)) .reader-main{
    margin-right:360px}
  body[data-page="xanathar"] .side-panel--left[data-persistent].collapsed{
    width:0;min-width:0;padding-left:0;padding-right:0;overflow:hidden;border:0}
  body[data-page="xanathar"] .side-panel--right[data-persistent].collapsed{
    transform:translateX(102%);pointer-events:none}
  body[data-page="xanathar"]:has(.side-panel--left[data-persistent]:not(.collapsed)){
    --gwyn-left-edge:calc(50% - min(50%,1020px) + 390px)}
  body[data-page="xanathar"]:has(.side-panel--right[data-persistent]:not(.collapsed)){
    --gwyn-right-edge:360px}
  body[data-page="xanathar"] .edge-tab{transition-duration:.18s}
}
.xgte-opener{max-width:var(--measure);margin:0 auto;padding:clamp(2rem,5vw,5.5rem) 0 2.2rem;
  container-type:inline-size;
  border-bottom:1px solid rgba(145,242,154,.2);text-align:center}
.xgte-kicker{margin:0 0 .7rem;color:var(--accent);font:800 .72rem/1.2 system-ui,sans-serif;
  letter-spacing:.2em;text-transform:uppercase}
.xgte-opener h1{margin:0;color:#f5ead7;font-family:'Gwyn Display',Georgia,serif}
.xgte-opener-title-main{display:block;color:#fff4df;font:400 clamp(3.1rem,8vw,7rem)/.9 'Gwyn Display',Georgia,serif;
  letter-spacing:.055em;text-transform:uppercase;text-shadow:0 3px 20px rgba(0,0,0,.38)}
/* The opener is a narrow reading column even on wide screens.  Use its
   inline size, rather than the viewport, so the display title remains a
   complete word instead of being clipped by the reader column. */
@supports (font-size:1cqw){
  .xgte-opener-title-main{font-size:clamp(3.1rem,13cqw,7rem)}
}
.xgte-opener-title-rest{display:block;margin-top:.28em;color:#e7d8c1;font:400 clamp(1.25rem,2.7vw,2.35rem)/1.1 'Gwyn Display',Georgia,serif;
  letter-spacing:.018em}
.xgte-opener-subtitle{margin:1rem 0 0;color:var(--muted);font:600 .82rem/1.4 system-ui,sans-serif;letter-spacing:.05em}
.xgte-opener-note{max-width:58rem;margin:1rem auto 0;color:#c8c0b1;font-size:.9rem}
.xgte-heading{position:relative;margin:1.55rem 0 .65rem;color:#f1dfc0;font-family:'Gwyn Display',Georgia,serif;
  scroll-margin-top:var(--reader-anchor-clearance)}
.xgte-heading--section{margin-top:2.8rem;padding-top:1.6rem;border-top:1px solid rgba(145,242,154,.2);color:#91f29a}
.xgte-commentary{margin:1.15rem 0;padding:1rem 1.15rem;background:rgba(112,75,33,.2);
  border:1px solid rgba(200,145,46,.45);border-left:3px solid #c8912e;border-radius:6px}
.xgte-commentary-title{display:flex;align-items:center;gap:.45rem;color:#f0dcae;font:800 .78rem/1.3 system-ui,sans-serif;
  letter-spacing:.08em;text-transform:uppercase}
.xgte-commentary-title span{font-weight:500;color:#bdab8e;text-transform:none;letter-spacing:0}
.xgte-commentary-title .page-scan-btn{margin-left:auto}
.xgte-commentary-pending{margin:.55rem 0 0;color:#d0c2ab;font-size:.9rem}
.xgte-page-empty{color:var(--muted);font-style:italic}
.xgte-art{max-width:100%;margin:1.8rem auto;padding:0;text-align:center;contain:content}
.xgte-art img{display:block;width:auto;max-width:100%;height:auto;margin:0 auto;border-radius:2px;box-shadow:0 1rem 2.5rem rgba(0,0,0,.24)}
.xgte-art figcaption{margin:.6rem 0 0;color:var(--muted);font:600 .72rem/1.4 system-ui,sans-serif}
.xgte-page-flow .spell-card{max-width:100%;margin-block:1.2rem}
.xgte-page-flow .spell-card + .spell-card{margin-top:1.7rem}
.xgte-page-flow .xgte-heading{overflow-wrap:anywhere}
.xgte-invocation-requirement{margin:-.15rem 0 .7rem;color:#d9c6aa;font-style:italic;line-height:1.42}
.xgte-invocation-requirement span{color:#f0dfc0;font-family:'Gwyn Display',Georgia,serif;font-style:normal;font-weight:700}
.xgte-content .xref{color:inherit;text-decoration-color:rgba(145,242,154,.5);text-decoration-thickness:1px}
.xgte-content .xref:hover,.xgte-content .xref:focus-visible{color:#91f29a;text-decoration-color:#91f29a}
.xgte-draft-banner{max-width:1280px;margin:1rem auto 0}
.libcard--draft .libcard-cover{box-shadow:0 0 0 1px rgba(200,145,46,.48)}
@media(max-width:900px){
  body[data-page="xanathar"]{--measure:100%;font-size:17px;--reader-anchor-clearance:72px}
  body[data-page="xanathar"] .reader-main{padding:0}
  .xgte-opener{padding:2.5rem 1.1rem 1.8rem}
  .xgte-content{padding:0 1rem}
  .xgte-heading--section{margin-top:2.1rem;padding-top:1.25rem}
}
@media(max-width:560px){
  @supports (font-size:1cqw){
    .xgte-opener-title-main{font-size:clamp(2.35rem,13cqw,3.1rem)}
  }
}

/* Keep the compact selector contract consumed by the existing visual-shell
   regression test; this is equivalent to the full .toc-alpha declaration. */
.toc-alpha{grid-template-columns:repeat(6,minmax(0,1fr))}

/* Audited art has a real hierarchy. Wide scenes may lead a passage; measure
   images support it; rail images are marginal accents and must not take over
   an entire screen. Every source remains complete and opens in the lightbox. */
.page-flow>.phb-art{grid-column:2;grid-row:auto;width:100%;
  margin:1.2rem 0 1.65rem;min-width:0}
.page-flow>.phb-art--wide{max-width:var(--measure)}
.page-flow>.phb-art--measure{width:min(100%,420px);justify-self:center}
.page-flow>.phb-art--rail{width:min(100%,280px);justify-self:end}
.page-flow>.phb-art--feature{width:min(100%,560px);justify-self:center;
  margin-top:1.8rem;margin-bottom:2.1rem}
.page-flow>.phb-art-pair{grid-column:2;width:100%;max-width:var(--measure);
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-items:end;
  gap:clamp(.5rem,1.5vw,1.2rem);margin:1.4rem 0 1.8rem;min-width:0}
.phb-art-pair>.phb-art{width:100%;min-width:0;margin:0}
.phb-art-button{display:flex;justify-content:center;width:100%;padding:0;border:0;border-radius:0;
  overflow:visible;background:transparent;cursor:zoom-in;box-shadow:none}
.phb-art-button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.phb-art-button img{display:block;width:auto;max-width:100%;height:auto;object-fit:contain}
.phb-art--wide>.phb-art-button img{width:100%;max-height:780px}
.phb-art--measure>.phb-art-button img{max-height:420px}
.phb-art--rail>.phb-art-button img{max-height:380px}
.phb-art--feature>.phb-art-button img{max-height:720px}
.phb-art-pair .phb-art-button img{width:auto;max-height:460px}
/* Page marker + scan-preview button: a zero-height inline anchor; real
   clickable tools hang on the right margin (no card, no divider, no left
   label). nav.js handles [data-copy] (copy deep-link) and [data-scan] (open
   the original scanned page in a lightbox) via delegated click handlers. */
.page-anchor{display:block;height:0;overflow:visible;position:relative}
/* Synthetic entry for a page with no OCR'd text yet (spreads.py
   _fill_page_gaps) -- soft label, not an error: the scan-preview button on
   the marker above it is the real way to see the page. */
.page-gap-note{color:var(--muted);font-style:italic;font-size:.85rem;
  margin:.4rem 0 1.2rem}
.page-tools{position:absolute;top:0;left:100%;margin-left:1.2rem;
  display:flex;align-items:center;gap:.5rem;white-space:nowrap}
.page-marker{display:inline-flex;align-items:center;gap:.35rem;border:0;
  background:none;color:var(--muted);opacity:.4;cursor:pointer;
  font-family:system-ui,sans-serif;font-size:.72rem;letter-spacing:.03em;
  padding:.1rem .3rem;border-radius:4px;
  transition:opacity .12s ease,color .12s ease}
.page-marker:hover,.page-marker:focus{opacity:1;color:var(--accent);outline:none}
.page-scan-btn{display:inline-flex;align-items:center;border:0;background:none;
  color:var(--muted);opacity:.4;cursor:pointer;padding:.15rem;border-radius:4px;
  transition:opacity .12s ease,color .12s ease}
.page-scan-btn svg{width:.95em;height:.95em}
.page-scan-btn:hover,.page-scan-btn:focus{opacity:1;color:var(--accent);outline:none}
@media(max-width:1100px){
  /* no right gutter for the marginal position -- fold the tools into normal
     flow instead of dropping them (page was still deep-linkable by id with
     display:none, but the copy-link/scan buttons themselves disappeared). */
  .page-anchor{height:auto;margin:0 0 .3rem}
  .page-tools{position:static;margin-left:0;display:inline-flex}
  .page-marker,.page-scan-btn{opacity:.6}
}

/* Scan-preview lightbox (podrecznik.html only). A single reusable overlay,
   toggled by nav.js -- not one per page, so this costs nothing per page. */
/* .92 opacity read as "opened a new page", not "overlay on top of the
   current one" -- lighter backdrop lets the book page show through enough
   that clicking it to close reads as an obvious, discoverable affordance
   (operator report). cursor:pointer on the backdrop reinforces that; the
   image itself resets to default since clicking IT doesn't close anything. */
.scan-lightbox{position:fixed;inset:0;z-index:40;background:rgba(6,10,7,.76);
  display:flex;align-items:center;justify-content:center;padding:4.5rem 5.5rem;
  cursor:pointer}
.scan-lightbox[hidden]{display:none}
.scan-lightbox-img{position:relative;z-index:1;max-width:100%;max-height:100%;border-radius:4px;
  box-shadow:0 10px 48px rgba(0,0,0,.72);background:#fff;cursor:default}
.scan-lightbox-close{position:absolute;z-index:5;top:1rem;right:1rem;
  width:4.5rem;height:4.5rem;border-radius:50%;border:1px solid rgba(145,242,154,.58);
  background:rgba(10,24,15,.9);color:#effff1;font-size:2.65rem;line-height:1;
  cursor:pointer;display:flex;align-items:center;justify-content:center;
  box-shadow:0 0 0 5px rgba(63,168,102,.08),0 0 22px rgba(88,225,116,.28);
  text-shadow:0 0 12px rgba(145,242,154,.58);
  transition:background .16s ease,border-color .16s ease,box-shadow .16s ease,
    transform .16s ease}
.scan-lightbox-close:hover,.scan-lightbox-close:focus-visible,
.scan-lightbox--close-hot .scan-lightbox-close{
  border-color:#b8ffc0;background:rgba(20,54,31,.96);color:#fff;outline:none;
  box-shadow:0 0 0 7px rgba(63,168,102,.15),0 0 30px rgba(88,225,116,.46);
  transform:scale(1.06)}
.scan-lightbox-caption{position:absolute;bottom:1.2rem;left:50%;
  transform:translateX(-50%);color:var(--muted);font-family:system-ui,sans-serif;
  font-size:.85rem;background:rgba(0,0,0,.5);padding:.3rem .8rem;border-radius:12px}
/* Prev/next within the open trigger's data-scan-group (nav.js). Same visual
   language as the close button; vertically centered at the sides. */
.scan-lightbox-nav{position:absolute;z-index:2;top:0;bottom:0;
  width:clamp(7rem,11vw,15rem);height:100%;border:0;border-radius:0;
  background:transparent;color:#effff1;font-size:4.8rem;line-height:1;
  cursor:pointer;display:flex;align-items:center;padding:0 1.5rem .35rem;
  text-shadow:0 0 9px #07140b,0 0 22px rgba(113,243,139,.62);
  transition:color .16s ease,background .16s ease,text-shadow .16s ease}
.scan-lightbox-nav--prev{left:0;justify-content:flex-start;
  background:linear-gradient(90deg,rgba(12,30,18,.6),transparent)}
.scan-lightbox-nav--next{right:0;justify-content:flex-end;
  background:linear-gradient(270deg,rgba(12,30,18,.6),transparent)}
.scan-lightbox-nav:hover:not(:disabled),.scan-lightbox-nav:focus:not(:disabled){
  color:#c9ffd0;outline:none;text-shadow:0 0 8px #10391d,0 0 30px #75ef8d}
.scan-lightbox-nav--prev:hover:not(:disabled),
.scan-lightbox-nav--prev:focus-visible:not(:disabled){
  background:linear-gradient(90deg,rgba(27,76,42,.82),transparent)}
.scan-lightbox-nav--next:hover:not(:disabled),
.scan-lightbox-nav--next:focus-visible:not(:disabled){
  background:linear-gradient(270deg,rgba(27,76,42,.82),transparent)}
.scan-lightbox-nav:disabled{opacity:.18;cursor:default}
@media(max-width:640px){
  .scan-lightbox{padding:4.7rem .65rem 3.5rem}
  .scan-lightbox-close{top:.65rem;right:.65rem;width:4rem;height:4rem;font-size:2.3rem}
  .scan-lightbox-nav{width:24vw;min-width:4.8rem;padding:0 .7rem;font-size:3.7rem}}
/* Errata thumbnails (end-of-book section, phb.py errata_html): a small row
   of clickable page previews opening in the lightbox. */
/* Dice/roll-range cells (blocks.py value-shape check): "01-02" never
   breaks into "01-" / "02". */
.tbl .td-roll{white-space:nowrap}
.errata-grid{display:flex;gap:1rem;flex-wrap:wrap;margin:.8rem 0 0}
.errata-thumb{background:none;border:1px solid var(--line);border-radius:6px;
  padding:0;cursor:pointer;overflow:hidden;width:180px;
  transition:border-color .12s ease,transform .12s ease}
.errata-thumb:hover,.errata-thumb:focus{border-color:var(--accent);
  transform:translateY(-3px);outline:none}
.errata-thumb img{display:block;width:100%;height:auto}
@media print{.scan-lightbox,.page-tools{display:none}}
/* Spell cards flow full-width, one per row (a spell description should fill the
   card, not sit in a narrow 320px tile). Statblocks keep their own DDB layout. */
.page-body--cards{display:block;margin:.4rem 0}
.page-body--cards>.spc{max-width:none;margin:0 0 1.1rem}
.page-body--statblocks{display:block;margin:.4rem 0}

/* Legacy fallback (flat flow, no spread cards -- only used if the semantic
   package fails to parse). Reuses the old break-out grid: prose anchored to
   a LEFT measure column, tables/statblocks/figures span the full width. */
.book--flat{display:grid;grid-template-columns:minmax(0,var(--measure)) minmax(0,1fr);
  align-content:start}
.book--flat>*{grid-column:1}
.book--flat>.tbl,.book--flat>.statblock-grid,
.book--flat>figure,.book--flat>.badge{grid-column:1/-1}
.book--flat .page{display:block;margin:0 0 2rem}

/* Display headline fonts (New Rocker / Pirata One, self-hosted -- see
   fonts.css.tpl) for the book's structural heading hierarchy. Bigger
   line-height than body text: blackletter faces have tall ascenders/
   descenders that clip at 1.0-1.1. */
/* Each PHB part opens with a complete illustration, promoted from the first
   chapter where possible. A dim, blurred cover layer fills the horizontal
   stage while the foreground image stays entirely visible and uncropped. */
.content>.part-opener{grid-column:1/-1;min-width:0}
.part-opener{position:relative;isolation:isolate;overflow:hidden;
  width:100%;margin:3.8rem 0 1.5rem;scroll-margin-top:64px;
  background:#0a0e0c;
  box-shadow:0 18px 54px rgba(0,0,0,.3)}
.part-opener::before{content:"";position:absolute;z-index:-2;inset:-2.5rem;
  background-image:linear-gradient(rgba(4,8,6,.52),rgba(4,8,6,.72)),var(--part-art);
  background-position:center;background-size:cover;
  filter:blur(22px) saturate(.8);transform:scale(1.08)}
.part-opener-media{height:clamp(575px,84vh,980px);position:relative;z-index:-1}
.part-opener-art{display:block;width:100%;height:100%;object-fit:contain;
  object-position:50% 48%;filter:saturate(1.03) contrast(1.035) brightness(.92)}
.part-opener-shade{position:absolute;z-index:1;inset:0;pointer-events:none;
  background:
    linear-gradient(180deg,rgba(2,5,3,.18) 0%,transparent 28%,
      rgba(2,5,3,.12) 56%,rgba(2,5,3,.9) 100%),
    linear-gradient(90deg,rgba(2,5,3,.28),transparent 24% 76%,rgba(2,5,3,.28))}
.part-opener-title{position:absolute;z-index:2;left:50%;
  bottom:clamp(1.45rem,5vw,4.4rem);transform:translateX(-50%);
  width:min(92%,1080px);margin:0;text-align:center;text-transform:uppercase;
  text-shadow:0 3px 22px rgba(0,0,0,.98)}
.part-opener-kicker{display:block;margin:0 0 .72rem;
  transform:translateY(-.14rem);color:#f5e6c4;
  font:600 clamp(1.12rem,1.9vw,1.65rem)/1.05 'Cinzel',Georgia,serif;
  letter-spacing:.3em}
.part-opener-name{display:block;color:#fffefb;
  font:700 clamp(2.3rem,5.7vw,5.55rem)/1.02 'Cinzel Decorative',Georgia,serif;
  letter-spacing:.012em;text-wrap:balance}
.part-opener-ornament{display:flex;align-items:center;justify-content:center;
  width:min(470px,64%);margin:.9rem auto 0;color:#fffefb}
.part-opener-ornament i{display:block;flex:1;height:1px;background:currentColor}
.part-opener-ornament b{display:block;width:.6rem;height:.6rem;margin:0 .14rem;
  border:1px solid currentColor;transform:rotate(45deg)}
.part-opener+h2.chapter{margin-top:.7rem}
/* scroll-margin-top: TOC/deep-links jump straight here (id on the h2 itself,
   not a separate zero-height marker like page/section anchors), so it needs
   the same sticky-topbar offset or the heading lands hidden behind it. */
h2.chapter{color:#e8d9b5;border-bottom:2px solid var(--accent);
  padding-bottom:.5rem;margin:1.4rem 0 .6rem;font-size:2.6rem;line-height:1.25;
  font-family:'Gwyn Display',Georgia,serif;font-weight:400;
  letter-spacing:.01em;text-wrap:balance;position:relative;scroll-margin-top:64px}
h3.sec{color:#e8d9b5;margin:2rem 0 .45rem;font-size:2rem;line-height:1.25;
  font-family:'Gwyn Display Sub',Georgia,serif;font-weight:400;
  letter-spacing:.02em;text-wrap:balance;position:relative}
h3.sec:first-child{margin-top:.2rem}
/* Class/race names (the 21 curated SUBSECTION_NAMES) are the only h3 that
   also gets a TOC entry -- the source data has no deeper heading levels
   below chapter, so without this every h3 from "WOJOWNIK" down to a single
   fighting-style subsection renders identically. Same accent-bar language
   as spell cards/statblocks ties it to the rest of the "this is a named
   game element" vocabulary instead of inventing a new visual idiom. */
h3.sec--major{font-size:2.3rem;padding-bottom:.35rem;
  border-bottom:1px solid rgba(63,168,102,.45)}
h4{color:#d8c69a;margin:1.4rem 0 .3rem;font-size:1.45rem;line-height:1.3;
  font-family:'Gwyn Display Sub',Georgia,serif;font-weight:400;
  letter-spacing:.01em;text-wrap:balance}
h4.spell-circle{color:var(--accent);margin:1.1rem 0 .3rem;font-size:1.15rem;
  line-height:1.3;font-family:'Gwyn Display Sub',Georgia,serif;font-weight:400;
  letter-spacing:.01em}
/* Bulk-pin star on a circle heading ("2 krąg"): hidden until the heading
   itself is hovered/focused, same reveal-on-hover convention as the
   per-spell star below. Own class (not the shared base .pin-btn opacity
   rule) because it never gets an is-pinned state -- see
   render_circle_bulk_pin in phb.py for why a many-spells-behind-one-star
   toggle can't honestly show on/off. */
h4.spell-circle .pin-btn--bulk{opacity:0;margin-left:.5em;vertical-align:.05em;
  transition:opacity .12s ease,color .12s ease}
h4.spell-circle:hover .pin-btn--bulk,h4.spell-circle:focus-within .pin-btn--bulk,
.pin-btn--bulk:focus{opacity:.7}
.spell-circle-list{columns:2;column-gap:2rem;margin:.2rem 0 1rem;padding-left:1.1rem}
.spell-circle-list li{margin:.1rem 0;break-inside:avoid;display:flex;
  align-items:baseline;gap:.3rem}
/* Per-spell hover star: fully hidden (not just dimmed) until the row is
   hovered/focused -- operator ask was specifically "gwiazdka pojawia się
   przy hoverze", not a permanently-visible dim star like the spell-card
   title's own .pin-btn. Points at the spell's real card anchor (set in
   render_class_circle), so the pin entry it creates opens the spell, not
   this list position. */
.spell-circle-list li .pin-btn--inline{opacity:0;flex:0 0 auto;
  width:1.15rem;margin-left:-1.15rem;padding:0;text-align:left;
  transition:opacity .12s ease,color .12s ease}
.spell-circle-list li:hover .pin-btn--inline,
.spell-circle-list li:focus-within .pin-btn--inline{opacity:.7}
.spell-circle-list li .pin-btn--inline:hover,
.spell-circle-list li .pin-btn--inline:focus{opacity:1}
@media(max-width:640px){.spell-circle-list{columns:1}}
@media(hover:none){
  /* No hover on touch -- the star must stay reachable by tap alone. */
  .spell-circle-list li .pin-btn--inline,h4.spell-circle .pin-btn--bulk{opacity:.55}
}
h5{color:#c9b98f;margin:1rem 0 .3rem;font-size:1.05rem;line-height:1.3;
  font-family:'Gwyn Display Sub',Georgia,serif;font-weight:400;text-wrap:balance}
@media(max-width:640px){
  .part-opener{margin:2.7rem 0 1.1rem}
  .part-opener-media{height:min(78svh,760px)}
  .part-opener-title{bottom:clamp(1rem,5vw,2rem);width:94%}
  .part-opener-kicker{font-size:.86rem;letter-spacing:.22em;margin-bottom:.52rem}
  .part-opener-name{font-size:clamp(1.7rem,8.5vw,2.95rem)}
  .part-opener-ornament{width:min(300px,68%);margin-top:.6rem}
  h2.chapter{font-size:2rem}
  h3.sec{font-size:1.4rem}
  h3.sec--major{font-size:1.7rem}
}
p{margin:.75rem 0;text-wrap:pretty}
/* a touch more air ABOVE a section heading that follows prose (clearer breaks
   between subsections at the wider 840px measure), tight below (proximity). */
p+h3.sec,ul+h3.sec,ol+h3.sec,figure+h3.sec{margin-top:2.6rem}
/* a new class/race is a bigger transition than a subsection -- more air above */
p+h3.sec--major,ul+h3.sec--major,ol+h3.sec--major,figure+h3.sec--major{margin-top:3.6rem}
p+h4,ul+h4,ol+h4{margin-top:1.8rem}
ul.label-group{list-style:none;display:flex;flex-wrap:wrap;gap:.3rem .5rem;
  margin:.5rem 0;padding:0}
ul.label-group li{margin:0;padding:.12rem .5rem;background:#2a2a2f;
  border:1px solid var(--line);border-radius:3px;color:var(--muted);
  font-family:system-ui,sans-serif;font-size:.8rem}
.content ul:not(.label-group):not(.spell-circle-list):not(.tfoot),
.content ol{margin:.5rem 0 .6rem;padding-inline-start:1.25rem}
.content ul:not(.label-group):not(.spell-circle-list):not(.tfoot){list-style:none;
  padding-inline-start:1.05rem}
.content ul:not(.label-group):not(.spell-circle-list):not(.tfoot)>li{position:relative}
.content ul:not(.label-group):not(.spell-circle-list):not(.tfoot)>li::before{
  content:"";position:absolute;left:-.86rem;top:.76em;width:.36rem;height:.36rem;
  transform:translateY(-50%) rotate(45deg);border:1px solid rgba(217,255,220,.42);
  border-radius:1px;
  background:linear-gradient(135deg,#d9ffdc 0 22%,#62c97b 23% 56%,#2b7246 57% 100%);
  box-shadow:0 0 4px rgba(98,201,123,.22)}
.content ol{padding-inline-start:1.42rem}
.content li{margin:.2rem 0}
blockquote{border-left:3px solid var(--accent);background:#242024;margin:.7rem 0;
  padding:.5rem .9rem;color:#d7d1c6;font-style:italic}
blockquote p{margin:.5rem 0}
blockquote p:first-child{margin-top:0}
blockquote p:last-child{margin-bottom:0}
/* A callout's own bold lead line ("### Tika and Artemis: ...") -- not
   italic like the body, so it reads as a title, not more prose. */
.bq-title{font-style:normal;font-weight:700;color:#e8d9b5}

.tbl{margin:1.7rem 0;overflow-x:auto;scrollbar-gutter:stable both-edges}
.tbl figcaption{font-weight:700;color:#e8d9b5;margin:0 0 .35rem;
  font-family:system-ui,sans-serif;font-size:.95rem}
.class-hit-points{margin:.55rem 0 1.2rem;padding:0}
.class-hit-points>div{display:grid;grid-template-columns:minmax(16rem,.72fr) minmax(0,1fr);
  gap:1rem;padding:.34rem 0;border-bottom:1px solid rgba(72,190,104,.14)}
.class-hit-points>div:last-child{border-bottom:0}
.class-hit-points dt{color:#d8c9a7;font-weight:700}
.class-hit-points dd{margin:0;color:var(--text)}
@media(max-width:700px){
  .class-hit-points>div{grid-template-columns:1fr;gap:.08rem;padding:.42rem 0}
}
/* border-collapse:separate (not collapse) + border-spacing:0, on purpose:
   position:sticky on a <th> inside a border-collapse:collapse table is a
   known cross-browser rendering trap -- the sticky cell can render at the
   wrong vertical offset ("popsute nagłówki z przesunięciem w pionie"),
   because collapsed borders change how the browser computes the cell's own
   box for sticky purposes. Separate + zero spacing looks identical to
   collapsed borders as long as each cell draws only its own right/bottom
   edge (below) instead of a full 1px border all around, which would
   otherwise double up between adjacent cells. */
table{border-collapse:separate;border-spacing:0;width:100%;
  font-family:system-ui,sans-serif;font-size:.88rem}
th,td{border-right:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:.35rem .55rem;text-align:left;vertical-align:top}
th:first-child,td:first-child{border-left:1px solid var(--line)}
thead th{border-top:1px solid var(--line)}
/* NOT position:sticky (tried twice, dropped both times): first the classic
   collapsed-border trap (fixed with border-collapse:separate), then a
   content-visibility:auto interaction -- when a .page-flow that was
   "skipped" gets scrolled into view and laid out for the first time, the
   sticky header can render one frame ahead of the now-freshly-laid-out
   first body row, so that row's overflow text appears to float above/
   behind the header (operator screenshots: "Rozwój postaci" table's row 1
   invisible, "Klasy" table showing a stray line from Barbarzyńca's row
   before the header). A merely-bold, non-sticky header can't glitch like
   that -- worth losing "stays pinned while scrolling a long table" for. */
thead th{background:var(--tblhead);color:#e8d9b5;border-bottom:2px solid var(--accent-bar)}
tbody tr:nth-child(even){background:#232327}
tbody tr{transition:background-color .14s ease,box-shadow .14s ease}
tbody tr:not(.tgroup):hover,tbody tr:not(.tgroup):focus-within{
  background:rgba(63,168,102,.16);box-shadow:inset 3px 0 0 var(--accent)}
tr.tgroup td{background:#2e2622;font-weight:700;color:#e8d9b5}
td.t-num,th.t-num{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
td.t-center,th.t-center{text-align:center}
td.t-formula{white-space:nowrap}
/* Table footnotes: already had size/color (muted, smaller) but still used
   the browser's default bullet + list indent, which read as "just another
   list under the table" rather than a note about it. Border-top + no bullet
   reads as a distinct callout, closer to .qnote's language. */
ul.tfoot{list-style:none;margin:.5rem 0 0;padding:.4rem 0 0;
  border-top:1px solid var(--line);font-size:.8rem;color:var(--muted)}
ul.tfoot li{margin:.15rem 0}

.badge{display:inline-block;font-family:system-ui,sans-serif;font-size:.72rem;
  padding:.1rem .5rem;border-radius:3px;margin:.3rem 0}
.badge.low{background:var(--badge);color:#e8c9a0}
.badge.warn{background:#4a2020;color:#f0b0b0}
.ocr-tag{display:flex;width:max-content;max-width:100%;align-items:center;
  gap:.35rem;margin:0 0 .5rem;font-family:system-ui,sans-serif;font-size:.72rem;
  color:#b8b0a2;letter-spacing:.02em;cursor:help}
.ocr-dot{width:.5rem;height:.5rem;border-radius:50%;background:#c8912e;
  display:inline-block;flex:0 0 auto;cursor:help;
  box-shadow:0 0 0 2px rgba(200,145,46,.18)}
.ocr-dot:focus{outline:2px solid var(--accent);outline-offset:2px}

.xref{color:inherit;text-decoration:none;cursor:pointer;
  border-bottom:1px solid transparent;
  transition:color .1s ease,border-color .1s ease}
.xref:hover{color:var(--accent);border-bottom:1px dotted var(--accent)}
.xref-spell{font-style:italic}

.term{border-bottom:1px dotted rgba(216,198,154,.5);cursor:help;
  transition:border-color .1s ease,color .1s ease}
.term:hover,.term:focus{color:#e8d9b5;border-bottom-color:var(--accent);outline:none}
.term:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* Wider than the old name-only tip (280px): tooltips now carry the rule/
   spell body text (5etools-style), so they need reading room. */
.term-tip{position:absolute;z-index:30;max-width:min(380px,86vw);
  background:#26201a;color:#e8e0cf;border:1px solid #6a4636;border-radius:6px;
  padding:8px 11px;font-family:system-ui,sans-serif;font-size:.8rem;line-height:1.4;
  box-shadow:0 6px 22px rgba(0,0,0,.5);pointer-events:none}
.term-tip[hidden]{display:none}
.term-tip-head{display:block;font-weight:700;color:#e8d9b5}
.term-tip-meta{display:block;color:var(--muted);font-size:.72rem;margin-top:1px}
.term-tip-en{font-style:italic}
.term-tip-page{display:block;color:#c8912e;font-size:.72rem;margin-top:3px}
.term-tip-spellmeta{display:block;font-size:.74rem;color:#d7d1c6;
  border-top:1px solid rgba(63,168,102,.35);margin-top:5px;padding-top:5px}
.term-tip-spellmeta strong{color:#d8c69a;font-weight:600}
.term-tip-body{display:block;font-size:.76rem;color:#cfc9bc;margin-top:5px;
  border-top:1px solid rgba(63,168,102,.35);padding-top:5px}

figure.card{margin:1rem 0}
figure.card figcaption{font-weight:700;color:#e8d9b5;margin:0 0 .4rem;
  font-family:system-ui,sans-serif;font-size:.95rem}
figure.card img{max-width:100%;height:auto;border:1px solid var(--line);
  border-radius:4px}
figure.ocr-graphic{margin:1.2rem 0;padding:.7rem .9rem;background:#242024;
  border:1px dashed #5a4a3a;border-radius:5px}
figure.ocr-graphic figcaption{color:var(--muted);font-family:system-ui,sans-serif;
  font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;margin:0 0 .5rem}
/* GWYN CHANGE vs. the original: a GRID, not nested `columns`, so this never
   sits inside .page-body's own multicol (nested CSS multicol is unreliable). */
figure.ocr-graphic .label-group{display:grid;
  grid-template-columns:repeat(2,1fr);gap:.15rem 1.5rem}
@media(min-width:700px){figure.ocr-graphic .label-group{grid-template-columns:repeat(3,1fr)}}
figure.ocr-graphic .label-group li{display:block;margin:.1rem 0}
/* Flattened-OCR random tables (background personality/ideals/bonds/flaws):
   honestly flagged as raw OCR pending structural correction, dimmed so it does
   not read as finished prose. */
figure.ocr-table{margin:1.2rem 0;padding:.7rem .9rem;background:#242024;
  border:1px dashed #5a4a3a;border-radius:5px}
figure.ocr-table figcaption{color:var(--muted);font-family:system-ui,sans-serif;
  font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;margin:0 0 .5rem}
figure.ocr-table .ocr-table-body{color:#b8b0a2;font-size:.9rem;line-height:1.5}
.xgte-table-quarantine{border-color:#a97839;background:rgba(52,35,24,.72)}
.xgte-table-quarantine figcaption{display:flex;align-items:center;gap:.45rem}
.xgte-table-quarantine .xgte-quarantine-scan{margin-left:auto}
.xgte-quarantine-note{margin:.65rem 0 0;color:#d7b978;font-size:.78rem;line-height:1.45}

/* --- Deep-link affordances: heading "#" + page marker (both static HTML,
   nav.js only adds ONE delegated click listener for copy-to-clipboard) --- */
.anchor-link{border:0;background:none;color:var(--accent);
  font-family:system-ui,sans-serif;font-weight:400;cursor:pointer;opacity:0;
  transition:opacity .12s ease;padding:0 .2em;font-size:.7em;
  vertical-align:middle;margin-left:.35em}
h2.chapter:hover .anchor-link,h3.sec:hover .anchor-link,.anchor-link:focus{opacity:.7}
.anchor-link:hover{opacity:1}
/* hover/focus-only on all viewports (a permanently-visible icon reads as a stray
   glyph next to every heading; deep-linking on touch stays via TOC + page markers) */

@media(max-width:900px){
  body[data-page="podrecznik"]{--measure:100%;font-size:17px}
  body[data-page="podrecznik"]{--reader-anchor-clearance:72px}
  .reader-main{padding:var(--topbar-clear) 1rem 6rem}
  body[data-page="podrecznik"] .reader-main{padding:0}
  .content,.reader-extras{padding-left:1rem;padding-right:1rem}
  .book-header-art{margin-bottom:2.25rem}
  /* The off-canvas drawer mechanic itself (.side-panel, shared shape/
     transform/transition for both edges and every page type) lives in
     base.css now -- GWYN CHANGE, mobile nav redesign. What's left here is
     just the drawer's own content typography. */
  .reader-drawer-home{display:flex}
  body[data-page="podrecznik"] .toc>li:not(.toc-part)>a,
  body[data-page="podrecznik"] .toc>li summary>a{white-space:normal}
  .toc>li:not(.toc-part)>a,.toc summary>a,.toc summary>.toc-summary-label,.toc-flat-title{font-size:.9rem}
  .toc a{padding:.18rem .45rem;line-height:1.18}
  .toc-part{margin:.38rem 0 .05rem}
  .toc-part>a{min-height:30px;font-size:.69rem;padding:.18rem .45rem}
  .toc-sub{margin-left:.45rem;padding-left:.58rem}
  .toc-sub a{min-height:34px;font-size:.82rem;padding:.22rem .45rem}
  .toc-alpha{margin-left:.48rem;margin-right:.45rem}
  /* Keep the visible caret small but make the native <summary> disclosure
     target meet the 44px touch-target guidance.  The chapter link remains a
     separate, ordinary navigation target. */
  .toc>li:not(.toc-part)>a,.toc summary{min-height:36px}
  .toc-flat-title{min-height:36px}
  .toc-caret{flex-basis:36px;width:36px;height:36px;padding:10px}
}
@media(max-width:640px){
  .book-hero-title{bottom:clamp(.55rem,3vw,1.2rem);width:94%}
  .book-hero-name{font-size:clamp(1.38rem,7.4vw,2.2rem)}
  .book-hero-kicker{font-size:.68rem;letter-spacing:.2em;transform:translateY(-.2rem)}
  .book-hero-ornament{margin-top:.45rem}
  .race-art--rail{width:min(80vw,240px)}
  .race-art--rail img{max-height:400px}
  .page-flow>.phb-art--measure{width:min(84vw,360px)}
  .page-flow>.phb-art--rail{width:min(72vw,220px)}
  .page-flow>.phb-art--feature{width:min(92vw,520px)}
  .phb-art--measure>.phb-art-button img{max-height:360px}
  .phb-art--rail>.phb-art-button img{max-height:320px}
  .phb-art--feature>.phb-art-button img{max-height:620px}
  .phb-art-pair .phb-art-button img{max-height:280px}
  .page-flow>.phb-art-pair{grid-template-columns:repeat(2,minmax(0,1fr));gap:.55rem}
}

/* XGtE keeps the shared shell but has book-specific rail/button transitions.
   Repeat the reduced-motion contract at the end of this stylesheet so these
   selectors cannot override the global accessibility rule. */
@media (prefers-reduced-motion: reduce){
  body[data-page="xanathar"] .side-panel,
  body[data-page="xanathar"] .edge-tab,
  body[data-page="xanathar"] .edge-tab-shard,
  body[data-page="xanathar"] .page-scan-btn,
  body[data-page="xanathar"] .xgte-art img,
  body[data-page="xanathar"] .spell-card,
  body[data-page="xanathar"] .spc-card,
  body[data-page="xanathar"] .spc-toggle,
  body[data-page="xanathar"] .xgte-content *,
  body[data-page="xanathar"] .toc-caret,
  body[data-page="xanathar"] .toc summary::before{
    transition:none !important;
    animation:none !important;
  }
}

/* Gwyn -- components.css: spell cards (DDB grid + legacy), statblocks, the
   Appendix-D gallery grid. Fonts referenced here live in fonts.css.tpl. */

/* Pin button (star toggle): shared by spell-card/statblock titles here AND
   the third .page-tools button in book.css -- one rule, three call sites,
   all delegated to dock.js via [data-pin]. Inline before the title text
   (not position:absolute) so it never overlaps card content at any width. */
.pin-btn{background:none;border:0;color:var(--muted);cursor:pointer;
  font-size:.8em;line-height:1;vertical-align:.05em;margin-right:.4em;
  padding:0;opacity:.5;transition:opacity .12s ease,color .12s ease}
.pin-btn:hover,.pin-btn:focus{opacity:1;color:#e8c24a;outline:none}
.pin-btn.is-pinned{opacity:1;color:#e8c24a}
@media print{.pin-btn{display:none}}

/* --- DDB-style spell card (primary, semantic renderer) --- */
.spc{border:1px solid #304138;border-top:3px solid var(--accent-bar,#3fa866);
  background:#211d18;border-radius:5px;padding:.8rem 1.1rem;margin:0 0 1.25rem;
  max-width:none;box-shadow:0 1px 4px rgba(0,0,0,.4)}
.spc-head{display:grid;grid-template-columns:3.45rem minmax(0,1fr);
  align-items:center;gap:.4rem .78rem;margin:0 0 .62rem;min-width:0}
.spc-heading-copy{min-width:0}
.spc-circle-pin{position:relative;display:grid;place-items:center;width:3.25rem;
  height:3.25rem;margin:0;padding:0;border:0;border-radius:.65rem;
  --spc-circle-ink:#91f29a;background:transparent;color:var(--spc-circle-ink);
  opacity:.9;isolation:isolate;
  transition:opacity .16s ease,filter .16s ease,transform .12s ease}
.spc-circle-pin::before{content:"";position:absolute;inset:.55rem;z-index:-1;
  border:1.5px solid currentColor;background:rgba(14,39,23,.04);
  transform:rotate(45deg);box-shadow:0 0 10px rgba(101,225,125,.06);
  transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,
    transform .14s ease}
.spc-circle-pin-level{font:800 .82rem/1 system-ui,sans-serif;letter-spacing:-.04em;
  color:transparent;-webkit-text-stroke:1px var(--spc-circle-ink);
  text-shadow:0 0 9px rgba(145,242,154,.2);
  transition:color .16s ease,-webkit-text-stroke-color .16s ease,
    text-shadow .16s ease,transform .14s ease}
.spc-circle-pin:hover,.spc-circle-pin:focus,.spc-circle-pin:focus-visible{--spc-circle-ink:#c8ffce;
  color:var(--spc-circle-ink);opacity:1;
  outline:none;filter:drop-shadow(0 0 8px rgba(101,225,125,.32));
  transform:scale(1.055)}
.spc-circle-pin:focus-visible{box-shadow:0 0 0 2px #211d18,0 0 0 4px #91f29a}
.spc-circle-pin:active{transform:scale(.9)}
.spc-circle-pin:active::before{transform:rotate(45deg) scale(.88)}
.spc-circle-pin.is-pinned{color:#91f29a;opacity:1;
  filter:drop-shadow(0 0 8px rgba(101,225,125,.3))}
.spc-circle-pin.is-pinned::before{border-color:#91f29a;
  background:linear-gradient(135deg,#143d2a,#236b43);
  box-shadow:inset 0 0 0 1px rgba(190,255,198,.08),
    0 0 15px rgba(101,225,125,.28)}
.spc-circle-pin.is-pinned .spc-circle-pin-level{color:#c8ffce;
  -webkit-text-stroke-color:transparent;text-shadow:0 0 9px rgba(145,242,154,.62);
  transform:scale(1.04)}
/* Title: full uppercase from ONE font (no font-variant:small-caps synthesis,
   which rendered the first capital from a different glyph set -> the "A" artifact). */
.spc-title{font-family:'Noto Serif',Georgia,serif;text-transform:uppercase;
  font-variant:normal;font-weight:600;font-size:1.3rem;line-height:1.1;
  margin:0;color:#e8dcc4;letter-spacing:.04em}
.spc-en{text-transform:none;font-variant:normal;font-size:.85rem;color:#a89e88;
  font-weight:400;letter-spacing:0}
.spc-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.5rem 1.1rem;
  padding:.5rem 0;border-top:1px solid rgba(63,168,102,.32);
  border-bottom:1px solid rgba(63,168,102,.32);margin-bottom:.5rem}
.spc-cell{min-width:0}
/* Long material-component text (EN, 5etools) -- full-width line under the
   grid instead of blowing up one grid cell's row height (Klon/Clone). */
.spc-material{margin:.2rem 0 .5rem;font-family:system-ui,sans-serif;
  font-size:.78rem;color:#a89e88;line-height:1.45}
.spc-k{font-family:system-ui,sans-serif;font-size:.66rem;text-transform:uppercase;
  letter-spacing:.05em;color:#d8c69a;font-weight:600}
.spc-v{color:#e8e0cf;font-size:.92rem;line-height:1.3}
.spc-accent{color:var(--accent,#3fa866);font-weight:600}
.spc-classes{margin:.2rem 0 0;font:600 .74rem/1.35 system-ui,sans-serif;
  color:#aeb8b0;letter-spacing:.015em}
.spc-classes-k{color:#d8c69a;font-weight:600}
/* The description is deliberately quieter than the spell name and rules grid,
   but it shares the complete card width with its lists and rules fields. */
.spc-body{margin-top:.65rem;font-size:.9em;line-height:1.56;font-weight:500;
  color:#c9c5bb;letter-spacing:.005em}
.spc-body p{max-width:none;margin:.6rem 0;hyphens:auto;text-wrap:pretty}
.spc-body p+p{margin-top:.72rem}
.spc-body strong{color:#e2d6bd;font-weight:600}
.spc-body--en{opacity:.72;font-style:italic}
.spc-mat{font-size:.82em;color:#a89e88}
@media(max-width:560px){
  .spc{padding:.72rem .78rem}
  .spc-head{grid-template-columns:3.15rem minmax(0,1fr);gap:.3rem .58rem}
  .spc-circle-pin{width:3rem;height:3rem}
  .spc-circle-pin::before{inset:.52rem}
  .spc-title{font-size:1.08rem;line-height:1.2}
  .spc-en{display:block;margin-top:.16rem;font-size:.78rem}
  .spc-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(prefers-reduced-motion:reduce){
  .spc-circle-pin,.spc-circle-pin::before,.spc-circle-pin-level{transition:none}
}
@media print{.spc{background:#f1f0e8;--accent:#245f39}.spc-title{color:#173d26}
  .spc-v{color:#141414}.spc-k{color:#7a5a1a}}

/* --- Legacy spell card (fallback renderer only) --- */
.spell-card{border:1px solid var(--line);border-left:3px solid var(--accent-bar,#3fa866);
  background:#242024;border-radius:5px;padding:.7rem 1rem;margin:0 0 1rem;
  max-width:42rem}
.spell-name{font-size:1.2rem;font-weight:700;color:#e8d9b5;margin:0}
.spell-en{font-size:.85rem;color:var(--muted);font-weight:400}
.spell-level{font-style:italic;color:var(--muted);margin:.1rem 0 .5rem;font-size:.9rem}
.spell-props{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:1px 0;
  margin:.3rem 0;padding:.4rem 0;border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);font-size:.9rem}
.spell-key{font-weight:700;color:#d8c69a}
.spell-key::after{content:':'}
.spell-val{padding-left:.6rem}
.spell-flag{font-family:system-ui,sans-serif;font-size:.78rem;color:#e8c9a0;
  background:var(--badge);display:inline-block;padding:.1rem .5rem;border-radius:3px;
  margin:.4rem 0}
.spell-mat{font-size:.82em;color:var(--muted)}
.spell-body{margin-top:.4rem;max-width:66ch}
.spell-body p{margin:.4rem 0;text-wrap:pretty}
.spell-sub strong{color:#d8c69a}
.spell-body--en{opacity:.72;font-style:italic}

/* --- Statblock (Appendix D) --- */
.statblock-grid{margin:1.9rem 0}
.statblock-grid.is-gallery{display:grid;
  grid-template-columns:repeat(auto-fill,minmax(360px,1fr));
  gap:1.25rem 1.5rem;align-items:start}
.statblock-grid.is-gallery>.sbw-wrap{max-width:none}

.sbw-wrap{--sbw-accent:#3fa866;--sbw-title:#e8dcc4;--sbw-label:#d8c69a;
  --sbw-ink:#e8e0cf;--sbw-dim:#a89e88;--sbw-bg:#211d18;
  --sbw-line:rgba(63,168,102,.32);--sbw-bar:#3fa866;
  margin:0 0 1.5rem;max-width:100%}
.sbw-wrap *{box-sizing:border-box}
/* single/twocol are the two auto-selected everyday modes (_pick_mode()) --
   matched to --measure so a statblock reads at the same width as the prose
   around it (operator: "szerokość panelu... jak domyślny tekst"), not a
   fixed px number that drifted from the reading column's own responsive
   width. hero is opt-in spotlight only (boss view), kept intentionally a
   touch wider for its art+content layout. */
.sbw-wrap--single{max-width:var(--measure)}
.sbw-wrap--twocol{max-width:var(--measure)}
.sbw-wrap--hero{max-width:840px}
.sbw-wrap.has-art{display:grid;align-items:start;gap:1rem 1.4rem}
.sbw-wrap--single.has-art{grid-template-columns:minmax(0,1fr) clamp(130px,24%,200px);
  max-width:800px}
.sbw-wrap--twocol.has-art{grid-template-columns:minmax(0,1fr) clamp(150px,22%,230px);
  max-width:980px}
.sbw-wrap--hero.has-art{grid-template-columns:minmax(0,1fr) clamp(210px,30%,300px);
  max-width:1120px}

.sbw{font-family:'Noto Sans',system-ui,sans-serif;
  font-weight:400;color:var(--sbw-ink);background:var(--sbw-bg);
  border:1px solid #55402f;border-top:3px solid var(--sbw-bar);border-radius:5px;
  box-shadow:0 1px 4px rgba(0,0,0,.4);break-inside:avoid;
  padding:14px 18px;width:100%;max-width:100%;font-size:13.5px;line-height:1.44}
.sbw-content{position:relative}
.sbw--twocol .sbw-content,.sbw--hero .sbw-content{display:grid;
  grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);gap:0 1.5rem;
  align-items:start}
.sbw-col{min-width:0}
.sbw-col--right{border-left:1px solid var(--sbw-line);padding-left:1.4rem}

/* Full uppercase from ONE font, not font-variant:small-caps -- same "A"
   artifact already diagnosed and fixed for .spc-title above: small-caps
   synthesis renders diacritic letters (Polish creature names are full of
   them) from a different glyph set than the rest of the word, a visible
   per-letter mismatch. This declaration had the old broken pattern too;
   the fix just never got copied over here. */
.sbw-title{font-family:'Noto Serif',Georgia,serif;text-transform:uppercase;
  font-variant:normal;font-weight:600;font-size:1.4rem;line-height:1.05;margin:0;
  color:var(--sbw-title);letter-spacing:.04em}
.sbw-sta{font-style:italic;margin:0 0 6px;font-size:.86rem;color:var(--sbw-dim)}
.sbw-general,.sbw-features{
  border-bottom:1px solid var(--sbw-line);padding-bottom:6px;margin-bottom:7px}
.sbw-general:last-child,.sbw-features:last-child{
  border-bottom:none;padding-bottom:0;margin-bottom:0}
.sbw-feature{margin:2px 0;text-indent:-1em;padding-left:1em;text-wrap:pretty}
.sbw-keyword{font-weight:600;color:var(--sbw-label)}
.sbw-keyword::after{content:'\00a0'}
.sbw-abilities-wrap{border-bottom:1px solid var(--sbw-line);
  padding-bottom:6px;margin-bottom:7px}
.sbw-abilities-wrap:last-child{border-bottom:none;padding-bottom:0;margin-bottom:0}
.sbw-abilities{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));
  text-align:center;gap:4px 2px;background:rgba(0,0,0,.14);border-radius:4px;
  padding:6px 4px;border-bottom:none;margin:0}
.sbw-ability{padding:2px 3px}
.sbw-abilityname{font-weight:600;color:var(--sbw-accent);font-size:.72rem;
  letter-spacing:.03em}
.sbw-abilitynums{display:flex;flex-direction:column;align-items:center;line-height:1.25}
.sbw-score{font-weight:600}
.sbw-mod{color:var(--sbw-dim);font-size:.9em}
.sbw-save{color:var(--sbw-dim);font-size:.9em}
.sbw-save.is-prof{color:var(--sbw-label);font-weight:700}
.sbw-abilitylegend{margin-top:5px;font-size:.66rem;color:var(--sbw-dim);
  text-align:center;letter-spacing:.02em}
.sbw-legend-prof{opacity:.8}
.sbw-cr{display:flex;justify-content:space-between;align-items:baseline;gap:.6rem;
  border-bottom:2px solid var(--sbw-bar);padding-bottom:4px;margin-bottom:7px}
/* Same small-caps diacritic-artifact fix as .sbw-title above -- "Wyzwanie"
   is plain text from statblocks.py, not pre-uppercased, so text-transform
   has to do that job now that font-variant isn't. */
.sbw-cr-label{font-family:'Noto Serif',Georgia,serif;text-transform:uppercase;
  font-variant:normal;letter-spacing:.03em;
  font-weight:600;font-size:1.04rem;color:var(--sbw-accent)}
.sbw-cr-val{font-weight:600;color:var(--sbw-title)}
.sbw-sectionheader{font-family:'Noto Serif',Georgia,serif;text-transform:uppercase;
  font-variant:normal;letter-spacing:.03em;
  font-weight:600;font-size:1.08rem;color:var(--sbw-accent);
  border-bottom:1px solid var(--sbw-line);margin:9px 0 3px;padding-bottom:1px}
.sbw-section:first-child .sbw-sectionheader,
.sbw-col--right>.sbw-section:first-child .sbw-sectionheader{margin-top:0}
.sbw-line{margin:5px 0;text-wrap:pretty}
.sbw-line .sbw-keyword{font-style:italic;color:var(--sbw-ink)}
.sbw-entries{margin-bottom:2px}

.sbw-art{align-self:start}
.sbw-art-img{display:block;width:100%;height:auto;object-fit:contain}
.sbw-wrap--hero .sbw-art{position:sticky;top:64px}
.sbw-wrap--hero .sbw-art-img{aspect-ratio:3/4;object-fit:cover;border-radius:6px;
  border:1px solid #55402f;box-shadow:0 2px 8px rgba(0,0,0,.45)}
.sbw-art--mono{display:flex;align-items:center;justify-content:center;aspect-ratio:3/4;
  background:#241f19;border:1px solid #3a3128;border-radius:6px}
/* No font-variant:small-caps (same diacritic artifact as .sbw-title) --
   moot for most names since statblocks.py already uppercases just the one
   initial letter in Python before this ever reaches CSS, but a handful of
   Polish creature names start with a diacritic letter, so don't leave the
   risky property in for no reason. */
.sbw-mono{font-family:'Noto Serif',Georgia,serif;font-variant:normal;
  font-weight:600;font-size:4.5rem;color:var(--sbw-label);opacity:.16;line-height:1}

@media(max-width:640px){
  .sbw-wrap.has-art{display:flex;flex-direction:column}
  .sbw-art{order:-1;width:100%;max-width:260px;margin:0 0 .6rem}
  .sbw-wrap--hero .sbw-art{position:static}
  .sbw--twocol .sbw-content,.sbw--hero .sbw-content{display:block}
  .sbw-col--right{border-left:0;padding-left:0}
}
@media print{.sbw-wrap{--sbw-ink:#141414;--sbw-title:#173d26;--sbw-label:#4e6337;
  --sbw-dim:#46534b;--sbw-bg:#f1f0e8;--sbw-line:#245f39;--sbw-accent:#245f39;
  --sbw-bar:#245f39}
  .sbw{box-shadow:none}}

/* Kroniki botaniczne: real session notes treated as pinned field specimens. */
.botanical-chronicles{position:relative;isolation:isolate;overflow:hidden;
  padding:clamp(4rem,8vw,7rem) 1.5rem clamp(5rem,9vw,8rem);
  background:
    radial-gradient(ellipse at 50% 0,rgba(63,168,102,.13),transparent 52%),
    linear-gradient(180deg,#060b08 0%,#09110c 48%,#050806 100%);
  border-top:1px solid rgba(145,242,154,.08)}
.botanical-chronicles::before{content:"";position:absolute;z-index:-1;inset:0;
  pointer-events:none;background:
    repeating-linear-gradient(122deg,transparent 0 68px,rgba(145,242,154,.018) 69px 70px),
    linear-gradient(90deg,transparent,rgba(145,242,154,.025),transparent)}
.botanical-chronicles-inner{width:min(100%,1500px);margin:0 auto;min-width:0}
.botanical-chronicles-heading{margin-bottom:1.4rem}
.botanical-chronicles-heading>p:last-child{max-width:50ch;
  font:italic 500 clamp(.95rem,1.4vw,1.1rem)/1.55 "EB Garamond",Georgia,serif}

.kwiatki-toolbar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;
  min-width:0;margin:0 auto 1.4rem}
.kwiatki-filters{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.kwiatki-filter-pill{cursor:pointer;padding:.36rem .8rem;border:1px solid #3a453e;
  border-radius:20px;background:transparent;color:#7d8880;
  font:600 .72rem/1 "Source Sans 3",system-ui,sans-serif;
  letter-spacing:.03em;white-space:nowrap;
  transition:color .18s ease,border-color .18s ease,background .18s ease}
.kwiatki-filter-pill span{display:inline-grid;place-items:center;min-width:1.25rem;
  min-height:1.25rem;margin-left:.42rem;padding:0 .25rem;border-radius:20px;
  background:rgba(255,255,255,.06);font-size:.62rem}
.kwiatki-filter-pill[aria-pressed="true"]{background:rgba(63,168,102,.22);
  border-color:#3fa866;color:#e8f5ea}
.kwiatki-filter-pill:hover,.kwiatki-filter-pill:focus-visible{border-color:#91f29a;
  color:#e8f5ea;outline:none}
.kwiatki-actions{display:flex;align-items:center;gap:.5rem;margin-left:auto;min-width:0}
.kwiatki-search{width:10.5rem;min-width:0;padding:.4rem .6rem;background:#12180f;
  border:1px solid #2c3830;border-radius:5px;color:#e8f0ea;
  font:500 .78rem/1.2 "Source Sans 3",system-ui,sans-serif}
.kwiatki-search::placeholder{color:#738078}
.kwiatki-search:focus{border-color:#3fa866;outline:2px solid rgba(145,242,154,.18);
  outline-offset:1px}
.kwiatki-draw{display:inline-flex;align-items:center;gap:.35rem;padding:.42rem .7rem;
  cursor:pointer;background:transparent;border:1px solid #3fa866;border-radius:5px;
  color:#c8f5d2;font:600 .72rem/1 "Source Sans 3",system-ui,sans-serif;
  white-space:nowrap}
.kwiatki-draw svg{width:13px;height:13px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.kwiatki-draw:hover,.kwiatki-draw:focus-visible{background:rgba(63,168,102,.14);
  color:#effff1;outline:none;box-shadow:0 0 0 2px rgba(145,242,154,.12)}

.kwiatki-collections{display:grid;gap:3.25rem}
.kwiatki-chapter-group[hidden]{display:none}
.kwiatki-chapter-heading{display:flex;align-items:end;justify-content:space-between;
  gap:1rem;margin:0 0 1.25rem;padding:0 0 .75rem;
  border-bottom:1px solid rgba(145,242,154,.16)}
.kwiatki-chapter-heading div>p{margin:0 0 .22rem;color:#62be79;
  font:700 .58rem/1 "Source Sans 3",system-ui,sans-serif;letter-spacing:.2em}
.kwiatki-chapter-heading h3{margin:0;color:#ebe8df;
  font:600 clamp(1.45rem,2.4vw,2rem)/1.1 "Cinzel",Georgia,serif}
.kwiatki-chapter-heading>p{margin:0;color:#738078;
  font:600 .72rem/1.2 "Source Sans 3",system-ui,sans-serif;white-space:nowrap}
.kwiatki-chapter-heading>p span{padding:0 .25rem;color:#3fa866}

.specimen-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  gap:1.15rem;min-width:0}
.specimen-chip{--specimen-tilt:-.45deg;position:relative;display:block;min-width:0;
  padding:8px;cursor:pointer;color:#3a3226;text-align:initial;
  border:1px solid rgba(64,48,31,.22);border-radius:2px;
  background:linear-gradient(160deg,#f2ede1,#e6ddc8);
  box-shadow:0 9px 20px rgba(0,0,0,.42);transform:rotate(var(--specimen-tilt));
  transition:transform .2s ease,box-shadow .2s ease;
  content-visibility:auto;contain-intrinsic-size:220px 270px}
.specimen-chip:nth-of-type(6n+2){--specimen-tilt:.35deg}
.specimen-chip:nth-of-type(6n+3){--specimen-tilt:-.2deg}
.specimen-chip:nth-of-type(6n+4){--specimen-tilt:.5deg}
.specimen-chip:nth-of-type(6n+5){--specimen-tilt:-.3deg}
.specimen-chip:nth-of-type(6n+6){--specimen-tilt:.2deg}
.specimen-chip[hidden]{display:none}
.specimen-paper{position:relative;display:block;min-width:0;overflow:hidden;
  background:#ded5c2;border:1px solid rgba(63,42,26,.16)}
.specimen-paper::after{content:"";position:absolute;z-index:1;inset:0;pointer-events:none;
  background:
    linear-gradient(180deg,rgba(4,14,8,.18) 0,transparent 38%,rgba(3,12,7,.72) 100%),
    linear-gradient(105deg,rgba(36,94,51,.14),transparent 56%)}
.specimen-photo{display:block;width:100%;aspect-ratio:4/5;object-fit:cover;
  filter:brightness(.82) saturate(.72) sepia(.16) hue-rotate(62deg) contrast(1.08);
  transition:filter .2s ease}
.specimen-code,.specimen-date,.specimen-photo-count,.specimen-title{
  position:absolute;z-index:2}
.specimen-code{left:7px;top:7px;padding:.38rem .5rem;border-left:3px solid #91f29a;
  background:rgba(5,18,10,.9);color:#f2ffef;font-size:.82rem;
  font-family:"Cinzel",Georgia,serif;font-weight:750;line-height:1;
  letter-spacing:.025em;box-shadow:0 3px 10px rgba(0,0,0,.4)}
.specimen-date{left:7px;top:2.55rem;padding:.28rem .4rem;
  background:rgba(7,18,11,.82);color:#d5ded7;font-size:.64rem;
  font-family:"Source Sans 3",system-ui,sans-serif;font-weight:650;line-height:1;
  letter-spacing:.055em;box-shadow:0 2px 7px rgba(0,0,0,.3)}
.specimen-photo-count{top:5px;right:5px;display:inline-flex;align-items:center;
  gap:.24rem;padding:.3rem .38rem;background:rgba(5,18,10,.86);color:#e8f5ea;
  font:650 .62rem/1 "Source Sans 3",system-ui,sans-serif;
  box-shadow:0 2px 7px rgba(0,0,0,.3)}
.specimen-photo-count svg{width:12px;height:12px;fill:none;stroke:currentColor;
  stroke-width:1.2}
.specimen-title{left:7px;right:7px;bottom:7px;overflow:hidden;
  padding:.42rem .52rem;border-left:2px solid rgba(120,215,140,.72);
  background:rgba(5,12,8,.72);color:#dce6de;
  font:650 .68rem/1.28 "Source Sans 3",system-ui,sans-serif;
  white-space:normal;text-wrap:balance;text-shadow:0 1px 2px #000;
  box-shadow:0 3px 11px rgba(0,0,0,.3);
  backdrop-filter:blur(2px)}
.specimen-chip:hover,.specimen-chip:focus-visible{z-index:2;
  transform:rotate(0deg) translateY(-3px) scale(1.03);
  box-shadow:0 15px 29px rgba(0,0,0,.55);outline:2px solid #91f29a;
  outline-offset:3px}
.specimen-chip:hover .specimen-photo,.specimen-chip:focus-visible .specimen-photo{
  filter:brightness(.91) saturate(.82) sepia(.1) hue-rotate(55deg) contrast(1.06)}
.kwiatki-empty{margin:1rem 0;color:#7d8880;
  font:italic 500 .88rem/1.5 "EB Garamond",Georgia,serif}
.kwiatki-empty[hidden],.kwiatek-photo-trigger[hidden]{display:none}
@media(max-width:700px){
  .botanical-chronicles{padding:3.5rem .85rem 5rem}
  .kwiatki-actions{width:100%;margin-left:0}
  .kwiatki-search{flex:1;width:auto}
  .kwiatki-collections{gap:2.5rem}
  .kwiatki-chapter-heading{align-items:start}
  .kwiatki-chapter-heading h3{font-size:1.3rem}
  .kwiatki-chapter-heading>p{padding-top:.15rem;font-size:.65rem}
  .specimen-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:.8rem}
  .specimen-chip{padding:6px;contain-intrinsic-size:165px 210px}
}
@media(prefers-reduced-motion:reduce){
  .specimen-chip{transition:none;transform:none}
  .specimen-chip:hover,.specimen-chip:focus-visible{transform:none}
}

/* Gwyn -- chrome.css: search result rows (shared by the homepage inline
   surface and the side-panel inline surface -- see base.css/md.css for
   their respective input fields), glossary tooltips (shared with book.css's
   .term/.term-tip), pinned items, copy toast.
   GWYN CHANGE (mobile nav redesign, round 2): the Ctrl+K modal overlay
   (.search-overlay/.search-box) is gone -- every non-index page now has an
   always-editable inline search field in its right panel instead, so
   there's no separate surface left to open. .search-input/.search-results/
   .search-item and everything below stay: they're the shared row styling
   both the homepage's and the panel's inline surfaces render into. */
.search-input{width:100%;box-sizing:border-box;background:#1c1c1e;color:var(--ink);
  border:0;border-bottom:1px solid var(--line);padding:.9rem 1rem;font-size:1.05rem;
  font-family:system-ui,sans-serif;outline:none}
.search-results{list-style:none;margin:0;padding:.3rem;overflow:auto;
  scrollbar-width:thin;
  scrollbar-color:rgba(145,242,154,.52) rgba(5,12,8,.82)}
.search-results::-webkit-scrollbar{width:7px;height:7px}
.search-results::-webkit-scrollbar-track{background:rgba(5,12,8,.82)}
.search-results::-webkit-scrollbar-thumb{background:rgba(145,242,154,.52);
  border:2px solid rgba(5,12,8,.82);border-radius:999px}
.search-results::-webkit-scrollbar-thumb:hover{background:rgba(145,242,154,.72)}
.home-search-results{position:absolute;top:100%;left:0;right:0;z-index:6;
  max-height:min(58vh,430px);overflow:auto;background:rgba(5,12,8,.97);
  border:1px solid rgba(145,242,154,.28);border-top:0;border-radius:0 0 7px 7px;
  box-shadow:0 18px 34px rgba(0,0,0,.42)}
.home-search-results[hidden]{display:none}
.search-item{display:grid;grid-template-columns:1.75rem minmax(0,1fr) max-content;
  align-items:center;column-gap:.72rem;min-height:3.45rem;padding:.52rem .68rem;
  border-bottom:1px solid rgba(145,242,154,.07);border-radius:4px;
  cursor:pointer;font-family:system-ui,sans-serif;text-align:left}
.search-item.active{background:rgba(63,168,102,.13)}
/* hover:hover (mouse only): a plain :hover rule on a tap target is a known
   WebKit/iOS trap -- the first tap just triggers the :hover state instead of
   the click, so the result LOOKS tappable but needs a second tap to actually
   navigate (reported as "can't tap the result on mobile"). Gating behind
   hover:hover removes :hover entirely on touch-only devices, so the tap's
   click always fires on the first try. */
@media(hover:hover){.search-item:hover{background:rgba(63,168,102,.1)}}
.search-marker{display:grid;place-items:center;width:1.75rem;height:1.75rem;
  color:var(--search-kind,#8f9691)}
.search-kind-dot{display:block;width:.54rem;height:.54rem;background:currentColor;
  border:1px solid rgba(255,255,255,.28);transform:rotate(45deg);
  box-shadow:0 0 9px color-mix(in srgb,currentColor 34%,transparent)}
.search-meta{justify-self:end;display:flex;justify-content:flex-end;min-width:7.4rem}
.search-type{display:inline-flex;align-items:center;justify-content:center;
  min-height:1.45rem;max-width:7.4rem;padding:.12rem .42rem;
  color:var(--search-kind,#a7ada9);border:1px solid currentColor;
  border-radius:2px;background:rgba(7,12,9,.72);font-size:.61rem;
  font-weight:750;line-height:1;text-transform:uppercase;letter-spacing:.055em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.search-type--0,.search-marker--0{--search-kind:#67d487}
.search-type--1,.search-marker--1{--search-kind:#91f29a}
.search-type--2,.search-marker--2{--search-kind:#d1a04a}
.search-type--3,.search-marker--3{--search-kind:#79aec0}
.search-type--4,.search-marker--4{--search-kind:#aa8dcc}
.search-type--5,.search-marker--5{--search-kind:#91b47e}
.search-type--6,.search-marker--6{--search-kind:#6db8a7}
.search-type--7,.search-marker--7{--search-kind:#aaa39a}
.spell-circle-badge{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:1.55rem;height:1.55rem;background:linear-gradient(135deg,#143d2a,#236b43);
  color:#91f29a;clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%);
  font-size:.64rem;font-weight:800;line-height:1;letter-spacing:-.03em}
.search-main{display:flex;flex-direction:column;min-width:0;gap:.05rem}
.search-name{display:block;min-width:0;color:var(--ink);font-size:.95rem;
  font-weight:650;line-height:1.25;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;text-align:left}
.search-details{display:flex;align-items:baseline;gap:.52rem;min-width:0;
  margin-top:.1rem;overflow:hidden;white-space:nowrap}
.search-en{min-width:0;color:#9da9a1;font-size:.74rem;font-style:italic;
  overflow:hidden;text-overflow:ellipsis}
.search-ctx{min-width:0;color:var(--muted);font-size:.7rem;overflow:hidden;
  text-overflow:ellipsis}
.search-en+.search-ctx::before{content:"·";margin-right:.52rem;color:#59635d}
.search-item--nojump{cursor:default}
.search-item--nojump:hover{background:transparent}
.search-empty{padding:.8rem;color:var(--muted);font-family:system-ui,sans-serif;
  font-size:.9rem;text-align:center}
@media(max-width:520px){
  .search-item{grid-template-columns:1.55rem minmax(0,1fr) max-content;
    column-gap:.52rem;padding:.48rem .5rem}
  .search-marker{width:1.55rem;height:1.55rem}
  .search-meta{min-width:0}
  .search-type{max-width:6.5rem;padding-inline:.32rem;font-size:.55rem}
  .search-name{font-size:.9rem}
}

.copy-toast{position:fixed;left:50%;bottom:2rem;transform:translateX(-50%) translateY(1rem);
  background:var(--accent);color:#fff;font-family:system-ui,sans-serif;font-size:.85rem;
  padding:.5rem .9rem;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,.5);
  opacity:0;pointer-events:none;transition:opacity .15s ease,transform .15s ease;z-index:30}
.copy-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

/* Pinned items: a section inside the right side panel (GWYN CHANGE, mobile
   nav redesign round 2 -- previously its own <aside id="pin-panel">, now
   just .pin-section within #right-panel, see base.css .pin-section /
   .side-panel for the panel's own positioning/transform). Items are added
   via the .pin-btn star on spell cards/statblocks/page markers
   (components.css / book.css .page-tools), never auto-tracked. Row styling
   below is unchanged from the old wide panel. */
.pin-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.35rem}
.pin-item{display:grid;grid-template-columns:1.75rem minmax(0,1fr) max-content 1rem;
  align-items:center;column-gap:.4rem;background:#1c1c1e;border:1px solid var(--line);
  border-radius:6px;padding:.3rem .3rem .3rem .5rem}
.pin-item .search-marker{justify-self:center}
.pin-item-name{min-width:0;color:var(--ink);text-decoration:none;
  font-size:.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pin-item-name:hover{color:var(--accent)}
.pin-item-remove{justify-self:center;background:none;border:0;color:var(--muted);
  cursor:pointer;font-size:1rem;line-height:1;padding:0 .1rem}
.pin-item-remove:hover{color:#91f29a}
@media print{.term-tip,.copy-toast{display:none}}

/* Gwyn -- md.css: source-book (biblioteka/*.html) and character (postacie.html)
   readers. Content is rendered fully and statically (no lazy chunking, no
   raw/rendered toggle -- see gwynbuild/mdrender.py), so this only needs the
   content typography, not the old progress-bar/toggle chrome. */

.mdreader{max-width:900px;margin:0 auto;padding:1rem 1.5rem 5rem}
.mdreader-bar{display:flex;align-items:center;gap:1rem;position:sticky;top:52px;
  background:var(--bg);padding:.6rem 0;border-bottom:1px solid var(--line);z-index:4}
.mdreader-bar h2{margin:0;font-size:1.3rem;color:#e8d9b5;flex:1 1 auto}
/* Source-book titles are document headings, not navigation chrome. Keeping
   them sticky made the rendered/raw text pass underneath and become hidden
   while scrolling. */
body[data-page^="biblioteka-"] .mdreader-bar{position:static;top:auto}
.mdcontent{content-visibility:auto;contain-intrinsic-size:auto 1800px}
/* max-width on every one of these, matching .mdcontent p/ul/ol below:
   without it, headings and <hr> stretch to the full .mdreader column
   (900px) while the text they sit above/below is capped at 70ch, so
   underlines/rules read as visibly wider than the prose (found via a
   direct operator report -- "kreski poziome szersze niż kolumna tekstu").
   A FIXED px, not 70ch: ch is relative to the element's OWN font-size, and
   headings render much bigger than the 18px body text -- 70ch on a 1.7rem
   h1 computes wider than the 630px 70ch actually is on an 18px paragraph
   (checked live: it came out to 1110px on .char-summary h2, wider than
   the 900px column itself, i.e. no constraint at all). 630px is 70ch
   measured at the shared 18px paragraph size, so every heading matches
   the paragraphs' actual rendered width instead of its own. */
.mdcontent h1{font-size:1.7rem;color:#e8d9b5;border-bottom:2px solid var(--accent);
  padding-bottom:.3rem;margin:1.8rem 0 1rem;max-width:630px}
.mdcontent h2{font-size:1.35rem;color:#e0d3ad;margin:1.4rem 0 .6rem;max-width:630px}
.mdcontent h3{font-size:1.12rem;color:#d8c69a;margin:1.1rem 0 .4rem;max-width:630px}
.mdcontent h4{font-size:1rem;color:#c9b98f;margin:.9rem 0 .3rem;max-width:630px}
.mdcontent p{margin:.6rem 0;max-width:70ch;text-wrap:pretty}
.mdcontent ul,.mdcontent ol{margin:.5rem 0 .65rem;padding-inline-start:1.25rem;
  max-width:70ch}
.mdcontent ul{list-style:none;padding-inline-start:1.05rem}
.mdcontent ul>li{position:relative}
.mdcontent ul>li::before{content:"";position:absolute;left:-.86rem;top:.76em;
  width:.36rem;height:.36rem;transform:translateY(-50%) rotate(45deg);
  border:1px solid rgba(217,255,220,.42);border-radius:1px;
  background:linear-gradient(135deg,#d9ffdc 0 22%,#62c97b 23% 56%,#2b7246 57% 100%);
  box-shadow:0 0 4px rgba(98,201,123,.22)}
.mdcontent ol{padding-inline-start:1.42rem}
.mdcontent li{margin:.2rem 0}
.mdcontent blockquote{border-left:3px solid var(--accent);background:#242024;
  margin:.7rem 0;padding:.5rem .9rem;color:#d7d1c6}
.mdcontent table{border-collapse:collapse;margin:1rem 0;font-family:system-ui,sans-serif;
  font-size:.88rem;width:auto}
.mdcontent th,.mdcontent td{border:1px solid var(--line);padding:.35rem .6rem;
  text-align:left;vertical-align:top}
.mdcontent thead th{background:var(--tblhead);color:#e8d9b5}
.mdcontent tbody tr:nth-child(even){background:#232327}
.mdcontent hr{border:0;border-top:1px solid var(--line);margin:1.5rem 0;max-width:630px}
.mdcontent img{max-width:100%;height:auto}
.mdcontent a{color:var(--accent)}
/* Dimmed "[raw lNNNN]" provenance anchors (character reader only). */
.md-raw-anchor{color:var(--muted);opacity:.5;font-size:.72em;
  font-family:ui-monospace,Consolas,monospace;white-space:nowrap}
/* postacie/<id>.html: one full page per roster character (summary + collapsed
   raw compendium). Title uses the display font (matches the shelf cards'
   huge names + book chapter headings) -- .mdcontent's own h1 rule below is
   for headings INSIDE the summary/raw text, not this page-level title. */
.char-title{font-family:'Gwyn Display',Georgia,serif;font-weight:400;
  font-size:2.3rem;line-height:1.2;color:#e8d9b5;margin:0 0 .3rem;
  border-bottom:2px solid var(--accent);padding-bottom:.5rem}
.char-subtitle{color:var(--muted);font-family:system-ui,sans-serif;
  font-size:.95rem;margin:.6rem 0 1.8rem}
/* Full-bleed character hero (chars.char_hero_html, LoL-Universe treatment):
   the portrait is the page-wide backdrop (--char-bg inline custom property)
   under the same bottom-heavy scrim recipe as the homepage PHB hero
   (base.css .home-hero) -- position biased toward the upper third, where
   portrait faces live. Dead characters get a grayscale filter on the
   whole banner. Without a portrait, chars.py falls back to the plain
   .char-title head inside the reading column. */
/* Gradient has more, gentler steps than the old 3-stop version (was a hard
   jump from .45 to var(--bg) in the last 12% of the banner -- "grafika
   kończy się i jest już tylko szaro", operator report on 6.png). The
   handoff continues below in .char-summary::before, a matching low-opacity
   glow at the top of the reading column, so the fade doesn't fully stop
   at the banner's bottom edge either. */
.char-hero2{position:relative;overflow:hidden;
  min-height:clamp(320px,42vw,540px);display:flex;align-items:flex-end;
  background:
    linear-gradient(180deg,rgba(10,9,8,.2) 0%,rgba(10,9,8,.4) 40%,
      rgba(10,9,8,.72) 70%,rgba(10,9,8,.92) 92%,var(--bg) 100%),
    var(--char-bg) center 22%/cover no-repeat;
  background-color:var(--bg)}
.char-hero2--dead{filter:grayscale(.85)}
.char-hero2-inner{position:relative;width:100%;max-width:900px;margin:0 auto;
  padding:2.4rem 1.5rem 1.6rem;display:flex;align-items:flex-end;gap:1.2rem}
.char-hero2-token{flex:0 0 auto;width:5.2rem;height:5.2rem;border-radius:50%;
  border:3px solid rgba(232,217,181,.5);box-shadow:0 4px 16px rgba(0,0,0,.6);
  background:var(--panel)}
.char-hero2-head{flex:1 1 auto;min-width:0}
.char-hero2-eyebrow{font-family:'Gwyn Display Sub',Georgia,serif;font-weight:400;
  color:var(--accent);font-size:1rem;letter-spacing:.12em;text-transform:uppercase;
  margin:0 0 .2rem;text-shadow:0 1px 6px rgba(0,0,0,.7)}
.char-hero2-name{font-family:'Gwyn Display',Georgia,serif;font-weight:400;
  font-size:clamp(2.2rem,6vw,3.6rem);line-height:1.05;color:#f3e7d0;margin:0;
  text-shadow:0 2px 14px rgba(0,0,0,.75)}
.char-hero2-epithet{color:#d7d1c6;font-family:system-ui,sans-serif;
  font-size:1rem;margin:.45rem 0 .8rem;text-shadow:0 1px 8px rgba(0,0,0,.7)}
.char-hero2 .char-sheet-btn{background:rgba(28,28,30,.55);backdrop-filter:blur(3px)}
@media(max-width:640px){
  .char-hero2{min-height:260px}
  .char-hero2-inner{gap:.8rem;padding:1.6rem 1rem 1.1rem}
  .char-hero2-token{width:3.6rem;height:3.6rem}
}
.char-sheet-btn{display:inline-flex;align-items:center;gap:.4rem;background:none;
  border:1px solid var(--line);color:var(--muted);cursor:pointer;
  padding:.35rem .8rem;border-radius:6px;font-family:system-ui,sans-serif;
  font-size:.82rem;transition:border-color .12s ease,color .12s ease}
.char-sheet-btn:hover,.char-sheet-btn:focus{border-color:var(--accent);
  color:var(--ink);outline:none}
.char-summary-missing{color:var(--muted);font-style:italic}
/* Story typography (.char-summary rides on .mdcontent): a drop cap on the
   opening paragraph, display-face section heads, and blockquotes restyled
   from "callout box" to "pull quote" -- thin accent rail, italic serif,
   no background panel. */
.char-summary{position:relative;margin-top:1.6rem}
/* Continues .char-hero2's fade instead of the reading column starting flat
   -- a quiet echo of the banner's own warmth, not a second visible band.
   Fixed px (not %/vh) since .char-summary's own height varies wildly by
   character (short vs. long compendium entries) and this only needs to
   cover the first screenful either way. */
.char-summary::before{content:"";position:absolute;top:0;left:0;right:0;height:420px;
  pointer-events:none;background:linear-gradient(180deg,rgba(63,168,102,.07),transparent)}
.char-summary>p:first-child::first-letter{font-family:'Gwyn Display',Georgia,serif;
  font-size:3.4em;line-height:.78;float:left;padding:.08em .12em 0 0;
  color:var(--accent)}
.char-summary--long>p{line-height:1.72;margin:.85rem 0}
.char-summary h2{font-family:'Gwyn Display Sub',Georgia,serif;font-weight:400;
  font-size:clamp(1.9rem,4vw,2.3rem);color:#e8d9b5;letter-spacing:.04em;margin:2.2rem 0 .7rem;
  border-bottom:1px solid rgba(63,168,102,.38);padding-bottom:.25rem;max-width:630px}
.char-summary blockquote{background:none;border-left:2px solid var(--accent);
  margin:1.4rem 0 1.4rem .4rem;padding:.15rem 0 .15rem 1.1rem;
  font-style:italic;font-size:1.12rem;color:#e0d3ad;max-width:56ch}
.char-summary blockquote footer{margin-top:.35rem;font-style:normal;
  font-family:system-ui,sans-serif;font-size:.78rem;color:var(--muted);
  letter-spacing:.02em}
.char-summary blockquote footer cite{font-style:italic}
/* Roster strip ("related champions"): small linked face-circles of the
   rest of the party; dead members grayed. */
.char-relations{margin:1.8rem 0 2rem}
.char-rel-row{display:flex;gap:1rem;flex-wrap:wrap}
.char-rel{display:flex;flex-direction:column;align-items:center;gap:.4rem;
  width:5.4rem;text-decoration:none;color:var(--ink);
  transition:transform .12s ease}
.char-rel:hover{transform:translateY(-3px)}
.char-rel--current{cursor:default}
.char-rel--current .char-rel-face{border-color:var(--accent);box-shadow:0 0 0 3px rgba(63,168,102,.16),0 2px 8px rgba(0,0,0,.45)}
.char-rel--current .char-rel-name{color:var(--ink)}
.char-rel-face{width:4.6rem;height:4.6rem;border-radius:50%;object-fit:cover;
  object-position:center 18%;border:2px solid var(--line);
  box-shadow:0 2px 8px rgba(0,0,0,.45);
  transition:border-color .12s ease}
.char-rel:hover .char-rel-face{border-color:var(--accent)}
.char-rel-face--mono{display:flex;align-items:center;justify-content:center;
  font-family:'Gwyn Display',Georgia,serif;font-size:1.9rem;color:#e8d9b5;
  background:linear-gradient(160deg,#3a302e,#241c1b)}
.char-rel-name{font-family:system-ui,sans-serif;font-size:.78rem;color:var(--muted);
  text-align:center;line-height:1.2}
.char-rel:hover .char-rel-name{color:var(--ink)}
.char-rel--dead .char-rel-face{filter:grayscale(1) brightness(.85)}
/* Delivered-photos gallery (chars.py _char_gallery_html): one horizontal,
   snap-scrolling strip of fixed-height thumbnails, Discord-attachments
   style -- browse full-size via the shared scan lightbox (data-scan). */
.char-gallery-section{margin:1.4rem 0 2rem}
.char-gallery-title{font-family:system-ui,sans-serif;font-size:.78rem;
  text-transform:uppercase;letter-spacing:.06em;color:var(--muted);
  margin:0 0 .5rem;font-weight:600}
.char-gallery{display:flex;gap:.6rem;overflow-x:auto;padding:.2rem 0 .6rem;
  scroll-snap-type:x proximity;scrollbar-width:thin;
  scrollbar-color:#44403a transparent}
.char-gallery-thumb{flex:0 0 auto;scroll-snap-align:start;background:none;
  border:1px solid var(--line);border-radius:6px;padding:0;cursor:pointer;
  overflow:hidden;height:150px;
  transition:border-color .12s ease,transform .12s ease}
.char-gallery-thumb:hover,.char-gallery-thumb:focus{border-color:var(--accent);
  transform:translateY(-2px);outline:none}
.char-gallery-thumb img{display:block;height:100%;width:auto}
.char-raw-toggle{margin:3rem 0 1rem;border-top:1px solid var(--line);
  padding-top:1.4rem}
.char-raw-toggle summary{cursor:pointer;color:var(--muted);
  font-family:system-ui,sans-serif;font-size:.85rem;user-select:none}
.char-raw-toggle summary:hover{color:var(--ink)}
.char-raw-toggle[open] summary{margin-bottom:1rem}
/* Deliberately UNRENDERED: the literal markdown source, escaped plain text,
   not parsed by gwynbuild.mdrender (operator request -- the full compendium
   entry is reference material below the summary now, not re-rendered). */
.md-source{white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;
  font-family:ui-monospace,Consolas,monospace;font-size:.82rem;line-height:1.6;
  color:#c9c4b8;background:#1a1a1c;border:1px solid var(--line);
  border-radius:6px;padding:1rem 1.2rem;max-width:100%;overflow-x:auto}

/* postacie.html: one section per roster character, stacked on one page.
   scroll-margin-top MUST match .mdreader-bar's own sticky top (52px): without
   it, jumping straight to a section (e.g. the very first one, whose
   padding-top is 0) lands the section so close to its own sticky-pin point
   that the bar's reserved flow space and its stuck visual position disagree
   for one frame, and the h1 right after it renders overlapping the bar. */
.mdreader-section{margin:0 0 3rem;padding-top:2.4rem;border-top:1px solid var(--line);
  scroll-margin-top:52px}
.mdreader-section:first-of-type{border-top:0;padding-top:0}

/* design-critique.html: raw/rendered toggle over the EN PHB source -- a
   working tool, not reader content, so it borrows .mdcontent/.md-source
   wholesale instead of its own typography. */
.critique-note{max-width:1100px;margin:0 auto 1.4rem;padding:0 1.5rem;
  color:var(--muted);font-family:system-ui,sans-serif;font-size:.88rem}
.critique-toggle{max-width:1100px;margin:0 auto 1.2rem;padding:0 1.5rem;
  display:flex;gap:.5rem}
.critique-tab{background:none;border:1px solid var(--line);color:var(--muted);
  cursor:pointer;padding:.4rem 1rem;border-radius:6px;font-family:system-ui,sans-serif;
  font-size:.85rem;transition:border-color .12s ease,color .12s ease}
.critique-tab:hover{color:var(--ink)}
.critique-tab.active{border-color:var(--accent);color:var(--ink);
  background:rgba(63,168,102,.14)}
.critique-view{max-width:1100px;margin:0 auto;padding:0 1.5rem 4rem}
.critique-view.md-source{white-space:pre-wrap}
/* biblioteka/*.html reuses the same toggle wiring, but .mdreader/.mdcontent
   already own the page's max-width/padding -- .critique-view's standalone
   geometry would just double it up, so neutralize it in that context. The
   raw <pre> keeps its look from .md-source (untouched by this override). */
.mdreader .critique-view{max-width:none;margin:0;padding:0}
.mdreader-bar{flex-wrap:wrap}
/* EN reader (biblioteka): sidebar TOC + view toggle live "z boku" in the
   same .side-panel--left chrome the PHB reader uses (book.css owns the
   sticky/scroll behavior); only the toggle's segmented look inside it is
   styled here. */
.side-panel--left .critique-toggle{max-width:none;margin:0 0 .9rem;padding:0;display:flex;gap:.4rem}
.side-panel--left .critique-tab{flex:1 1 auto;font-size:.76rem;padding:.35rem .4rem;text-align:center}
/* Anchor landing under the sticky topbar, both views. */
.mdcontent h1,.mdcontent h2,.mdcontent h3,.mdcontent h4{scroll-margin-top:70px}
.md-source .raw-h{scroll-margin-top:70px}

/* Illuminated Codex character chronicles. The blurred/toned cover layer and
   the contained foreground are deliberately separate: the stage fills even
   on 5K2K displays while the approved artwork is never cropped. */
.char-codex{position:relative;isolation:isolate;overflow:hidden;
  min-height:clamp(620px,88svh,1080px);background:#070c09;
  border-bottom:1px solid rgba(63,168,102,.44)}
.char-hero-bg{position:absolute;inset:0;z-index:-2;background-image:
  linear-gradient(90deg,rgba(7,12,9,.94) 0%,rgba(7,12,9,.62) 47%,rgba(7,12,9,.16) 100%),
  linear-gradient(0deg,#070c09 0%,transparent 38%),var(--char-bg);
  background-size:cover;background-position:var(--char-focal,50% 50%);
  filter:saturate(.74) brightness(.66);transform:scale(1.025)}
.char-codex::after{content:"";position:absolute;inset:auto 0 0;height:38%;z-index:-1;
  background:linear-gradient(0deg,#070c09,transparent);pointer-events:none}
.char-codex-inner{width:min(100%,1680px);min-height:inherit;margin:0 auto;
  padding:clamp(7rem,14vh,12rem) clamp(1.4rem,6vw,7rem) clamp(3rem,7vh,6rem);
  display:grid;grid-template-columns:minmax(17rem,.82fr) minmax(20rem,1.18fr);
  align-items:end;gap:clamp(1rem,4vw,5rem)}
.char-codex-copy{position:relative;z-index:2;align-self:center;text-shadow:0 4px 28px #000}
.char-codex-kicker,.char-section-kicker{margin:0 0 .85rem;color:#91f29a;
  font:600 .78rem/1.2 system-ui,sans-serif;letter-spacing:.26em;text-transform:uppercase}
.char-codex-name{margin:0;color:#f1f0e8;font-family:'Gwyn Display',Georgia,serif;
  font-size:clamp(3.8rem,8vw,9rem);font-weight:400;line-height:.82;letter-spacing:-.025em}
.char-codex-epithet{max-width:38ch;margin:1.3rem 0 0;color:#d8ddd8;
  font:500 clamp(1rem,1.5vw,1.45rem)/1.45 system-ui,sans-serif}
.char-codex-ornament{display:block;width:min(22rem,78%);height:2px;margin-top:2rem;
  background:linear-gradient(90deg,#3fa866,#91f29a 62%,transparent)}
.char-hero-figure{display:block;align-self:end;justify-self:center;width:100%;height:auto;
  max-height:min(80svh,980px);object-fit:contain;object-position:var(--char-figure-position,center bottom);
  filter:drop-shadow(0 24px 38px rgba(0,0,0,.74))}
.char-codex[data-art-mode="typographic"]{background:
  radial-gradient(circle at 72% 38%,rgba(63,168,102,.18),transparent 30%),
  repeating-linear-gradient(118deg,transparent 0 38px,rgba(145,242,154,.025) 39px 40px),#070c09}
.char-codex[data-art-mode="typographic"] .char-codex-inner{grid-template-columns:1fr}
.char-codex[data-art-mode="typographic"] .char-codex-copy{max-width:1100px}
.char-codex--dawn[data-art-mode="typographic"]{background:
  radial-gradient(circle at 70% 38%,rgba(241,240,232,.16),transparent 28%),#070c09}
.char-codex--ember-memory[data-art-mode="typographic"]{background:
  radial-gradient(circle at 68% 40%,rgba(63,168,102,.14),transparent 24%),#050807}
.char-codex--mystra-veil[data-art-mode="typographic"]{background:
  radial-gradient(circle at 68% 36%,rgba(106,182,154,.16),transparent 26%),#070c09}

.char-sheet-section,.char-atlas-section{margin:clamp(4rem,8vw,8rem) 0}
.char-sheet-section>h2,.char-atlas-section>h2{margin:.15rem 0 1.5rem;color:#f1f0e8;
  font:400 clamp(2.3rem,5vw,4.8rem)/1 'Gwyn Display',Georgia,serif}
.char-sheet-spread{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(1rem,3vw,2.5rem)}
.char-sheet-spread--single{grid-template-columns:minmax(0,42rem);justify-content:start}
.char-sheet-page,.char-atlas-button{display:block;width:100%;padding:0;border:0;
  background:transparent;color:inherit;cursor:zoom-in}
.char-sheet-page img{display:block;width:100%;height:auto;object-fit:contain}
.char-atlas-button{display:grid;place-items:center}
.char-atlas-button img{display:block;width:auto;max-width:100%;height:auto;
  max-height:min(78svh,860px);object-fit:contain}
.char-sheet-page{box-shadow:0 22px 54px rgba(0,0,0,.44);transition:transform .2s ease}
.char-sheet-page:hover,.char-sheet-page:focus-visible{transform:translateY(-5px);outline:2px solid #91f29a;outline-offset:5px}
.char-atlas{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:clamp(1rem,2vw,2.25rem);align-items:start}
.char-atlas-item{grid-column:span 6;min-width:0}
.char-atlas-item--wide{grid-column:1/-1}
.char-atlas-item--portrait{grid-column:span 4}
.char-atlas-item--measure{grid-column:span 6}
.char-atlas-item--pair{grid-column:span 6}
.char-atlas-button{transition:transform .2s ease,filter .2s ease}
.char-atlas-button:hover,.char-atlas-button:focus-visible{transform:translateY(-4px);filter:brightness(1.08);outline:2px solid #91f29a;outline-offset:5px}

@media(min-width:2200px){
  .char-codex-inner{width:min(calc(100% - 8rem),2800px);grid-template-columns:minmax(30rem,.8fr) minmax(42rem,1.2fr)}
  .view-char>.mdreader{max-width:1600px}
}
@media(max-width:700px){
  .char-codex{min-height:auto}
  .char-codex-inner{min-height:100svh;padding:7.5rem 1.2rem 2.5rem;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch}
  .char-codex-copy{order:2;margin-top:-3.5rem}
  .char-hero-figure{order:1;max-height:58svh}
  .char-codex-name{font-size:clamp(3.5rem,18vw,6.2rem)}
  .char-sheet-spread,.char-atlas{grid-template-columns:1fr}
  .char-atlas-item,.char-atlas-item--wide,.char-atlas-item--portrait,
  .char-atlas-item--measure,.char-atlas-item--pair{grid-column:1}
  .char-atlas-button img{max-height:72svh}
}
@media(prefers-reduced-motion:reduce){
  .char-sheet-page,.char-atlas-button{transition:none}
}
