/* Self-hosted UI type (latin-subset variable WOFF2, OFL 1.1 — see fonts/LICENSE.txt).
   Map-label glyphs (Libre Baskerville / Noto Sans PBFs in libs/glyphs/) are a separate,
   deliberately unchanged system. */
@font-face{
  font-family:'Plus Jakarta Sans';
  src:url('fonts/PlusJakartaSans-var.woff2') format('woff2');
  font-weight:400 800;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Plus Jakarta Sans';
  src:url('fonts/PlusJakartaSans-italic-var.woff2') format('woff2');
  font-weight:400 800;font-style:italic;font-display:swap;
}

/* ============================================================================
   THEME TOKENS. The site has two complete skins driven by the map style toggle:
   the default PAPER theme (light editorial chrome in the map's own parchment
   family, bronze accent) and body.theme-night (the original CK3 dark navy chrome
   with the gold accent — kept exactly as it always was). :root holds the light
   values; body.theme-night overrides every themed token. The class is wired in
   src/main.js next to the style button.
   ============================================================================ */
:root{
  /* "Stone" light chrome (picked 2026-07-08): warm-gray greige, dimmer and far
     less brown than the map parchment, so the plate reads as the warm object. */
  --bg:#e8e5dc;
  --bg2:#e0ddd2;
  /* Paper-map palette (see src/paper.js — keep in sync). */
  --water:#d3cbaf;
  --land:#1b2435;
  --usland:#202b40;
  --stateline:#31405c;
  --ink:#282419;
  --ink-dim:#6d6759;
  /* Brick — cartographer's annotation red (picked 2026-07-08 from the live
     accent comparison; night keeps the CK3 gold). */
  --accent:#9c4a31;
  --card:#f1eee6;
  --card-edge:#c8c3b3;
  /* chrome: header/footer gradients, floating map controls, popovers */
  --chrome-hi:#edeae2;
  --chrome-lo:#e4e1d7;
  --float:rgba(240,238,230,.88);
  --float-strong:rgba(240,238,230,.92);
  --float-hover:rgba(233,230,221,.96);
  --overlay:rgba(245,243,237,.97);
  --on-accent:#f1eee6;
  --accent-tint:rgba(156,74,49,.12);
  --accent-tint-strong:rgba(156,74,49,.18);
  --band:rgba(60,55,40,.06);
  --band-alt:rgba(60,55,40,.11);
  --track:rgba(60,55,40,.15);
  --well:#d9d5c8;
  --row-edge:rgba(155,148,130,.5);
  --shadow-col:rgba(70,63,45,.32);
  --shadow-col-strong:rgba(70,63,45,.42);
  --scroll-thumb:#bdb7a4;
  --grab:#aca691;
  /* type: one sans everywhere (2026-07 spike; Segoe stays as the swap-in fallback) */
  --font-ui:'Plus Jakarta Sans',"Segoe UI",system-ui,sans-serif;
  --font-display:'Plus Jakarta Sans',"Segoe UI",system-ui,sans-serif;
  /* Editorial "placard" surfaces (reading panel / Chronicle / About): soft stone
     paper with a bronze accent — museum placard next to the exhibit. */
  --srf:#f1eee6;
  --srf-edge:#cfcaba;
  --srf-ink:#282419;
  --srf-dim:#6d6759;
  --srf-accent:#9c4a31;
  /* 4.77:1 on --srf — the 11px caps labels must clear WCAG AA (4.5:1). */
  --srf-label:#6f6857;
  --srf-hover:rgba(156,74,49,.08);
  --srf-now:rgba(156,74,49,.13);
  --srf-now-edge:rgba(156,74,49,.42);
  --srf-kbd:rgba(40,36,25,.07);
  --srf-kbd-edge:rgba(40,36,25,.28);
  /* motion */
  --ease-out:cubic-bezier(.2,.8,.2,1);
  --dur-1:150ms;
  --dur-2:280ms;
  --dur-3:450ms;
}
/* Night map = the original dark CK3 chrome, token for token. */
body.theme-night{
  --bg:#0d1320;
  --bg2:#111a2b;
  --ink:#e9edf5;
  --ink-dim:#9aa7bd;
  --accent:#e8b34b;
  --card:#141d30;
  --card-edge:#243353;
  --chrome-hi:#101a2e;
  --chrome-lo:#0d1320;
  --float:rgba(13,19,32,.8);
  --float-strong:rgba(13,19,32,.85);
  --float-hover:rgba(20,28,46,.95);
  --overlay:rgba(10,15,26,.95);
  --on-accent:#101626;
  --accent-tint:rgba(232,179,75,.14);
  --accent-tint-strong:rgba(232,179,75,.18);
  --band:rgba(120,150,200,.05);
  --band-alt:rgba(120,150,200,.11);
  --track:rgba(120,150,200,.18);
  --well:#0d1320;
  --row-edge:rgba(36,51,83,.5);
  --shadow-col:rgba(0,0,0,.5);
  --shadow-col-strong:rgba(0,0,0,.55);
  --scroll-thumb:#2a3a5c;
  --grab:#3a4a68;
  --srf:var(--card);
  --srf-edge:var(--card-edge);
  --srf-ink:var(--ink);
  --srf-dim:var(--ink-dim);
  --srf-accent:#c9b891;
  --srf-label:#9a917e;
  --srf-hover:#1b2740;
  --srf-now:#243150;
  --srf-now-edge:#3c5183;
  --srf-kbd:#1d2940;
  --srf-kbd-edge:#324569;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  background:var(--bg);
  color:var(--ink);
  font-family:var(--font-ui);
  overflow:hidden;
}
/* 100dvh tracks the *visible* viewport as the mobile browser toolbar shows/hides
   (Android Chrome/Brave, iOS Safari). Plain 100vh = the toolbar-hidden height, which
   pushed the bottom timeline bar off-screen with body{overflow:hidden} = no way to reach it.
   100vh stays first as the fallback for browsers without dvh. */
#app{display:flex;flex-direction:column;height:100vh;height:100dvh}

header{
  display:flex;align-items:center;justify-content:space-between;
  padding:10px 20px 8px;
  border-bottom:1px solid var(--card-edge);
  background:linear-gradient(180deg,var(--chrome-hi),var(--chrome-lo));
  flex:0 0 auto;
}
h1{
  font-family:var(--font-display);
  font-size:21px;font-weight:700;letter-spacing:-.01em;
}
#subtitle{color:var(--ink-dim);font-size:12px;margin-top:2px}
/* Separator between the About and Suggest-a-correction links; kept visible even
   on mobile where the rest of the subtitle collapses to font-size:0. */
.subtitle-sep{color:var(--ink-dim)}
/* "Find a related book" — the header's one call-to-action (>1024px; the ≤1024px
   block swaps it for the compact map-strip twin). Accent-outlined so it reads
   as a button, not chrome. */
#bookfinder-open{
  flex:0 0 auto;margin:0 16px;
  font:inherit;font-size:14px;font-weight:600;color:var(--accent);
  background:transparent;border:1px solid var(--accent);
  border-radius:9px;padding:10px 22px;cursor:pointer;
  transition:background .12s,color .12s;
}
@media (hover: hover){ #bookfinder-open:hover{background:var(--accent);color:var(--on-accent)} }
#bookfinder-open:focus:not(:focus-visible){outline:none}
#bookfinder-open:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* The map-strip twin exists only on touch layouts. */
#bookfinder-open-mobile{display:none}
#yearblock{text-align:right}
#year{
  font-family:var(--font-display);font-size:40px;font-weight:700;
  color:var(--accent);line-height:1;font-variant-numeric:tabular-nums;
  letter-spacing:-.01em;
}
#age{
  color:var(--accent);font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:.13em;margin-top:5px;
  opacity:.85;
}
#age:empty{display:none}
#era{color:var(--ink-dim);font-size:12px;margin-top:3px;max-width:430px}

#main{display:flex;flex:1 1 auto;min-height:0}
#mapwrap{position:relative;flex:1 1 auto;min-width:0;background:var(--water)}
#map{display:block;width:100%;height:100%;cursor:grab}
#map:active{cursor:grabbing}
/* Soft sepia vignette framing the paper map. Generated content so it needs no
   markup; z-indexed under the overlay controls (which get z-index:2 below). */
#mapwrap::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:1;
  box-shadow:inset 0 0 110px rgba(74,58,40,.28), inset 0 0 18px rgba(74,58,40,.18);
}

/* base geography */
.world{fill:var(--land);stroke:none}
.nation{fill:var(--usland);stroke:#3a4a68;stroke-width:.6}
.stateline{fill:none;stroke:var(--stateline);stroke-width:.5;stroke-dasharray:2 3;opacity:.7}

/* territories */
.t-solid{fill-opacity:.34;stroke-width:1.5;stroke-opacity:.95;vector-effect:non-scaling-stroke;cursor:pointer}
.t-claim{fill-opacity:.10;stroke-width:1.3;stroke-opacity:.8;stroke-dasharray:7 5;vector-effect:non-scaling-stroke;cursor:pointer}
g.terr.hover .t-solid{fill-opacity:.5}
g.terr.hover .t-claim{fill-opacity:.2}
.t-unknown{fill:url(#pat-unknown);stroke:#6d7a93;stroke-width:1;stroke-opacity:.45;stroke-dasharray:1 4;vector-effect:non-scaling-stroke;opacity:.85;pointer-events:none}

/* labels */
g.lbl{pointer-events:none}
g.lbl rect{
  fill:rgba(9,13,22,.82);
  stroke-width:1;rx:9;
}
g.lbl text{
  fill:#eef2f8;font-size:11px;text-anchor:middle;dominant-baseline:middle;
}
g.lbl.native text{font-family:Georgia,serif;font-style:italic}
g.lbl.colony text{font-family:"Segoe UI",sans-serif;font-size:10px;letter-spacing:.8px;text-transform:uppercase}
g.lbl.hidden{opacity:0 !important}

/* Floating settings button, top-left corner — the twin of #stylebtn. The whole
   controls card (region + layer filters) opens from it and starts collapsed. */
#settings-btn{
  position:absolute;top:10px;left:12px;z-index:3;
  font:inherit;font-size:11.5px;color:var(--ink-dim);
  background:var(--float);border:1px solid var(--card-edge);
  border-radius:6px;padding:4px 10px;cursor:pointer;
  transition:color .12s,border-color .12s;
}
@media (hover: hover){ #settings-btn:hover{color:var(--ink);border-color:var(--accent)} }
#settings-btn[aria-expanded="true"]{color:var(--ink);border-color:var(--accent)}

#controls{
  position:absolute;top:44px;left:12px;z-index:2;
  background:var(--float-strong);border:1px solid var(--card-edge);
  border-radius:8px;padding:8px 12px;font-size:12px;color:var(--ink-dim);
  display:none;flex-direction:column;gap:5px;backdrop-filter:blur(4px);
}
#controls.open{display:flex}
#controls label{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none}
#controls input{accent-color:var(--accent)}

/* Polity search (SPIKE): top-left, tucked right beside the ⚙ Settings button.
   The dropdown is a dark card matching the tooltip/controls; rows carry a name
   line + a dim disambiguation line (years · type · region). */
/* z-index 45: above the mobile bottom sheet (40) so the results dropdown isn't
   swallowed when the sheet is open, below the tooltip (50). */
#searchbox{
  position:absolute;top:10px;left:100px;z-index:45;
  width:min(340px,42%);
}
#search-input{
  width:100%;box-sizing:border-box;font:inherit;font-size:12.5px;color:var(--ink);
  background:var(--float-strong);border:1px solid var(--card-edge);
  border-radius:6px;padding:6px 11px;outline:none;
  transition:border-color .12s;
}
#search-input::placeholder{color:var(--ink-dim)}
#search-input:focus{border-color:var(--accent)}
#search-results{
  position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:4;
  max-height:min(340px,50vh);overflow-y:auto;
  background:var(--overlay);border:1px solid var(--card-edge);
  border-radius:8px;box-shadow:0 6px 22px var(--shadow-col);backdrop-filter:blur(4px);
}
.sr-row{padding:7px 11px;cursor:pointer;border-top:1px solid var(--row-edge)}
.sr-row:first-child{border-top:none}
.sr-row.active{background:var(--accent-tint)}
.sr-name{font-size:12.5px;color:var(--ink)}
.sr-name b{color:var(--accent)}
.sr-meta{font-size:11px;color:var(--ink-dim);margin-top:1px}

#counts{
  position:absolute;left:12px;bottom:10px;z-index:2;
  font-size:11.5px;color:var(--ink-dim);
  background:var(--float);border:1px solid var(--card-edge);
  border-radius:6px;padding:4px 10px;
}

/* Map-style toggle (paper atlas vs original night theme), bottom-right corner. */
#stylebtn{
  position:absolute;right:12px;bottom:10px;z-index:2;
  font:inherit;font-size:11.5px;color:var(--ink-dim);
  background:var(--float);border:1px solid var(--card-edge);
  border-radius:6px;padding:4px 10px;cursor:pointer;
  transition:color .12s,border-color .12s;
}
@media (hover: hover){ #stylebtn:hover{color:var(--ink);border-color:var(--accent)} }

/* Side-panel collapse tab: a chevron button vertically centered on the map/panel
   seam (right edge of the map). Left-rounded so it reads as a pull-tab; when the
   panel is hidden it sits flush to the window's right edge as a "bring it back"
   handle. Hiding the panel lets the map take the full width — body.side-collapsed
   drives the layout, JS calls map.resize() after. */
#side-toggle{
  position:absolute;top:50%;right:0;transform:translateY(-50%);z-index:3;
  display:flex;align-items:center;justify-content:center;
  width:22px;height:54px;padding:0;
  font:inherit;font-size:16px;line-height:1;color:var(--ink-dim);
  background:var(--float-strong);border:1px solid var(--card-edge);border-right:none;
  border-radius:8px 0 0 8px;cursor:pointer;
  box-shadow:-2px 0 8px var(--shadow-col);
  transition:color .12s,border-color .12s,background .12s;
}
@media (hover: hover){ #side-toggle:hover{color:var(--ink);border-color:var(--accent);background:var(--float-hover)} }
body.side-collapsed #side{display:none}

/* The night theme flips the map surround back to the original dark water and
   drops the paper vignette; everything else is repainted in src/map.js. */
body.theme-night #mapwrap{background:#0d1320}
body.theme-night #mapwrap::after{display:none}

#tooltip{
  position:absolute;display:none;pointer-events:none;z-index:50;
  background:var(--overlay);border:1px solid var(--card-edge);
  border-radius:8px;padding:8px 11px;max-width:280px;
  font-size:12px;line-height:1.45;color:var(--ink);
  box-shadow:0 4px 18px var(--shadow-col);
}
#tooltip .tt-name{font-weight:700;font-size:13px}
#tooltip .tt-meta{color:var(--accent);font-size:11px;margin:1px 0 3px}
#tooltip .tt-note{color:var(--ink-dim)}

/* sidebar */
#side{
  flex:0 0 308px;overflow-y:auto;
  border-left:1px solid var(--card-edge);
  background:var(--bg2);
  padding:12px;display:flex;flex-direction:column;gap:12px;
}
/* Cards are editorial "placards": paper in the map's parchment family (dark under
   body.theme-night) mounted on the dark sidebar gutter — see the --srf-* tokens. */
.card{
  background:var(--srf);border:1px solid var(--srf-edge);
  border-radius:10px;padding:14px 16px;font-size:13px;line-height:1.55;
  color:var(--srf-ink);
}
.card-title{
  font-family:var(--font-ui);font-weight:700;color:var(--srf-label);
  letter-spacing:.09em;margin-bottom:8px;text-transform:uppercase;font-size:11px;
}
.hint{color:var(--srf-dim);margin-top:8px;font-size:12px}
.small{color:var(--srf-dim);font-size:12px}
.kbd{
  display:inline-block;background:var(--srf-kbd);border:1px solid var(--srf-kbd-edge);border-radius:4px;
  padding:0 5px;font-size:10.5px;font-family:Consolas,monospace;margin:0 1px;
}


#eventfeed{display:flex;flex-direction:column;gap:2px;max-height:300px;overflow-y:auto}
.ev{
  display:flex;gap:9px;padding:6px 8px;border-radius:6px;cursor:pointer;
  font-size:12.5px;line-height:1.45;
  transition:background var(--dur-1) var(--ease-out);
}
.ev:hover{background:var(--srf-hover)}
.ev.now{background:var(--srf-now);outline:1px solid var(--srf-now-edge)}
.ev .ev-yr{color:var(--srf-accent);font-family:var(--font-ui);font-weight:700;flex:0 0 48px;font-variant-numeric:tabular-nums}
.ev .ev-tx{color:var(--srf-ink)}
.ev .ev-sub{color:var(--srf-dim);font-size:11.5px;display:block}

#info a{color:var(--srf-accent)}
#info .i-name{font-family:var(--font-display);font-size:19px;font-weight:700;letter-spacing:-.01em;line-height:1.25}
#info .i-chip{
  display:inline-block;font-size:10px;letter-spacing:.8px;text-transform:uppercase;
  border-radius:99px;padding:1px 8px;margin:5px 0 7px;border:1px solid;font-weight:600;
}
#info .i-period{color:var(--srf-accent);font-size:12px;margin-bottom:6px}
#info .i-note{color:var(--srf-ink)}
#info .i-about{color:var(--srf-ink);margin-top:8px;font-size:12.5px;line-height:1.55}
#info .i-fate{color:var(--srf-dim);margin-top:7px;font-size:12px;border-top:1px solid var(--srf-edge);padding-top:7px}
#info .i-reading{margin-top:10px;border-top:1px solid var(--srf-edge);padding-top:9px}
#info .i-reading-h{font-family:var(--font-ui);font-size:11px;font-weight:700;color:var(--srf-label);letter-spacing:.09em;text-transform:uppercase;margin-bottom:7px}
#info .i-book{margin-bottom:8px;font-size:12.5px;line-height:1.5}
#info .i-book-t{font-style:italic;color:var(--srf-ink)}
#info .i-book-meta{color:var(--srf-dim)}
#info .i-book-why{display:block;color:var(--srf-dim);font-size:11.5px;margin-top:1px}
#info .i-reading-h2{margin-top:10px}
#info .i-book-more{margin-top:8px;font-size:12px}
#info .i-book-more a{color:var(--srf-accent);text-decoration:none;font-weight:600}
#info .i-book-more a:hover{text-decoration:underline}
#info .i-book-disclosure{margin-top:8px;font-size:10px;font-style:italic;color:var(--srf-dim)}

/* "Get the book" pill — the shared buy affordance on every linked book (reading
   lists here + the book-finder dialog). Placard (--srf-*) tokens so it sits on
   paper in both themes; unlinked books simply lack it. */
.book-get{
  display:inline-block;margin-top:3px;padding:2px 11px;
  font-family:var(--font-ui);font-size:10.5px;font-weight:700;
  letter-spacing:.03em;line-height:1.7;
  color:var(--srf-accent);border:1px solid var(--srf-accent);border-radius:999px;
  text-decoration:none;
  transition:background .12s,color .12s;
}
#info a.book-get{color:var(--srf-accent)} /* beat the #info a specificity */
.book-get:hover{background:var(--srf-accent);color:var(--srf)}
#info a.book-get:hover{color:var(--srf)}
#info .i-book .book-get{display:block;width:fit-content}

/* Reading-panel reveal: panel.js swaps #info-body wholesale on select/deselect,
   so the fresh nodes re-run this stagger on every pick. The Chronicle rows are
   deliberately NOT animated — chronicle.update() rebuilds them on every scrub
   tick, which would strobe during playback. */
@media (prefers-reduced-motion: no-preference){
  #info-body > *{animation:placard-reveal var(--dur-2) var(--ease-out) backwards}
  #info-body > :nth-child(2){animation-delay:35ms}
  #info-body > :nth-child(3){animation-delay:70ms}
  #info-body > :nth-child(4){animation-delay:105ms}
  #info-body > :nth-child(5){animation-delay:140ms}
  #info-body > :nth-child(6){animation-delay:175ms}
  #info-body > :nth-child(n+7){animation-delay:210ms}
}
@keyframes placard-reveal{from{opacity:0;transform:translateY(8px)}}

/* timeline */
#timelinewrap{
  flex:0 0 92px;display:flex;align-items:center;gap:10px;
  padding:4px 18px 8px;border-top:1px solid var(--card-edge);
  background:linear-gradient(0deg,var(--chrome-hi),var(--chrome-lo));
}
#play{
  flex:0 0 auto;width:40px;height:40px;border-radius:50%;
  background:var(--accent);color:var(--on-accent);border:none;font-size:15px;
  cursor:pointer;transition:transform .12s;
}
#play:hover{transform:scale(1.08)}
#timeline{flex:1 1 auto;height:80px;cursor:pointer}
.tl-axis{stroke:#33425f;stroke-width:2}
.tl-tick{stroke:#33425f;stroke-width:1}
.tl-tick-lbl{fill:var(--ink-dim);font-size:10.5px;text-anchor:middle;font-family:Georgia,serif}
.tl-ev{fill:var(--accent);opacity:.75;cursor:pointer}
.tl-ev:hover{opacity:1}
.tl-handle-line{stroke:var(--accent);stroke-width:2}
.tl-handle{fill:var(--accent);stroke:#0d1320;stroke-width:2;cursor:ew-resize}
.tl-yr-flag{fill:var(--accent);font-family:Georgia,serif;font-weight:700;font-size:13px;text-anchor:middle}

::-webkit-scrollbar{width:9px}
::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:6px}
::-webkit-scrollbar-track{background:transparent}

/* Bottom-sheet grab handle: hidden on desktop, shown + styled in the mobile block.
   Kept here (not in index.html's inline <style>) so the mobile `display:block`
   override below sits in the same stylesheet, after this base rule, and wins. */
#sheet-handle{display:none}

/* ============================================================================
   MOBILE / TABLET (≤1024px) — full-width-map layout.
   The desktop layout is a map + fixed right sidebar + footer timeline. On a phone
   OR TABLET that sidebar eats the screen (an iPad portrait is 810-834px wide, so
   the old 768px cutoff handed it the desktop layout and a 526px map), so we flip
   #main to a column (full-width map) and turn #side into a bottom sheet:
   collapsed it shows just a grab handle; tapping the handle (or selecting a
   territory — see src/mobile.js) expands it. The sheet is position:fixed anchored
   just above the footer timeline, and collapses by animating its height (no
   transform/clip games) so its body can never paint over the timeline. Component
   tweaks for the timeline/controls live in index.html's inline <style> block —
   keep its media query in sync with this one.
   ============================================================================ */
@media (max-width: 1024px){
  /* Kill the browser's pull-to-refresh / overscroll glow — a downward drag on the
     bottom sheet was being hijacked as a page refresh instead of dismissing it. */
  body{overscroll-behavior:none}

  header{padding:8px 14px 7px}
  h1{font-size:17px}
  /* Touch layouts swap the header book-finder button for a compact twin pinned
     to the right of the (narrowed) search box in the map's top strip. */
  #bookfinder-open{display:none}
  #bookfinder-open-mobile{
    display:block;position:absolute;top:8px;right:8px;z-index:45;
    font:inherit;font-size:12.5px;font-weight:600;color:var(--accent);
    background:var(--float-strong);border:1px solid var(--accent);
    border-radius:8px;padding:0 12px;height:39px;cursor:pointer;
    touch-action:manipulation;
  }
  #subtitle{font-size:10.5px;margin-top:1px}
  #year{font-size:26px}
  #age{font-size:9.5px;letter-spacing:.1em;margin-top:3px}
  #era{font-size:10.5px;max-width:46vw}

  /* Full-width map; the sidebar leaves the flow and becomes a fixed overlay sheet. */
  #main{flex-direction:column}

  /* Footer reserves the phone's home-indicator inset so the play button isn't flush
     against the system bar (which was eating taps). The bottom sheet anchors to the
     same height so it stays flush above the footer — keep these two in sync. */
  #timelinewrap{
    flex-basis:calc(84px + env(safe-area-inset-bottom));
    /* max(...) guarantees real clearance even when the safe-area inset is 0 (no
       notch / Android), lifting the play + speed buttons out of the bottom toolbar
       / home-indicator gesture zone where the OS was swallowing their taps. */
    padding-bottom:max(14px, env(safe-area-inset-bottom));
  }

  /* Both top-strip buttons match the search input's rendered height (16px font
     + 8px padding + border = 39px) so the strip reads as one aligned bar. */
  #settings-btn{top:8px;left:8px;padding:7px 10px;height:39px;touch-action:manipulation}
  #controls{top:48px;left:8px;right:8px;padding:7px 9px;gap:6px;max-width:none}

  /* Search sits beside the settings button; it no longer claims the whole top
     edge — the book-finder button takes the right end of the strip. */
  #searchbox{top:8px;left:112px;right:114px;transform:none;width:auto}
  /* 16px is the iOS threshold: any focused input below it makes Safari zoom the
     whole page (header + timeline included) and leave it zoomed. Don't shrink.
     Only the INPUT font triggers the zoom — the placeholder is styled smaller
     so the full "Search civilizations…" still fits the narrowed box. */
  #search-input{padding:8px 11px;font-size:16px}
  #search-input::placeholder{font-size:12.5px}
  .sr-row{padding:10px 11px}

  /* Keep the counts readout just above the collapsed sheet's 44px peek handle
     (which is pinned ~44px above the footer, i.e. ~44px up from #mapwrap's base). */
  #counts{bottom:52px}
  #stylebtn{bottom:52px;right:8px;padding:7px 10px;touch-action:manipulation}

  /* The desktop collapse toggle is meaningless here — the panel is a bottom sheet
     with its own grab handle. Hide the button and cancel any persisted collapsed
     state so the sheet always shows. */
  #side-toggle{display:none}
  body.side-collapsed #side{display:flex}

  /* #side as a bottom sheet pinned above the footer. Collapsed = 44px (just the
     handle, overflow clipped); .open grows to 64vh and scrolls internally. */
  #side{
    /* --footer-h is measured + kept current in src/mobile.js; the calc() is the
       pre-JS fallback so the sheet still sits sensibly before the script runs. */
    position:fixed;left:0;right:0;bottom:var(--footer-h, calc(84px + env(safe-area-inset-bottom)));
    flex:none;height:44px;max-height:64dvh;overflow:hidden;
    border-left:none;border-top:1px solid var(--card-edge);
    border-radius:16px 16px 0 0;
    box-shadow:0 -8px 26px var(--shadow-col);
    transition:height .28s ease;
    overscroll-behavior:contain;
    z-index:40;padding-top:0;
  }
  #side.open{height:64dvh;overflow-y:auto}

  /* The grab handle: a full-width tap strip with a centered pill, pinned to the
     top of the sheet so it's reachable in both states. */
  #sheet-handle{
    display:block;position:sticky;top:0;z-index:1;flex:0 0 44px;
    width:100%;height:44px;margin:0 0 4px;padding:0;
    background:var(--bg2);border:none;border-radius:16px 16px 0 0;cursor:pointer;
    /* none (not manipulation): we drive a vertical drag-to-dismiss gesture on the
       handle in src/mobile.js, so the browser must not claim the vertical pan. */
    touch-action:none;
  }
  #sheet-handle::before{
    content:"";display:block;width:42px;height:5px;border-radius:3px;
    margin:11px auto 0;background:var(--grab);
  }
}

/* ============================================================================
   COMPACT HEADER (phones ≤600px wide, or any landscape squeeze ≤500px tall) —
   every vertical pixel goes to the map. The header collapses to a single slim
   row: title + About on the left, just the year on the right. The tagline text
   hides (font-size:0 keeps the About button alive inside it) and the age/era
   captions go — that context still lives in the timeline's era bands.
   ============================================================================ */
@media (max-width: 600px), (max-height: 500px){
  header{padding:5px 12px 4px}
  #titleblock{display:flex;align-items:baseline;gap:8px;min-width:0}
  h1{font-size:15px;white-space:nowrap}
  #subtitle{font-size:0;margin-top:0}
  /* Keep the two text links (and their separator) legible when the rest of the
     subtitle collapses. */
  #subtitle .about-link,#subtitle .subtitle-sep{font-size:11px}
  #yearblock{display:flex;align-items:baseline}
  #year{font-size:20px}
  #age{display:none}
  #era{display:none}
}

/* ============================================================================
   "About this website" modal — welcome / roadmap dialog (see src/about.js).
   ============================================================================ */
.about-link{
  background:none;border:none;padding:0;cursor:pointer;
  color:var(--accent);font:inherit;text-decoration:underline;text-underline-offset:2px;
}
.about-link:hover{opacity:.85}
/* Android/Brave paints the UA focus ring after tap (and after the modal returns
   focus on close) — suppress it for pointer input, keep a themed ring for keyboard. */
.about-link:focus:not(:focus-visible){outline:none}
.about-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}

.about-overlay{
  position:fixed;inset:0;z-index:200;
  display:flex;align-items:center;justify-content:center;
  padding:24px;
  background:rgba(6,10,18,.72);backdrop-filter:blur(2px);
}
.about-overlay[hidden]{display:none}

/* Editorial placard register (see the --srf-* tokens): paper surface, larger
   type scale, bronze accent — the About dialog is the site's most "informational
   site" surface, so it leads the treatment. */
.about-dialog{
  position:relative;
  width:min(560px,100%);overflow-y:auto;
  /* dvh = the VISIBLE viewport (URL bar / toolbars excluded) — on Android the plain
     vh unit is the large viewport, which let the dialog run under the browser chrome.
     The vh line stays as a fallback for engines without dvh. */
  max-height:86vh;
  max-height:min(86vh,calc(100dvh - 48px));
  background:var(--srf);border:1px solid var(--srf-edge);border-radius:14px;
  box-shadow:0 18px 50px var(--shadow-col-strong);
  padding:30px 32px 26px;
  color:var(--srf-ink);
}
.about-close{
  position:absolute;top:12px;right:14px;
  width:32px;height:32px;line-height:1;
  background:none;border:none;border-radius:8px;cursor:pointer;
  color:var(--srf-dim);font-size:24px;
  transition:background var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out);
}
.about-close:hover{background:rgba(43,36,24,.08);color:var(--srf-ink)}
body.theme-night .about-close:hover{background:#1d2940}
.about-close:focus:not(:focus-visible){outline:none}
.about-close:focus-visible{outline:2px solid var(--srf-accent);outline-offset:2px}

.about-dialog h2{
  font-family:var(--font-display);font-size:25px;font-weight:700;
  color:var(--srf-ink);margin:0 36px 12px 0;letter-spacing:-.01em;line-height:1.25;
}
.about-dialog h3{
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.09em;
  color:var(--srf-label);margin:20px 0 8px;
}
.about-body{color:var(--srf-ink);font-size:14.5px;line-height:1.65}
.about-body p{margin:0 0 12px}
.about-body ul{margin:0 0 5px;padding-left:19px}
.about-body li{margin:0 0 7px}
.about-body a{color:var(--srf-accent);text-underline-offset:2px}
.about-note{color:var(--srf-dim)}
.about-note strong{color:var(--srf-ink)}
.about-welcome{margin-top:16px;color:var(--srf-dim);font-style:italic}

/* Contributor leaderboard (src/leaderboard.js) — table inherits the dialog chrome;
   tokens keep it correct in both skins. */
.lb-table{width:100%;border-collapse:collapse;margin:14px 0 4px;font-size:12.5px}
.lb-table th{text-align:left;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--srf-label);padding:0 8px 6px 0;border-bottom:1px solid var(--srf-edge)}
.lb-table td{padding:7px 8px 7px 0;border-bottom:1px solid var(--srf-edge);vertical-align:top}
.lb-rank{width:24px;color:var(--srf-dim)}
.lb-name{font-weight:600}
.lb-pts{width:64px;text-align:right;font-variant-numeric:tabular-nums;font-weight:600;color:var(--srf-accent)}
.lb-empty td{color:var(--srf-dim);font-style:italic;font-weight:400}
.lb-empty .lb-pts{color:var(--srf-dim)}
.lb-cta{margin-top:12px}
.lb-cta a{color:var(--srf-accent)}

/* Smooth open: backdrop fades, dialog rises. Purely additive — [hidden] still
   just display:none, and reduced-motion users get an instant open. */
@media (prefers-reduced-motion: no-preference){
  .about-overlay:not([hidden]){animation:about-overlay-in var(--dur-2) var(--ease-out)}
  .about-overlay:not([hidden]) .about-dialog{animation:about-dialog-in var(--dur-3) var(--ease-out)}
}
@keyframes about-overlay-in{from{opacity:0}}
@keyframes about-dialog-in{from{opacity:0;transform:translateY(14px)}}

/* "Find a related book" dialog — the curated top-10 shelf (src/books.js) inside
   the same overlay/dialog shell as About. Items are ranked rows on the placard. */
.books-dialog{width:min(600px,100%)}
#books-list{margin:14px 0 4px;border-top:1px solid var(--srf-edge)}
.bf-item{display:flex;gap:12px;padding:11px 2px;border-bottom:1px solid var(--srf-edge)}
.bf-rank{flex:0 0 20px;font-family:var(--font-display);font-size:17px;font-weight:700;color:var(--srf-label);text-align:right;line-height:1.3}
.bf-body{flex:1 1 auto;font-size:13.5px;line-height:1.5}
.bf-t{font-style:italic;color:var(--srf-ink);font-weight:600}
.bf-meta{color:var(--srf-dim)}
.bf-why{display:block;color:var(--srf-dim);font-size:12px;margin-top:2px}
.bf-body .book-get{margin-top:7px}
.bf-more{margin-top:12px;font-size:13px}
.bf-more a{color:var(--srf-accent);text-decoration:none;font-weight:600}
.bf-more a:hover{text-decoration:underline}
.bf-disclosure{margin-top:10px;font-size:10.5px;font-style:italic;color:var(--srf-dim)}

@media (max-width: 768px){
  .about-overlay{padding:14px;align-items:flex-start}
  /* Fixed 6px margin (not 6vh) and a dvh-capped height so the whole dialog —
     title through bottom edge — stays inside the visible viewport on phones. */
  .about-dialog{padding:24px 20px 20px;margin-top:6px;max-height:calc(100vh - 40px);max-height:calc(100dvh - 40px)}
  .about-dialog h2{font-size:21px}
  .about-body{font-size:13.5px}
}
