/* The accent: links, focus, active controls, cursor accents (js/cursors.js). Change it here and it changes site-wide. */
:root { --accent-light: #6d4fe0; --accent-dark: #8b85ff; }
/* Theme tokens: a theme changes these, never the rules that use them (STYLE-GUIDE.md → Theme tokens) */
:root {
  --font-ui: Inter, system-ui, -apple-system, sans-serif;   /* all text */
  --font-num: system-ui;                                      /* small number readouts (fps meter) */
  --font-head: var(--font-ui);                                /* category titles */
  --font-caps: var(--font-ui);                                /* first-level bars in spaced caps */
  --font-date: var(--font-ui);                                /* dates: on bars, under rows, the time line; count tags and title notes */
  --date-style: normal;                                       /* their slant (a theme may set italic) */
  --font-side: var(--font-ui);                                /* the sidebar: its heading, group names and category names */
  --font-label: var(--font-ui); --label-case: uppercase;     /* small labels everywhere: menu groups, settings, card fields, search, map, tables */
  --font-title: var(--font-ui);                               /* big titles: the card's name, Data view headings */
  --font-name: var(--font-ui);                                /* names in lists: search results */
  --pic-bg: #fff;                                             /* disc behind symbol pictures */
  --icon-r: .0792;                                            /* line icons: line weight as a share of the icon's grid (one pen for all) */
  /* category icon colour (Settings → Theme): three colours from the theme's palette, the first as designed; --ic-wash is the shape behind the icon; --ic-plate is the page's own colour, what the swatches sit on, so each shows as it will look */
  --ic-1: #fff; --ic-2: #17181c; --ic-3: var(--accent); --ic-col: var(--ic-1); --ic-int: 1; --ic-wash: 22%; --ic-plate: var(--bg);
  --set-size: 12.5px; --set-fold-size: 14px; --set-fold-w: 700;   /* the Settings panel's lettering in the sidebar's font (--font-side): a setting's name, a fold's heading; serif themes set them larger */
  --icon-w: 1; --icon-cap: round; --icon-join: round;         /* the theme's pen: weight factor, line ends, corners */
  --shadow-chip: 0 1px 3px rgba(0,0,0,.18);                   /* small accent chips: cursor date, zoom range */
  --shadow-pop: 0 1px 2px rgba(0,0,0,.06), 0 18px 40px -16px rgba(0,0,0,.35), 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent);   /* menus, popovers */
  --shadow-drawer: 0 0 48px -12px rgba(0,0,0,.45);           /* side drawers on small screens */
  /* Layout switches (761px and up; phones keep one full-width column). Defaults draw the flat look: header, sidebar and
     timeline share one surface. A theme sets --gap and the radii to lift the sidebar and the timeline (or Data view)
     into separate rounded pieces on a back layer (--back, any background value) under a see-through header. */
  --rail-w: 46px; --rail-pad: 0px;                            /* the collapsed sidebar: its width and side padding (icon slot 30px + 8px each side) */
  --section-gap: 12.5px;                                      /* the one space between all sections: menu above and below, sidebar and timeline (change it here only) */
  --gap: 0px;                                                 /* space around the sidebar and the timeline panel */
  --edge: 0px;                                                /* space between the screen's edge and the sidebar / timeline: 0 bleeds off it, --gap floats them in (Experimental, html.inset) */
  --side-r: 0px; --panel-r: 0px;                              /* sidebar corners; the panel's left corners */
  --side-bd: 1px solid var(--line);                           /* the sidebar's right edge */
  --panel-bd: 0;                                              /* the panel's outline (its right edge is never drawn) */
  --panel-clip: visible;                                      /* clip to the rounded corners (clip) or not (visible) */
  --panel: var(--bg);                                         /* the timeline's and Data view's own surface */
  --back: none;                                               /* the back layer behind it all */
  --top-bg: var(--surface); --top-bd: 1px solid var(--line);  /* the header's fill and lower edge */
}
	@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d0e11; --surface: #15171b; --surface-2: #1c1f24; --text: #ecedf0; --muted: #8f95a1;
    --faint: #6e7480; --line: #23262c; --line-strong: #33373e; --cap-ring: #3a3e46; --accent: var(--accent-dark);
    --hover-ink: color-mix(in srgb, var(--accent) 62%, #fff); --on-accent: #0d0e11; --danger: #f87171;
    --shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.4); color-scheme: dark; --bar-tint0: 26%; --bar-tint: calc(var(--bar-tint0) * var(--k)); --bar-tint-hover: min(90%, calc(38% * var(--k)));
  }
}
:root[data-theme="dark"] {
  --bg: #0d0e11; --surface: #15171b; --surface-2: #1c1f24; --text: #ecedf0; --muted: #8f95a1;
  --faint: #6e7480; --line: #23262c; --line-strong: #33373e; --cap-ring: #3a3e46; --accent: var(--accent-dark);
  --hover-ink: color-mix(in srgb, var(--accent) 62%, #fff); --on-accent: #0d0e11; --danger: #f87171;
  --shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.4); color-scheme: dark; --bar-tint0: 26%; --bar-tint: calc(var(--bar-tint0) * var(--k)); --bar-tint-hover: min(90%, calc(38% * var(--k)));
}
html, body { height: 100%; margin: 0; }
/* custom cursors (js/cursors.js, Settings → Controls): every cursor below reads a --cur-* token */
body { cursor: var(--cur-default, auto); }
input:is([type=text], [type=search], :not([type])), textarea { cursor: var(--cur-text, text); }
button { font: inherit; color: inherit; }
.app {
  height: 100vh; height: 100dvh; display: grid;
  grid-template-columns: calc(var(--side-w, 248px) + var(--edge)) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
  position: relative; background: var(--back);
}
/* ---------- Header ---------- */
.top {
  /* every header control is a capsule of the same height, styled and animated like the region bar */
  --hdr-c: 36px; --ease: cubic-bezier(.65, 0, .25, 1); --ease-out: cubic-bezier(.2, .9, .25, 1);
  grid-column: 1 / -1; display: flex; align-items: center; gap: 28px; padding: var(--section-gap) 18px;
  border-bottom: var(--top-bd); background: var(--top-bg);
}
/* The search sits in the middle of the window: what is left of it and the controls right of it take equal shares
   of the width. When one side needs more than its share, the search moves aside instead of anything being cut. */
.top-l { display: flex; align-items: center; gap: inherit; flex: 1 1 0; min-width: 0; }
/* the title and view switch never shrink: the region button beside them no longer slides under "Data" */
.brand { display: flex; align-items: center; gap: 28px; flex: none; }
.brand > * { flex: none; }
/* Wide screens show the visible years too: they take only the room that is left (a grid track that may shrink to
   nothing), never pushing the region button or search */
@media (min-width: 2101px) {
  .brand { display: grid; grid-template-columns: auto auto minmax(0, max-content); column-gap: 0; flex: 0 1 auto; }
  .brand > .seg { margin-left: 28px; }
  .range-in { padding-left: 28px; }
}
.brand h1 { font-size: 15px; font-weight: 650; margin: 0; letter-spacing: -.01em; white-space: nowrap; }
.h-short { display: none; }   /* the short title: a theme that wants it shows it (Record) */
[data-themes][hidden] { display: none !important; }   /* a setting the current theme has no use for (js/settings.js) */
/* The visible years: chips set like the dates on bars in every theme (.sh-span takes each skin's date chip). With
   both datings on, BCE/CE sits above HE in one chip that fits inside the 36px controls, so the header keeps its height */
/* Short of room it hides whole, never cut: the stack wraps onto a second line, below the clip, past an empty first item */
.range { --c: var(--accent); display: flex; flex-wrap: wrap; height: var(--hdr-c); overflow: hidden; font-size: var(--t-meta); white-space: nowrap; }
.range::before { content: ""; height: 100%; }
.range-in { display: flex; align-items: center; height: 100%; }
#range .sh-span { margin-left: 0; line-height: 14px; font-weight: 600; --ink-date: 100%; --ink-years: 100%; }   /* id: wins over each skin's date chip line height */
/* both datings: one chip of two lines (34px, inside the 36px controls), split by a hairline that stops short of its sides */
/* the two lines share one grid: start | arrow | end, so both arrows sit in one column (starts right-aligned) */
#range .sh-pair { display: grid; grid-template-columns: auto auto auto; row-gap: 2px; padding-block: 1px; }
.sh-line { position: relative; height: 14px; display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; }
.sh-from { justify-self: end; }
.sh-line + .sh-line::before {
  content: ""; position: absolute; left: 0; right: 0; top: -1.5px; height: 1px; background: color-mix(in srgb, currentColor 30%, transparent);
}
.controls { display: flex; align-items: center; justify-content: flex-end; gap: 20px; flex: 1 1 0; }
/* Header controls keep their size: shrinking squeezed the capsules into tall slivers (Safari). When room runs out,
   the region name gives way (.rmap-cur ellipsis), then the header wraps (tablet). */
.controls > *, .controls button { flex-shrink: 0; }
.seg {
  position: relative; display: inline-flex; padding: 3px; gap: 2px; border-radius: 999px;
  background: var(--surface-2); background-image: var(--cap-sheen, none); box-shadow: inset 0 0 0 1px var(--cap-ring), var(--cap-lift, 0 0 #0000);
}
.seg button:hover { color: var(--text); }
.seg button:hover::before { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.seg.theme button, .seg.icons button { padding: 0 9px; display: grid; place-items: center; }
.top .seg button { height: calc(var(--hdr-c) - 6px); padding-block: 0; }
/* An icon-only header button is as wide as its capsule is tall, so its highlight and chosen pill (both full height)
   are circles, never tall ovals */
.top .seg.icons button { width: var(--hdr-c); padding: 0; }
/* the chosen option: a pill glides to it (js/segpill.js), presses on a click, its icon pops. It is flush with the
   outer capsule (full height, reaching the ends for the first and last option): its inset ring lies on the outer ring */
.seg-pill {
  position: absolute; left: 0; top: 0; border-radius: 999px; pointer-events: none; opacity: 0; background: var(--surface);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, var(--cap-ring)), 0 0 14px -4px color-mix(in srgb, var(--accent) 35%, transparent);
}
.seg.ready .seg-pill { transition: transform .46s cubic-bezier(.65, 0, .25, 1), width .46s cubic-bezier(.65, 0, .25, 1), height .3s ease, opacity .25s ease; }
.seg-pill.press { animation: pill-press .5s cubic-bezier(.2, .9, .25, 1); }
.seg button.press svg { animation: globe-press .4s cubic-bezier(.2, .9, .25, 1); }
.seg button { position: relative; z-index: 1; }
/* Bars sit in world coordinates; render() shifts each lane body with a transform to pan. */
body, .top, .side, .axis, .overview, .item, .detail { transition: background-color .2s, border-color .2s, color .2s; }
.seg button[aria-checked="true"] {
  background: var(--surface); color: var(--accent);
  box-shadow: 0 1px 2px rgba(0,0,0,.08), 0 0 0 1px var(--line);
}
.seg.has-pill button[aria-checked="true"] { background: transparent; box-shadow: none; }
/* Header buttons: capsules on the same track as the region bar; hover tints them, a click presses and pops.
   Controls that belong together share one capsule (.hdr-cap: zoom and Overview; Settings and the theme),
   split by a hairline (.hdr-sep) */
.hdr-cap, .hdr-btn {
  height: var(--hdr-c); border: 0; border-radius: 999px; background: var(--surface-2); color: var(--muted);
  background-image: var(--cap-sheen, none);   /* a theme's faint light from above, and its lift off the back layer */
  box-shadow: inset 0 0 0 1px var(--cap-ring), var(--cap-lift, 0 0 #0000);
  transition: background-color .25s ease, color .25s ease, box-shadow .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
}
.hdr-cap { display: inline-flex; align-items: center; gap: 2px; padding: 3px; }
.hdr-sep { flex: none; width: 1px; height: 18px; margin: 0 3px; background: var(--line-strong); }
.hdr-cap > .seg { padding: 0; background: none; box-shadow: none; }   /* a switch inside a capsule uses its track */
.hdr-cap > button, .hdr-cap > .seg > button {
  border: 0; background: transparent; cursor: var(--cur-pointer, pointer); width: var(--hdr-c, 36px); height: 30px; display: grid;
  place-items: center; color: var(--muted); border-radius: 999px;
  transition: background-color .25s ease, color .25s ease, box-shadow .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
}
/* One hover for every header button (the look of Fit), segmented options included: lifted onto the surface with an
   accent ring, text a brighter tint of the accent */
.hdr-cap > button:hover, .hdr-cap > .seg > button:hover, .hdr-btn:hover, #settingsBtn[aria-expanded="true"], .rmap-btn:hover,
.top .seg button:not([aria-checked="true"]):hover { color: var(--hover-ink); }
.hdr-btn:hover, .rmap-btn:hover,
.hdr-cap > button:hover::before, .hdr-cap > .seg > button:hover::before, #settingsBtn[aria-expanded="true"]::before,
.top .seg button:not([aria-checked="true"]):hover::before {
  background: var(--surface); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, var(--cap-ring));
}
/* A highlight inside a capsule (hover, like the chosen pill) is merged with it: drawn behind the button, it spans the
   capsule's full height through its 3px padding and reaches the capsule's end for a button at either end */
.seg button::before, .hdr-cap > button::before {
  content: ""; position: absolute; inset: -3px 0; z-index: -1; border-radius: 999px; pointer-events: none;
  transition: background-color .25s ease, box-shadow .25s ease;
}
.hdr-cap > button { position: relative; z-index: 0; }
.seg button:first-of-type::before, .hdr-cap > button:first-child::before { left: -3px; }
.seg button:last-of-type::before, .hdr-cap > button:last-child::before { right: -3px; }
.hdr-cap > .seg:not(:first-child) > button:first-of-type::before { left: 0; }
.hdr-cap > .seg:not(:last-child) > button:last-of-type::before { right: 0; }
/* Header controls are 36px tall, the capsule's full height: no inset ring, so the tap target and its highlight are one size */
.top .seg, .hdr-cap { padding: 0; }
.top .seg button, .hdr-cap > button, .hdr-cap > .seg > button { height: var(--hdr-c, 36px); }
.top .seg button::before, .hdr-cap > button::before, .hdr-cap > .seg > button::before { inset: 0; }
.top .seg button:first-of-type::before, .hdr-cap > button:first-child::before { left: 0; }
.top .seg button:last-of-type::before, .hdr-cap > button:last-child::before { right: 0; }
/* One motion for every header control (the Overview button's): on hover its content, icon and text alike, grows a
   little (--pop) with a small bounce and stays grown until the pointer leaves; a click presses the button in and it
   springs back. Text is wrapped in .btn-tx (js/text.js) so it can
   move; flex makes every child transformable. */
.top .seg button, .hdr-cap > button, .hdr-cap > .seg > button { display: inline-flex; align-items: center; justify-content: center; }
/* Line icons share one pen (STYLE-GUIDE.md → Icons): every line is the same share of its icon's grid (--icon-r, 1.9 on
   the 24 grid, 1.27 on the 16 grid), so an icon of any grid or size draws the same line as its neighbours, and a theme
   thins or firms all of them at once (--icon-w, --icon-cap, --icon-join; Atlas engraves hairlines, Chronicle and Datum draw finer).
   Covers category icons, seals' symbols, header, settings, card, search, sheet and tie icons; not the seal's frame,
   the death mark (its own setting), maps or tie lines. The settings samples' thin and thick lines keep their contrast. */
:where(svg[viewBox^="0 0 10 "]) { --vb: 10; } :where(svg[viewBox^="0 0 11 "], svg[viewBox^="0 0 12 "]) { --vb: 12; }
:where(svg[viewBox^="0 0 14 "]) { --vb: 14; } :where(svg[viewBox^="0 0 16 "]) { --vb: 16; }
:where(svg[viewBox^="0 0 18 "], svg[viewBox^="0 0 22 "]) { --vb: 18; } :where(svg[viewBox^="0 0 24 "]) { --vb: 24; }
:where(svg[stroke-width], svg [stroke-width]):not(.seal > path) {
  stroke-width: calc(var(--vb, 16) * var(--icon-r) * var(--icon-w) * 1px); stroke-linecap: var(--icon-cap); stroke-linejoin: var(--icon-join);
}
:where(.set-ic [stroke-width="1"]) { stroke-width: calc(1px * var(--icon-w)); }
:where(.set-ic [stroke-width="2.4"]) { stroke-width: calc(2.4px * var(--icon-w)); }
:where(.set-ic [stroke-width="3"]) { stroke-width: calc(3px * var(--icon-w)); }
:root { --hair: 1px; }   /* one thin line: always a whole number of screen pixels, so all four sides of a box match (js/hair.js) */
:root { --pop: 1.036; --pop-nudge: -.3px; --press: .92; --press-ico: .86; --pop-t: scale .32s cubic-bezier(.3, 1.5, .4, 1), translate .32s cubic-bezier(.3, 1.5, .4, 1); }
.hdr-cap > button > *, .hdr-cap > .seg > button > *, .hdr-btn > *, .rmap-btn > *, .top .seg button > *,
.sheet-tab .dot svg, .btn.primary svg, .lane-tools svg, .rmap-tag .ico { transition: var(--pop-t); }
@media (hover: hover) {
  .hdr-cap > button:hover > *, .hdr-cap > .seg > button:hover > *, .hdr-btn:hover > *, .rmap-btn:hover > *,
  .top .seg button:not([aria-checked="true"]):hover > *, .sheet-tab:hover .dot svg, .btn.primary:hover svg,
  .lane-tools:hover svg, .rmap-g:hover .ico, .rmap-tag:hover .ico { scale: var(--pop); translate: var(--pop-nudge) 0; }
}
.top .seg button.press > * { animation: globe-press .4s var(--ease-out); }
.hdr-cap > button:active, .hdr-cap > .seg > button:active, .hdr-btn:active, .rmap-btn:active, .top .seg button:active { scale: var(--press); transition-duration: .25s, .25s, .25s, .08s; }
@media (prefers-reduced-motion: reduce) { .top button > * { animation: none !important; scale: none !important; translate: none !important; } }
.zoom .fit { width: auto; padding: 0 11px; }
.fit-ic { display: none; }   /* the icon stands in for the word once labels fold (1800px and less) */
/* Buttons with a label: icon then caps text. Expand all holds both its labels in one cell, so it keeps one width */
.hdr-cap .hdr-tx-btn { width: auto; gap: 7px; padding: 0 11px; }
.hdr-alt { display: grid; }
.hdr-alt > * { grid-area: 1 / 1; }
.hdr-alt > [aria-hidden] { visibility: hidden; }
/* ---------- Sidebar ---------- */
.side {
  --group-ink: color-mix(in srgb, var(--faint) 50%, var(--text));   /* set again here: inside the dark sidebar it must take that sidebar's inks */
  grid-column: 1; grid-row: 2; border-right: var(--side-bd); background: var(--surface);
  padding: 16px 12px; overflow-y: auto; -webkit-user-select: none; user-select: none;
  overscroll-behavior: none;   /* our elastic edges only (js/elastic.js) */
  /* no scrollbar (it still scrolls): where scrollbars take room (a mouse, or "always show") it would cost the names
     its width and, as the icon rail has none, shift the counts when the rail opens into the sidebar */
  scrollbar-width: none;
}
.side::-webkit-scrollbar { display: none; }
/* Wide screens: the rows scroll in their own area, thinning out at its foot, so nothing passes under the expand/collapse button and it
   needs no fill of its own. Phones: the drawer scrolls as a whole (.side), with no such button. */
@media (min-width: 761px) {
  .side { overflow: hidden; }
  .side-rows {
    flex: 1 1 auto; min-height: 0; overflow: hidden auto; overscroll-behavior: none; scrollbar-width: none; padding-bottom: 14px;
    -webkit-mask-image: linear-gradient(#000 calc(100% - 14px), transparent); mask-image: linear-gradient(#000 calc(100% - 14px), transparent);
  }
  .side-rows::-webkit-scrollbar { display: none; }
  .side-rows .cat:focus-visible { outline-offset: -2px; }   /* its edge would cut a ring outside the row */
}
@media (max-width: 760px) { .side-rows { display: contents; } }
/* Reload: until the script has folded the header, filled the text and drawn the first frame, the page is still the
   desktop header squeezed onto a phone; it stays hidden, then fades in once (js/app.js removes .booting) */
.app { transition: opacity .25s ease; }
.booting .app { opacity: 0; transition: none; }
.booting .menu-scrim { display: none; }
@media (prefers-reduced-motion: reduce) { .app { transition: none; } }
.side-h {
  display: flex; justify-content: space-between; align-items: center; font-size: 11.5px; font-weight: 600;
  text-transform: var(--label-case); letter-spacing: .06em; color: var(--muted); padding: 0 8px 10px;
}
.side-h button {
  border: 0; background: none; cursor: var(--cur-pointer, pointer); color: var(--accent); font-size: 12px; font-weight: 500;
  text-transform: none; letter-spacing: 0; padding: 0;
}
.cats { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.cat {
  width: 100%; display: flex; align-items: center; gap: 10px; border: 0; background: transparent;
  cursor: var(--cur-pointer, pointer); text-align: left; padding: 7px 8px; border-radius: 8px;
  transition: background .12s, opacity .12s;
}
.cat:hover, .cats li.swept .cat { background: var(--cat-hover-bg, transparent); }
/* the category in view, as the rail on the right shows it (js/scrollrail.js): a wash of its colour */
.cats li.current .cat { background: var(--cat-current-bg, transparent); }
.cats li.current .cat .nm { color: var(--nm-current, color-mix(in srgb, var(--c) 70%, var(--text))); }
/* a theme can change how the category in view, hovered or swept shows its icon: --sw-hi-fg (icon), --sw-hi-bg (its shape);
   --sw-rest is the shape at rest. Defaults keep the category colour. */
.cats li:not(.off):is(.current, .swept, .dragging) .cat .sw, .cats li:not(.off) .cat:hover .sw { color: var(--sw-hi-fg, var(--c)); }
.cats li:not(.off):is(.current, .swept, .dragging) .cat .sw::before, .cats li:not(.off) .cat:hover .sw::before {
  background: var(--sw-hi-bg, var(--sw-rest, color-mix(in srgb, currentColor calc(12.5% * var(--k)), transparent)));
}
.cat .sw {
  display: grid; place-items: center; width: 30px; height: 24px; flex: none; color: var(--c);
  transition: color .12s; position: relative;
}
/* the icon (22px, full size) sits in its category's lightly tinted shape (12.5%, subtler than the header's 16%),
   the same shape and size as its header icon, drawn behind it. The 30px slot keeps every shape clear of the name
   and the row edge (the semicircle, 40px, overhangs it by 5px each side) and rows stay 38px tall, so shapes never
   touch. */
.cat .sw::before {
  content: ""; position: absolute; left: 50%; top: 50%; z-index: -1; width: var(--sw); height: var(--sh);
  translate: -50% -50%; border-radius: var(--sr); clip-path: var(--clip, none);
  background: var(--sw-rest, color-mix(in srgb, currentColor calc(12.5% * var(--k)), transparent)); transition: background .15s;
}
.cat .sw > svg { translate: var(--dx, 0px) var(--dy); }
/* Sidebar icon shapes off (Settings → Theme, every theme): the bare icon, no shape behind it; lit or hovered it
   turns the theme's accent, as the shape that carried the accent is gone */
html.no-side-shapes .cat .sw::before { display: none; }
html.no-side-shapes :is(.cat .sw, .cat-tag-ic) > svg { translate: var(--ix, 0px) var(--iy, 0px); }   /* no shape to fit: the ink itself is centred (inkShift, js/icons.js) */
html.no-side-shapes .side { --sw-hi-fg: var(--accent); }
.cat { isolation: isolate; }
.cats li.sub .sw { height: 18px; }   /* people rows under a theme stay shorter; their shape overhangs into the row's padding */
.cat .nm { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.side :is(.side-h, .cat-group, .cat .nm) { font-family: var(--font-side); }   /* the theme's sidebar font (--font-side) */
.cat .ct {
  color: var(--muted); background: var(--surface-2); border-radius: 6px; padding: 1px 7px;
  min-width: 24px; text-align: center;
}
/* Hidden categories: dimmed, with the eye (crossed out) always visible */
.cats li.off .cat { opacity: .45; }
.cats li.off .sw { color: var(--faint); }
.hint { margin: 18px 8px 0; font-size: 12px; color: var(--faint); line-height: 1.6; }
.hint kbd {
  font: inherit; font-size: 11px; padding: 0 5px; border: 1px solid var(--line-strong);
  border-radius: 4px; color: var(--muted);
}
/* ---------- Stage ---------- */
.stage {
  grid-column: 2; grid-row: 2; display: flex; flex-direction: column; min-width: 0; min-height: 0;
  position: relative;
}
/* the timeline and the Data view sit on --panel (--bg inside them, so sticky titles match it) */
:is(.stage, .sheet, .mapview, .relview, .dx) { --bg: var(--panel); background: var(--panel); background-image: var(--panel-glow, none); }   /* --panel-glow: a theme's soft corner light (STYLE-GUIDE.md → Layers) */
/* On the timeline the picture lies on its own still layer (as .dx in css/datatab.css): painted on .stage itself, Safari
   repainted the whole panel each frame an info card came in over Datum's contour lines or Atlas's chart (about 25% of frames late; 1-2% this way) */
.stage { isolation: isolate; background-image: none; }
.stage::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; border-radius: inherit;
  background-image: var(--panel-glow, none); will-change: transform;
}
@media (min-width: 761px) {
  /* by default both run off the screen: the sidebar's left edge and the panel's right edge are square and flush;
     html.inset (Settings → Theme → Screen edges) floats them in with --edge and rounds all four corners */
  .side { margin: 0 0 var(--gap) var(--edge); border-radius: calc(var(--side-r) * var(--inset, 0)) var(--side-r) var(--side-r) calc(var(--side-r) * var(--inset, 0)); }
  :is(.stage, .sheet, .mapview, .relview, .dx) {
    margin: 0 var(--edge) var(--gap) var(--gap); border: var(--panel-bd); border-right: 0; border-radius: var(--panel-r) calc(var(--panel-r) * var(--inset, 0)) calc(var(--panel-r) * var(--inset, 0)) var(--panel-r);
    overflow: var(--panel-clip, visible);
  }
  html.inset { --edge: var(--gap); --inset: 1; }
  html.inset :is(.stage, .sheet, .mapview, .relview, .dx) { border-right: var(--panel-bd); }
}
/* dragging here selects a range */
.axis {
  position: relative; flex: none; background: var(--surface); border-bottom: 1px solid var(--line);
  overflow: hidden; cursor: var(--zoom-cursor); touch-action: none; -webkit-user-select: none; user-select: none;
}
.axis-row { position: relative; height: 28px; }
.axis-row + .axis-row { border-top: 1px dashed var(--line); }
.axis-tag {
  position: absolute; left: 0; top: 0; bottom: 0; z-index: 2; display: flex; align-items: center;
  padding: 0 10px 0 12px; background: linear-gradient(90deg, var(--surface) 75%, transparent);
  font-size: 10.5px; font-weight: 650; letter-spacing: .06em; color: var(--accent);
}
.tick { position: absolute; left: 0; top: 0; bottom: 0; border-left: 1px solid var(--line-strong); }
.tick > span {
  position: absolute; left: 6px; top: 50%; transform: translateY(-50%); font-size: 11.5px;
  color: var(--muted); white-space: nowrap;
}
.tick.zero { border-left-color: var(--accent); }
.tick.zero span { color: var(--accent); font-weight: 600; }
/* The five big eras as subtle brackets across the top of the timeline (js/app.js renderEraBrackets): a hairline under each
   era's span with a short tick at each end, its mark and name in the part on screen. Low contrast in the theme's accent */
.era-brackets { position: relative; flex: none; height: 20px; overflow: hidden; contain: strict; pointer-events: none; background: var(--surface); }
.era-brackets[hidden] { display: none; }
.eb { position: absolute; left: 0; top: 0; bottom: 4px; will-change: transform;
  border-bottom: var(--hair) solid var(--eb-line); border-left: var(--hair) solid var(--eb-line); border-right: var(--hair) solid var(--eb-line);
  --eb-line: color-mix(in srgb, var(--accent) 34%, transparent); }
.eb::before, .eb::after { content: ""; position: absolute; bottom: 0; height: 7px; }   /* the ticks rise only 7px: the side borders are clipped below */
.eb { border-left-color: transparent; border-right-color: transparent; }
.eb::before { left: calc(-1 * var(--hair)); border-left: var(--hair) solid var(--eb-line); }
.eb::after { right: calc(-1 * var(--hair)); border-right: var(--hair) solid var(--eb-line); }
.eb.open-a::before, .eb.open-b::after { display: none; }
.eb-l { position: absolute; left: 0; top: 2px; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font: 600 10px/14px var(--font-label, var(--font-ui)); letter-spacing: .1em; text-transform: var(--label-case, uppercase);
  color: color-mix(in srgb, var(--muted) 85%, transparent); }
.eb-l .era-ico { color: color-mix(in srgb, var(--accent) 70%, var(--muted)); }
.viewport {
  position: relative; flex: 1; min-height: 0; overflow: hidden; cursor: var(--cur-grab, grab); touch-action: none;
  -webkit-user-select: none; user-select: none;   /* Safari still needs the prefix */
}
.viewport.dragging, .axis.dragging { cursor: var(--cur-grabbing, grabbing); }
/* the far right edge: sweeping up and down it acts as hovering the sidebar's categories (js/app.js) */
.edge-sweep {
  position: absolute; top: 0; right: 0; bottom: 0; width: 20px; z-index: 8; cursor: var(--cur-ns, ns-resize);
  background: linear-gradient(270deg, color-mix(in srgb, var(--accent) 12%, transparent), transparent);
  opacity: 0; transition: opacity .15s;
}
.edge-sweep:hover { opacity: 1; }
.edge-sweep.locked { opacity: 0; cursor: inherit; }   /* off after a drag-magnify near it (js/app.js lockEdge) */
.grid { position: absolute; inset: 0; pointer-events: none; }
.gl { position: absolute; left: 0; top: 0; bottom: 0; border-left: 1px solid var(--line); }
.gl.zero { border-left: 1px dashed var(--accent); opacity: .5; }
.gl.now { border-left: 1.5px solid var(--accent); opacity: .7; }
.gl.now::before {
  content: "Now"; position: absolute; top: 6px; left: 4px; font-size: 10px; font-weight: 650;
  letter-spacing: .05em; text-transform: uppercase; color: var(--accent);
}
.lanes {
  position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; padding-bottom: calc(24px + var(--sheet-room, 0px));   /* sheet room: js/app.js, goTo */
  overscroll-behavior: none;   /* our elastic edges only (js/elastic.js): the browser's own bounce fought them in Safari */
  scrollbar-width: none;   /* the category rail (.srail) stands in for its scrollbar */
}
.lanes::-webkit-scrollbar { display: none; }
/* The new drawing (js/glview.js, Settings → Timeline → Experimental): the canvas behind the rows, which keep scrolling
   over a spacer as tall as the model; the bands and lines are drawn on the canvas, so the page's grid steps aside */
.glrows { position: absolute; inset: 0; }
.glrows canvas { position: absolute; left: 0; top: 0; width: 100%; height: 100%; display: block; }
.viewport.gl-on .grid { display: none; }
.viewport.gl-on .lanes { background: none; }   /* a theme's row paper is the canvas's to draw */
/* Edge fades (Settings → Theme, html.edge-fades): entries thin out toward both sides over --edge-fade. The canvas does it itself
   (js/draw/webgl.js); this mask is only for the page's own bars, where there is no WebGL2 */
:root { --edge-fade: 64px; }
.edge-fades .viewport:not(.gl-on) .lanes {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--edge-fade), #000 calc(100% - var(--edge-fade)), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 var(--edge-fade), #000 calc(100% - var(--edge-fade)), transparent);
}
.gl-space { width: 1px; pointer-events: none; }
.gl-probe, .gl-probe *, .gl-probe *::before, .gl-probe *::after { transition: none !important; animation: none !important; }   /* read at their final values */
.gl-text-sheet { display: none; -webkit-font-smoothing: antialiased; }   /* a sheet draws its text as the page does, and is never painted itself (js/draw/atlas.js) */
.lane.gl-probe { color: var(--text);   /* its own ink: names that inherit theirs would take a fading ancestor's colour mid-change of theme */
  position: absolute; visibility: hidden; pointer-events: none; content-visibility: visible; contain: none; }   /* a sample row read for its fonts, colours and sizes (js/draw/look.js): laid out in full, so its pseudo-elements exist */
/* a row's ghost: hidden, at the row's place, so the rail and every lookup of a row find it (js/glview.js syncGhosts) */
.lane.gl-ghost { position: absolute; left: 0; width: 1px; visibility: hidden; pointer-events: none; contain: strict; margin: 0; padding: 0; border: 0; }
/* Expand all / Collapse all: the rows' scroll height stays where it was until every row has its new size (js/app.js,
   holdFloor), so the scrollbar changes once: rows sliding up into place counted to the end of their slide meanwhile.
   An element of its own: a class or variable on .lanes restyled every row in Safari (~130ms before the first frame) */
.rows-floor { position: absolute; left: 0; width: 1px; height: 1px; pointer-events: none; visibility: hidden; }
/* Settings → Controls "Category rail" off (tl.rail): no rail or edge sweep, the rows' own scrollbar instead */
.no-rail :is(.srail, .edge-sweep) { display: none; }
.no-rail .lanes { scrollbar-width: thin; }
.no-rail .lanes::-webkit-scrollbar { display: block; }
/* Category rail (js/scrollrail.js): every category row as a vertical block in its colour down the right edge, its
   header icon (.lane-ic, 30px, scaled) beside it, magnified like the macOS Dock around the one in view (or the one
   hovered on the edge or in the sidebar): --m is 1 at the focus and shrinks with distance. Its name shows beside it.
   Fades in and out like the scrollbars; it takes no clicks, so the edge sweep under it keeps working. */
.srail {
  position: absolute; top: 0; right: 0; bottom: 0; width: 56px; z-index: 9; pointer-events: none;
  /* hidden: slid out past the right edge and blurred; it slides in sharpening, and back out the same way */
  opacity: 0; translate: 18px 0; filter: blur(6px);
  transition: opacity .2s ease-in, translate .2s ease-in, filter .2s ease-in;
}
.srail.on { opacity: 1; translate: none; filter: none; transition-duration: .18s; transition-timing-function: cubic-bezier(.2,.8,.2,1); }
.srail-it {
  --m: .3; position: absolute; top: 0; right: 5px; display: flex; flex-direction: row-reverse; align-items: center; gap: 4px;
  /* faded with its colours' alpha (--a), not opacity: an opacity per block made Safari paint each apart every frame */
  --a: calc(40% + 60% * var(--m));
  transition: transform .16s ease-out, height .16s ease-out;
}
.srail-bk {
  align-self: stretch; width: calc(3px + 7px * var(--m)); border-radius: 3px; background: color-mix(in srgb, var(--c) var(--a), transparent);
  transition: width .16s ease-out, background-color .16s ease-out;
}
.srail-it .lane-ic {
  background: color-mix(in srgb, color-mix(in srgb, var(--c) 22%, var(--surface)) var(--a), transparent);
  color: color-mix(in srgb, var(--c) var(--a), transparent);
  scale: var(--m); transform-origin: right center; margin-left: calc(var(--sw) * (var(--m) - 1));   /* takes only the room it shows in */
  transition: scale .16s ease-out, margin .16s ease-out, background-color .16s ease-out, color .16s ease-out;
}
.srail-tag {
  position: absolute; right: 56px; translate: 0 -50%; padding: 3px 8px; border-radius: 7px; white-space: nowrap;
  color: color-mix(in srgb, var(--c) 70%, var(--text)); background: color-mix(in srgb, var(--c) 16%, var(--surface));
  box-shadow: var(--shadow); font-size: var(--t-caps); font-weight: 650; letter-spacing: var(--caps); text-transform: uppercase;
  transition: top .16s ease-out;
}
/* while the rows scroll, the blocks follow them frame by frame without easing (js/scrollrail.js) */
.srail.scrolling :is(.srail-it, .srail-bk, .lane-ic, .srail-tag) { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .srail-it, .srail-bk, .srail-it .lane-ic, .srail-tag { transition: none; }
  .srail { translate: none; filter: none; }   /* fades only */
}
/* Each category row: a faint wash of its colour, a colour rail on the left and a gap between rows */
.lane {
  /* rows off screen are skipped entirely (no layout, no painting), which keeps zooming and panning smooth with
     thousands of entries; their exact height is set by js/app.js (sizeLane). Enlarged pictures may reach a little
     past a row's edge, hence the clip margin. */
  content-visibility: auto; contain-intrinsic-size: auto 120px; overflow-clip-margin: 32px;
  position: relative; margin: 0 0 6px;   /* no wash of the category colour: the rows share the timeline's background */
  /* the top line is an inset shadow (not a border) so the rail below starts at the row's very top: the row
     clips its painting to its padding box, and Safari would otherwise leave the border's height uncovered */
  padding-top: 1.5px; box-shadow: var(--lane-edge, inset 0 1.5px 0 color-mix(in srgb, var(--c) 30%, transparent));
  /* the title bar is the category's solid block (--hd-fill, text --hd-ink); the rail beside it is a deeper shade of the
     category colour (lighter in dark mode), so it still shows against the solid bar. Sub bars below stay pale. */
  --hd-fill: light-dark(var(--c), color-mix(in srgb, var(--c) 55%, var(--surface)));
  --hd-ink: #fff;
  --rail: light-dark(color-mix(in srgb, var(--c) 58%, #000), var(--c));
  border-bottom: var(--lane-bd, 1px solid color-mix(in srgb, var(--c) 10%, transparent));
}
.lane::before {
  display: var(--rail-show, block); content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; z-index: 6;
  background: var(--rail); pointer-events: none;
}
.lane[hidden] { display: none; }
.lane-ic {
  display: grid; place-items: center; flex: none; width: var(--sw); height: var(--sh); border-radius: var(--sr);
  clip-path: var(--clip, none); color: var(--c); background: color-mix(in srgb, var(--c) calc(16% * var(--k)), transparent);
}
/* Category shapes: one clip per cut shape, shared by the header icon, the bar pictures (.av), search (.gs-pic)
   the sidebar icon (.cat .sw) and its hover tag (.cat-tag) */
:is(.lane-ic, .av, .gs-pic, .cat .sw, .cat-tag) {
  &.shield { --clip: polygon(0 0, 100% 0, 100% 55%, 85% 80%, 50% 100%, 15% 80%, 0 55%); }
  &.hex { --clip: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%); }
  &.signpost { --clip: polygon(0 0, 74% 0, 100% 50%, 74% 100%, 0 100%); }
  &.diamond { --clip: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
  &.pentagon { --clip: polygon(50% 0, 100% 34%, 100% 100%, 0 100%, 0 34%); }   /* a temple front: straight walls under a pointed roof */
  &.pennant { --clip: polygon(0 0, 100% 0, 84% 50%, 100% 100%, 0 100%); }   /* a swallowtail flag: flat hoist, notched fly */
  &.octagon { --clip: polygon(29.3% 0, 70.7% 0, 100% 29.3%, 100% 70.7%, 70.7% 100%, 29.3% 100%, 0 70.7%, 0 29.3%); }
  &.trapezoid { --clip: polygon(20% 0, 80% 0, 100% 100%, 0 100%); }
  &.banknote { --clip: polygon(0 0, 100% 0, 100% 36%, 93% 50%, 100% 64%, 100% 100%, 0 100%, 0 64%, 7% 50%, 0 36%); }   /* a banknote: a wide note with a notch in each short side */
  &.banner { --clip: polygon(0 0, 100% 0, 100% 100%, 50% 80%, 0 100%); }
  &.book { --clip: polygon(0 4%, 22% 0, 40% 3%, 50% 12%, 60% 3%, 78% 0, 100% 4%, 100% 88%, 78% 85%, 60% 88%, 50% 100%, 40% 88%, 22% 85%, 0 88%); }
  /* a thought cloud: four shallow arcs over a flat bottom */
  &.cloud { --clip: polygon(9.9% 100%, 6.8% 96.1%, 4.2% 91.6%, 2.2% 86.5%, 0.8% 81%, 0.1% 75.2%, 0 69.3%, 0.6% 63.5%, 1.9% 58%, 3.9% 52.8%, 6.4% 48.2%, 9.4% 44.2%, 12.9% 41%, 13.1% 34.4%, 14.1% 27.9%, 15.8% 21.8%, 18.2% 16.1%, 21.3% 11.1%, 24.9% 6.9%, 28.9% 3.6%, 33.2% 1.3%, 37.8% 0.1%, 42.5% 0, 47.1% 1%, 51.5% 3%, 55.9% 1%, 60.5% 0, 65.2% 0.1%, 69.7% 1.3%, 74.1% 3.6%, 78.1% 6.9%, 81.7% 11.1%, 84.7% 16.1%, 87.2% 21.8%, 88.9% 27.9%, 89.9% 34.4%, 90.1% 41%, 93.2% 44.8%, 95.8% 49.4%, 97.8% 54.5%, 99.2% 60%, 99.9% 65.8%, 100% 71.6%, 99.4% 77.4%, 98.1% 83%, 96.1% 88.2%, 93.6% 92.8%, 90.6% 96.8%, 87.1% 100%); }
  /* a sound wave: one gentle crest and trough over a flat bottom */
  &.wave { --clip: polygon(0% 11.5%, 4.2% 8.5%, 8.3% 5.8%, 12.5% 3.4%, 16.7% 1.5%, 20.8% 0.4%, 25% 0%, 29.2% 0.4%, 33.3% 1.5%, 37.5% 3.4%, 41.7% 5.8%, 45.8% 8.5%, 50% 11.5%, 54.2% 14.5%, 58.3% 17.2%, 62.5% 19.6%, 66.7% 21.5%, 70.8% 22.6%, 75% 23%, 79.2% 22.6%, 83.3% 21.5%, 87.5% 19.6%, 91.7% 17.3%, 95.8% 14.5%, 100% 11.5%, 100% 100%, 0 100%); }
}
/* The tinted icon shape of a category, as in its header (30px): width, height, corner radius, and how far its
   icon sits off-centre to fit the shape. The sidebar icon and its hover tag draw the same shape at the same size. */
:is(.lane-ic, .cat .sw, .cat-tag) {
  --sw: 30px; --sh: 30px; --sr: 9px; --dy: 0px;
  &.portrait { --sr: 50%; }
  &.photo { --sw: 38px; --sh: 28px; --sr: 6px; }
  &.canvas { --sw: 25px; --sr: 3px; }
  &.arch { --sw: 26px; --sr: 13px 13px 3px 3px; }
  &.semicircle { --sw: 40px; --sh: 22px; --sr: 20px 20px 0 0; --dy: .5px; }
  &.capsule { --sw: 40px; --sh: 26px; --sr: 13px; }
  &:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave) { --sr: 0; }
  &.shield { --dy: -2.5px; }
  &.hex { --sw: 36px; }
  &:is(.diamond, .pentagon) { --sw: 34px; }
  &.pennant { --sw: 36px; --sh: 26px; }
  &.signpost { --sw: 36px; --dx: -2.5px; }
  &.pentagon { --dy: 1.5px; }
  &.octagon { --sw: 32px; --sh: 32px; }
  &.trapezoid { --sw: 38px; --sh: 26px; --dy: 1px; }
  &.banknote { --sw: 38px; --sh: 26px; }
  &.banner { --sw: 24px; --sh: 24px; --dy: -2px; }
  &.book { --sw: 38px; --sh: 28px; --dy: .5px; }
  &.cloud { --sw: 38px; --sh: 27px; --dy: 1.5px; }
  &.wave { --sw: 38px; --sh: 28px; --dy: 2px; }
}
.lane-ic > svg { translate: var(--dx, 0px) var(--dy); }
.lane-empty { padding: 0 14px 10px 51px; font-size: 12px; color: var(--faint); font-style: italic; }
/* Bars and boxes off screen sideways (js/app.js renderItems) are hidden like this, never with display: none: Safari
   keeps no style for an undisplayed element, so each row shift re-styled every one of them (pan and zoom at 30fps) */
.lane-b > .away { visibility: hidden; content-visibility: hidden; }
.item {
  position: absolute; height: calc(26px * var(--bar-th, 1)); margin-top: calc((26px - 26px * var(--bar-th, 1)) / 2);
  /* the edge is mixed into the fill (--bar-fill), not drawn see-through over it: the same colour, and Safari paints
     opaque rounded borders faster while every bar resizes (zooming while scrolling, Follow rows) */
  --bar-fill: color-mix(in srgb, #000 var(--bar-dark, 0%), color-mix(in srgb, var(--c) var(--bar-tint), var(--surface))); background: var(--bar-fill);
  /* the outline gains most with Contrast (--k squared): it is what sets a bar apart from the background */
  --bar-edge: color-mix(in srgb, var(--c) min(100%, calc(30% * var(--k) * var(--k))), var(--bar-fill));
  border: var(--edge-w, var(--hair)) solid var(--bar-line); border-left: 3px solid var(--c);
  border-radius: 6px; cursor: var(--cur-pointer, pointer);
  transition: background-color .15s, box-shadow .18s, top .3s cubic-bezier(.2,.8,.2,1), height .18s cubic-bezier(.2,.8,.3,1.2), margin-top .18s cubic-bezier(.2,.8,.3,1.2), opacity .15s;
}
/* Hover: highlight and swell slightly. .hov: shown as hovered after following a relationship line (js/app.js),
   while the bars gliding under the still pointer take no hover (.held). A touch is not a hover: a tap leaves :hover
   stuck on the bar tapped, so while the last input was a touch (html.touch-input, js/app.js) bars ignore :hover. */
.lanes.held :is(.item, .frame) { pointer-events: none; }
/* Parent bars (with sub bars or people: closed, or opened as a box) wear their own outline, shadow and glow
   (Settings → Rows → Outlines, the root's --*-p); every other bar keeps the plain tokens, copied at the
   root as --*-c. A closed parent's stacked edge is a sliver of a child bar, so it takes the child values back. Any
   theme that builds its edge, shadow and glow from --edge-k, --edge-w, --ss, --sb and --glow-k follows */
:root { --edge-ink-c: var(--edge-ink, 100%); --edge-k-c: var(--edge-k, 1); --edge-w-c: var(--edge-w, var(--hair)); --ss-c: var(--ss, .35); --sb-c: var(--sb, .6); --glow-k-c: var(--glow-k, 1); }
:is(.item.parent, .frame) {
  --edge-ink: var(--edge-ink-p, var(--edge-ink-c)); --edge-k: var(--edge-k-p, var(--edge-k-c)); --edge-w: var(--edge-w-p, var(--edge-w-c));
  --ss: var(--ss-p, var(--ss-c)); --sb: var(--sb-p, var(--sb-c)); --glow-k: var(--glow-k-p, var(--glow-k-c));
}
.item.parent > .item.under { --edge-ink: var(--edge-ink-c); --edge-k: var(--edge-k-c); --edge-w: var(--edge-w-c); --ss: var(--ss-c); --sb: var(--sb-c); --glow-k: var(--glow-k-c); }
/* A bar's outline at rest: the theme's --bar-edge in the slider's strength, so the line can step finely while its width stays whole screen pixels (--edge-ink, js/settings.js) */
:is(.item, .frame) { --bar-line: color-mix(in srgb, var(--bar-edge) var(--edge-ink, 100%), transparent); }
/* One hovered outline and glow for every bar, collapsed or opened (the opened entry's lit box, .frame below) */
.item, .frame {
  --hover-edge: color-mix(in srgb, var(--c) 48%, color-mix(in srgb, var(--c) var(--bar-tint-hover), var(--surface)));
  --hover-glow: 0 0 14px 1px color-mix(in srgb, var(--c) 26%, transparent), 0 8px 22px -8px color-mix(in srgb, var(--c) 42%, transparent);
}
.item:is(:hover:not(.touch-input *), .hov) {
  z-index: 4; height: calc(30px * var(--bar-th, 1)); margin-top: calc(-2px + (26px - 26px * var(--bar-th, 1)) / 2);
  --bar-fill: color-mix(in srgb, var(--c) var(--bar-tint-hover), var(--surface));
  border-color: var(--hover-edge); box-shadow: var(--hover-glow);
}
.item.sel {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--c) 80%, transparent), 0 0 16px 2px color-mix(in srgb, var(--c) 28%, transparent);
  z-index: 3;
}
.item.sel:is(:hover:not(.touch-input *), .hov) {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--c) 80%, transparent), 0 0 18px 3px color-mix(in srgb, var(--c) 32%, transparent), 0 8px 22px -8px color-mix(in srgb, var(--c) 42%, transparent);
}
.lbl {
  display: flex; align-items: center; gap: 8px; height: 100%; padding: 0 8px; white-space: nowrap;
  /* a square clip: the label has no background and its padding keeps clear of the bar's round corners, and a
     rounded clip makes Safari repaint every bar slowly while zooming */
  pointer-events: none; overflow: hidden;
  /* Safari lets a part with a running animation or its own layer (the count, toggle, chips fading in) paint past an
     overflow clip; contain: paint holds every part inside the label's box in every browser and theme */
  contain: paint;
}
/* Portrait avatar: grows on hover. The whole picture, as drawn, belongs to its bar: hovering any part of it
   (even past the end of a short bar, or once enlarged) counts as hovering the bar */
.av {
  position: absolute; top: 50%; left: 3px; z-index: 1; width: 20px; height: 20px; margin-top: -10px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--c) 35%, var(--surface));
  box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.5px color-mix(in srgb, var(--c) 55%, transparent);
  transform-origin: 50% 50%; transition: transform .22s cubic-bezier(.2,.8,.3,1.25), box-shadow .22s;
  display: grid; place-items: center; font-size: 9px; font-weight: 700; color: var(--text);
  /* its own layer: Safari then doesn't scale the picture down again each time its bar is redrawn while zooming, or while
     its bar glides (without it, Expand all ran 4x more late frames on a phone-sized window). A pinned picture moves as
     this layer too (translate, js/app.js renderItems). Dropped while enlarged (below), so the grown picture is drawn sharp. */
  will-change: transform;
}
/* no disc behind a picture: its colour would show as a dark fringe round the picture's own rounded edge */
.av:has(img) { background: none; }
.av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; display: block; border-radius: inherit; }
/* Symbols (e.g. religions) are shown whole on a light disc rather than cropped like portraits */
.av.symbol { background: var(--pic-bg); }
.av.symbol img { object-fit: contain; padding: 2px; }
.item:is(:hover:not(.touch-input *), .hov) .av {
  transform: scale(3.2); will-change: auto;
  /* the dark surface gap stays once enlarged: it reads as a fine outline between the picture and its ring */
  --ring: var(--c); box-shadow: 0 0 0 .6px var(--surface), 0 0 0 1px var(--ring), 0 4px 10px rgba(0,0,0,.25);
}
/* a bar mostly off the screen keeps its picture: it rides out with the bar's end, under the edge (no .tiny rule) */
.item.has-av .lbl { transition: transform .22s cubic-bezier(.2,.8,.3,1.25); }
.item.has-av:is(:hover:not(.touch-input *), .hov) .lbl { transform: translateX(var(--shift, 26px)); }
/* Rectangular photos (e.g. ages): same grow-on-hover as portraits */
.av.photo { width: 30px; height: 20px; border-radius: 4px; }
.av.photo img { object-position: 50% 50%; }
/* Upright canvas (art periods) and arched window (architecture) */
.av.canvas { width: 17px; height: 22px; margin-top: -11px; border-radius: 2px; }
.av.arch { width: 18px; height: 22px; margin-top: -11px; border-radius: 9px 9px 2px 2px; }
.av.canvas img, .av.arch img { object-position: 50% 50%; }
/* Rounded-square tiles (e.g. musical periods) */
.av.tile { width: 22px; height: 22px; margin-top: -11px; border-radius: 6px; }
.av.tile img { object-position: 50% 50%; }
.item.has-photo .lbl { transition: transform .22s cubic-bezier(.2,.8,.3,1.25); }
.item.has-photo:is(:hover:not(.touch-input *), .hov) .lbl { transform: translateX(var(--shift, 38px)); }
.item.tiny .lbl { visibility: hidden; }
/* ---------- Overview ---------- */
/* Hover cursor line */
/* moved with a transform on its own layer (js/app.js drawCursor), so following the pointer repaints nothing */
.cursor { position: absolute; left: 0; top: 0; width: 0; pointer-events: none; z-index: 5; display: none; will-change: transform; }
.cursor.on { display: block; }
.cur-line { position: absolute; top: 0; bottom: 0; left: 0; border-left: 1px solid var(--accent); opacity: .55; }
.cur-pill {
  position: absolute; top: -1px; display: flex; flex-direction: column; align-items: center; gap: 1px;
  background: var(--solid, var(--accent)); color: var(--on-accent); white-space: nowrap; padding: 3px 11px 5px; border-radius: 0 0 8px 8px;
  box-shadow: var(--shadow-chip); font-size: 12px; font-weight: 600; line-height: 1.25; font-variant-numeric: tabular-nums;
}
.cur-pill .cur-ce { font-family: var(--font-date); }
.cur-pill .cur-he { font-family: var(--font-head); font-size: 11px; font-weight: 500; letter-spacing: .04em; opacity: .85; }
.cur-pill .cur-ce + .cur-he { padding-top: 2px; border-top: 1px solid color-mix(in srgb, var(--on-accent) 28%, transparent); }
/* Time map: a thin strip of all of time at the bottom; its box is the part on screen, click or drag to move it.
   --ov-l/--ov-r are the track's side insets (js/app.js places the box with them). */
.overview {
  --ov-l: 76px; --ov-r: 12px;
  position: relative; flex: none; height: 14px; margin: 0; border-top: 1px solid var(--line);
  background: var(--surface); cursor: var(--cur-pointer, pointer); touch-action: none;
}
.ov-lbl {
  position: absolute; left: 12px; top: 0; bottom: 0; display: flex; align-items: center; pointer-events: none;
  font-size: 9px; font-weight: 600; letter-spacing: var(--caps); text-transform: var(--label-case); color: var(--faint); white-space: nowrap;
}
.ov-track { position: absolute; top: 5px; bottom: 5px; left: var(--ov-l); right: var(--ov-r); border-radius: 2px; overflow: hidden; background: var(--surface-2); }
.ov-seg { position: absolute; top: 0; bottom: 0; opacity: .55; }
.ov-win {
  position: absolute; top: 2px; bottom: 2px; min-width: 6px; border: 1px solid var(--accent);
  border-radius: 3px; background: color-mix(in srgb, var(--accent) 14%, transparent);
  pointer-events: none;
}
@media (pointer: coarse) { .overview { height: 22px; } .ov-track { top: 9px; bottom: 9px; } .ov-win { top: 4px; bottom: 4px; } }
@media (max-width: 600px) { .overview { --ov-l: 12px; } .ov-lbl { display: none; } }
/* ---------- Detail card ---------- */
.detail {
  position: fixed; right: 20px; top: 76px; width: 320px; z-index: 20; max-height: calc(100vh - 96px);
  overflow-y: auto; background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: var(--shadow);   /* fades in and dissolves: js/card.js */
}
/* the card being paged away from, a still copy beside the next one (js/cardswipe.js); a touch on it while it glides
   catches it, but nothing inside it can be pressed */
.detail.ghost { transition: none; }
.detail.ghost * { pointer-events: none; }
.detail.ghost .d-scroll { overflow: hidden; }
/* Header: the category (its group, then its name) at the top left, a quiet close button at the right; it drags the card */
.d-head {
  flex: none; display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 8px 0 16px; cursor: var(--cur-grab, grab);
  border-bottom: 1px solid var(--line);
}
.d-cat {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 7px; white-space: nowrap;
  font-size: var(--t-label); font-weight: 600; letter-spacing: var(--caps); text-transform: var(--label-case); color: var(--muted);
}
.d-cat-ic { flex: none; display: grid; color: color-mix(in srgb, var(--c) 80%, var(--text)); }
.d-group { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--faint); }
.d-group:empty { display: none; }
.d-group::after { content: " /"; }
.d-catname { flex: none; color: color-mix(in srgb, var(--c) 55%, var(--text)); }
.detail .cl, .d-tip-x {   /* the paging tip's × too */
  flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 6px; cursor: var(--cur-pointer, pointer);
  display: grid; place-items: center; color: var(--faint); background: none; opacity: .75;
  transition: color .15s, background-color .15s, opacity .15s;
}
:is(.detail .cl, .d-tip-x):is(:hover, :focus-visible) { color: var(--text); background: var(--surface-2); opacity: 1; }
/* The card's picture (any shape, photos included) runs to the card's edges and fades into it at the bottom;
   the name sits on that faded end (upright card). Seals stay drawn, on a band tinted in the category colour. */
.detail {
  --hero-h: 230px;
  --hero-fade: linear-gradient(#000 38%, rgba(0,0,0,.35) 72%, transparent);
}
.detail .portrait {
  position: relative; display: block; overflow: hidden; width: calc(100% + 36px); height: var(--hero-h);
  margin: -18px -18px 0; -webkit-mask-image: var(--hero-fade); mask-image: var(--hero-fade); container-type: size;
}
/* Every face at the same size: the picture is zoomed so the face (--fx, --fy, --fh: its centre and height as
   fractions, from tools/find-faces.mjs) is --face-size of the frame's height, centred, its eyes (--ey) at --eye-y from the top.
   Never smaller than the frame's height nor zoomed past 3×; without a face the whole picture shows. Where the
   picture is narrower than the card, a blurred copy of itself fills the sides in its own colors and its edges fade into it. */
.detail, .rel-hero {
  --face-size: .24; --eye-y: .4;   /* one face size and eye level for every card in every theme: the same as tools/portrait.swift (FACE, EYE) */
  --side-fade: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
}
/* A blurred copy of a picture behind it (the card's portrait, tall gallery photos): drawn at an eighth of its size
   with an eighth of the blur, then scaled up. It looks the same and is far cheaper to paint (in Safari a full-size
   blur held up every card opening). --bl-out: how far it reaches past the frame. */
:is(.detail, .rel-hero) .portrait::before, .g-slide.fit::before {
  content: ""; position: absolute; left: calc(-1 * var(--bl-out)); top: calc(-1 * var(--bl-out));
  width: calc((100% + 2 * var(--bl-out)) / 8); height: calc((100% + 2 * var(--bl-out)) / 8);
  transform: scale(8); transform-origin: 0 0; background: var(--bg) center / cover;
  filter: blur(calc(var(--bl) / 8)) saturate(var(--bl-sat));
}
:is(.detail, .rel-hero) .portrait::before { --bl-out: 30px; --bl: 26px; --bl-sat: 1.15; opacity: .8; }
:is(.detail, .rel-hero) .portrait img {
  --h: clamp(100cqh, 100cqh * var(--face-size) / var(--fh, 1), 300cqh); --w: calc(var(--h) * var(--ar, .75));
  position: absolute; display: block; height: var(--h); width: var(--w); max-width: none;
  left: clamp(min(0px, 100cqw - var(--w)), 50cqw - var(--fx, .5) * var(--w), max(0px, 100cqw - var(--w)));
  top: clamp(100cqh - var(--h), 100cqh * var(--eye-y) - var(--ey, var(--fy, .5)) * var(--h), 0px);
  -webkit-mask-image: var(--side-fade); mask-image: var(--side-fade);
}
/* A stored portrait (js/people.js placePortrait, tools/portraits.mjs): already framed on a 2:1 sheet with the eyes centred at
   --eye-y and its edges carried on, so it only fills the frame's height (or its width, on a frame wider than 2:1, keeping
   the eyes at the same height). No blurred copy, no side fades. */
:is(.detail, .rel-hero) .portrait.eyed::before { display: none; }
:root :is(.detail, .rel-hero) .portrait.eyed img {
  --h: max(100cqh, 50cqw); height: var(--h); width: calc(var(--h) * 2); left: calc(50cqw - var(--h)); top: calc((100cqh - var(--h)) * var(--eye-y));
  -webkit-mask-image: none; mask-image: none;
}
/* Pictures arrive softly: until a picture has loaded, its frame shows the category's tint (never the bare card, which
   flashed white while paging between cards), then the picture fades in over it (200ms ease, as label changes).
   One already loaded shows at once (js/card.js, js/gallery.js); the cards a swipe would page to load theirs ahead. */
.detail .portrait, .detail .g-slide, .rel-hero .portrait { background: color-mix(in srgb, var(--c, var(--muted)) 22%, var(--surface-2)); }
:is(.detail, .rel-hero) :is(.portrait, .g-slide) > img, :is(.detail, .rel-hero) .portrait::before { transition: opacity .1s ease; }
:is(.detail, .rel-hero) :is(.portrait, .g-slide):not(.ready) > img, :is(.detail, .rel-hero) .portrait:not(.ready)::before { opacity: 0; }
.detail .portrait.symbol { background: var(--pic-bg); }
.detail .portrait:is(.symbol, .seal)::before { display: none; }
.detail .portrait.symbol img { position: static; width: 100%; height: 100%; object-fit: contain; padding: 24px; -webkit-mask-image: none; mask-image: none; }
.detail .portrait.seal {
  width: auto; height: 170px; display: grid; justify-items: center; align-content: start; padding-top: 28px;
  -webkit-mask-image: none; mask-image: none;
  background: linear-gradient(color-mix(in srgb, var(--c) 20%, var(--surface)), var(--surface));
}
.detail .gallery.hero { margin: -18px -18px 0; }
.detail .gallery.hero .g-track { border-radius: 0; box-shadow: none; -webkit-mask-image: var(--hero-fade); mask-image: var(--hero-fade); }
.detail .gallery.hero .g-slide { aspect-ratio: auto; height: var(--hero-h); }
.detail .gallery.hero figcaption, .detail .gallery.hero .g-slide.fit::before { display: none; }
.detail .gallery.hero .g-slide.fit img { object-fit: cover; object-position: 50% 30%; }
.detail .gallery.hero .g-dots { position: absolute; left: 0; right: 0; top: 16px; margin: 0; }
.detail :is(.portrait, .gallery.hero) + h2 {
  position: relative; margin-top: -60px; margin-right: 0; text-shadow: 0 0 16px var(--surface), 0 0 3px var(--surface);
}
.detail .portrait.seal + h2 { margin-top: -36px; }
/* what it belongs to (a period, a civilization), under the name */
.d-sub { margin: -4px 0 0; font-size: var(--t-ui); color: var(--muted); }
/* The span ruler (js/cardspan.js): the timeline in small, the entry's bar on its axis. And the mini map's survey marks (js/minimap.js).
   A theme turns them on by showing them; elsewhere they stay hidden. Their look comes from the theme's own tokens. */
.d-span, .mm-sheet { display: none; }
.d-span { position: relative; flex: 0 0 100%; height: 42px; overflow: hidden; font: 400 9.5px/12px var(--font-date); }
.d-tick, .d-now { position: absolute; top: 0; bottom: 0; font-style: normal; }
.d-tick { border-left: var(--hair) solid var(--span-grid, var(--line)); }
.d-tick span { position: absolute; top: 3px; left: 4px; white-space: nowrap; color: var(--muted); }
.d-tick.end span { left: auto; right: 4px; }
.d-now { border-left: var(--hair) solid var(--accent); }
.d-spanbar { position: absolute; top: 19px; height: 16px; min-width: 3px; background: var(--solid, var(--accent)); }
.d-age {
  position: absolute; top: 2px; left: 2px; height: 12px; padding: 0 3px; display: inline-flex; align-items: center; gap: 2px; font-weight: 500; line-height: 12px; white-space: nowrap;
  color: var(--solid, var(--accent)); background: var(--on-accent);
}
.d-age .died { width: 13px; height: 7px; }
.d-spanbar.out-r .d-age { top: 0; left: calc(100% + 7px); height: 16px; color: var(--on-accent); background: var(--solid, var(--accent)); }
.d-spanbar.out-l .d-age { top: 0; left: auto; right: calc(100% + 7px); height: 16px; color: var(--on-accent); background: var(--solid, var(--accent)); }
.detail dd { margin: 0; font-weight: 500; }
/* the card keeps the label ladder: the name, then the entry's length, then its dates */
.d-yrs { font: var(--date-style) 500 var(--t-ui) var(--font-date); letter-spacing: 0; text-transform: none; color: color-mix(in srgb, var(--text) var(--ink-years), transparent); }
.detail dl > .d-when dd { font-size: var(--t-ui); font-weight: 400; color: color-mix(in srgb, var(--text) var(--ink-date), transparent); }
/* ---------- The card in motion (STYLE-GUIDE.md → Motion, "Card entry"; js/cardtilt.js). A theme picks:
   --card-tilt: how far it leans toward the pointer (degrees,
   unset = still); --card-sheen: the light that follows the
   pointer; --card-form: tall keeps every entry's card upright. The card has no flip: it simply appears.
   One tilt for every card-like thing (the info card, the Ties tab's web): --tilt-p is its eye distance, --tilt-k scales the
   theme's degrees for a piece of another size (negative: the side under the pointer comes forward), both set per element, never per theme. ---------- */
:is(.detail, .rel-disc) { --tx: 0; --ty: 0; }
/* the Ties web: a wide disc, and the side under the pointer comes forward (negative). --rel-lean (Settings → Controls →
   Experimental "Ties tilt", 0-2, 1 = today) scales it: at 1 it leans twice as far as the first disc (-1.6) */
.rel-disc { --tilt-p: 1600px; --tilt-k: calc(-3.2 * var(--rel-lean, 1)); }
:is(.detail, .rel-disc).tilting { transform: perspective(var(--tilt-p, 1600px)) rotateX(calc(var(--ty) * var(--card-tilt, 0) * var(--tilt-k, 1) * -1deg)) rotateY(calc(var(--tx) * var(--card-tilt, 0) * var(--tilt-k, 1) * 1deg)); transition: transform .14s linear; }
.detail.tilting:has(.d-map) { transform: none; }   /* the map is placed in the window's frame: a lean would carry it off */
@media (hover: hover) and (pointer: fine) and (min-width: 761px) {
  .detail:not(.tilting) { transition: transform .7s cubic-bezier(.3,1.35,.5,1), background-color .2s, border-color .2s, color .2s; }
  .rel-disc:not(.tilting) { transition: transform .7s cubic-bezier(.3,1.35,.5,1); }
}
:is(.detail, .rel-disc).tilting::before {
  content: ""; position: absolute; z-index: 7; inset: -50%; pointer-events: none; background: var(--card-sheen, none);
  transform: translate(calc(var(--tx) * 20%), calc(var(--ty) * 20%)); animation: card-sheen-in .35s ease-out;
}
@keyframes card-sheen-in { from { opacity: 0; } }
/* ---------- Mobile ---------- */
@media (max-width: 760px) {
  .app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .detail { left: 16px; right: 16px; top: auto; bottom: 16px; width: auto; }
}
.load-error { margin: 32px 24px; max-width: 520px; color: var(--muted); line-height: 1.6; }
.load-error code { color: var(--text); }
.load-error a { color: var(--accent); }
/* ---------- Views: Timeline | Data ---------- */
body[data-view="data"] .side, body[data-view="data"] .stage, body[data-view="data"] .tl-only:not(.data-too) { display: none; }
body[data-view="data"][data-dmode="edit"] .sheet { display: flex; }   /* Edit; Browse is css/datatab.css */
/* ---------- Data view (spreadsheet) ---------- */
.sheet {
  display: none; grid-column: 1 / -1; min-height: 0; flex-direction: column; background: var(--bg);
}
/* the chosen category's color (--c) runs through the whole view: tab, header badge, row hover, focus */
.sheet-tabs {
  display: flex; flex-wrap: wrap; gap: 4px; padding: 8px 20px; flex: none;
  border-bottom: 1px solid var(--line); background: var(--surface);
}
.sheet-tab {
  display: flex; align-items: center; gap: 6px; flex: none; height: 26px; padding: 0 9px 0 3px; border: 0;
  border-radius: 999px; background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring); cursor: var(--cur-pointer, pointer);
  font-size: var(--t-ui); font-weight: 500; color: var(--muted);
  transition: background-color .25s ease, color .25s ease, box-shadow .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
}
.sheet-tab:hover { color: var(--text); background: color-mix(in srgb, var(--c) 8%, var(--surface-2)); }
.sheet-tab:active { scale: .95; transition-duration: .25s, .25s, .25s, .08s; }
.sheet-tab .dot {
  display: grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; color: var(--c);
  background: color-mix(in srgb, var(--c) 14%, var(--surface));
}
.sheet-tab .ct { font-size: var(--t-tiny); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--faint); }
.sheet-tab[aria-selected="true"] {
  color: color-mix(in srgb, var(--c) 70%, var(--text)); font-weight: 600; background: var(--surface);
  box-shadow: 0 1px 3px rgba(0,0,0,.1), 0 0 0 1px color-mix(in srgb, var(--c) 35%, var(--cap-ring)), 0 0 14px -4px color-mix(in srgb, var(--c) 45%, transparent);
}
.sheet-tab[aria-selected="true"] .dot { color: var(--surface); background: var(--c); }
.sheet-tab[aria-selected="true"] .ct { color: var(--c); }
/* Category header and toolbar share one row (the toolbar wraps below on narrow screens) */
.sheet-top { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 24px; padding: 10px 20px; flex: none; }
.sheet-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
.sh-badge {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;
  color: color-mix(in srgb, var(--c) 80%, var(--text)); background: color-mix(in srgb, var(--c) 14%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 22%, transparent);
}
.sh-text { min-width: 0; }
.sh-text h2 {
  margin: 0 0 2px; font-size: var(--t-title); font-weight: 650; text-transform: var(--label-case);
  letter-spacing: var(--caps-wide); color: color-mix(in srgb, var(--c) 75%, var(--text));
}
.sh-text p {
  margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; font-size: var(--t-meta); color: var(--muted);
}
.sh-text p b { font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; }
.sh-text .sep { margin: 0 7px; color: var(--faint); }
/* the span the category covers, set like the dates on its bars */
.sh-span {
  margin-left: 12px; padding: 0 6px; line-height: 17px; font-variant-numeric: tabular-nums; --date-base: var(--text);
  border: 1px solid color-mix(in srgb, var(--c) 35%, transparent); border-radius: min(4px, var(--r-ui, 4px));   /* never rounder than the theme allows */
}


/* Toolbar: capsules like the header controls */
.sheet-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; flex: 1 1 320px; min-width: 0; }   /* 320px: too little room beside the title and it takes its own line, never squeezed into a column */
.btn {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 13px; border: 0; border-radius: 999px;
  cursor: var(--cur-pointer, pointer); background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring);
  font-size: var(--t-ui); font-weight: 500; color: var(--muted);
  transition: background-color .25s ease, color .25s ease, box-shadow .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
}
.btn:hover { background: color-mix(in srgb, var(--accent) 7%, var(--surface-2)); color: var(--accent); }
.btn:active { scale: .94; transition-duration: .25s, .25s, .25s, .08s; }
.btn.primary {
  padding-left: 11px; background: var(--solid, var(--accent)); color: var(--on-accent); font-weight: 600;
  box-shadow: 0 1px 2px rgba(0,0,0,.12), 0 4px 14px -4px color-mix(in srgb, var(--accent) 60%, transparent);
}
.btn.primary:hover { background: var(--solid, var(--accent)); color: var(--on-accent); filter: brightness(1.08); }
.search {
  display: inline-flex; align-items: center; gap: 7px; height: 30px; width: 200px; padding: 0 12px; border-radius: 999px;
  background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring); color: var(--faint); cursor: var(--cur-text, text);
  transition: box-shadow .2s ease, background-color .2s ease;
}
.search svg { flex: none; }
.search input {
  flex: 1; min-width: 0; height: 100%; border: 0; padding: 0; background: none; outline: 0;
  color: var(--text); font: inherit; font-size: var(--t-ui);
}
.search input::placeholder { color: var(--faint); }
.search:focus-within {
  background: var(--surface); color: var(--accent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 45%, var(--line)), 0 0 0 4px color-mix(in srgb, var(--accent) 12%, transparent);
}
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.save-status { margin-left: auto; font-size: var(--t-meta); color: var(--muted); }
.save-status.error { color: var(--danger); }
.save-status .link {
  border: 0; background: none; padding: 0; cursor: var(--cur-pointer, pointer); color: var(--accent); font: inherit;
  font-weight: 600;
}
/* Table */
.sheet-scroll { flex: 1; min-height: 0; overflow: auto; padding: 0 20px 28px; }
.table {
  width: 100%; min-width: 1080px; border-collapse: separate; border-spacing: 0;
  background: var(--surface); border-radius: 12px; font-size: var(--t-ui);
  box-shadow: 0 0 0 1px var(--line), var(--shadow);
}
.table th {
  position: sticky; top: 0; z-index: 1; text-align: left; background: var(--surface);
  color: var(--muted); font-size: var(--t-tiny); font-weight: 600; text-transform: var(--label-case);
  letter-spacing: var(--caps); padding: 8px 10px 7px; border-bottom: 1px solid var(--line);
}
.table th:first-child { border-top-left-radius: 12px; }
.table th:last-child { border-top-right-radius: 12px; }
.table td { padding: 0; border-bottom: 1px solid var(--line); vertical-align: middle; transition: background-color .15s; }
.table tr:last-child td { border-bottom: 0; }
.table tr:last-child td:first-child { border-bottom-left-radius: 12px; }
.table tr:last-child td:last-child { border-bottom-right-radius: 12px; }
/* hovering a row tints it in the category's color, with a stripe at its start */
.table tbody tr:hover td { background: color-mix(in srgb, var(--c) 5%, var(--surface)); }
.table tbody tr:hover td:first-child { box-shadow: inset 3px 0 0 var(--c); }
.table tbody tr:focus-within td { background: color-mix(in srgb, var(--c) 7%, var(--surface)); }
.c-show { width: 64px; text-align: center; }
.c-name { width: 24%; }
.c-year { width: 150px; }
.c-approx { width: 44px; text-align: center; }
.c-place { width: 200px; }
.c-image { width: 230px; }
.c-del { width: 40px; }
.table th.c-show, .table th.c-approx { text-align: center; }
.table input[type="text"], .table input[type="url"], .table input[type="number"] {
  width: 100%; border: 0; background: transparent; color: var(--text); font: inherit; padding: 6px 10px;
  border-radius: 6px;
}
.table input:focus, .table select:focus {
  outline: 2px solid color-mix(in srgb, var(--c) 70%, var(--accent)); outline-offset: -3px; background: var(--surface);
}
.name-cell { display: flex; align-items: center; gap: 4px; padding-left: 6px; }
.name-cell .gs-slot { width: 34px; }
.name-cell .gs-ic { width: 24px; height: 24px; opacity: .7; }
.name-cell .gs-pic { zoom: .86; }   /* a step smaller than in search, to keep rows short */
.c-name input { font-size: var(--t-name); font-weight: 550; padding-left: 6px !important; }
.c-note input { color: var(--muted) !important; }
.c-place input { color: var(--muted) !important; }
.c-image input { font-size: var(--t-meta); color: var(--faint) !important; }
.table input::placeholder { color: var(--faint); }
.table input[type="number"] { -moz-appearance: textfield; }
.table input[type="number"]::-webkit-inner-spin-button, .table input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0;
}
/* years read like the dates on bars: bold figures, a quiet era */
.year { display: flex; align-items: center; }
.year input[type="number"] {
  width: 78px; text-align: right; padding-right: 6px; font-weight: 400; font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--text) var(--ink-years), transparent) !important;
}
.year select {
  appearance: none; -webkit-appearance: none;   /* the theme's, never the browser's (theme check: drawn by the browser) */
  border: 0; background: transparent; color: var(--muted); font: inherit; font-size: var(--t-label); font-weight: 500;
  letter-spacing: var(--caps); padding: 6px 4px; border-radius: 6px; cursor: var(--cur-pointer, pointer);
}
.deep { display: block; padding: 6px 10px; color: var(--muted); font-size: var(--t-meta); font-style: italic; }
.img-cell { display: flex; align-items: center; }
.del {
  width: 28px; height: 28px; display: grid; place-items: center; margin: 0 auto; border: 0;
  border-radius: 999px; background: none; color: var(--muted); cursor: var(--cur-pointer, pointer); opacity: 0;
  transition: opacity .15s, background-color .2s, color .2s;
}
.table tr:hover .del, .del:focus-visible { opacity: 1; }
.del:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); }
/* rows fade in when switching category */
#sheetRows.enter tr { animation: sheet-in .3s cubic-bezier(.2, .9, .25, 1) both; }
@keyframes sheet-in { from { opacity: 0; transform: translateY(4px); } }
@media (prefers-reduced-motion: reduce) { #sheetRows.enter tr { animation: none; } }
/* On/off switch */
.switch { position: relative; display: inline-block; width: 30px; height: 18px; cursor: var(--cur-pointer, pointer); vertical-align: middle; }
.switch input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: var(--cur-pointer, pointer); }
.switch span {
  position: absolute; inset: 0; border-radius: 999px; background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring);
  transition: background-color .15s, box-shadow .15s;
}
.switch span::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--muted); transition: transform .25s cubic-bezier(.65, 0, .25, 1), background-color .15s;
}
/* "chosen": a soft tint of the theme's accent and a thin accent edge, never a solid block. Declared on the controls so a panel that shifts --accent carries through */
.settings-pop .seg, .switch, input[type="checkbox"] { --chosen-fill: color-mix(in srgb, var(--accent) 18%, transparent); --chosen-edge: color-mix(in srgb, var(--accent) 60%, transparent); }
.switch input:checked + span { background: var(--chosen-fill); box-shadow: inset 0 0 0 1px var(--chosen-edge); }
.switch input:checked + span::after { background: var(--accent); transform: translateX(12px); }
@media (hover: hover) { .switch:hover input:not(:checked) + span { box-shadow: inset 0 0 0 1px var(--accent), 0 0 8px -2px color-mix(in srgb, var(--accent) 50%, transparent); }
.switch:hover input:checked + span { box-shadow: inset 0 0 0 1px var(--accent), 0 0 8px -2px var(--accent); } }
.switch input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Checkbox (a yes/no inside a row, e.g. "approximate" in Data view): same accent, track color and focus ring as the
   switch. Settings use the switch itself. */
input[type="checkbox"]:not(.switch input) {
  appearance: none; flex: none; display: inline-grid; place-items: center; width: 16px; height: 16px; margin: 0;
  border-radius: min(5px, var(--r-ui, 5px)); background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring); cursor: var(--cur-pointer, pointer); vertical-align: middle; transition: background .15s;
}
input[type="checkbox"]:not(.switch input)::after {
  content: ""; width: 8px; height: 4.5px; margin-top: -2px; border: solid var(--accent); border-width: 0 0 1.75px 1.75px;
  transform: rotate(-45deg) scale(0); transition: transform .2s cubic-bezier(.65, 0, .25, 1);
}
input[type="checkbox"]:not(.switch input):checked { background: var(--chosen-fill); box-shadow: inset 0 0 0 1px var(--chosen-edge); }
input[type="checkbox"]:not(.switch input):checked::after { transform: rotate(-45deg) scale(1); }
input[type="checkbox"]:not(.switch input):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Hidden and invalid rows */
.table tr.off td:not(.c-show) { opacity: .45; }
.table td.bad {
  background: color-mix(in srgb, var(--danger) 9%, var(--surface)) !important;
  box-shadow: inset 0 -2px 0 var(--danger);
}
.table tr.empty td { padding: 32px; text-align: center; color: var(--muted); }
/* Ties tab (js/sheet-ties.js): its own columns and toolbar; each person's picture keeps their category's
   ring colour, and the tie is a capsule in its colour (green close, red conflict) as on the timeline's tags */
.sheet:not(.ties-mode) .ties-only, .sheet.ties-mode .cat-only { display: none; }
.sheet.ties-mode .table { min-width: 860px; }
.c-person { width: 26%; }
.c-role { width: 17%; }
.c-kind { width: 120px; }
.c-person input { font-size: var(--t-name); font-weight: 550; padding-left: 6px !important; }
.c-role input { color: var(--muted) !important; }
.tie-kind {
  --tc: var(--tie-close); --tm: var(--tie-close-mark);
  display: inline-flex; align-items: center; margin-left: 6px; border-radius: 999px;
  background: color-mix(in srgb, var(--tc) 12%, var(--surface)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tc) 30%, transparent);
}
.tie-kind.conflict { --tc: var(--tie-conflict); --tm: var(--tie-conflict-mark); }
.tie-kind select {
  appearance: none; border: 0; background: transparent; color: var(--tm); font: inherit; font-size: var(--t-meta); font-weight: 600;
  padding: 3px 8px; border-radius: 999px; cursor: var(--cur-pointer, pointer);
}
@media (max-width: 760px) {
  .sheet-tabs, .sheet-top { padding-left: 16px; padding-right: 16px; }
  .sheet-tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .sheet-scroll { padding: 0 16px 20px; }
  .save-status { margin-left: 0; width: 100%; }
  .search { width: 100%; }
}
/* ---------- Detail card photo carousel ---------- */
.gallery { position: relative; margin: 14px 0 0; }
.g-track::-webkit-scrollbar { display: none; }
.g-slide { position: relative; flex: 0 0 100%; margin: 0; aspect-ratio: 16 / 10; scroll-snap-align: start; }
.g-slide figcaption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 22px 10px 8px; font-size: 11.5px;
  line-height: 1.35; color: #fff; background: linear-gradient(transparent, rgba(0,0,0,.65));
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.gallery.loading .g-track { aspect-ratio: 16 / 10; animation: g-pulse 1.2s ease-in-out infinite alternate; }
@keyframes g-pulse { to { opacity: .55; } }
.g-nav {
  position: absolute; top: 50%; margin-top: -15px; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; border: 0; cursor: var(--cur-pointer, pointer); color: var(--text);
  background: color-mix(in srgb, var(--surface) 85%, transparent); backdrop-filter: blur(6px);
  box-shadow: 0 1px 4px rgba(0,0,0,.2); opacity: 0; transition: opacity .15s;
}
.g-prev { left: 8px; }
.g-next { right: 8px; }
.gallery:hover .g-nav:not(:disabled) { opacity: 1; }
.g-nav:disabled { pointer-events: none; }
.gallery.single .g-nav, .gallery.single .g-dots, .gallery.loading .g-nav { display: none; }
.g-dots { display: flex; justify-content: center; gap: 5px; margin-top: 8px; }
.g-dots i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--line-strong);
  transition: background .15s, width .15s;
}
.g-dots i.on { width: 16px; border-radius: 3px; background: var(--c); }
/* ---------- Reordering categories in the sidebar ---------- */
/* Every row keeps a gutter on its left (--gut) for its drag handle; the icon rail closes it */
.cats { position: relative; --gut: 12px; }
.cats li { position: relative; touch-action: none; padding-left: var(--gut); }
.cats .grip {
  position: absolute; left: 0; top: 50%; width: 11px; height: 16.5px; margin-top: -8.25px;
  background: radial-gradient(circle, var(--muted) 1.5px, transparent 1.95px) 0 0 / 5.5px 5.5px; opacity: 0;
  transition: opacity .12s; pointer-events: none;
}
.cats .cat-group .grip { top: auto; bottom: 4px; margin: 0; opacity: .45; }
.cats li:hover .grip { opacity: .85; }
.cats.reordering, .cats.reordering .cat { cursor: var(--cur-grabbing, grabbing); -webkit-user-select: none; user-select: none; }
.cats.reordering { align-content: start; }   /* the list holds its height while a block is folded (js/app.js lift): the rows don't stretch into it */
.cats li.dragging { z-index: 3; will-change: transform; }
.cats li.settling { z-index: 1; }
.cats .cat { transition: background .12s, opacity .12s, box-shadow .2s, transform .2s; }
/* A drag's look, one set of tokens for every theme (STYLE-GUIDE.md -> Motion, Reordering). Behaviour and motion stay here; a theme sets only these:
   the lifted row (--drag-*), the cards stacked under it (--stack-*), the slot where it will land (--slot-*), the group it would join (--home-ink).
   Lifted things follow the accent like a hover (STYLE-GUIDE.md -> Hover), never a neutral shadow. */
.cats {
  --drag-fill: var(--surface); --drag-ink: var(--text); --drag-edge: 0 0 0 var(--hair) var(--accent); --drag-glow: var(--glow, var(--shadow)); --drag-scale: 1.025; --drag-r: 8px;
  --stack-fill: var(--surface); --stack-edge: inset 0 0 0 var(--hair) var(--line-strong);
  --slot-fill: color-mix(in srgb, var(--accent) 10%, transparent); --slot-edge: inset 0 0 0 var(--hair) color-mix(in srgb, var(--accent) 50%, transparent);
  --slot-r: 8px; --slot-bleed: 0px; --slot-end: 22px; --slot-pic: none;   /* --slot-end: the eye button's column, which the row stops short of */
  --home-ink: var(--text);
}
.cats li.dragging:not(.stacked) .cat { color: var(--drag-ink); background: var(--drag-fill); box-shadow: var(--drag-edge), var(--drag-glow); transform: scale(var(--drag-scale)); border-radius: var(--drag-r); }
.cats li.dragging .grip { opacity: 1; }
/* The slot: one mark behind the rows, showing where the block lands. It glides with the rows making way (js/app.js flip, mark) */
.cats.reordering::before {
  content: ""; position: absolute; left: calc(var(--gut) - var(--slot-bleed)); right: calc(var(--slot-end) - var(--slot-bleed)); top: 0; height: var(--slot-h); pointer-events: none;
  transform: translateY(var(--slot-y)); background: var(--slot-pic), var(--slot-fill); box-shadow: var(--slot-edge); border-radius: var(--slot-r);
  transition: transform .3s cubic-bezier(.2,.8,.2,1), height .3s cubic-bezier(.2,.8,.2,1); animation: slot-in .16s ease-out;
}
@keyframes slot-in { from { opacity: 0; } }
.cats li.cat-group.drop-home { color: var(--home-ink); }
/* A dragged block folds into a stack: the rows under its head (a group's categories, a category's nested people)
   glide in behind it and become blank cards peeking out beneath, then move with it as one (js/app.js) */
#cats li.dragging.stacked {   /* by id: wins over the rail's indent and padding transition */
  height: 0; padding-left: var(--gut); align-items: flex-start; transform-origin: 50% 0; pointer-events: none;
  transition: opacity .3s;
}
.cats li.stacked > :not(.cat), .cats li.stacked .cat > * { filter: opacity(0); transition: filter .16s; }
.cats li.stacked::before { visibility: hidden; }
.cats li.stacked .cat { background: var(--stack-fill); box-shadow: var(--stack-edge); transform: none; border-radius: var(--drag-r); }
/* a dragged group heading lifts onto a card of its own, which fades away once dropped */
.cats li.cat-group:is(.dragging, .settling) { color: var(--drag-ink); }
.cats li.cat-group:is(.dragging, .settling)::before {
  content: ""; position: absolute; inset: 8px 24px -4px 0; z-index: -1; border-radius: var(--drag-r);
  background: var(--drag-fill); box-shadow: var(--drag-edge), var(--drag-glow); animation: lift .22s cubic-bezier(.2,.8,.2,1);
}
.cats li.cat-group:first-child:is(.dragging, .settling)::before { top: -4px; }
.cats li.cat-group.settling::before { animation: unlift .45s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes lift { from { opacity: 0; transform: scale(.96); } }
@keyframes unlift { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  #cats li.dragging.stacked { transition: none; }
  .cats.reordering::before { transition: none; animation: none; }
}
@media (max-width: 760px) {
  .cats { --gut: 0px; }
  .cats .grip { display: none; }
}
/* ---------- Sub-periods (level 2) and phases (level 3): slimmer bars beneath their parent ---------- */
.item.lvl2:is(:hover:not(.touch-input *), .hov) { height: calc(24px * var(--bar-th, 1)); margin-top: calc(1px + (22px - 22px * var(--bar-th, 1)) / 2); }
.item.lvl3 {
  height: calc(18px * var(--bar-th, 1)); margin-top: calc(4px + (18px - 18px * var(--bar-th, 1)) / 2); --bar-fill: color-mix(in srgb, var(--c) calc(var(--bar-tint) * .44), var(--surface));
  border-style: dashed;
}
.item.lvl3:is(:hover:not(.touch-input *), .hov) { height: 20px; margin-top: 3px; }
/* ---------- Global eras as faint bands behind all rows ---------- */
.no-era-bands .era-band { display: none; }
.era-band { position: absolute; left: 0; top: 0; bottom: 0; background: var(--text); opacity: .028; }
/* ---------- Sidebar group headings ---------- */
.cats .cat-group { padding: 14px 8px 4px calc(var(--gut) + 8px); text-transform: var(--label-case); color: var(--group-ink); }
.cats .cat-group:first-child { padding-top: 2px; }
/* ---------- People nested inside their periods ---------- */
.nest-btn, .nest-pill button {
  display: inline-flex; align-items: center; gap: 5px; border: 0; cursor: var(--cur-pointer, pointer); font: inherit;
}
.nest-btn {
  margin-left: 4px; padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 500;
  color: var(--muted); background: transparent; box-shadow: inset 0 0 0 1px var(--line-strong);
  opacity: 0; transition: opacity .15s, color .15s;
}
.lane-h:hover .nest-btn, .nest-btn:focus-visible { opacity: 1; }
.nest-btn:hover { color: var(--text); }
.nest-pill {
  display: inline-flex; align-items: center; margin-left: auto; margin-right: 6px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--gc) 14%, transparent); font-size: 11.5px;   /* at the far right of the header */
}
.nest-pill button { padding: 3px 8px; background: transparent; color: var(--text); font-weight: 550; }
.nest-pill button + button { padding: 3px 9px 3px 4px; color: var(--muted); font-size: 14px; line-height: 1; }
.nest-pill button:hover { background: color-mix(in srgb, var(--gc) 12%, transparent); }
/* Nested people sit above the periods by order (js/app.js buildLane puts them last), not by z-index: a z-index
   would seal each picture inside its own bar, so the next person's bar would cover it */
.item.guest {
  transition: background-color .15s, box-shadow .18s, top .3s cubic-bezier(.2,.8,.2,1), height .18s, margin-top .18s, opacity .15s;
}
/* Collapsed: count badge on each period */
/* ---------- Group bars (open with a click; the number of entries inside at their end) and framed blocks ---------- */
/* ---------- Global search ---------- */
/* Search: a capsule like the region bar, centred in the header (.top-l); the region bar grows into the free space
   on its left */
.gsearch {
  position: relative; flex: 0 1 380px; min-width: 190px; display: flex;
  align-items: center; gap: 8px; height: var(--hdr-c); padding: 0 12px 0 12px; border-radius: 999px;
  /* the main way in: accent-tinted, with an accent ring and icon, so it stands out from the grey header capsules */
  background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  color: var(--accent); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 45%, var(--line));
  transition: box-shadow .25s ease, background-color .25s ease, color .25s ease;
}
.gsearch:hover {
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 70%, var(--line));
}
/* bridges the gap above the open results, so the mouse can cross it without leaving the search (which closes it) */
.gsearch[aria-expanded="true"]::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 8px; }
.gsearch-ico { flex: none; }
.gsearch:focus-within {
  background: var(--surface); color: var(--accent);
  box-shadow: inset 0 0 0 1.5px var(--accent), 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
}
.gsearch input {
  flex: 1; align-self: stretch; min-width: 0;   /* the capsule's full height takes the tap */ text-overflow: ellipsis; border: 0; outline: 0; background: transparent; color: var(--text);
  font: inherit; font-size: var(--t-ui);
}
/* the prompt reads like a header button's label: muted caps, the hover ink on hover */
.gsearch input::placeholder { color: var(--muted); transition: color .25s ease; }
.gsearch:hover input::placeholder { color: var(--hover-ink); }
.gsearch input::-webkit-search-cancel-button { display: none; }
.gsearch kbd {
  font: inherit; font-size: 11px; padding: 0 6px; line-height: 18px; border-radius: 5px;
  color: var(--accent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, var(--line-strong));
}
.gsearch:focus-within kbd { display: none; }
/* The active filter ("only artists", "only Europe and China"): a capsule before the input, tinted in its first item's colour like the
   region map's picks (.rmap-chip); a click clears it */
.gs-only {
  flex: 0 1 auto; min-width: 0; max-width: 55%; display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 8px;
  border: 0; border-radius: 999px; background: color-mix(in srgb, var(--c) 14%, transparent); color: var(--c);
  cursor: var(--cur-pointer, pointer); font: inherit; font-size: var(--t-label); font-weight: 600; letter-spacing: var(--caps);
  text-transform: var(--label-case); white-space: nowrap; transition: background-color .2s ease;
}
.gs-only[hidden] { display: none; }
.gs-only:not([hidden]) + .gs-lens:not([hidden]) { max-width: 30%; }   /* the topic lens's capsule (same look, the accent's tint) beside a filter */
.gs-only:not([hidden]) ~ input { flex: 1 1 48px; }   /* narrow headers: the capsule gives way, so there is room to type */
/* the tightest header (tablet portrait, .hdr-folded-more, js/headerfold.js) has no room for both either: as on phones below */
.hdr-folded-more .gsearch:focus-within .gs-only { display: none; }
.hdr-folded-more .gsearch:not(:focus-within):has(.gs-only:not([hidden])) :is(.gsearch-ico, kbd) { display: none; }
.hdr-folded-more .gsearch:not(:focus-within) .gs-only:not([hidden]) { max-width: 100%; }
.hdr-folded-more .gsearch:not(:focus-within) .gs-only:not([hidden]) ~ input { position: absolute; width: 1px; padding: 0; opacity: 0; pointer-events: none; }
/* no room for both (phones, phone landscape): the capsule steps aside while typing; the list's rows name the filter */
@media (max-width: 760px), (max-height: 500px) {
  .gsearch:focus-within .gs-only { display: none; }
  .gs-only { max-width: 100%; }
  /* while it shows, the capsule is the filter: the magnifier and the empty field step out of the row (the field stays
     focusable, a tap on the capsule puts the cursor in it), so the × never lands on the field */
  .gsearch:not(:focus-within):has(.gs-only:not([hidden])) .gsearch-ico { display: none; }
  .gsearch:not(:focus-within) .gs-only:not([hidden]) ~ input { position: absolute; width: 1px; padding: 0; opacity: 0; pointer-events: none; }
}
/* the narrowest phones (320): only the filter's mark and its × fit; its name is in the capsule's title and the list */
@media (max-width: 360px) {
  .gsearch:not(:focus-within):has(.gs-only:not([hidden])) { padding-inline: 6px; }
  .gs-only { padding: 0 6px; }
  .gs-only span { display: none; }
}
.gs-only:hover { background: color-mix(in srgb, var(--c) 24%, transparent); color: color-mix(in srgb, var(--c) 70%, var(--text)); }   /* the deeper wash takes some contrast: the words lean toward the text colour to keep it */
.gs-only svg { flex: none; }
.gs-only span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }   /* shrinks first, so the × never spills out of the capsule */
.gs-only .gs-x { flex: none; }
.gs-only .gs-x { font-style: normal; font-size: 13px; opacity: .7; padding: 0 3px; transition: background-color .15s ease, opacity .15s ease; }
.gs-only .gs-x:hover { opacity: 1; color: var(--text); background: color-mix(in srgb, var(--c) 18%, transparent); }
.gsearch-list {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40; min-width: 320px;
  max-height: min(420px, 70vh); overflow-y: auto; padding: 6px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 20px;
  box-shadow: var(--shadow-pop);
  transform-origin: 50% 0;
}
/* Opening: unrolls from the search with a slight spring; results cascade in */
.gsearch-list.fresh { animation: gs-drop .42s cubic-bezier(.2, 1.25, .35, 1); }
@keyframes gs-drop {
  from { opacity: 0; transform: translateY(-10px) scale(.96); clip-path: inset(0 0 100% 0 round 20px); }
  60% { clip-path: inset(0 0 0 0 round 20px); }
  to { opacity: 1; transform: none; clip-path: inset(0 0 0 0 round 20px); }
}
.gsearch-list .gs-row { animation: gs-refresh .16s ease-out both; }
.gsearch-list.fresh .gs-row {
  animation: gs-in .38s cubic-bezier(.2, .9, .25, 1) both; animation-delay: calc(60ms + var(--i) * 32ms);
}
@keyframes gs-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes gs-refresh { from { opacity: .4; } }
/* the moving highlight */
.gs-hl {
  position: absolute; left: 6px; right: 6px; top: 6px; height: 46px; border-radius: 14px;
  pointer-events: none; background: color-mix(in srgb, var(--c, var(--accent)) 13%, var(--surface-2));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c, var(--accent)) 18%, transparent);
  transition: transform .32s cubic-bezier(.3, 1.3, .4, 1), height .25s ease, background-color .25s ease;
}
.gs-hl.instant { transition: none; }
.gs-row[aria-selected="true"] .gs-ic { animation: gs-pop .34s cubic-bezier(.2, 1.4, .4, 1); }
@keyframes gs-pop { 40% { scale: 1.14; } }
.gs-row {
  position: relative; width: 100%; display: flex; align-items: center; gap: 10px; padding: 7px 8px;
  border: 0; border-radius: 8px; background: transparent; cursor: var(--cur-pointer, pointer); text-align: left;
  color: var(--text);
}
.gs-row[aria-selected="true"] .gs-name { color: var(--text); }
.gs-ic {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px;
  color: var(--c); background: color-mix(in srgb, var(--c) 14%, transparent);
}
.gs-text { min-width: 0; display: flex; flex-direction: column; }
.gs-name { font-size: 13.5px; font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gs-name mark { background: transparent; color: var(--accent); font-weight: 700; }
.gs-meta { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gs-empty { padding: 14px 10px; font-size: 13px; color: var(--muted); }
/* Regions and countries sit above the entries as the larger thing they belong to: a row tinted in the region's
   colour, a bigger name, the kind in the region's colour, "Show only" at the right, and a line before the entries */
.gs-row.place { background: color-mix(in srgb, var(--c) 7%, transparent); border-radius: 14px; margin-bottom: 2px; }
.gs-row.place .gs-ic { width: 32px; height: 32px; border-radius: 50%; background: color-mix(in srgb, var(--c) 20%, transparent); }
.gs-row.place .gs-name { font-size: 14.5px; font-weight: 650; white-space: normal; }   /* a filter reads whole: long ones wrap */
.gs-row.place .gs-meta {
  color: color-mix(in srgb, var(--c) 75%, var(--text)); font-size: var(--t-tiny); font-weight: 600;
  letter-spacing: var(--caps); text-transform: var(--label-case);
}
.gs-go {
  margin-left: auto; flex: none; padding: 3px 9px; border-radius: 999px; font-size: var(--t-tiny); font-weight: 600;
  color: color-mix(in srgb, var(--c) 80%, var(--text)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 35%, transparent);
  opacity: .55; transition: opacity .15s ease;
}
.gs-row[aria-selected="true"] :is(.gs-go, .gs-add) { opacity: 1; }
/* Filter rows: a round "+" before "Show only" adds the category and keeps the search open for the next one */
.gs-add {
  margin-left: auto; flex: none; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%;
  font-size: 15px; font-weight: 600; line-height: 1; color: color-mix(in srgb, var(--c) 80%, var(--text));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 35%, transparent); opacity: .55;
  transition: opacity .15s ease, background-color .15s ease;
}
.gs-add:hover { background: color-mix(in srgb, var(--c) 18%, transparent); }
.gs-add + .gs-go { margin-left: 6px; }
.gs-row.place + .gs-row:not(.place) { margin-top: 13px; }
.gs-row.place + .gs-row:not(.place)::before {
  content: ""; position: absolute; left: 8px; right: 8px; top: -8px; border-top: 1px solid var(--line);
}
/* Result groups (js/search.js): a small heading in the label face before each group, "Clear" on the recent ones */
.gs-group {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 26px; padding: 10px 10px 2px;
  font-family: var(--font-label); font-size: var(--t-tiny); font-weight: 600; letter-spacing: var(--caps); text-transform: var(--label-case); color: var(--muted);
}
.gs-hl + .gs-group { padding-top: 2px; }
.gsearch-list.fresh .gs-group { animation: gs-in .38s cubic-bezier(.2, .9, .25, 1) both; animation-delay: calc(60ms + var(--i) * 32ms); }
.gs-clear {
  border: 0; padding: 2px 8px; background: transparent; font: inherit; color: var(--accent); cursor: var(--cur-pointer, pointer);
  transition: background-color .15s ease, color .15s ease;
}
.gs-clear:hover { color: var(--hover-ink); background: color-mix(in srgb, var(--accent) 9%, transparent); }
.gs-row.gs-more .gs-name { font-size: var(--t-ui); font-weight: 600; color: var(--accent); }
.gs-row.gs-more { min-height: 38px; }   /* one line, still a full touch target */
.gs-row.gs-q .gs-ic { background: transparent; color: var(--muted); }
.gs-role { font-weight: 600; color: color-mix(in srgb, var(--c) 75%, var(--text)); }
.gs-hl.off { opacity: 0; }
@media (pointer: coarse) { .gs-clear { min-height: 36px; padding-inline: 12px; } }
/* A bar reached from search or a swiped card glows once it has landed (js/app.js waits for the zoom): its colour
   blooms quickly, then lets go slowly into its own look (selected or hovered). No 0% / 100% keyframes, so it starts
   from and settles into whatever the bar shows, never snapping at the end. */
@keyframes found {
  22% {
    background-color: color-mix(in srgb, var(--c) var(--bar-tint-hover), var(--surface));
    box-shadow: 0 0 0 2.5px color-mix(in srgb, var(--c) 55%, transparent), 0 0 22px 4px color-mix(in srgb, var(--c) 45%, transparent);
    animation-timing-function: cubic-bezier(.4, 0, .2, 1);
  }
}
.item.flash { animation: found 1.4s cubic-bezier(.2, .7, .3, 1); z-index: 5; }
.item.open-parent.flash { animation: none; }   /* an opened entry: its whole box lights up instead (.frame.flash) */
/* While an entry is hovered, its row floats above the category header (sticky, z-index 3) and the
   neighbouring rows, so the enlarged picture is never cut off. Each row body is its own stacking
   context (it is moved with a transform), so the row itself has to be raised, not just the bar. */
/* .lifted is set by js/app.js (liftRow) on the hovered entry's bracket, and on its row body when its picture reaches up
   to the sticky title bar: a :has(:hover) rule here would make the browser re-check every row on each pointer move. */
.lane-b.lifted { z-index: 5; }
/* Each row is a stacking context too (content-visibility, translate), so a later row would paint over
   a picture reaching down from the row above: a row whose bottom line is hovered is raised above its neighbours. */
.lane.lifted { z-index: 1; }
/* ---------- Hierarchy: entries that contain sub-periods or people carry more visual weight ---------- */
.item.parent:is(:hover:not(.touch-input *), .hov) .av { transform: scale(3.2); }
/* ---------- One box for every expanded entry ----------
   Civilizations, regional periods and musical periods (with their musicians) all share it: an opened entry is its
   closed bar with the bottom let down to hold what is inside. The bar keeps its place, look and name; the box (.frame,
   behind it) starts at the bar's top with the bar's fill, edge and corners and reaches down past its last sub bar.
   The heading (.item.open-parent) draws no fill or edge of its own, so bar and box read as one shape. --inset: how far
   right of the bar's start the box begins (a picture's centre, so no box shows left of it). */
.frame {
  /* hoverable: the pointer anywhere in the box lights it (js/app.js, hoverAt); a click there closes the entry */
  position: absolute; z-index: 0; border-radius: var(--box-r, 7px);
  /* the closed bar's own fill and edge (.item above); each theme sets them for .frame as it does for its period bars */
  --bar-fill: color-mix(in srgb, #000 var(--bar-dark, 0%), color-mix(in srgb, var(--c) var(--bar-tint), var(--surface)));
  --bar-edge: color-mix(in srgb, var(--c) min(100%, calc(30% * var(--k) * var(--k))), var(--bar-fill));
  background: var(--bar-fill);
}
.frame::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  border: var(--edge-w, var(--hair)) solid var(--bar-line);
  transition: border-color .15s, inset .18s cubic-bezier(.2,.8,.3,1.2), border-radius .18s cubic-bezier(.2,.8,.3,1.2);
}
/* Lit (the pointer anywhere in it, or reached from search): like a hovered bar, the box swells 2px all round (the outline moves out,
   the fill follows by a ring of its own colour) and glows softly in its colour. The glow sits on ::after and only
   fades (opacity), so lighting even a screen-wide box costs no repaint. */
/* It sits where the lit outline does (2px out), so the glow starts right outside the line, as on a bar: starting
   inside it, its densest band hugged the line and made it look thicker. The 2px swell of fill is its inset ring. */
.frame::after {
  content: ""; position: absolute; inset: -2px; border-radius: calc(var(--box-r, 7px) + 2px); opacity: 0;
  box-shadow: inset 0 0 0 2px var(--bar-fill), var(--hover-glow);
  transition: opacity .18s;
}
.frame:is(.lifted, .lit, .flash) { --bar-fill: color-mix(in srgb, var(--c) var(--bar-tint-hover), var(--surface)); }
.frame:is(.lifted, .lit, .flash)::before {
  inset: -2px; border-radius: calc(var(--box-r, 7px) + 2px);
  border: .75px solid var(--hover-edge);   /* a hovered bar's edge and glow, exactly */
}
.frame:is(.lifted, .lit)::after { opacity: 1; }
/* Reached from search: lit, its glow blooming once and letting go like a bar's (found, above, same timing); the fill
   then fades back too */
@keyframes found-box { 22% { opacity: 1; animation-timing-function: cubic-bezier(.4, 0, .2, 1); } }
.frame.flash::after { animation: found-box 1.4s cubic-bezier(.2, .7, .3, 1); }
.frame { transition: background-color .3s ease; }
@media (prefers-reduced-motion: reduce) { :is(.item.flash, .frame.flash::after) { animation: none; } }
/* the heading: the closed bar's picture and name, its fill and edge left to the box behind it (one shape) */
.item.open-parent, .item.open-parent:is(:hover:not(.touch-input *), .hov) {
  background: transparent; border-color: transparent; box-shadow: none; height: 26px; margin-top: 0;
}
/* ---------- Category tools: expand all / collapse all ----------
   One button that reads "Collapse all" while any entry is open, otherwise "Expand all". At the right of the header,
   just left of the people pill, in the pill's colour scheme (a tint of the category colour, darker on hover); its
   text and icon in the colour of the category title. The icon pops on hover and it presses in on a click. */
.lane-tools {
  display: inline-flex; align-items: center; gap: 5px; margin-left: auto; margin-right: 6px; padding: 3px 9px 3px 8px;
  border: 0; border-radius: 999px; font: inherit; font-size: 11.5px; font-weight: 550; cursor: var(--cur-pointer, pointer);
  color: color-mix(in srgb, var(--c) 65%, var(--text)); background: color-mix(in srgb, var(--c) 14%, transparent);
  transition: background-color .25s ease, --wash .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
}
.lane-tools ~ .nest-pill { margin-left: 0; }
.lane-tools, .nest-pill, .nest-btn { white-space: nowrap; flex: none; }
/* phones: the title bar's buttons and people pill fold to their icons (and count), named by their tooltips */
@media (max-width: 760px) { .lane-h .fold-tx { display: none; } .lane-tools { padding-inline: 7px; } }
.lane-tools:hover { background: color-mix(in srgb, var(--c) 26%, transparent); }
.lane-tools:active { scale: .92; transition-duration: .25s, .08s; }
.lane-tools:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nest-pill-name {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 4px 3px 8px; font-weight: 550;
  color: color-mix(in srgb, var(--gc) 70%, var(--text));
}
.nest-pill-name b { font-weight: 650; }
/* Group headings can be dragged to reorder whole groups */
.cats .cat-group { position: relative; cursor: var(--cur-grab, grab); -webkit-user-select: none; user-select: none; }
/* People categories sit indented under their theme in the sidebar, joined by a light elbow */
.cats li.sub { padding-left: calc(var(--gut) + 18px); }
.cats li.sub::before {
  content: ""; position: absolute; left: calc(var(--gut) + 14px); top: -4px; width: 9px; height: 19px; pointer-events: none;
  border-left: 1px solid var(--line-strong); border-bottom: 1px solid var(--line-strong);
  border-bottom-left-radius: 5px;
}
/* ---------- Sidebar rows: click to jump, eye to show/hide ---------- */
.cats li[data-cat] { display: flex; align-items: center; min-width: 0; }
/* never wider than the sidebar */
.cats li[data-cat] .cat { flex: 1; min-width: 0; width: auto; padding-right: 4px; }
.cats .eye {
  flex: none; display: grid; place-items: center; width: 22px; height: 26px; border: 0;
  border-radius: 7px; background: transparent; color: var(--muted); cursor: var(--cur-pointer, pointer); opacity: 0;
  transition: opacity .12s, background .12s, color .12s;
}
.cats li[data-cat]:hover .eye, .cats .eye:focus-visible, .cats li.off .eye { opacity: 1; }
.cats .eye:hover { background: var(--surface-2); color: var(--text); }
.cats li.off .eye { color: var(--faint); }
@media (max-width: 760px) {
  .cats .eye { opacity: 1; width: 22px; height: 22px; }
}
/* no hover on phones */
/* A row reached from the sidebar briefly lights its header: the icon, name, count and the rules glow in the accent,
   with no box behind them */
@keyframes lane-found-ink { 35% { color: var(--accent); } }
@keyframes lane-found-rule { 35% { border-color: var(--accent); } }
.lane.found .lane-h :is(.lane-ic, .lane-name, .lane-ct) { animation: lane-found-ink 1.4s ease-in-out; }
.lane.found .lane-h::after { animation: lane-found-rule 1.4s ease-in-out; }
/* ---------- Zoom selection (drag on the date ruler, or Shift + drag) ---------- */
.zoomsel {
  position: absolute; top: 0; z-index: 6; pointer-events: none;
  background: color-mix(in srgb, var(--accent) 12%, transparent); border-left: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  border-right: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
}
/* The pointer becomes a magnifying glass on the date ruler and while selecting a range */
:root { --zoom-cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3E%3Cg fill='none' stroke-linecap='round'%3E%3Cpath d='M17.5 17.5 25 25' stroke='%23000' stroke-opacity='.55' stroke-width='6'/%3E%3Cpath d='M17.5 17.5 25 25' stroke='%23fff' stroke-width='3.5'/%3E%3Ccircle cx='11' cy='11' r='8' fill='%23fff' fill-opacity='.35' stroke='%23000' stroke-opacity='.55' stroke-width='4'/%3E%3Ccircle cx='11' cy='11' r='8' stroke='%23fff' stroke-width='2'/%3E%3Cpath d='M7.5 11h7M11 7.5v7' stroke='%23222' stroke-width='1.8'/%3E%3C/g%3E%3C/svg%3E") 11 11, zoom-in; }
.zooming, .zooming * { cursor: var(--zoom-cursor) !important; }
.zoomsel span {
  position: absolute; top: 6px; white-space: nowrap; padding: 2px 7px; border-radius: 5px;
  font-size: 11px; font-weight: 600; color: var(--on-accent); background: var(--solid, var(--accent));
  box-shadow: var(--shadow-chip);
}
.zoomsel .zs-a { right: calc(100% + 4px); }
.zoomsel .zs-b { left: calc(100% + 4px); }
/* clearly tinted with its category colour (13%, scaled by Contrast, --k) */
.lane-h {
  position: sticky; top: 0; left: 0; z-index: 3; display: flex; align-items: center; gap: var(--hd-gap, 9px);
  height: var(--hd-h, 40px); padding: 0 max(16px, calc(var(--now-gap, 0px) - 1px)) 0 var(--hd-pad-l, 16px); background: var(--hd-fill);
  font-size: 13px; font-weight: 650; color: var(--hd-ink); letter-spacing: -.005em; cursor: var(--cur-pointer, pointer);
  /* the row's colour rail, drawn here too: Safari paints this sticky header over it */
  box-shadow: var(--hd-edge, inset 3px 0 0 var(--rail));
}
.lane-top { position: absolute; top: 0; left: 0; width: 1px; height: 1px; pointer-events: none; }
.lane-h { transition: background-color .2s ease; }
.lane-h.stuck { background: var(--hd-stuck, var(--hd-fill)); box-shadow: var(--hd-stuck-fade, var(--hd-edge, inset 3px 0 0 var(--rail))); }   /* --hd-stuck-fade: a soft shadow of the fill under the title, so rows dissolve beneath it */   /* --hd-stuck: the fill while the title sticks over sliding rows */
/* Title layout switches: --rule-show: block draws a line from the name across to the icon and count (--rule: its colour,
   the gilt hairline under it is --rule-2); --hd-gap and --hd-pad-l set the spacing; --ord-ic and --ord-tools order the icon
   and count, then the buttons, after the line (the default 0 keeps the order as written) */
.lane-h::after, .titles-mid .lane-rest::before {
  content: ""; display: var(--rule-show, none); order: 1; flex: 1 1 0; min-width: 0; height: calc(var(--rule-h, 4px) * var(--rule-w, 1)); box-sizing: border-box;
  /* Settings → Rows, "Category titles": --rule-a fades both lines by their colour's alpha (capped at 100%),
     --rule-up (its part above 100, halved) darkens them toward the text colour, --rule-w scales them */
  border-top: calc(1.5px * var(--rule-w, 1)) solid color-mix(in srgb, color-mix(in srgb, var(--text) var(--rule-up, 0%), var(--rule, currentColor)) var(--rule-a, 100%), transparent);
  border-bottom: calc(1px * var(--rule-w, 1)) solid color-mix(in srgb, color-mix(in srgb, var(--text) var(--rule-up, 0%), var(--rule-2, currentColor)) var(--rule-a, 100%), transparent);
}
/* --rule-at: where the line lies. "mid" (the default) runs it between the name and the right side; "top" or "bottom" lays it
   across the whole row at that edge, the row's division (--rule-pos: absolute, --rule-top or --rule-bottom: 0). --rule-scale:
   the line's inside is graduated in filled and empty steps of this length (Atlas's map border) */
.lane-h::after { position: var(--rule-pos, static); left: 0; right: 0; top: var(--rule-top, auto); bottom: var(--rule-bottom, auto); background: var(--rule-fill, none); }
.titles-mid .lane-rest::before { display: var(--rule-show-2, var(--rule-show, none)); }
/* A theme's emblem: the category's shape as a fine line in --ic-ring round the icon, the same pen as the icon, so the two read
   as one mark (--ic-ring-w; --ic-core: the fill inside). --ic-k scales the mark, --ic-g the icon inside it. --ic-slot: every
   emblem is centred in one column of this width, so all the names start on one line whatever their shape */
.lane-h .lane-ic {
  position: relative; isolation: isolate; width: calc(var(--sw) * var(--ic-k, 1)); height: calc(var(--sh) * var(--ic-k, 1));
  margin-inline: calc((var(--ic-slot, calc(var(--sw) * var(--ic-k, 1))) - var(--sw) * var(--ic-k, 1)) / 2);
}
.lane-h .lane-ic > svg { width: calc(19px * var(--ic-k, 1) * var(--ic-g, 1)); height: calc(19px * var(--ic-k, 1) * var(--ic-g, 1)); }
.lane-h .lane-ic::before { content: ""; display: var(--ic-ring-show, none); position: absolute; inset: var(--ic-ring-w, 0); z-index: -1; border-radius: inherit; clip-path: var(--clip, none); background: var(--ic-core, transparent); }
.lane-title, .lane-rest { display: contents; }
/* Experimental "Titles in the middle": the icon and name stay centred on the full row (three columns, the outer two equal).
   The rule runs in from both sides; everything else keeps to the right, clear of the now line. With too little room the
   right column keeps its contents and the title gives way to the left, so nothing overlaps. The now line's padding sits
   on the right column only, so the title holds still while panning. --push-k: the sidebar glide moves a centred title
   half as far as the row's edge, and what is pinned to the right not at all */
.titles-mid .lane-h { display: grid; grid-template-columns: 1fr auto 1fr; padding-inline: var(--hd-pad-l, 16px); }
.titles-mid .lane-h::after { grid-area: 1 / 1; }
.titles-mid .lane-title { grid-area: 1 / 2; display: flex; align-items: center; gap: var(--hd-gap, 9px); --push-k: .5; }
.titles-mid .lane-rest {
  grid-area: 1 / 3; display: flex; align-items: center; justify-content: flex-end; gap: var(--hd-gap, 9px); --push-k: 0;
  padding-right: max(calc(16px - var(--hd-pad-l, 16px)), calc(var(--now-gap, 0px) - 1px - var(--hd-pad-l, 16px)));
}
.titles-mid .lane-note { flex: 1000 1 0; width: 0; max-width: max-content; }   /* the note takes only what's free, ellipsised, so it never moves the title */
/* Experimental "Compact people count": the people tag is just its icon and number. The whole tag becomes the Separate button
   (its x hidden, the button stretched over it), so that action stays one click or tap, and the tooltip names the people */
.compact-guests .nest-pill { position: relative; }
.compact-guests .nest-pill .fold-tx { display: none; }
.compact-guests .lane-h .nest-pill .nest-pill-name { padding-inline: 8px; }
.compact-guests .lane-h .nest-pill button { position: absolute; inset: 0; padding: 0; font-size: 0; }
/* both switches on a phone: the count stacks over the people tag and titles are a little smaller, so they can stay centred */
@media (max-width: 760px) {
  .titles-mid.compact-guests .lane-name { --title-fit: .85; }
  @media (max-width: 340px) { .titles-mid.compact-guests .lane-name { --title-fit: .75; } }   /* the smallest phones */
  .titles-mid.compact-guests .lane-note { display: none; }   /* no room for more than a stub of it */
  .titles-mid.compact-guests .lane-rest.has-guests { display: grid; grid-template-rows: auto auto; justify-content: end; justify-items: end; align-content: center; gap: 1px 0; }
  .titles-mid.compact-guests .lane-rest.has-guests::before { display: none; }
  .titles-mid.compact-guests .lane-rest.has-guests .nest-pill { grid-row: 2; margin: 0 0 0 3px; }
  .titles-mid.compact-guests .lane-rest.has-guests .lane-ct { grid-area: 1 / 1 / 2 / span 9; margin: 0; line-height: 16px; padding-block: 0; }
  .titles-mid.compact-guests .lane-h .lane-rest.has-guests .nest-pill .nest-pill-name { padding-block: 0; line-height: 18px; }
}
.lane-h .lane-ic { order: -1; }   /* the category icon leads the title, before the name */
.lane-h .lane-ct { order: var(--ord-ic, 0); }
.lane-h :is(.lane-tools, .nest-pill) { order: var(--ord-tools, 0); }
@property --wash { syntax: '<color>'; inherits: false; initial-value: transparent; }
/* everything on the solid title bar is inked in --hd-ink, with capsules as a wash of it (--wash). With no solid bar
   (--hd-fill transparent), --hd-halo is the knockout under each item: the name, note and capsules sit on a patch of
   the panel colour (a wash over it, a spread of it round them) so rows sliding under the sticky title never show
   through the text, while the grid and the rule stay open between the items. Default transparent = no change */
:root[data-ic="2"] { --ic-col: var(--ic-2); }
:root[data-ic="3"] { --ic-col: var(--ic-3); }
:root[data-ic="custom"] { --ic-col: var(--ic-custom, var(--ic-1)); }
.lane-h .lane-ic { color: color-mix(in srgb, var(--ic-col) calc(var(--ic-int) * 100%), var(--muted)); --wash: color-mix(in srgb, color-mix(in srgb, var(--ic-col) calc(var(--ic-int) * 100%), var(--muted)) var(--ic-wash), transparent); }
/* each category's own colour; not in Current, whose title bar is already that colour (the swatch hides there too) */
:root[data-skin][data-ic="category"] .lane-h .lane-ic { --ic-col: var(--c-true, var(--c)); }
:root:not([data-skin]) #iconColorSeg [data-v="category"] { display: none; }
.lane-h .lane-name { color: var(--hd-ink); }
.lane-h .lane-ct { color: var(--hd-ink); --wash: color-mix(in srgb, var(--hd-ink) 20%, transparent); }
.lane-h .lane-note { color: color-mix(in srgb, var(--hd-ink) 80%, transparent); }
.lane-h .lane-tools { color: var(--hd-ink); --wash: color-mix(in srgb, var(--hd-ink) 18%, transparent); }
.lane-h .lane-tools:hover { --wash: color-mix(in srgb, var(--hd-ink) 30%, transparent); }
.lane-h .nest-pill { --wash: color-mix(in srgb, var(--hd-ink) 18%, transparent); }
.lane-h :is(.lane-ic, .lane-ct, .lane-tools, .nest-pill) { background: linear-gradient(var(--wash), var(--wash)), var(--hd-halo, transparent); }
.lane-h :is(.lane-name, .lane-note) { background: var(--hd-halo, transparent); }
.lane-h :is(.lane-name, .lane-note, .lane-tools, .nest-pill) { box-shadow: 0 0 0 var(--hd-halo-r, 5px) var(--hd-halo, transparent); }
.lane-h .nest-pill button, .lane-h .nest-pill-name { color: var(--hd-ink); }
.lane-h .nest-pill button + button { color: color-mix(in srgb, var(--hd-ink) 75%, transparent); }
.lane-h .nest-pill button:hover { background: color-mix(in srgb, var(--hd-ink) 16%, transparent); }
.lane-h .nest-btn { color: var(--hd-ink); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--hd-ink) 45%, transparent); }
/* clicking a row's header fits the whole category */
/* A labelled header button on its own capsule (Expand all) */
.hdr-btn {
  display: inline-flex; align-items: center; gap: 7px; padding: 0 13px; font: inherit; cursor: var(--cur-pointer, pointer); white-space: nowrap;
}
/* Collapsed entries show only a small count at the far end of the bar (full wording on hover),
   and when space runs short the dates give way before the name does */
.nest-ct {
  border-radius: 999px; line-height: 18px; font-size: 10.5px; font-weight: 600; white-space: nowrap;
  color: color-mix(in srgb, var(--gc) 75%, var(--text));
  background: color-mix(in srgb, var(--gc) 16%, transparent); margin-left: auto;
  min-width: 18px; padding: 0 6px; text-align: center; flex: none;
}
/* Corner toggle of a parent: a small chip at the top right, on a closed bar at its far end after the count, on an open
   entry's box cut into its top edge like the name's gap. Opens or closes that entry (js/app.js, toggleBtn), never
   zooms: it takes the pointer itself, though the label it sits in lets presses through to the bar (which zoom). */
.nest-tg {
  position: relative; flex: none; display: grid; place-items: center; width: 18px; height: 18px; margin: 0 -2px 0 4px; padding: 0;
  pointer-events: auto;
  border: 0; border-radius: min(6px, var(--r-ui, 6px)); cursor: var(--cur-pointer, pointer);
  color: color-mix(in srgb, var(--c) 75%, var(--text)); background: color-mix(in srgb, var(--c) 16%, var(--surface));
  transition: color .15s, background-color .15s, box-shadow .18s;
}
.item.open-parent > .nest-tg { position: absolute; top: calc(50% - 9px); right: 10px; margin: 0; z-index: 1; }
.item:is(.tiny, .away) > .nest-tg { display: none; }
.nest-tg:is(:hover:not(.touch-input *), :focus-visible, .hov) { color: var(--hover-ink, var(--text)); box-shadow: inset 0 0 0 1px var(--accent), var(--hover-glow, none); }
.nest-tg .ico { width: 12px; height: 12px; }
/* ---------- Dates at the bottom of each row, at the grid lines ("500 BCE", "9,501 HE" or both; js/app.js) ---------- */
/* --ls-y (up from the row's bottom; negative sits below it), --ls-x (right of the grid line), --ls-size and --ls-a (strength);
   values chosen with ?datelab=1 */
.lane-scale {
  position: absolute; left: 0; bottom: var(--ls-y, -7.5px); height: calc(var(--ls-size, 9.5px) + 1.5px); white-space: nowrap;
  pointer-events: none; z-index: 1;
}
.lane-scale > span {
  position: absolute; left: 0; top: 0; padding-left: var(--ls-x, 4px);
  transform: translateX(calc(var(--o) * var(--ppy) * 1px));   /* its year from the first date, times the zoom */
  font-size: var(--ls-size, 9.5px); line-height: calc(var(--ls-size, 9.5px) + 1.5px); letter-spacing: .04em;
  /* faded in its colour, not with opacity: an opacity per label made Safari paint each one apart on every zoom frame */
  color: color-mix(in srgb, var(--muted) var(--ls-a, 85%), transparent);
}
.lane-scale > span.zero { color: color-mix(in srgb, var(--accent) var(--ls-a, 85%), transparent); }
.lanes.no-scale .lane-scale { display: none; }
.lane[hidden] .lane-scale { display: none; }
/* Experimental "Dates follow the pointer" (tl.cursorDates): one hairline across the timeline at the pointer, the same
   dates sitting just above it; the rows' own date strips are hidden meanwhile. Moved with `translate` only. */
.lanes.dates-at-pointer .lane-scale, .lanes.dates-top .lane-scale { display: none; }
.cursor-dates {
  position: absolute; left: 0; right: 0; top: 0; height: 0; z-index: 7; pointer-events: none;
  border-top: 1px solid color-mix(in srgb, var(--muted) 45%, transparent);
}
.cursor-dates > .lane-scale { bottom: 3px; }
.lanes.no-scale ~ .cursor-dates > .lane-scale { display: none; }
/* ---------- Bar labels settings (native popover, anchored under its button) ---------- */
.hdr-look { anchor-name: --settings; }   /* the panel lines up with the right end of the Settings capsule */
.settings-pop {
  position-anchor: --settings; inset: auto; top: calc(anchor(bottom) + 8px); right: max(8px, anchor(right));   /* never past the window's edge */
  margin: 0; width: min(380px, calc(100vw - 16px)); padding: 10px 14px 12px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface); color: var(--text); box-shadow: 0 10px 30px -10px rgba(0,0,0,.35);
  font-size: 12.5px;
  --set-ink: color-mix(in srgb, var(--text) 84%, var(--surface));   /* a setting's name: the panel's ink, softened into its fill (a theme may set its own) */
}
/* one height for every tab, so switching never makes the panel jump; never taller than the window: the tab's body
   scrolls under the fixed tabs (narrow screens hold the header's switches too). Open only: a closed popover stays hidden */
.settings-pop { height: min(600px, calc(100dvh - 72px)); overflow: hidden; }
.settings-pop:popover-open { display: flex; flex-direction: column; }
/* Tabs: icon over name, equal widths, an underline in the accent gliding to the chosen one (the switches' pill motion) */
.settings-tabs {
  --n: 5; position: relative; flex: none; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr));
  border-bottom: 1px solid var(--line);
}
.settings-tabs::after {
  content: ""; position: absolute; left: 0; bottom: -1px; width: calc(100% / var(--n)); height: 2px; background: var(--accent);
  transform: translateX(calc(var(--i, 0) * 100%)); transition: transform .46s cubic-bezier(.65, 0, .25, 1); pointer-events: none;
}
.settings-tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; min-width: 0; min-height: 46px; padding: 5px 0 7px;
  border: 0; border-radius: 8px 8px 0 0; background: none; color: var(--muted); font: 600 var(--t-label)/1.2 var(--font-label); text-transform: var(--label-case); letter-spacing: var(--caps); cursor: var(--cur-pointer, pointer);
  transition: background-color .25s ease, color .25s ease;
}
.settings-tab > * { transition: var(--pop-t); }
.settings-tab:hover { color: var(--hover-ink); background: color-mix(in srgb, var(--accent) 7%, transparent); }
@media (hover: hover) { .settings-tab:hover > * { scale: var(--pop); translate: var(--pop-nudge) 0; } }
.settings-tab:active > * { scale: .92; }
.settings-tab.press svg { animation: globe-press .4s var(--ease-out); }
.settings-tab[aria-selected="true"] { color: var(--accent); font-weight: 650; }
.settings-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* the body reaches 8px past the panel's padding, so the Experimental zone can stand wider than the settings; its text stays on their edge */
.settings-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; margin-inline: -8px; padding: 0 8px 8px; font: 400 var(--set-size)/1.3 var(--font-side); color: var(--set-ink); }
.settings-tabpanel { padding-top: 14px; }
/* touch: sliders and the footer link get the 36px target; narrow panels let a many-option switch (Theme) fit by tightening its options */
@media (pointer: coarse) { .settings-link { display: inline-flex; align-items: center; min-height: 46px; } }
@media (max-width: 760px) { .settings-pop .seg button { padding-inline: 6px; } .settings-tab { font-size: 11px; letter-spacing: 0; text-transform: none; } }
.settings-tabpanel:not([hidden]) { animation: sheet-in .3s cubic-bezier(.2, .9, .25, 1) both; }
@media (prefers-reduced-motion: reduce) { .settings-tabs::after { transition: none; } .settings-tabpanel:not([hidden]) { animation: none; } .settings-tab > * { scale: none !important; translate: none !important; animation: none !important; } }
/* the header's switches (layout, theme, dating), moved in on narrow screens (js/headerfold.js), fill the panel's width with their full labels */
body:not(.hdr-folded) :is(.settings-fold, .fold-slot) { display: none; }
.top .settings-pop .long { display: inline; }
.top .settings-pop .short { display: none; }
.settings-pop.fresh { animation: gs-drop .42s cubic-bezier(.2, 1.25, .35, 1); }   /* the search menu's drop */
/* one group per topic: a heading, then its settings, space alone setting groups apart. Every setting is one row: the
   name on the left, its control on the right edge, so the eye runs down one column of names and one of controls.
   Experimental keeps its own denser stack of sliders (STYLE-GUIDE.md → Settings menu) */
.settings-grp + .settings-grp { margin-top: 20px; }
.settings-grp:not(.settings-lab) > .settings-h { margin-bottom: 2px; color: var(--text); font-weight: 700; }
.settings-grp:not(.settings-lab) > :is(label, .set-row) { min-height: 38px; padding-block: 2px; }
.settings-pop:has(#datesAtSeg [data-v="top"][aria-checked="true"]) .cursor-dates-set { display: none; }   /* dates follow the pointer only instead of the rows' own */
.set-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 2px 12px; }
.set-row > .fold-slot { margin-left: auto; }   /* on a screen too narrow for both, the control drops under its name, still on the right */
.settings-pop .set-row > .seg { margin: 0 0 0 auto; }
.settings-pop .seg { display: flex; margin: 2px 0 4px; }
.settings-pop .seg button { flex: 1 1 auto; min-width: 0; padding-inline: 10px; font-family: var(--font-side); font-size: var(--set-size); color: var(--set-ink); }
/* the chosen option wears the "chosen" look (--chosen-fill, --chosen-edge): the same on switches and ticked boxes */
.settings-pop .seg .seg-pill { background: var(--chosen-fill); box-shadow: inset 0 0 0 1px var(--chosen-edge); }
.settings-pop .seg button[aria-checked="true"] { color: var(--text); font-weight: 600; }
.settings-pop .seg:not(.has-pill) button[aria-checked="true"] { background: var(--chosen-fill); box-shadow: inset 0 0 0 1px var(--chosen-edge); }   /* before its pill exists, and on the style guide page */
@media (hover: hover) and (pointer: fine) { .settings-pop .set-row .seg button { height: 30px; } }   /* touch keeps the full-height target */
/* Sliders in the theme's colours, never the browser's: a hairline track filled with the accent up to the value
   (--v, js/settings.js fillSliders) and an accent knob that glows on hover */
.settings-pop input[type="range"] { appearance: none; -webkit-appearance: none; display: block; width: 100%; height: 18px; margin: 2px 0 6px; background: none; color: var(--accent); cursor: var(--cur-pointer, pointer); }
.settings-pop input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: linear-gradient(to right, var(--accent) var(--v, 50%), var(--line-strong) 0); }
.settings-pop input[type="range"]::-moz-range-track { height: 4px; border-radius: 2px; background: linear-gradient(to right, var(--accent) var(--v, 50%), var(--line-strong) 0); }
.settings-pop input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; margin-top: -5px; border: 0; border-radius: 50%; background: var(--accent); transition: box-shadow .15s; }
.settings-pop input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border: 0; border-radius: 50%; background: var(--accent); transition: box-shadow .15s; }
.settings-pop input[type="range"]:is(:hover, :focus-visible)::-webkit-slider-thumb { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent), 0 0 10px color-mix(in srgb, var(--accent) 45%, transparent); }
.settings-pop input[type="range"]:is(:hover, :focus-visible)::-moz-range-thumb { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent), 0 0 10px color-mix(in srgb, var(--accent) 45%, transparent); }
.settings-pop input[type="range"]:focus-visible { outline: none; }
@media (pointer: coarse) {   /* touch: the 36px target and a knob a thumb can see */
  .settings-pop input[type="range"] { height: 36px; margin: 0; }
  .settings-pop input[type="range"]::-webkit-slider-thumb { width: 20px; height: 20px; margin-top: -8px; }
  .settings-pop input[type="range"]::-moz-range-thumb { width: 20px; height: 20px; }
}
/* Experimental: features still being tried, last in their tab, in a zone of their own: the panel's second layer inside
   a dashed accent edge, wider than the settings, under an accent band (flask, name, note) that stays pinned while the
   zone scrolls, so it is clear at a glance which side of the divide a setting is on, wherever the tab is scrolled to */
.settings-lab { margin: 22px -7px 6px; padding: 0 6px 12px; border: 1px dashed color-mix(in srgb, var(--accent) 70%, transparent); border-radius: 10px; background: var(--surface-2); }
.settings-tabpanel > .settings-lab:first-child, body:not(.hdr-folded) .settings-fold + .settings-lab { margin-top: 0; }
.settings-grp + .settings-lab { margin-top: 22px; }
.settings-lab > .settings-h {
  position: sticky; top: 0; z-index: 2; gap: 7px; margin: 0 -6px 10px; padding: 8px 10px;
  border-radius: 9px 9px 0 0; border-bottom: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 34%, var(--surface-2)); color: var(--text); font-weight: 700;
}
.settings-lab > .settings-h::after { content: none; }
.settings-h .set-ic { margin: 0; color: inherit; }
.lab-note { margin-left: auto; font: 400 11px/1.2 var(--font-side); letter-spacing: 0; text-transform: none; color: var(--text); white-space: nowrap; }
.settings-labgrp + .settings-labgrp { margin-top: 10px; }
/* a fold inside a tab: related sliders under one collapsible heading (rule: STYLE-GUIDE.md → Settings menu) */
.settings-rollup { margin: 10px 0 2px; border-top: 1px solid var(--line-strong); }
/* fold headings stand out: larger, bolder, on a band with an accent bar at the left (only Experimental's band wears the accent) */
.settings-rollup > summary { display: flex; align-items: center; justify-content: space-between; min-height: 38px; margin: 6px 0 2px; padding: 0 10px; border-left: 3px solid var(--accent); border-radius: 6px; background: color-mix(in srgb, var(--text) 6%, transparent); font-size: var(--set-fold-size); font-weight: var(--set-fold-w); color: var(--text); cursor: var(--cur-pointer, pointer); list-style: none; }
.settings-rollup > summary:hover { color: var(--hover-ink); }
.settings-rollup > summary::-webkit-details-marker { display: none; }
.settings-rollup > summary::after { content: ""; width: 6px; height: 6px; border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); transition: transform .2s; }
.settings-rollup[open] > summary::after { transform: rotate(-135deg); }
/* an on/off setting: its name, then the switch at the right edge */
.settings-pop label { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 4px 0; cursor: var(--cur-pointer, pointer); }
/* a setting with a choice of several: its name on a line above them */
.settings-sub { margin: 0; padding: 4px 0 0; }
/* text tuner: one block per role (category title, level 1-3 bar titles) = name + live sample, its size slider, and its
   boldness slider indented under a rule. The samples reuse the roles' own tokens, so they follow the theme and the sliders */
.tune-role { margin: 6px 0 10px; padding-bottom: 6px; border-bottom: 1px solid var(--line-strong); }
.tune-role:last-of-type { border-bottom: 0; }
.tune-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 8px 0 2px; min-height: 28px; }
.tune-name { font-size: var(--set-size); font-weight: 650; flex: none; }
.tune-sample { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.tune-sub { margin: 0 0 0 8px; padding-left: 10px; border-left: 2px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.tune-sub .settings-sub { color: var(--muted); font-size: .96em; }
.tune-where { margin: 0 0 2px; font-size: 11px; line-height: 1.35; color: var(--muted); }
.tune-sample.s-p { font-size: calc(var(--t-name) * var(--tune-p, 1)); font-weight: 550; letter-spacing: -.005em; -webkit-text-stroke: var(--tune-pb, 0em) currentColor; }
.tune-sample.s-t { font-family: var(--font-head); font-size: calc(var(--title-size, var(--t-title)) * var(--tune-t, 1)); font-weight: var(--title-weight, 700); letter-spacing: var(--title-ls, var(--caps-wide)); text-transform: var(--title-case, uppercase); -webkit-text-stroke: calc(var(--title-stroke, 0em) + var(--tune-tb, 0em)) currentColor; }
.tune-sample.s-1 { font-family: var(--period-font, var(--font-caps)); text-transform: var(--period-case, uppercase); font-size: calc(var(--t-level) * var(--t-ratio) * var(--t-lift, 1) * var(--tune-1, 1)); font-weight: var(--period-weight, 700); letter-spacing: var(--period-ls, var(--caps)); -webkit-text-stroke: var(--tune-1b, 0em) currentColor; font-style: var(--period-style-1, var(--period-style, normal)); }
.tune-sample:is(.s-2, .s-3) { font-family: var(--period-font, inherit); text-transform: var(--period-case, inherit); letter-spacing: var(--period-ls, -.005em); font-style: var(--period-style, normal); }
.tune-sample.s-2 { font-size: calc(var(--t-level) * var(--tune-2, 1)); font-weight: var(--period-weight, 500); -webkit-text-stroke: var(--tune-2b, 0em) currentColor; color: var(--period-ink-2, var(--text)); }
.tune-sample.s-3 { font-size: calc(var(--t-level) / var(--t-ratio) * var(--tune-3, 1)); font-weight: var(--period-weight, 500); -webkit-text-stroke: var(--tune-3b, 0em) currentColor; color: var(--period-ink-3, var(--text)); }
.tune-out { float: right; font: 400 11.5px/1.5 var(--font-date); font-variant-numeric: tabular-nums; color: var(--muted); }   /* a tuner slider's value */
/* Topic lens (js/topiclens.js; Settings → Timeline → Experimental): a chip per topic, under its group; the chosen one
   wears the theme's accent, an empty topic is shown but can't be picked. --lens-dim: how far entries without the topic
   dim on the timeline (their colour's alpha, js/draw/webgl.js), shared so every theme has it */
:root { --lens-dim: .22; }
.lens-roll > summary .lens-cur { float: none; margin: 0 10px 0 auto; }
.lens-roll > summary::after { margin-left: 0; }
.lens-pick { padding: 2px 0 8px; }
.lens-h { color: var(--muted); font-size: var(--t-label); padding-top: 8px; }
.lens-g { display: flex; flex-wrap: wrap; gap: 4px; padding-top: 4px; }
.lens-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 26px; padding: 0 9px; border: 0; border-radius: var(--r-ui, 999px);
  background: var(--surface-2); box-shadow: inset 0 0 0 var(--hair) var(--cap-ring); color: var(--text); font: inherit; font-size: var(--t-label);
  cursor: var(--cur-pointer, pointer); transition: background-color .2s ease, color .2s ease;
}
.lens-chip .lens-n { font: 400 11px/1 var(--font-date); font-variant-numeric: tabular-nums; color: var(--muted); }
.lens-chip:not([aria-checked="true"], :disabled):hover { background: color-mix(in srgb, var(--accent) 10%, var(--surface-2)); color: var(--hover-ink, var(--text)); }
.lens-chip[aria-checked="true"] { background: var(--surface); color: var(--accent); box-shadow: inset 0 0 0 var(--hair) var(--accent); }   /* as a chosen option (.seg); solid in Solid-hover themes (below) */
.lens-chip:is([aria-checked="true"], :hover) .lens-n { color: inherit; }
.lens-chip:disabled { color: var(--muted); background: transparent; cursor: default; }
.settings-pop .settings-again {   /* an action in a setting's row (Experimental: show the card paging tip again) */
  flex: none; padding: 3px 8px; border: 0; border-radius: 6px; background: none; color: var(--accent);
  font: inherit; font-weight: 550; cursor: var(--cur-pointer, pointer); text-decoration: none;
}
.settings-again:is(:hover, :focus-visible) { background: var(--surface-2); }
/* ---------- Duration at the left of each bar ("3,120 yrs"): shown once the bar has room ---------- */
.item.lvl3 .dur { line-height: 13px; }
/* ---------- Movable item card: grip at the top, the header area drags it ---------- */
.detail .grip { position: absolute; top: 0; left: 0; right: 0; height: 14px; z-index: 4; cursor: var(--cur-grab, grab); }
.detail .grip::before {
  content: ""; position: absolute; top: 5px; left: 50%; width: 34px; height: 4px; margin-left: -17px;
  border-radius: 2px; background: var(--line-strong); opacity: 0; transition: opacity .15s;
}
.detail:hover .grip::before, .detail.moving .grip::before { opacity: 1; }
.detail.moving, .detail.moving * { cursor: var(--cur-grabbing, grabbing); -webkit-user-select: none; user-select: none; }
.detail.moving { box-shadow: 0 18px 40px -12px rgba(0,0,0,.45); transition: none; }
/* ---------- Sidebar on hover (Settings): a rail of category icons that opens into the full sidebar ----------
   The content keeps its full width and the rail simply clips it, so nothing reflows when it opens:
   the icons stay exactly where they are and the names, counts and headings fade in beside them. */
@media (min-width: 761px) {
  body.side-auto .app { grid-template-columns: calc(var(--rail-w) + var(--edge)) minmax(0, 1fr); }   /* the 30px icon slot, centred */
  /* --rail-extra: room the rail takes beyond --rail-w, half each side (Raise the timeline, below) */
  /* closes after a short pause */
  body.side-auto .side {
    position: absolute; grid-column: 1; grid-row: 2; top: 0; bottom: 0; left: 0; z-index: 15; width: calc(var(--rail-w) + var(--rail-extra, 0px));
    padding: 16px calc(var(--rail-pad) + var(--rail-extra, 0px) / 2); overflow: hidden;
    transition: width .24s cubic-bezier(.2,.8,.2,1) .2s, padding .24s cubic-bezier(.2,.8,.2,1) .2s, box-shadow .24s .2s;
  }
  body.side-auto .side > * { width: calc(var(--side-w, 248px) - 24px); }   /* the opened sidebar's 12px padding each side */
  body.side-auto .side :is(.side-h, .hint, .nm, .ct, .eye) { opacity: 0; transition: opacity .12s .15s; }
  body.side-auto .side .cats li.sub::before { opacity: 0; transition: opacity .12s .15s; }
  body.side-auto .side :is(.cats, .side-lock) { --gut: 0px; }
  /* a theme with a rail narrower than the 46px row moves the rows left by --rail-shift to centre them (translate: no layout per frame) */
  body.side-auto .side :is(.cat, .side-lock) { translate: var(--rail-shift, 0px) 0; transition: background .12s, color .12s, opacity .12s, translate .24s cubic-bezier(.2,.8,.2,1) .2s, margin-left .24s cubic-bezier(.2,.8,.2,1) .2s; }
  /* the heading and the hint have no room on the rail: the icons start higher and all fit without scrolling */
  body.side-auto .side .side-h { flex: none; max-height: 0; padding-bottom: 0; overflow: hidden; transition: opacity .12s .15s, max-height .24s cubic-bezier(.2,.8,.2,1) .2s, padding .24s cubic-bezier(.2,.8,.2,1) .2s; }
  body.side-auto .side .hint { display: none; }
  body.side-auto .side .cat-group { max-height: 12px; padding-top: 4px; padding-bottom: 0; overflow: hidden; transition: color .12s .15s, max-height .24s cubic-bezier(.2,.8,.2,1) .2s, padding .24s cubic-bezier(.2,.8,.2,1) .2s; }
  body.side-auto .side .cat-group:first-child { max-height: 4px; padding-top: 0; }
  body.side-auto .side .cats li { transition: padding .24s cubic-bezier(.2,.8,.2,1) .2s, max-height .24s cubic-bezier(.2,.8,.2,1) .2s, opacity .12s .15s, visibility 0s .35s; }
  /* the people rows nested under a theme fold away on the rail (their themes hold them); they unfold as it opens */
  body.side-auto .side .cats li.sub { max-height: 0; opacity: 0; visibility: hidden; overflow: clip; }
  body.side-auto .side .cats li.sub { padding-left: 0; }
  /* every person icon (nested or not, e.g. Scientists) shrinks the same, so all people icons match */
  body.side-auto .side .cats :is(li.sub .sw, .sw.portrait) { transform: scale(.85); transition: transform .24s .2s; }
  /* group headings become short dividers between the icon groups */
  body.side-auto .side .cat-group { color: transparent; }
  body.side-auto .side .cat-group::after {
    content: ""; position: absolute; left: calc(50% - 6px); bottom: 7px; width: 12px; height: 1.5px;
    border-radius: 1px; background: var(--line-strong); transition: opacity .12s .15s;
  }
  body.side-auto .side .cat-group:first-child::after { display: none; }
  body.side-auto .side .grip { opacity: 0; }   /* no handles on the icon rail */
  body.side-auto .side .cat .sw { opacity: var(--rail-ink, .8); transition: opacity .12s; }
  body.side-auto .side :is(.cat:hover, li.swept .cat, li.current .cat) .sw { opacity: 1; }
  body.side-auto .side-lock svg { opacity: var(--rail-ink, .8); transition: opacity .12s; }   /* at rest as dim as the rail's icons */
  body.side-auto .side-lock:hover svg { opacity: 1; }
  /* open (not while the pointer only passes through from another window: body.arriving, js/app.js) */
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) {
    width: var(--side-w, 248px); padding-inline: 12px; box-shadow: 14px 0 36px -18px rgba(0,0,0,.45);   /* as when always shown */
    transition-delay: 0s;
  }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) :is(.side-h, .hint, .nm, .ct) { opacity: 1; transition-delay: .06s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats li.sub::before { opacity: 1; transition-delay: .06s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) :is(.cats, .side-lock) { --gut: 12px; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) :is(.cat, .side-lock) { translate: 0 0; transition-delay: 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats li { transition-delay: 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats li.sub { padding-left: calc(var(--gut) + 18px); }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .side-h { max-height: 40px; padding-bottom: 10px; transition-delay: .06s, 0s, 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .hint { display: block; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats li.sub { max-height: 48px; opacity: 1; visibility: visible; overflow: visible; transition-delay: 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats :is(li.sub .sw, .sw.portrait) { transform: none; transition-delay: 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats li.off .eye { opacity: 1; transition-delay: .06s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cats li[data-cat]:hover .eye { opacity: 1; transition-delay: 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cat-group { color: var(--group-ink); max-height: 40px; padding-top: 14px; padding-bottom: 4px; overflow: visible; transition-delay: .06s, 0s, 0s; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cat-group:first-child { max-height: 40px; padding-top: 2px; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cat-group .grip { opacity: .45; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) li:is(:hover, .dragging) .grip { opacity: .85; }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cat-group:is(.dragging, .settling) { color: var(--drag-ink); }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cat-group.drop-home { color: var(--home-ink); }
  :is(body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), body.side-opening .side) .cat-group::after { opacity: var(--group-mark-open, 0); transition-delay: 0s; }
}
/* ---------- Map in the item card: area filled in the entry's colour, a pin for a single place ---------- */
.minimap { margin: 14px 0 0; }
.minimap svg {
  display: block; width: 100%; aspect-ratio: 2; border-radius: 10px;
  background: color-mix(in srgb, #4a90c8 9%, var(--surface-2)); box-shadow: inset 0 0 0 1px var(--line);
}
.minimap path { vector-effect: non-scaling-stroke; stroke-width: .6; stroke-linejoin: round; }
.mm-land {
  fill: color-mix(in srgb, var(--text) 9%, var(--surface));
  stroke: color-mix(in srgb, var(--text) 16%, transparent);
}
.mm-area { fill: color-mix(in srgb, var(--c) 55%, var(--surface)); stroke: color-mix(in srgb, var(--c) 85%, var(--text)); }
.mm-pin .halo { fill: color-mix(in srgb, var(--c) 30%, transparent); }
.mm-pin .dot { fill: var(--c); stroke: var(--surface); stroke-width: 1.5; }
.minimap figcaption { margin-top: 6px; font-size: 11.5px; color: var(--muted); }
/* In the item card the map floats under the place only while the pointer is on it (js/card.js) */
.d-map {
  position: fixed; z-index: 21; margin: 0; pointer-events: none; border-radius: 12px; box-shadow: var(--shadow);
  animation: d-map-in .16s ease-out;
}
@keyframes d-map-in { from { opacity: 0; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .d-map { animation: none; } }
/* A nested category's own row holds only the entries that fit in none of its host's entries */
.lane-note { margin-left: 4px; font-size: 11.5px; font-weight: 400; color: var(--faint); font-style: italic; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* one line: on a narrow title bar it gives way first */
/* ---------- Scrollbars: invisible, no track, until the element is scrolled (js/scrollfade.js) ---------- */
:root {
  --bg: #f6f7f9; --surface: #ffffff; --surface-2: #f1f2f5; --text: #15171c; --muted: #6a707c;
  --faint: #a3a8b2; --line: #e8eaee; --line-strong: #d5d8de; --cap-ring: #d2d5dc; --accent: var(--accent-light);
  /* header hover text: a brighter tint of the accent (less in light mode, to stay readable on white) */
  --hover-ink: color-mix(in srgb, var(--accent) 85%, #fff);
  --on-accent: #fff; --danger: #dc2626;   /* text on accent fills; delete and error red */
  --shadow: 0 1px 2px rgba(16,24,40,.04), 0 8px 24px rgba(16,24,40,.08); --radius: 10px;
  color-scheme: light; --bar-tint0: 16%; --bar-tint: calc(var(--bar-tint0) * var(--k)); --bar-tint-hover: min(90%, calc(28% * var(--k)));
  /* Contrast (one fixed scale now; the old setting is gone): how strongly bars and category icons
     stand out from the background: bar fill, and most of all the bar outline (--bar-edge); category title bars, header and sidebar icons. Step 3 is the default */
  --k: 1;
  --scroll-thumb: color-mix(in srgb, var(--text) 28%, transparent); --scroll-w: 6px;   /* a theme restyles the thumb's ink; its corner is --r-ui */
  /* relationship lines: close ties, conflicts. Category-palette tones (OKLCH L≈0.48), lifted like catColor() in dark */
  --tie-close: light-dark(#056f4f, oklch(from #056f4f 0.78 calc(c * .85) h));
  --tie-conflict: light-dark(#a1302c, oklch(from #a1302c 0.78 calc(c * .85) h));
  --tie-close-mark: var(--tie-close); --tie-conflict-mark: var(--tie-conflict);   /* a tag's icons, where the line's colour won't show on the tag */
  --tie-w: .75px;   /* a relationship line's width, in every theme */
  /* a line or tag that a click would follow (STYLE-GUIDE.md → Ties view): a lighter tint of its own colour (never the theme's accent,
     which may be red), words in a deep shade of it, a soft glow around the line. A theme whose ties are plain ink sets these per kind (Record) */
  --tie-hot-close: color-mix(in srgb, var(--tie-close) 50%, #fff); --tie-hot-conflict: color-mix(in srgb, var(--tie-conflict) 50%, #fff);
  --tie-hot-close-ink: color-mix(in srgb, var(--tie-close) 45%, #000); --tie-hot-conflict-ink: color-mix(in srgb, var(--tie-conflict) 45%, #000);
  --tie-halo-close: var(--tie-hot-close); --tie-halo-conflict: var(--tie-hot-conflict);
  --tie-hot-w: 1.25px; --tie-halo-w: 6px; --tie-halo-soft: 1;   /* soft: 1 a glow fading out, 0 a solid edge */
  /* the bow while circling a picture (js/draw/webgl.js): its width; tapered limbs (1) or even (0); the paper line around it; its mark a solid
     wedge (1) or a line with an open head (0); the pointer's dot: half its size, its paper edge, 1 round or 0 square. --bow-rev (unset here): the ink it takes
     where it crosses the hovered bar, for a theme whose hovered bar is the bow's own colour */
  --bow-w: 2.4px; --bow-taper: 1; --bow-case: 1.5px; --bow-head: 1; --bow-dot: 3px; --bow-dot-edge: 1px; --bow-dot-round: 1;
}
* { box-sizing: border-box; scrollbar-width: thin; scrollbar-color: transparent transparent; }
.is-scrolling { scrollbar-color: var(--scroll-thumb) transparent; }
@supports not (scrollbar-color: red blue) {
  /* older Safari */
  ::-webkit-scrollbar { width: 8px; height: 8px; background: transparent; }
  ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: transparent; border-radius: 4px; border: 2px solid transparent;
    background-clip: padding-box;
  }
  .is-scrolling::-webkit-scrollbar-thumb { background-color: var(--scroll-thumb); }
}
.g-track {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  border-radius: 10px; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line);
  scrollbar-width: none;
}
/* the photo carousel has its own dots */
/* ---------- Region map (header): a capsule button that opens a large world map (js/regions.js) ----------
   The globes along the top are the regions (hover zooms the map to one, a click picks it all); the land is
   countries (a click picks one, a drag paints several). Unchosen land is neutral; a region takes its colour
   when hovered (faint) or chosen (strong), so the map always reads as the current choice. */
.rmap { position: relative; flex: none; min-width: var(--hdr-c); }   /* never squeezed below its globe */
/* one-row header short of room: the region name shortens (…), down to 120px */
@media (min-width: 1601px) { .rmap { flex-shrink: 1; min-width: 120px; } }
.rmap-btn {
  display: inline-flex; align-items: center; gap: 8px; max-width: 100%; height: var(--hdr-c); padding: 0 12px 0 7px; border: 0;
  border-radius: 999px; background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring);
  color: var(--muted); cursor: var(--cur-pointer, pointer); font: inherit;
  transition: background-color .25s ease, color .25s ease, box-shadow .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
}
.rmap-btn[aria-expanded="true"] { background: color-mix(in srgb, var(--c) 9%, var(--surface-2)); }
.rmap.chosen .rmap-btn { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 35%, var(--line)); }
.rmap-ico { display: grid; color: var(--c); }
.rmap-cur { min-width: 0; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rmap.chosen .rmap-cur { color: var(--c); }
.rmap-chev { transition: transform .3s cubic-bezier(.3,1.3,.4,1), var(--pop-t); }
.rmap-btn[aria-expanded="true"] .rmap-chev { transform: rotate(180deg); }
/* The map grows with the screen: 56% of the window's width (at least 728px when it fits), never so wide
   that its height (half its width) passes about half the window's height, nor wider than the room beside the
   sidebar (--rmap-room, regions.js) */
.rmap-panel {
  --rmap-w: min(calc(100vw - 32px), max(728px, 56vw), 98vh, var(--rmap-room, 100vw));
  position: fixed; z-index: 40; width: var(--rmap-w);   /* left and top: under the button (regions.js) */
  padding: 8px 3px 3px; background: var(--surface); border: 1px solid var(--line); border-radius: 19px;
  box-shadow: 0 1px 2px rgba(0,0,0,.06), 0 24px 60px -20px rgba(0,0,0,.4), 0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent);
  container-type: inline-size; transform-origin: 50% 0;
}
.rmap-panel.sized { --rmap-w: var(--rmap-size); }   /* its size from the corner (regions.js), at the world's shape */
.rmap-panel.sized .rmap-stage { height: calc((var(--rmap-w) - 8px) / var(--map-aspect)); }
.rmap-panel.resizing { -webkit-user-select: none; user-select: none; }
.rmap-panel::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 10px; }   /* bridges the gap to the button, so the pointer never leaves the map on the way */
.rmap-panel.fresh { animation: gs-drop .42s cubic-bezier(.2, 1.25, .35, 1); }
/* phones only: the globes, one capsule track like every header control, spread across the map's width
   (desktop has the region buttons on the map instead) */
.rmap-rail {
  --dur: .46s; position: relative; display: none; height: 46px; border-radius: 999px;
  background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring);
}
.rmap-rail.instant, .rmap-rail.instant * { transition: none !important; }
.rmap-pill {
  position: absolute; left: 0; top: 3px; width: 40px; height: 40px; border-radius: 999px; pointer-events: none;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0,0,0,.1), 0 0 0 1px color-mix(in srgb, var(--c) 35%, var(--line)), 0 0 18px -3px color-mix(in srgb, var(--c) 55%, transparent);
  transition: transform var(--dur) var(--ease), width var(--dur) var(--ease), box-shadow .3s ease;
}
.rmap-pill.press { animation: pill-press .5s var(--ease-out); }
.rmap-g {
  position: absolute; left: 0; top: 3px; z-index: 1; display: inline-flex; align-items: center; height: 40px;
  padding: 0 6px; border: 0; border-radius: 999px; background: transparent; cursor: var(--cur-pointer, pointer); color: var(--c);
  transition: transform var(--dur) var(--ease), background-color .2s ease, box-shadow .2s ease;
}
.rmap-g .ico { flex: none; opacity: .75; transition: opacity .25s ease, filter .25s ease, var(--pop-t); }
.rmap-g:hover .ico, .rmap-g.hot .ico, .rmap-g.open .ico, .rmap-g.on .ico, .rmap-g.part .ico { opacity: 1; }
.rmap-g:hover .ico, .rmap-g.hot .ico { filter: drop-shadow(0 0 5px color-mix(in srgb, var(--text) 45%, transparent)); }
.rmap-g.hot:not(.open) { background: color-mix(in srgb, var(--text) 9%, var(--surface)); }
.rmap-g.on { box-shadow: 0 0 0 1.5px var(--c), 0 0 12px -2px color-mix(in srgb, var(--c) 50%, transparent); }
.rmap-g.part { box-shadow: 0 0 0 1px color-mix(in srgb, var(--c) 45%, transparent); }
.rmap-g.press .ico { animation: globe-press .4s var(--ease-out); }
@keyframes globe-press { 35% { scale: var(--press-ico); } }   /* from and back to the current size (hover-grown or not) */
/* the name: its box opens with the pill; the text rises in a beat later (js/regions.js) */
.rmap-nm { display: block; width: 0; overflow: hidden; white-space: nowrap; transition: width var(--dur) var(--ease); }
.rmap-g.open .rmap-nm { width: var(--lw); }
.rmap-nm > span {
  display: inline-flex; align-items: baseline; gap: 7px; padding: 0 12px 0 7px; line-height: 24px; opacity: 0;
  color: var(--c); font-size: var(--t-caps); font-weight: 650; letter-spacing: var(--caps); text-transform: var(--label-case);
}
.rmap-g[data-region=""] .rmap-nm > span { color: var(--text); }
.rmap-nm i { color: var(--muted); font-style: normal; font-size: var(--t-tiny); font-weight: 500; letter-spacing: 0; text-transform: none; font-variant-numeric: tabular-nums; }
/* how many countries of a region are chosen, when not all: on the globe's shoulder */
.rmap-k {
  display: none; position: absolute; top: -1px; left: 25px; min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 8px; background: var(--c); color: var(--surface); font-size: var(--t-tiny); font-weight: 650;
  line-height: 15px; text-align: center; font-variant-numeric: tabular-nums;
}
.rmap-g.part .rmap-k, .rmap-tag.part .rmap-k { display: block; }
/* desktop: a label per region on the map: a small frosted capsule with its globe and name in its colour. Only the
   globe takes the pointer (::before, the button proper); hover makes its region glow (and the label lifts); picked, it
   fills with its colour. The name lets the pointer through to the land beneath: over a country the label tucks back
   to its globe (.clear), so what lies under it can be seen and clicked; over the sea it answers for its region (regions.js). */
.rmap-tags { position: absolute; inset: 0; pointer-events: none; }
.rmap-tag {
  position: absolute; translate: -50% -50%; display: inline-flex; align-items: center; gap: 5px;
  height: 26px; padding: 0 10px 0 5px; border: 0; border-radius: 999px; cursor: var(--cur-pointer, pointer); white-space: nowrap;
  color: var(--text); font-size: var(--t-tiny); font-weight: 650; letter-spacing: var(--caps); text-transform: var(--label-case);
  background: color-mix(in srgb, var(--surface) 82%, transparent); backdrop-filter: blur(6px);
  box-shadow: 0 0 0 1px var(--line-strong), 0 2px 6px -2px rgba(0,0,0,.18);
  clip-path: inset(-20px round 999px);
  transition: background-color .2s ease, color .2s ease, box-shadow .25s ease, scale .25s var(--ease-out), clip-path .22s var(--ease-out);
}
.rmap-tag::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 28px; pointer-events: auto; }
.rmap-tag .ico { flex: none; }
.rmap-tag:hover, .rmap-tag.hot { scale: 1.06; background: var(--surface);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--text) 40%, var(--line)), 0 0 16px -3px color-mix(in srgb, var(--text) 35%, transparent); }
.rmap-tag.press .ico { animation: globe-press .4s var(--ease-out); }
.rmap-tag.on { background: var(--c); color: var(--on-accent); box-shadow: 0 0 0 1px var(--c), 0 0 14px -3px var(--c); }
.rmap-tag.clear { scale: none; clip-path: inset(-1px calc(100% - 27px) -1px -1px round var(--r-ui, 999px)); }
.rmap-tag.out, .rmap-g.out { opacity: .55; }   /* a region left out (Shift): faded, its name struck through */
.rmap-tag.out span { text-decoration: line-through; }
.rmap-tag.drag { pointer-events: auto; cursor: var(--cur-grab, grab); touch-action: none; outline: 2px dashed var(--accent); outline-offset: 2px; }   /* layout tool: drag the labels into place (regions.js) */
.rmap-panel.moving .rmap-map { pointer-events: none; }
.rmap-foot .rmap-move { flex: none; margin-right: 8px; }
.rmap-foot .rmap-move[hidden] { display: none; }
@media (max-width: 760px) { .rmap-foot .rmap-move, .settings-pop .move-labels-set { display: none; } }   /* phones have no labels */
@media (max-width: 760px), (hover: none) { .settings-pop .card-near-set { display: none; } }   /* pointers only: touch keeps its own card */
.rmap-tag:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rmap-tag .rmap-k { top: -6px; left: auto; right: -6px; }
/* the map: as large as the window allows, at the world's own shape, on a shaded-relief picture. Countries are
   thin borders; hover tints the region and makes the country glow; picked countries are tinted in their colour. */
.rmap-stage {
  position: relative; height: min(calc((var(--rmap-w) - 6px) / var(--map-aspect)), 47vh); border-radius: 16px; overflow: hidden;
  background: #a9c6dc;   /* the relief picture's sea, while it loads */
}
.rmap-map { position: relative; display: block; width: 100%; height: 100%; touch-action: none; user-select: none; }
/* something chosen: over the sea the pointer shows a clear mark, a click there clears it all */
.rmap.chosen .rmap-map { cursor: var(--cur-remove, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='26' viewBox='0 0 26 26'%3E%3Ccircle cx='13' cy='13' r='9.5' fill='%23fff' stroke='%23000' stroke-opacity='.45' stroke-width='3'/%3E%3Cpath d='M9.6 9.6l6.8 6.8M16.4 9.6l-6.8 6.8' stroke='%23222' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") 13 13, pointer); }
.rmap-map path {
  fill: var(--c); fill-opacity: 0; stroke: rgba(30, 32, 36, .5); stroke-width: .6; stroke-linejoin: round;
  vector-effect: non-scaling-stroke; cursor: var(--cur-pointer, pointer); transition: fill-opacity .18s ease;
}
/* the shaded-relief picture under the countries (data/relief.jpg, tools/build-relief.mjs); clicks pass to the sea */
.rmap-relief { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; filter: saturate(.5); }   /* colours 50% more muted, to sit with the UI */
:root[data-theme="dark"] .rmap-relief { filter: brightness(.72) saturate(.43); }   /* dark: dimmed, so it doesn't glare */
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .rmap-relief { filter: brightness(.72) saturate(.43); } }
.rmap .rmap-map.on-tag { cursor: var(--cur-pointer, pointer); }   /* a label's name, over the sea */
.rmap-map path.sea-land { fill-opacity: 0; stroke: none; pointer-events: none; }   /* land in no region answers as the sea */
.rmap-map path.reg { fill-opacity: 0; }
.rmap-map path.on { fill: var(--c); fill-opacity: .55; stroke: color-mix(in srgb, var(--c) 60%, #000); }
.rmap-map path.out { fill: #23262b; fill-opacity: .55; stroke: rgba(0, 0, 0, .55); }   /* left out (Shift): greyed, dark */
.rmap-map { --edge: color-mix(in srgb, var(--accent) 60%, #000); }   /* hover outline: the same orange as a picked country's edge */
.rmap-map.lit path.reg { fill-opacity: 0; stroke: var(--edge); stroke-width: .9; }   /* a region button hovered: its region deepens in its own colour */
.rmap-map path.hot { fill-opacity: 0; stroke: var(--edge); stroke-width: 1.2; }
.rmap-map.lit path.reg.on { fill: var(--c); fill-opacity: .55; }   /* a picked country keeps its fill while its region is hovered */
.rmap-map.lit path.reg.out { fill: #23262b; fill-opacity: .55; }
.rmap-map path.out.reg, .rmap-map path.out.hot { fill: #23262b; fill-opacity: .55; }
.rmap-map path.on.hot { fill: var(--c); fill-opacity: .55; stroke: var(--edge); }   /* the country under the pointer: its region's colour, a firmer edge */
/* The survey sheet (js/mapsheet.js): the timeline's own marks on the map, hidden until a theme shows .rmap-sheet and sets --map-grid and --map-edge */
.rmap-sheet, .rmap-n { display: none; }
.rmap-sheet { position: absolute; pointer-events: none; }
.rmap-grid, .rmap-aim { inset: 0; width: 100%; height: 100%; }
:is(.rmap-grid, .rmap-aim) path { fill: none; vector-effect: non-scaling-stroke; stroke-width: var(--hair); }
.rmap-lines { stroke: var(--map-grid); }
.rmap-zero { stroke: var(--map-edge); stroke-dasharray: 2 3; }
.rmap-world { stroke: var(--map-edge); stroke-dasharray: 4 3; }
.rmap-crosses { stroke: var(--grid-cross-ink); }
.rmap-scale { inset: 0; overflow: hidden; }
.rmap-scale span { position: absolute; padding: 1px 3px; font-family: var(--font-date); font-size: var(--t-tiny); line-height: 1; color: var(--muted); opacity: .75; white-space: nowrap; }
.rmap-scale .lon { translate: 2px 3px; }   /* just below the 60°S line, right of its meridian, as a row's dates sit right of their grid line */
.rmap-scale .lat { translate: 2px calc(-100% - 2px); }   /* on its parallel, inside the world's edge */
.rmap-note { right: 8px; bottom: 6px; font-family: var(--font-date); font-size: var(--t-tiny); line-height: 1; color: var(--muted); white-space: nowrap; }
@container (max-width: 640px) { .rmap-stage .rmap-scale, .rmap-stage .rmap-note { display: none; } }
/* the crosshair and its readings: only while a mouse is over the world */
.rmap-aim path { stroke: var(--accent); }
.rmap-stage:not(.aiming) :is(.rmap-aim, .rmap-read) { visibility: hidden; }
.rmap-read { left: 0; top: 0; padding: 2px 4px; background: var(--solid, var(--accent)); color: var(--on-accent); font-family: var(--font-date); font-size: var(--t-tiny); font-weight: 500; line-height: 1; white-space: nowrap; }
/* selection brackets: four corners that glide to what is aimed at; a whole region adds a dashed frame and square handles */
.rmap-sel {
  --b: var(--sel-brackets, 9px); --i: var(--accent);
  background:
    linear-gradient(var(--i) 0 0) 0 0 / var(--b) var(--hair), linear-gradient(var(--i) 0 0) 0 0 / var(--hair) var(--b),
    linear-gradient(var(--i) 0 0) 100% 0 / var(--b) var(--hair), linear-gradient(var(--i) 0 0) 100% 0 / var(--hair) var(--b),
    linear-gradient(var(--i) 0 0) 0 100% / var(--b) var(--hair), linear-gradient(var(--i) 0 0) 0 100% / var(--hair) var(--b),
    linear-gradient(var(--i) 0 0) 100% 100% / var(--b) var(--hair), linear-gradient(var(--i) 0 0) 100% 100% / var(--hair) var(--b);
  background-repeat: no-repeat; opacity: 0;
  transition: left .14s var(--ease-out), top .14s var(--ease-out), width .14s var(--ease-out), height .14s var(--ease-out), opacity .1s linear;
}
.rmap-sel.snap { transition: opacity .1s linear; }
.rmap-sel.show { opacity: 1; }
.rmap-sel.framed { outline: var(--hair) dashed var(--accent); outline-offset: calc(-1 * var(--hair)); }
.rmap-sel.framed::before, .rmap-sel.framed::after {
  content: ""; position: absolute; width: var(--frame-handle, 5px); height: var(--frame-handle, 5px); background: var(--accent);
}
.rmap-sel.framed::before { left: calc(var(--frame-handle, 5px) / -2); top: calc(var(--frame-handle, 5px) / -2); }   /* at the corners: a region's own label often sits over its sides */
.rmap-sel.framed::after { right: calc(var(--frame-handle, 5px) / -2); bottom: calc(var(--frame-handle, 5px) / -2); }
@media (prefers-reduced-motion: reduce) { .rmap-sel { transition: none; } }
.rmap-ripple {
  position: absolute; width: 120px; height: 120px; margin: -60px 0 0 -60px; border-radius: 50%; pointer-events: none;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 60%, transparent), 0 0 22px color-mix(in srgb, var(--accent) 40%, transparent);
  animation: rmap-ripple .6s cubic-bezier(.2, .9, .25, 1) forwards;
}
@keyframes rmap-ripple { from { scale: .1; opacity: 1; } to { scale: 1; opacity: 0; } }
/* the country under the pointer */
.rmap-tip {
  position: absolute; left: 0; top: 0; display: flex; align-items: baseline; gap: 8px; padding: 5px 10px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow);
  white-space: nowrap; pointer-events: none; opacity: 0; transition: opacity .12s ease;
}
.rmap-tip.show { opacity: 1; }
.rmap-tip b { font-size: var(--t-name); font-weight: 550; }
/* Country flags: one rectangular drawing for each country, at one 4:3 shape everywhere (tip, picks, the header button with one country,
   search). --flag-w sets the size (whole pixels at 4:3); a hair of edge keeps a white flag on a white sheet. Corners follow --r-ui; a theme restyles only
   its pictures' filter. They are decoration beside the name, so they take no clicks. */
.flag {
  display: inline-block; flex: none; align-self: center; width: var(--flag-w, 20px); height: calc(var(--flag-w, 20px) * .75);
  object-fit: cover; border-radius: min(1px, var(--r-ui, 1px)); pointer-events: none;
  box-shadow: 0 0 0 var(--hair) color-mix(in srgb, var(--text) 22%, transparent);
}
.rmap-tf { display: flex; align-self: center; }
.rmap-tf:empty { display: none; }
.rmap-chip, .gs-only { --flag-w: 16px; }
.rmap-ico .flag { margin-inline: 1px; align-self: center; }   /* 22px wide, as the globe it stands in for */
.gs-ic { --flag-w: 24px; }
.gs-row.place .gs-ic { --flag-w: 28px; }
.gs-row .gs-ic:has(> .flag) { background: none; }   /* a flag has its own edge: no tile behind it */
.rmap-tip b + span { color: var(--muted); font-size: var(--t-tiny); font-weight: 600; letter-spacing: var(--caps); text-transform: var(--label-case); }
/* what is chosen, centred under the globes: a capsule per choice, tinted in its region's colour, a click removes it */
.rmap-foot { display: flex; justify-content: center; align-items: center; min-height: 32px; padding: 2px 6px 8px; text-align: center; }
.rmap-hint { color: var(--muted); font-size: var(--t-meta); }
.rmap.chosen .rmap-hint { display: none; }
.rmap-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; }
.rmap-chip {
  display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px; border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--c) 14%, transparent); color: var(--c); cursor: var(--cur-pointer, pointer); font: inherit;
  font-size: var(--t-label); font-weight: 600; letter-spacing: var(--caps); text-transform: var(--label-case);
  transition: background-color .2s ease;
}
.rmap-chip:hover { background: color-mix(in srgb, var(--c) 24%, transparent); }
.rmap-chip span { font-size: 13px; opacity: .7; }
.rmap-chip s { text-decoration: none; }
body[data-view="data"] .rmap { display: none; }
/* The visible-years label repeats the date ruler; it gives way first when the header runs short */
@media (max-width: 2100px) {
  .range { display: none; }
}
@media (1601px <= width <= 2100px) { .gsearch { flex-basis: 260px; } }
/* Then the header buttons' labels fold away to their icons (named by their tooltips), phones included */
@media (max-width: 1800px) {
  .hdr-tx { display: none; }
  .zoom .fit { width: var(--hdr-c); padding: 0; }
  .fit-ic { display: block; }
  .hdr-cap .hdr-tx-btn { width: var(--hdr-c); padding: 0; }
  .hdr-btn { width: var(--hdr-c); padding: 0; justify-content: center; }
}
/* the panel's footer, under every tab: Version history on the left, how the menu opens small on the right */
.settings-foot { display: flex; flex: none; align-items: center; justify-content: space-between; gap: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
.settings-link { color: var(--accent); font-size: 12.5px; font-weight: 550; text-decoration: none; }
.settings-pop .settings-foot .seg { flex: none; margin: 0; }
.settings-pop .settings-foot .seg button { padding-inline: 8px; font-size: var(--t-caps); }
@media (hover: hover) and (pointer: fine) { .settings-pop .settings-foot .seg button { height: 22px; padding-block: 0; } }   /* touch keeps the full-height target */
.settings-link:hover { text-decoration: underline; }
/* Compact header when space is short: icon-only Overview, short era labels */
.short { display: none; }
@media (max-width: 1900px) {
  .top .long { display: none; }
  .top .short { display: inline; }
}
/* A hovered bar always floats above the others (nested people included), and above selected ones */
.item:is(:hover:not(.touch-input *), .hov), .item.guest:is(:hover:not(.touch-input *), .hov) { z-index: 6; }
/* Item card: resizable from its bottom-right corner (size remembered by js/cardmove.js) */
.detail {
  min-width: 260px; min-height: 180px; max-width: min(720px, 80vw); display: flex; flex-direction: column;
  overflow: hidden; padding: 0;
}
.detail[hidden] { display: none; }
/* the card scrolls as one piece: handle, header, ruler and content together (the wide form keeps its columns, below) */
.d-scroll { position: relative; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.detail-body { flex: 1 1 auto; min-height: 0; padding: 18px 18px 16px; }
/* Paging tip (js/pagetip.js): a small see-through pill centred over the top of the card (over its picture), saying
   the entries beside it are an arrow key or a swipe away, with a ×. It rises in a moment after the card (the
   tooltip's timing) and fades out when used or hidden. */
.d-tip {
  position: absolute; z-index: 5; top: 50px; left: 0; right: 0; width: max-content; max-width: calc(100% - 32px);
  margin: 0 auto; display: flex; align-items: center; gap: 7px; padding: 4px 4px 4px 11px; border-radius: 999px;
  font-size: var(--t-meta); line-height: 1.3; white-space: nowrap; color: var(--text);
  background: color-mix(in srgb, var(--surface) 42%, transparent); backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4); box-shadow: 0 0 0 1px color-mix(in srgb, var(--surface) 45%, transparent), 0 4px 16px -6px rgb(0 0 0 / .25);
  animation: tip-in .28s cubic-bezier(.2, .9, .25, 1) .7s backwards;
}
.d-tip.out { opacity: 0; transition: opacity .14s ease; }
@keyframes tip-in { from { opacity: 0; translate: 0 3px; } }
.d-tip-keys { flex: none; display: flex; gap: 3px; }
.d-tip kbd {
  font: inherit; font-size: var(--t-tiny); font-weight: 600; min-width: 18px; padding: 0 4px; line-height: 17px;
  text-align: center; border-radius: 5px; color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.d-tip-swipe { flex: none; color: var(--accent); }
.d-tip .d-tip-x { width: 20px; height: 20px; border-radius: 50%; opacity: 1; color: var(--muted); }
/* The peek (js/pagetip.js): the next card, drawn inside the card's own spot, shows at the edge it slid away from */
.peek-win { position: fixed; z-index: 19; overflow: hidden; pointer-events: none; }
.peek-card {
  position: absolute; top: 0; width: 100%; height: 100%; border-radius: inherit; overflow: hidden;
  background: var(--surface); box-shadow: inset 0 0 0 1px var(--line);
}
.peek-pic { height: 46%; background: color-mix(in srgb, var(--c) 18%, var(--surface)); }
.peek-pic img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 25%; display: block; }
.peek-card b { display: block; padding: 12px 18px; font-size: var(--t-hero); font-weight: 600; letter-spacing: -.025em; line-height: 1.12; color: var(--text); }
@media (prefers-reduced-motion: reduce) { .d-tip { animation: none; } }
/* Resize corner (region map, card): a short arc that follows the rounded corner; brighter on hover */
.sizer {
  position: absolute; right: 2px; bottom: 2px; z-index: 4; width: 20px; height: 20px; display: grid;
  place-items: end; padding: 2px; cursor: var(--cur-nwse, nwse-resize); touch-action: none; color: var(--faint);
  opacity: .7; transition: color .2s ease, opacity .2s ease;
}
:is(.detail, .rmap-panel):hover .sizer { opacity: 1; }
.sizer:hover, .resizing .sizer { color: var(--accent); opacity: 1; filter: drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 60%, transparent)); }
/* on the map: no box, only the arc, sharing the centre of the map's rounded corner (16px); the hit area fills that
   corner. A soft halo in the surface colour keeps it readable over ice and sea alike */
.rmap-panel .sizer {
  right: 3px; bottom: 3px; z-index: 2; width: 28px; height: 28px; padding: 6px; opacity: 1;
  color: color-mix(in srgb, var(--text) 62%, transparent);   /* the text ink: the map is pale in light themes, dimmed in dark */
  filter: drop-shadow(0 0 1px var(--surface));
}
.rmap-panel .sizer:hover, .rmap-panel.resizing .sizer { color: var(--accent); filter: drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 60%, transparent)); }
.detail.resizing { -webkit-user-select: none; user-select: none; }
/* =====================================================================
   Typography system
   Inter (variable, optical sizes) throughout, card titles and notes included. Rules: STYLE-GUIDE.md. Each timeline level steps down in size, weight and tint, and its left edge
   changes too (solid, thinner, dashed), so depth reads before the words do.
   ===================================================================== */
/* One family (Inter) and one scale. Sizes step down by level; capitals are kept for titles and labels you scan, and
   always share one tracking, so a caps label is a little smaller than the mixed-case text it sits beside. */
:root {
  --t-display: 22px;   /* page titles (style guide) */
  --t-hero: 28px;      /* card title, set on the card's picture */
  --t-body: 14px;      /* reading text in the card */
  --t-title: 13px;     /* category titles (caps) */
  --t-ui: 13px;        /* menus, card facts, search */
  --lbl-k-years: .8; --lbl-k-date: .78; --lbl-min-years: 9px; --lbl-min-date: 8.5px;   /* the label ladder: years and dates, sized from the name */
  --ink-years: 84%; --ink-date: 68%; --lbl-tint: 20%; --years-fill-a: 16%; --ink-date-gap: 6%;   /* the date slider's top stops this far under the years' ink */
  --t-name: 12.5px;    /* names: people */
  /* Bar levels: one ladder. --t-level is the second level (sub-periods); the first level is one --t-ratio step up, the
     third one step down, and each step also changes style or ink (--period-ink-*). A theme sets only these two and
     its fonts; the sizes are worked out where they are used, so a theme's values reach them. */
  --t-level: 12.5px;
  --t-ratio: 1.04;     /* small here: first-level bars are caps and bold, which already read larger */
  /* a theme whose first level has no bold weight lifts it a little more with --t-lift (default 1) */
  --t-bar: calc(var(--t-level) * var(--t-ratio));    /* first level, as the default look draws it (style guide) */
  --t-small: calc(var(--t-level) / var(--t-ratio));  /* third level (phases) */
  --t-meta: 11.5px;    /* dates beside names */
  --t-caps: 11px;      /* header controls and small caps labels */
  --t-label: 10.5px;   /* small labels: card category, menu groups, settings (caps) */
  --group-ink: color-mix(in srgb, var(--faint) 50%, var(--text));   /* sidebar group headings: halfway from faint to text */
  --t-tiny: 9.5px;     /* durations, card field names (caps) */
  --caps: .08em;       /* tracking for all capitals */
  --caps-wide: .1em;   /* category titles */
}
body {
  background: var(--bg); color: var(--text); font: 14px/1.4 var(--font-ui);
  -webkit-font-smoothing: antialiased; overflow: hidden; font-optical-sizing: auto;
  font-feature-settings: "cv11" 1;
}
/* figures line up wherever numbers are compared */
.lbl .d, .lbl .dur, .lane-ct, .cat .ct, .gs-meta, .detail dl, .tick > span, .lane-scale span, .range, .vbar-label {
  font-variant-numeric: tabular-nums;
}
.lbl .d, .tick > span, .lane-scale span, .zoomsel span, .lbl .dur, .nest-ct, .lane-ct, .lane-note,
  .nest-pill, .nest-btn, .cat .ct, .cat-tag-ct, .sheet-tab .ct, .sh-span { font-family: var(--font-date); }
/* tags speak like the dates: lengths and counts on bars, counts on category titles, sidebar, hover tag and phone tabs,
   the "Scientists & Innovators 126" pill and "show inside" button on titles; the title's note stays italic and light */
.lbl .d, .tick > span, .lane-scale span, .lbl .dur, .nest-ct, .lane-ct,
  .nest-pill, .nest-btn, .cat .ct, .cat-tag-ct, .sheet-tab .ct, .sh-span { font-style: var(--date-style); }
/* Timeline: category > parent bar > sub-period > person or phase > meta */
/* breathing room between a category's title bar and its first row (margin, so bar positions are untouched) */
.lane-b {
  transition: height .3s cubic-bezier(.2,.8,.2,1); will-change: transform; position: relative;
  margin-bottom: 16px; margin-top: 8px;
}
.lane-ct {
  font-weight: 600; color: color-mix(in srgb, var(--c) 75%, var(--text));
  background: color-mix(in srgb, var(--c) 12%, transparent); border-radius: 6px; padding: 1px 7px;
  font-size: 11px;
}
.lane-of { font-weight: 450; opacity: .7; }
/* the name's forms (full, short) share one cell, so swapping them is a crossfade in place */
.lbl .nb { display: grid; grid-template-columns: minmax(0, 1fr); flex: 0 1 auto; min-width: 2.5em; overflow: hidden; }
.lbl .n {
  grid-area: 1 / 1; overflow: hidden; text-overflow: ellipsis; min-width: 0; font-size: var(--n-size);
  font-weight: 550; letter-spacing: -.005em;
}
.item.lvl2 {
  height: calc(22px * var(--bar-th, 1)); margin-top: calc(2px + (22px - 22px * var(--bar-th, 1)) / 2); --bar-fill: color-mix(in srgb, var(--c) calc(var(--bar-tint) * .62), var(--surface));
  border-left-width: 2px;
}
.item.lvl2 .lbl .n { font-weight: 500; }
.item.lvl3 .lbl .n { font-weight: 500; }
.item.guest .lbl .n { font-weight: 500; }
.item:is(.person, .guest) .lbl .n { -webkit-text-stroke: var(--tune-pb, 0em) currentColor; }   /* Experimental text tuner: people names */
/* Label ladder, every theme: name first, then the years, then the dates. Both step down from the name: size (--lbl-k-*
   of --n-size), weight (500, 400) and ink (--ink-years, --ink-date of the label's own colour, whatever the bar's state,
   so a hovered or solid bar needs no rule of its own). The order is shared, the values are defaults: a theme tunes its own through these tokens, never by rules that break the order
   (STYLE-GUIDE.md -> Labels on bars; tools/theme-check.js tests every role and state) */
.item { --n-size: calc(var(--t-level) * var(--t-ratio) * var(--t-lift, 1) * var(--tune-1, 1)); }
.item.lvl2 { --n-size: calc(var(--t-level) * var(--tune-2, 1)); }
.item.lvl3 { --n-size: calc(var(--t-level) / var(--t-ratio) * var(--tune-3, 1)); }
.item:is(.person, .guest) { --n-size: calc(var(--t-name) * var(--tune-p, 1)); }
.lbl .d {
  overflow: hidden; text-overflow: ellipsis; display: none; opacity: 0; flex: 0 100 auto; min-width: 0;
  font-size: max(var(--lbl-min-date), calc(var(--n-size) * var(--lbl-k-date)));
  /* a thin outline sets the dates apart from the name, in their own fading ink */
  padding: 0 5px; line-height: 15px; border: var(--hair) solid color-mix(in srgb, currentColor calc(45% * var(--d-int, 1)), transparent); border-radius: 4px;
}
/* the date slider (--d-lo, --d-hi) runs from nothing to just under the years' ink, never past it */
:is(.lbl .d, .sh-span) {
  font-weight: 400;
  color: color-mix(in srgb, color-mix(in srgb, var(--c) var(--lbl-tint), var(--date-base, currentColor)) calc(var(--ink-date) * var(--d-lo, 1) + (var(--ink-years) - var(--ink-date-gap) - var(--ink-date)) * var(--d-hi, 0)), transparent);
}
:is(.lbl .d, .sh-span) :is(.era, .ca) { font-weight: 400; color: color-mix(in srgb, currentColor 75%, transparent); }
:is(.lbl .d, .sh-span) .to { margin: 0 3px; font-weight: 400; }
/* Menus: group labels whisper, items speak, sub-items step back */
.cats .cat-group { font-size: var(--t-label); font-weight: 600; letter-spacing: var(--caps); }
.cat { font-size: 14px; font-weight: 500; }
.cats li.sub .cat { padding-top: 5px; padding-bottom: 5px; font-size: 13px; font-weight: 450; color: var(--muted); }
.cats li.sub .cat:hover { color: var(--text); }
.cat .ct { font-size: 12px; }
.seg button {
  border: 0; background: transparent; cursor: var(--cur-pointer, pointer); padding: 5px 11px; border-radius: 999px;
  font-weight: 500; color: var(--muted); white-space: nowrap;
  transition: background-color .25s ease, color .25s ease, scale .4s cubic-bezier(.3, 1.5, .4, 1);
  font-size: 13px;
}
.settings-h {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 4px; font-weight: 600; text-transform: var(--label-case); color: var(--muted); font-size: var(--t-label);
  line-height: 1.3; letter-spacing: var(--caps);
}
.settings-h::after { content: ""; flex: 1; height: 1px; background: var(--line-strong); }   /* the theme's rule runs on from a group's name, like a category's title */
/* Info card: a reading surface */
.detail h2 { font-size: var(--t-hero); font-weight: 600; letter-spacing: -.025em; line-height: 1.12; margin: 2px 0 10px; }
/* the facts on a panel tinted in the category colour, running to the card's edges: two columns of label above
   value with a quieter line under it; the place takes the full width below a hairline */
.d-facts {
  margin: 14px -18px 0; padding: 14px 18px; font-size: var(--t-body);
  background: color-mix(in srgb, var(--c) 6%, var(--surface-2));
  border-block: 1px solid color-mix(in srgb, var(--c) 10%, var(--line));
}
.detail dl { margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 14px 16px; }
.detail dl > div { display: grid; gap: 3px; align-content: start; }
.detail dl small { font-size: var(--t-ui); color: var(--muted); }
.d-place {
  grid-column: 1 / -1; margin: 0 -8px; padding: 12px 8px 4px; border-top: 1px solid color-mix(in srgb, var(--c) 12%, var(--line));
  border-radius: 0 0 6px 6px; cursor: var(--cur-default, default); outline: none;
}
.d-place dd { display: flex; align-items: flex-start; gap: 6px; }
.d-pin { margin-top: 3px; }
.d-place small { padding-left: 17px; }   /* under the name, past the pin */
.d-pin { flex: none; color: color-mix(in srgb, var(--c) 70%, var(--muted)); transition: color .15s; }
.d-place:is(:hover, :focus-visible) .d-pin { color: var(--c); }
.d-place:focus-visible { box-shadow: inset 0 0 0 1.5px var(--accent); }
/* notes: a label, then reading text */
.d-about { margin: 18px 0 0; }
/* the fact check tag, bottom right under the text (js/factcheck.js), sized like the card's compare button;
   the Game cards share it (lab/game/css/card.css) */
.d-fact { display: flex; width: fit-content; height: 24px; margin: 8px 0 0 auto; padding: 0 9px 0 7px; gap: 5px; font-size: var(--t-label); text-decoration: none; }
.d-fact[hidden] { display: none; }
.d-fact .ico { flex: none; }
.d-about h3, .detail dt {
  margin: 0; color: var(--faint); font-size: var(--t-tiny); font-weight: 600; letter-spacing: var(--caps);
  text-transform: var(--label-case);
}
.detail p {
  margin: 6px 0 0; font-size: var(--t-body);
  line-height: 1.55; letter-spacing: -.011em; color: color-mix(in srgb, var(--text) 80%, var(--muted));
}
.detail h2, .detail .portrait, .detail dl > div:not(.d-place) { cursor: var(--cur-grab, grab); }
.detail .gallery:not(.hero) { margin-top: 18px; }
/* ---------- Capitals for labels: things you scan get spaced caps, names stay in mixed case ---------- */
.lane-name {
  font-family: var(--font-head); font-size: calc(var(--title-size, var(--t-title)) * var(--tune-t, 1) * var(--title-fit, 1)); font-weight: var(--title-weight, 700);
  letter-spacing: var(--title-ls, var(--caps-wide)); text-transform: var(--title-case, uppercase); line-height: var(--title-lh, inherit);
  /* bold for a head font that has no bold weight: a theme sets --title-stroke (a fraction of an em); --tune-* are the
     Experimental text size tuner's factors (settings.js), 1 and 0em when untouched */
  -webkit-text-stroke: calc(var(--title-stroke, 0em) + var(--tune-tb, 0em)) currentColor;
  color: color-mix(in srgb, var(--c) 65%, var(--text));
}
.era { font-size: .8em; letter-spacing: .06em; font-weight: 500; }
.lbl .dur {
  display: none; opacity: 0; flex: none; overflow: hidden; margin-right: -2px; padding: 0 5px; border-radius: 4px; font-weight: 500;
  line-height: 15px; font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, color-mix(in srgb, var(--c) var(--lbl-tint), currentColor) var(--ink-years), transparent);
  background: color-mix(in srgb, var(--years-tint, var(--c)) var(--years-fill-a), transparent); text-transform: uppercase;
  letter-spacing: var(--caps); font-size: max(var(--lbl-min-years), calc(var(--n-size) * var(--lbl-k-years)));
}
/* Header: every text control speaks in the same small caps */
#eraSeg button, #viewSeg button, .zoom .fit, .hdr-tx, .rmap-cur, .gsearch input::placeholder {
  font-size: var(--t-caps); font-weight: 600; letter-spacing: var(--caps); text-transform: var(--label-case);
}
/* moved into Settings (narrow screens), the dating switch speaks like the other settings */
.settings-pop #eraSeg button { font-family: var(--font-side); font-size: var(--set-size); font-weight: 500; letter-spacing: normal; text-transform: none; }
/* People who died in modern times: a small blossom beside their age */
.lbl .dur .died { display: inline-block; vertical-align: 0; margin-left: 5px; --died-base: 80%; }
/* every death mark (bars and card): thickness --died-w (factor, 1 = usual) and opacity --died-a (0-1) / --died-up
   (0-1, past 100%: fully opaque, darker), Settings → Rows → Dates and marks. Faded by the colour's alpha, not
   opacity, so Safari doesn't paint each mark on its own layer */
.died {
  overflow: visible;
  color: color-mix(in srgb, color-mix(in srgb, var(--text) calc(60% * var(--died-up, 0)), currentColor)
    calc(var(--died-base, 100%) * var(--died-a, 1) + (100% - var(--died-base, 100%)) * var(--died-up, 0)), transparent);
}
.died path { stroke-width: calc(1.5px * var(--died-w, 1) * var(--icon-w)); }
.died circle { transform-box: fill-box; transform-origin: center; transform: scale(var(--died-w, 1)); }
.detail h2 .died-mark {
  display: inline-block; margin-left: 8px; color: var(--muted); vertical-align: 2px; white-space: nowrap;
}
.detail h2 .died-mark .died { width: 22px; height: 11px; }
/* Photo slides: crop from the top, so faces stay in view (most of all for people) */
.g-slide img { width: 100%; height: 100%; object-fit: cover; display: block; object-position: 50% 30%; }
.gallery.people .g-slide img { object-position: 50% 12%; }
/* Reading text is always upright */
.detail p, .detail h2 { font-style: normal; font-synthesis: none; }
/* Hovered bars grow to fit their name, so text never runs past the end (min-width beats the set width) */
.item:not(.open-parent):is(:hover:not(.touch-input *), .hov) { min-width: max-content; padding-right: var(--shift, 0px); }
.item.tiny:is(:hover:not(.touch-input *), .hov) .lbl { visibility: visible; }
/* Tall photos are shown whole over a blurred copy of themselves */
.g-slide.fit { overflow: hidden; }
.g-slide.fit::before { --bl-out: 20px; --bl: 18px; --bl-sat: 1.1; opacity: .55; }
.g-slide.fit img { position: relative; object-fit: contain; object-position: 50% 50%; }
/* Search pictures: the same shapes as on the timeline bars, with the same thin coloured ring */
.gs-slot { flex: none; width: 38px; display: grid; place-items: center; }
.gs-pic {
  display: block; overflow: hidden; background: color-mix(in srgb, var(--c) 30%, var(--surface));
  box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.5px color-mix(in srgb, var(--c) 55%, transparent);
}
.gs-pic img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.gs-pic.portrait { width: 28px; height: 28px; border-radius: 50%; }
.gs-pic.portrait img { object-position: 50% 20%; }
.gs-pic.symbol { width: 28px; height: 28px; border-radius: 50%; background: var(--pic-bg); }
.gs-pic.symbol img { object-fit: contain; padding: 3px; }
.gs-pic.tile { width: 28px; height: 28px; border-radius: 8px; }
.gs-pic.photo { width: 38px; height: 26px; border-radius: 5px; }
.gs-pic.canvas { width: 23px; height: 30px; border-radius: 3px; }
.gs-pic.arch { width: 23px; height: 30px; border-radius: 12px 12px 3px 3px; }
.gs-pic.shield { width: 28px; height: 33px; }
.gs-pic.hex { width: 34px; height: 30px; }
.gs-pic.signpost { width: 38px; height: 30px; }
.gs-pic.diamond { width: 34px; height: 34px; }
.gs-pic.pentagon { width: 33px; height: 31px; }
.gs-pic.pennant { width: 34px; height: 26px; }
.gs-pic.banner { width: 28px; height: 29px; }
.gs-pic.octagon { width: 31px; height: 31px; }
.gs-pic.trapezoid { width: 38px; height: 27px; }
.gs-pic.banknote { width: 38px; height: 26px; }
.gs-pic.book { width: 38px; height: 29px; }
.gs-pic.cloud { width: 38px; height: 27px; }
.gs-pic.wave { width: 38px; height: 28px; }
.gs-pic.capsule { width: 38px; height: 26px; border-radius: 13px; }
.gs-pic.semicircle { width: 40px; height: 22px; border-radius: 20px 20px 0 0; }
.gs-pic:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave) {
  position: relative; clip-path: var(--clip); box-shadow: none; background: color-mix(in srgb, var(--c) 55%, transparent);
}
.gs-pic:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave)::before { content: ""; position: absolute; inset: 1px; background: var(--surface); clip-path: var(--clip); }
.gs-pic:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave) img { position: absolute; inset: 2.5px; width: calc(100% - 5px); height: calc(100% - 5px); clip-path: var(--clip); }
/* Search: the category's icon, in its colour, before its name */
.gs-cat { display: inline-flex; align-items: center; gap: 4px; vertical-align: -2px; }
.gs-cat .ico { flex: none; color: var(--c); }

/* ---------- Bar starts: the picture leads the bar ----------
   The picture's left edge is where the bar begins, so the bar's rounded end sits hidden behind it. Pictures
   keep a white ring and then a ring in the category colour, and are a little larger than the bar, so they
   stand out over its edges. No coloured stripe. */
.item, .item.lvl2 { border-left: var(--edge-w, var(--hair)) solid var(--bar-line); border-radius: 7px; }
.item.lvl3 { border-left-style: dashed; }
/* Pictures lie over every label, and nothing (backing, shadow, glow, ring) shows outside a picture's own shape: the
   canvas drawing reads this switch (js/draw/look.js picShapeOnly). Record keeps its older drawing (0, skin-record.css) */
:root { --pic-shape-only: 1; }
/* --ring: the picture's ring colour, also used by the relationship rings (js/ties.js) */
.av { --ring: color-mix(in srgb, var(--c) 70%, transparent); box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.75px var(--ring); }
.item.parent .av { transform: none; }
.item:is(:hover:not(.touch-input *), .hov) .av, .item.parent:is(:hover:not(.touch-input *), .hov) .av { transform: scale(2.4); }
.item.cap-portrait .av { width: 30px; height: 30px; margin-top: -15px; border-radius: 50%; }
.av.photo { width: 40px; height: 30px; margin-top: -15px; border-radius: 5px; }
.av.tile { width: 30px; height: 30px; margin-top: -15px; border-radius: 7px; }
.av.canvas { width: 24px; height: 32px; margin-top: -16px; border-radius: 3px; }
.av.arch { width: 25px; height: 33px; margin-top: -17px; border-radius: 12.5px 12.5px 3px 3px; }
/* the bar's start curves inside the picture, so none of it shows around it */
.item.cap-portrait, .item.cap-seal { border-radius: 13px 7px 7px 13px; }
.item:is(.cap-photo, .cap-signpost) { border-radius: 5px 7px 7px 5px; }
.item.cap-tile { border-radius: 6px 7px 7px 6px; }
.item.cap-canvas { border-radius: 4px 7px 7px 4px; }
.item.cap-arch { border-radius: 5px 7px 7px 5px; }
.item:is(.cap-octagon, .cap-capsule) { border-radius: 13px 7px 7px 13px; }
/* The other shapes: the bar's start is an oval end (width / height), found by tools/ui-audit.js (barStarts) to stay
   inside the outline at every bar height; where no end fits inside a point or notch, the picture is also set a few
   px ahead of the bar (AV_AHEAD in js/app.js) */
.item.cap-semicircle { border-radius: 12px 7px 7px 12px / 13px 7px 7px 13px; }
.item.cap-shield { border-radius: 14px 7px 7px 14px / 10px 7px 7px 10px; }
.item.cap-hex { border-radius: 20px 7px 7px 20px / 10px 7px 7px 10px; }
.item.cap-diamond { border-radius: 24px 7px 7px 24px / 10px 7px 7px 10px; }
.item.cap-pennant { border-radius: 4px 7px 7px 4px / 13px 7px 7px 13px; }
.item.cap-pentagon { border-radius: 19px 7px 7px 19px / 13px 7px 7px 13px; }
.item.cap-trapezoid { border-radius: 28px 7px 7px 28px / 13px 7px 7px 13px; }
.item.cap-banknote { border-radius: 4px 7px 7px 4px / 13px 7px 7px 13px; }
.item.cap-banner { border-radius: 21px 7px 7px 21px / 12px 7px 7px 12px; }
.item.cap-book { border-radius: 23px 7px 7px 23px / 12px 7px 7px 12px; }
.item.cap-cloud { border-radius: 12px 7px 7px 12px / 13px 7px 7px 13px; }
.item.cap-wave { border-radius: 31px 7px 7px 31px / 13px 7px 7px 13px; }
/* Shield (wars) and hexagon (religion photos): cut shapes, so their two rings are drawn as layers inside the
   box (the ring colour behind, a surface-coloured cut-out over it, the picture inset on top) instead of shadows */
.av.shield { width: 30px; height: 35px; margin-top: -17.5px; }
.av.hex { width: 36px; height: 32px; margin-top: -16px; }
.av.signpost { width: 42px; height: 32px; margin-top: -16px; }
.av.diamond { width: 38px; height: 38px; margin-top: -19px; }
.av.pentagon { width: 36px; height: 34px; margin-top: -17px; }
.av.pennant { width: 40px; height: 30px; margin-top: -15px; }
.av.banner { width: 28px; height: 35px; margin-top: -17.5px; }
.av.octagon { width: 34px; height: 34px; margin-top: -17px; }
.av.trapezoid { width: 42px; height: 30px; margin-top: -15px; }
.av.banknote { width: 42px; height: 28px; margin-top: -14px; }
.av.book { width: 42px; height: 34px; margin-top: -17px; }   /* tall enough that its top notch clears the bar */
.av.cloud { width: 42px; height: 30px; margin-top: -15px; }
.av.wave { width: 42px; height: 30px; margin-top: -15px; }
.av.capsule { width: 42px; height: 28px; margin-top: -14px; border-radius: 14px; }
.av.semicircle { width: 44px; height: 24px; margin-top: -12px; border-radius: 22px 22px 3px 3px; }
.av:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave), .item:is(:hover:not(.touch-input *), .hov) .av:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave) {
  clip-path: var(--clip); border-radius: 0; background: var(--ring); box-shadow: none;
}
.av:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave)::after {
  inset: calc(var(--ro) - var(--rw)); padding: 0; border-radius: 0; mask: none; z-index: -1;
  background: var(--surface); clip-path: var(--clip); opacity: 1; transition: none;
}
.av:is(.shield, .hex, .signpost, .diamond, .pentagon, .pennant, .octagon, .trapezoid, .banknote, .banner, .book, .cloud, .wave) img { inset: var(--ro); width: calc(100% - 2 * var(--ro)); height: calc(100% - 2 * var(--ro)); border-radius: 0; clip-path: var(--clip); object-position: 50% 50%; }

/* ---------- Expandable bars: the same as any bar, with a stacked edge peeking out beneath (more inside) ---------- */
/* The edge is a sliver of the bar inside (.item.under, js/app.js: the first sub-period's level, else a person, at
   the layer inside): the theme's own bar rules give it that bar's fill, wash and edge, so a closed Baroque shows a
   person's bar beneath it. Only its place is set here. It starts just below the bar's own bottom line: a hovered bar
   is a stacking context, so the edge then paints over the bar and would hide that line. On hover it takes the hover
   edge with its bar. It reaches 4px below the bar and stays put while the bar swells (it only gets shallower), so it
   always keeps the 4px gap above the next row (ROW in js/app.js: 26px bar + 4px edge + 4px gap). */
.item.parent > .item.under {
  position: absolute; top: auto; left: 7px; right: 7px; bottom: -4px; width: auto; height: 3.25px; margin: 0; padding: 0;
  z-index: -1; pointer-events: none; box-shadow: none; border-top: 0;
  border-left-width: var(--edge-w, var(--hair)); border-top-left-radius: 0; border-top-right-radius: 0;
  transition: border-color .15s, bottom .18s cubic-bezier(.2,.8,.3,1.2), height .18s cubic-bezier(.2,.8,.3,1.2);
}
.item.parent.open-parent > .item.under, .lanes .item.brief > .item.under { visibility: hidden; }
.item.parent:is(:hover:not(.touch-input *), .hov) > .item.under {
  bottom: -2px; height: 1.25px;   /* the bar grew 2px down: the edge's bottom stays where it was */
  border-color: var(--hover-edge, color-mix(in srgb, var(--c) 48%, var(--bar-fill)));
}

/* ---------- First level of every category in spaced capitals (people keep their names as written) ---------- */
.item:not(.lvl2):not(.lvl3):not(.person) .lbl .n {
  font-family: var(--period-font, var(--font-caps)); text-transform: var(--period-case, uppercase);
  font-weight: var(--period-weight, 700); letter-spacing: var(--period-ls, var(--caps));
  -webkit-text-stroke: var(--tune-1b, 0em) currentColor;
  font-style: var(--period-style-1, var(--period-style, normal));
}
/* Period names (not people): a theme sets --period-font, --period-style, --period-case, --period-weight, --period-ls,
   --period-lh, --period-style-1 (first level only), the ladder (--t-level, --t-ratio, --t-lift) and the inks --period-ink-2/-3; the defaults are the look above */
.item:not(.person, .guest) .lbl .n { font-style: var(--period-style, normal); line-height: var(--period-lh, inherit); }
.item:not(.person, .guest):is(.lvl2, .lvl3) .lbl .n { font-family: var(--period-font, inherit); text-transform: var(--period-case, inherit); letter-spacing: var(--period-ls, -.005em); }
.item.lvl2:not(.person, .guest) .lbl .n { font-weight: var(--period-weight, 500); -webkit-text-stroke: var(--tune-2b, 0em) currentColor; }
.item.lvl3:not(.person, .guest) .lbl .n { font-weight: var(--period-weight, 500); -webkit-text-stroke: var(--tune-3b, 0em) currentColor; }
/* each step down is also a step lighter in ink; low specificity, so an opened or lit parent's own colour still wins */
:where(.item.lvl2:not(.person, .guest)) .lbl { color: var(--period-ink-2, var(--text)); }
:where(.item.lvl3:not(.person, .guest)) .lbl { color: var(--period-ink-3, color-mix(in srgb, var(--text) 60%, var(--muted))); }
/* the wash on period bars (parents and sub-periods): any background-image, e.g. a gradient into the fill */
.item:not(.person, .guest) { background-image: var(--period-bg, none); }

/* ---------- Seals: symbols (religions) in a soft hexagon, larger than the bar like other pictures ---------- */
.av.seal { width: 32px; height: 32px; margin: -16px 0 0 -4px; background: none; box-shadow: none; border-radius: 0; overflow: visible; }
.av.seal .seal, .gs-pic.seal .seal, .detail .portrait.seal .seal { display: block; width: 100%; height: 100%; }
.gs-pic.seal { width: 32px; height: 32px; background: none; box-shadow: none; overflow: visible; }
.detail .portrait.seal .seal { width: 92px; height: 92px; }
.detail.wide .portrait.seal .seal { width: 84px; height: 84px; }
.item:is(:hover:not(.touch-input *), .hov) .av.seal { box-shadow: none; }


/* =====================================================================
   Tablet and phone
   ===================================================================== */
/* Narrow screens (1600px and less): the header tightens, the search takes what is left between the region button and
   the controls (at most 380px), and the region name shortens (…) before anything else gives way. The sidebar is an
   icon rail from 1180px (js/settings.js) */
@media (max-width: 1600px) {
  .top { gap: 14px; padding: var(--section-gap) 14px; }
  .brand { gap: 14px; }
  .top-l { flex: 0 1 auto; }
  .brand { flex: none; }
  .rmap { flex: 0 1 auto; }
  .gsearch { flex: 1 1 160px; min-width: 160px; max-width: 380px; }
  .gsearch-list { right: auto; left: 50%; min-width: 0; width: max(100%, 360px); margin-left: calc(max(100%, 360px) / -2); }
  .controls { flex: 1 0 auto; gap: 8px; }
}
/* Phone landscape: height is what runs short, so the header keeps the phone's tighter 8px */
@media (max-height: 500px) { .top { padding-block: 8px; } }
/* When the header would not fit on one row, its dating, layout and theme switches move into Settings
   (js/headerfold.js measures it, .hdr-folded). Overview and Settings are then alone in their capsules: a round button
   the capsule's full size, its highlight filling it exactly */
.hdr-folded .zoom > .hdr-sep, .hdr-folded .hdr-look > .hdr-sep { display: none; }
.hdr-folded :is(.zoom, .hdr-look) { padding: 0; }
.hdr-folded :is(.zoom, .hdr-look) > button { width: var(--hdr-c); height: var(--hdr-c); }
body.hdr-folded :is(.zoom, .hdr-look) > button::before { inset: 0; }
/* still too wide once folded (js/headerfold.js): the zoom buttons go, as on phones (Overview stays), and the header's
   gaps tighten toward the phone's (a theme with wide lettering, Atlas, needs both at tablet portrait) */
.hdr-folded-more :is(#zoomOut, #zoomFit, #zoomIn) { display: none; }
.hdr-folded-more .top { gap: 8px; padding-inline: 10px; }
.hdr-folded-more :is(.top-l, .brand) { gap: 8px; }
/* Settings holds the theme now, so it stays in the Data view too (its timeline-only parts hide there) */
body.hdr-folded[data-view="data"] #settingsBtn { display: inline-flex; }
/* Tablet portrait: too little room left for the region name beside the view switch, so the region button is its
   globe alone, as on phones (the name squeezed the capsule into a sliver against "Data") */
@media (max-width: 880px) {
  .rmap-btn { width: var(--hdr-c); padding: 0; justify-content: center; }
  .rmap-cur, .rmap-chev { display: none; }
  .top-l { min-width: max-content; }   /* the title, view switch and globe never slide under the search */
  .gsearch { min-width: 88px; }        /* the search gives way instead (touch tablets have larger capsules) */
}
/* Phone: one header row: the menu button (the categories, title and view switch, in a drawer), the region globe
   (no name), the search, then Overview, Expand all and Settings as round icon buttons. Touch-sized capsules (40px).
   The item card is a bottom sheet */
.menu-btn, .menu-scrim, .side-brand { display: none; }
@media (max-width: 760px) {
  .top { --hdr-c: 40px; position: relative; gap: 6px; padding: 8px 10px; }
  .top-l, .controls { display: contents; }
  #zoomOut, #zoomFit, #zoomIn { display: none; }   /* pinch zooms; Overview resets the view */
  .menu-btn { display: inline-flex; align-items: center; justify-content: center; width: var(--hdr-c); padding: 0; flex: none; }
  .rmap { flex: none; }
  .rmap-btn { width: var(--hdr-c); padding: 0; justify-content: center; }
  .rmap-cur, .rmap-chev { display: none; }
  .gsearch { flex: 1 1 0; min-width: 0; max-width: none; gap: 6px; padding: 0 12px 0 11px; position: static; }
  .gsearch kbd { display: none; }
  .gsearch-list { top: calc(100% + 6px); left: 10px; right: 10px; width: auto; margin: 0; }
  .controls > .hdr-look { margin-left: auto; }   /* alone at the right in the Data view */
  /* the drawer: slides in from the left, under the header, over a dimmed timeline (js/menu.js) */
  .side {
    position: fixed; z-index: 50; top: var(--pop-top, 57px); bottom: 0; left: 0; width: min(320px, 86vw); padding: 14px 12px 24px;
    box-shadow: var(--shadow-drawer); overflow-y: auto; overscroll-behavior: contain;
    translate: -105% 0; visibility: hidden;
    transition: translate .3s cubic-bezier(.2,.8,.2,1), visibility 0s .3s;
  }
  body.menu-open .side { translate: 0 0; visibility: visible; transition: translate .3s cubic-bezier(.2,.8,.2,1); }
  /* faded by its colour and hidden when closed: Safari tints its own toolbars from fixed elements at the window's
     edges, and kept them dark while a see-through scrim was still there */
  .menu-scrim {
    display: block; position: fixed; inset: 0; z-index: 49; background: rgb(0 0 0 / 0);
    pointer-events: none; visibility: hidden; transition: background-color .25s ease, visibility 0s .25s;
  }
  :is(body.menu-open, body.settings-open) .menu-scrim { background: rgb(0 0 0 / .35); pointer-events: auto; visibility: visible; transition: background-color .25s ease; }
  /* the title and the Timeline/Data switch head the drawer; in the Data view they are all it holds */
  .side-brand { display: block; flex: none; margin: 0 0 12px; padding: 2px 8px 14px; border-bottom: 1px solid var(--line); }
  .side-brand .brand { display: flex; flex-direction: column; align-items: stretch; gap: 12px; }
  .side-brand h1 { font-size: 17px; }
  .side-brand #viewSeg { display: flex; }
  .side-brand #viewSeg button { flex: 1; height: 34px; }
  .set-row:has(#sideSeg) { display: none; }   /* the sidebar is the menu drawer here */
  body[data-view="data"] .side { display: flex; bottom: auto; padding-bottom: 0; border-bottom-right-radius: 16px; }
  body[data-view="data"] .side > :not(.side-brand) { display: none; }
  body[data-view="data"] .side-brand { margin: 0; border: 0; }
  /* Settings: a drawer from the right under the header, square at the screen edge like the menu drawer, with no pop-in
     (js/settings.js). It stays open while changes are made; a tap on the dim backdrop or Esc closes it */
  .settings-pop {
    inset: var(--pop-top, 57px) 0 0 auto; width: min(320px, 86vw); height: calc(100dvh - var(--pop-top, 57px)); padding: 8px 12px 16px;
    border-radius: 0; border-width: 0 0 0 1px; box-shadow: var(--shadow-drawer);
    translate: 105% 0; transition: translate .3s cubic-bezier(.2,.8,.2,1);
  }
  .settings-pop.slid { translate: 0 0; }
  body:is(.menu-open, .settings-open) .menu-scrim { top: var(--pop-top, 57px); }   /* the header stays clear under both drawers: its buttons can be tapped to close */
  .settings-pop.fresh { animation: none; }
  .stage { grid-column: 1; grid-row: 2; }
  .lane-name { white-space: nowrap; }
  .rmap-rail { display: flex; height: auto; padding: 3px; gap: 2px; }   /* phones: no names, the globes share the width */
  .rmap-tags { display: none; }
  .rmap-foot { padding-top: 8px; }
  .rmap-g { position: relative; top: 0; transform: none !important; flex: 1 1 0; justify-content: center; min-width: 0; height: 30px; padding: 0; }
  .rmap-g .ico { width: 22px; height: 22px; }
  .rmap-nm, .rmap-pill { display: none; }
  .detail {
    left: 0; right: 0; bottom: 0; top: auto; width: auto; height: auto; min-width: 0; max-width: none;
    min-height: 0; max-height: min(72dvh, calc(100dvh - var(--sheet-keep, 0px))); border-radius: 18px 18px 0 0; border-bottom: 0;   /* --sheet-keep: room for the bar above it (js/app.js) */
  }
  @media (max-height: 500px) { .detail { max-height: min(60dvh, calc(100dvh - var(--sheet-keep, 0px))); } }   /* short screens: room above it for the bar it shows (js/app.js, goTo) */
  .sizer { display: none; }
  /* a handle on top, scrolling with the rest: drag down on any of it, once scrolled to the top, to push it away
     (js/cardswipe.js) */
  .d-scroll { padding-top: 12px; }
  .d-tip { top: 62px; }   /* under the handle and header */
  .detail .grip { height: 18px; cursor: var(--cur-grab, grab); }
  .detail .grip::before { top: 6px; opacity: 1; }
  /* a person with relationship lines opens low on touch, so the lines stay in view (js/card.js, --sheet-peek); raised by a tap or a drag up */
  .detail { transition: background-color .2s, border-color .2s, color .2s, transform .42s cubic-bezier(.32,.72,0,1); }
  .detail.low { transform: translateY(max(0px, calc(100% - var(--sheet-peek, 170px) - var(--sheet-lift, 0px)))); }
  .detail.low .d-scroll { overflow: hidden; touch-action: none; }   /* every drag on a low sheet is the sheet's own: it raises or pages (js/cardswipe.js) */
  .detail.swiping { transition: none; }
  .detail.swiping .d-scroll { overflow: hidden; }
  .rmap-g { height: 36px; }
}

/* While a row settles after opening or closing (js/app.js rebuildAnimated: the rebuilt row, or all of them), hover effects apply at once, without the
   spring, so the picture under the pointer doesn't bounce */
:is(.lanes, .lane).settling :is(.item, .av, .lbl, .lbl :is(.n, .d, .d2, .dur), .nest-tg) { transition: none; }
/* and the bar or box under the pointer is lit at once (js/app.js rehover), so the hovered look carries over unbroken;
   so is the open/close toggle under it (.nest-tg.hov): rebuilt, it faded in from its resting colour on every open */
:is(.lanes, .lane).settling .frame, :is(.lanes, .lane).settling .frame::before, :is(.lanes, .lane).settling .frame::after { transition: none; }
/* bars and boxes rising from under an entry's bar as it opens take no pointer until they are out (js/app.js), so the
   pointer on that bar (its toggle, just clicked) stays on it rather than on a hidden bar beneath */
:is(.item, .frame).rising, :is(.item, .frame).rising .nest-tg { pointer-events: none; }
/* rebuilt bars show their label whole while rows glide: a fade of no length (not `none`, which would start the fade
   when this lifts), so the duration chip neither blinks out nor opens again */
:is(.lanes, .lane).settling .lbl :is(.n, .d, .dur) { animation-duration: 0s !important; }
/* Opening, closing or re-laying out rows holds the focus in place itself, frame by frame (js/app.js holdFocus).
   The browser's scroll anchoring would latch onto a row below the one closing and scroll along as it shrinks */
.lanes.pinning { overflow-anchor: none; }

/* =====================================================================
   Short bars: beads (js/shortbars.js). A bar is "short" when it is narrower than its first letters need;
   hovering one shows it in full again.
   ===================================================================== */
/* an opened box too narrow to show its contents is left out */
.lanes .frame.brief { display: none; }
/* The bead: the picture, small, on a hairline. On hover the bead stays a bead: its picture grows and the
   name floats beside it, instead of the bar stretching over its neighbours */
.lanes .item.brief:not(.open-parent) {
  background: none; border: 0; box-shadow: none; border-radius: 0; min-width: 0; padding-right: 0;
  height: 26px; margin-top: 0;
}
.lanes .item.brief:not(.open-parent)::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 13px; margin-top: -6.5px;
  /* chosen look: a 1px pale-orange line with a short vertical tick at its end */
  --bl-line: color-mix(in srgb, var(--bl-color, #e1b8a8) var(--bl-alpha, 100%), transparent);
  background:
    linear-gradient(var(--bl-line), var(--bl-line)) 100% 50% / var(--bl-w, 1px) var(--bl-s, 6px) no-repeat,
    linear-gradient(var(--bl-line), var(--bl-line)) 0 50% / 100% var(--bl-w, 1px) no-repeat;
}
.lanes[data-bl-end="none"] .item.brief:not(.open-parent)::before {
  background: linear-gradient(var(--bl-line), var(--bl-line)) 0 50% / 100% var(--bl-w, 1px) no-repeat;
}
.lanes[data-bl-end="dot"] .item.brief:not(.open-parent)::before {
  background:
    radial-gradient(circle at calc(100% - var(--bl-s, 6px) / 2) 50%, var(--bl-line) calc(var(--bl-s, 6px) / 2 - .5px), transparent calc(var(--bl-s, 6px) / 2)),
    linear-gradient(var(--bl-line), var(--bl-line)) 0 50% / calc(100% - var(--bl-s, 6px) / 2) var(--bl-w, 1px) no-repeat;
}
.lanes[data-bl-end="ring"] .item.brief:not(.open-parent)::before {
  background:
    radial-gradient(circle at calc(100% - var(--bl-s, 6px) / 2) 50%, transparent calc(var(--bl-s, 6px) / 2 - var(--bl-w, 1px) - .5px), var(--bl-line) calc(var(--bl-s, 6px) / 2 - var(--bl-w, 1px)), var(--bl-line) calc(var(--bl-s, 6px) / 2 - .5px), transparent calc(var(--bl-s, 6px) / 2)),
    linear-gradient(var(--bl-line), var(--bl-line)) 0 50% / calc(100% - var(--bl-s, 6px)) var(--bl-w, 1px) no-repeat;
}
.lanes[data-bl-end="bar"] .item.brief:not(.open-parent)::before {
  background:
    linear-gradient(var(--bl-line), var(--bl-line)) 100% 50% / var(--bl-w, 1px) var(--bl-s, 6px) no-repeat,
    linear-gradient(var(--bl-line), var(--bl-line)) 0 50% / 100% var(--bl-w, 1px) no-repeat;
}
.lanes[data-bl-end="round"] .item.brief:not(.open-parent)::before {
  background:
    radial-gradient(circle at calc(100% - var(--bl-w, 1px) / 2) 50%, var(--bl-line) calc(var(--bl-w, 1px) / 2 - .5px), transparent calc(var(--bl-w, 1px) / 2)),
    linear-gradient(var(--bl-line), var(--bl-line)) 0 50% / calc(100% - var(--bl-w, 1px) / 2) var(--bl-w, 1px) no-repeat;
}
.lanes .item.brief:not(.open-parent)::after { display: none; }
.lanes .item.brief:not(.open-parent) .av { display: grid; transform: scale(.62); transform-origin: 0 50%; }
.lanes .item.brief:not(.open-parent):is(:hover:not(.touch-input *), .hov) .av { transform: scale(1.5); }
.lanes .item.brief:not(.open-parent):not(.has-av, .has-photo)::after {
  content: ""; display: block; position: absolute; left: -3px; top: 50%; width: 9px; height: 9px; margin-top: -4.5px;
  bottom: auto; right: auto; border: 0; border-radius: 50%; background: var(--c);
}
.lanes .item.brief:not(.open-parent) .lbl { display: none; }
.lanes .item.brief:not(.open-parent):is(:hover:not(.touch-input *), .hov) .lbl {
  display: flex; visibility: visible; position: absolute; left: var(--tag-gap, calc(2 * var(--shift, 3px) + 5px)); top: 1px; height: 24px; pointer-events: auto;
  width: max-content; padding: 0 9px !important; transform: none;
}
/* Label tag: a hovered label wider than its bar (a bead's, or a narrow opened box's: .spill, js/shortbars.js) sits on
   an opaque tag above its neighbours, so it covers their labels instead of printing over them */
.lanes .item.brief:not(.open-parent):is(:hover:not(.touch-input *), .hov) .lbl,
.lanes .item.open-parent.spill:is(:hover:not(.touch-input *), .hov) .lbl,
.lanes .item:is(.tag-left, .tag-right):is(:hover:not(.touch-input *), .hov) .lbl {
  border-radius: 7px; overflow: visible; contain: none;
  background: var(--bar-fill);   /* the bar's own hovered fill (no alpha: it hides what is under it) */
  box-shadow: 0 0 0 .75px var(--hover-edge), var(--hover-glow);   /* a hovered bar's edge and glow */
}
/* (room at its end for the box's open/close toggle, which sits at the grown bar's end: it never covers the dates) */
.lanes .item.open-parent.spill:is(:hover:not(.touch-input *), .hov) .lbl { max-width: none; height: 24px; margin-top: 1px; padding-right: 34px; }
/* ...and at the window's right edge it opens to the left of the picture, clear of it, so nothing covers the dates
   (placeTag, js/app.js); the bar keeps its true width. With too little room on either side it wraps. */
.lanes .item:is(.tag-left, .tag-right):is(:hover:not(.touch-input *), .hov) { min-width: 0; padding-right: 0; }
.lanes .item:is(.tag-left, .tag-right):is(:hover:not(.touch-input *), .hov) .lbl {
  display: flex; visibility: visible; position: absolute; top: 1px; height: 24px; width: max-content; margin: 0;
  padding: 0 9px !important; transform: none; pointer-events: auto; z-index: 2;
}
.lanes .item.tag-left:is(:hover:not(.touch-input *), .hov) .lbl { left: auto !important; right: calc(100% + var(--tag-gap)); }
.lanes .item.tag-right:is(:hover:not(.touch-input *), .hov) .lbl { left: var(--tag-gap) !important; right: auto; }
.lanes .item.tag-wrap:is(:hover:not(.touch-input *), .hov) .lbl { max-width: var(--tag-max) !important; height: auto !important; min-height: 24px; flex-wrap: wrap; row-gap: 2px; padding-block: 3px !important; }
.lanes .item.tag-wrap:is(:hover:not(.touch-input *), .hov) .lbl :is(.nb, .n, .d) { white-space: normal; min-width: 0; max-width: 100%; }
/* Two lines (.tag-two, Experimental): the tag stays on the right, centred on the bar, its duration and name above its dates and count */
.lanes .item.tag-two:is(:hover:not(.touch-input *), .hov) .lbl { top: 50% !important; translate: 0 -50%; box-sizing: border-box; width: var(--tag-w) !important; max-width: none !important; height: auto !important; min-height: 24px; flex-wrap: wrap; gap: 2px 8px; margin: 0 !important; padding-block: 3px !important; }
.lanes .item.tag-two:is(:hover:not(.touch-input *), .hov) .lbl::after { content: ""; order: 1; flex: 0 0 100%; }   /* the line break */
.lanes .item.tag-two:is(:hover:not(.touch-input *), .hov) .lbl > :not(.dur, .nb) { order: 2; }
.lanes .item.tag-two:is(:hover:not(.touch-input *), .hov) .lbl .d { white-space: normal; }   /* both dating systems, in too little room: a line each */
.lanes .item.open-parent.tag-two:is(:hover:not(.touch-input *), .hov) .lbl { padding-right: 34px !important; }
.lanes .item.open-parent.tag-two:is(:hover:not(.touch-input *), .hov) > .nest-tg { left: var(--tag-tg); right: auto; z-index: 3; }
/* the gap between tag and picture still counts as the bar, so the pointer can move from one to the other */
.lanes .item.tag-right:is(:hover:not(.touch-input *), .hov) .lbl::before { content: ""; position: absolute; right: 100%; top: 0; bottom: 0; width: 6px; }
/* ...and a tag on the left is tied to its bar's picture by a thin line in the tag's own edge colour, level with the
   picture's centre however tall the tag wraps (--tag-link: the tag-to-picture gap, placeTag in js/app.js) */
.lanes .item.tag-left:is(:hover:not(.touch-input *), .hov) .lbl::after { content: ""; position: absolute; left: 100%; top: 0; bottom: 0; width: var(--tag-gap); background: linear-gradient(var(--hover-edge), var(--hover-edge)) 0 11px / var(--tag-link) 1px no-repeat; }
/* the tag sits just past the grown picture (--tag-gap, placeTag in js/app.js, from the picture's real width, the hover
   size and where it is pinned), and the gap between them still counts as the bar, so the pointer can move from one to the other */
.lanes .item.brief:not(.open-parent):is(:hover:not(.touch-input *), .hov) .lbl::before { content: ""; position: absolute; right: 100%; top: 0; bottom: 0; width: 6px; }
/* short names: shown instead of the full name while only they fit; hover shows the full name */
.n.ns { display: none; opacity: 0; }
.item.abbr:not(:hover:not(.touch-input *), .hov) .n:not(.ns) { display: none; opacity: 0; }
.item.abbr:not(:hover:not(.touch-input *), .hov) .n.ns { display: inline; opacity: 1; }
/* a name with no room fades out (e.g. as its bar slides off the edge of the screen) */
.item.unnamed:not(:hover:not(.touch-input *), .hov) .lbl .n { opacity: 0; }
/* Every part of a label fades in and out (js/shortbars.js picks them): the name's two forms crossfade in their
   shared cell, the dates fade at the end, and the duration also opens and closes its width, so the name beside it
   glides over rather than jumping. Opacity and display only (the duration's width too), never a layout of the row. */
.lbl :is(.n, .d) { transition: opacity .2s ease, display .2s allow-discrete; }
/* Both datings on, room for one: the second gives way before anything else, fading as it closes its width (js/shortbars.js
   oneD). Hovered, the whole label shows both, wrapping as text. */
.lbl .d2 { display: inline-block; vertical-align: bottom; overflow: hidden; white-space: pre; max-width: var(--d2-w, 30em); transition: opacity .2s ease, max-width .2s ease; }
.item.one-d .lbl .d2 { max-width: 0; opacity: 0; }
.item:is(:hover:not(.touch-input *), .hov) .lbl .d2 { display: inline; overflow: visible; white-space: normal; max-width: none; opacity: 1; }
.lbl .dur { max-width: 0; padding-inline: 0; margin-right: -8px;
  transition: opacity .2s ease, max-width .2s ease, padding .2s ease, margin .2s ease, display .2s allow-discrete; }
.item.show-d .d, .item.show-dur .dur, .item:is(:hover:not(.touch-input *), .hov) .d, .lanes:not(.no-dur) .item:not(.tiny):is(:hover:not(.touch-input *), .hov) .dur { display: inline; opacity: 1; }
.item.show-dur .dur, .lanes:not(.no-dur) .item:not(.tiny):is(:hover:not(.touch-input *), .hov) .dur { max-width: var(--dur-w, 8em); padding-inline: 5px; margin-right: -2px; }
.item.tiny .dur, .lanes.no-dur .dur { display: none; }   /* after the rules above, which they override */
/* appearing: a keyframe fade (it restarts by itself whenever a part is shown again); @starting-style would do the
   same but made Safari restyle every bar coming into view, slowing panning zoomed out */
.item.abbr .n.ns, .item .lbl .d { animation: lbl-in .2s ease; }
/* the full name coming back from its short form (js/shortbars.js swaps nfa/nfb to restart it), not on every bar
   that scrolls into view */
.item.nfa:not(.abbr) .n:not(.ns) { animation: lbl-in .2s ease; }
.item.nfb:not(.abbr) .n:not(.ns) { animation: lbl-in2 .2s ease; }
@keyframes lbl-in2 { from { opacity: 0; } }
.item .lbl .dur { animation: lbl-open .2s ease; }
@keyframes lbl-in { from { opacity: 0; } }
@keyframes lbl-open { from { opacity: 0; max-width: 0; padding-inline: 0; margin-right: -8px; } }
@media (prefers-reduced-motion: reduce) { .lbl :is(.n, .d, .d2, .dur) { transition: none; animation: none; } }
/* Too little room even for the short name: the name stays as it is and slides under a soft fade at the bar's end
   (a mask on the clipped names only, so it suits any background), never cut letter by letter */
.item.clip:not(:hover:not(.touch-input *), .hov) .lbl .nb {
  min-width: 0; -webkit-mask-image: linear-gradient(to left, transparent, #000 14px); mask-image: linear-gradient(to left, transparent, #000 14px);
}
.item.clip:not(:hover:not(.touch-input *), .hov) .lbl .n { text-overflow: clip; }
/* Nothing of a label is ever cut hard or pokes out at a bar's end: once the count and toggle (which stay whole while
   the name gives way) meet the end, the name has no room left, and every part (name, dates, duration, count,
   toggle) fades together under one soft mask on the label (js/shortbars.js "over"). In style.css, not a theme, so
   every theme and future one inherits it. */
.item:is(.over, .unnamed):not(:hover:not(.touch-input *), .hov) .lbl .nb { min-width: 0; }   /* an unseen name takes no room, so the count keeps its place */
.item.over:not(:hover:not(.touch-input *), .hov) .lbl {
  -webkit-mask-image: linear-gradient(to left, transparent, #000 18px); mask-image: linear-gradient(to left, transparent, #000 18px);
}
/* Dates reaching the end of a bar leaving on the left (js/shortbars.js "tail"): they stay and slide under the same
   soft fade, outline and all, rather than vanishing; the name keeps its whole width */
.item.tail:not(:hover:not(.touch-input *), .hov) .lbl .nb { flex-shrink: 0; }
.item.tail:not(:hover:not(.touch-input *), .hov) .lbl .d {
  text-overflow: clip; -webkit-mask-image: linear-gradient(to left, transparent, #000 14px); mask-image: linear-gradient(to left, transparent, #000 14px);
}
/* hovered bars grow to show their whole label */
.item:is(:hover:not(.touch-input *), .hov) { min-width: max-content; }
.item.has-av:is(:hover:not(.touch-input *), .hov) { padding-right: var(--shift, 26px); }
.item.has-photo:is(:hover:not(.touch-input *), .hov) { padding-right: var(--shift, 38px); }
.item:is(:hover:not(.touch-input *), .hov) .lbl :is(.nb, .n) { overflow: visible; }

/* The sidebar's expand/collapse button, under the rows (js/settings.js). It is one more row of the sidebar: the same box and place as a
   category row, the rail's ink and pen at rest, no fill of its own, and a row's hover in every theme (--sw-hi-fg, --cat-hover-bg, or the
   theme's hover list). A theme restyles those tokens, never this button. */
.side { display: flex; flex-direction: column; }
.side > .side-lock.side-lock {   /* past .side > * */
  flex: none; align-self: flex-start; display: grid; place-items: center; --gut: 12px; margin-left: var(--gut);
  width: 46px; height: 38px; padding: 0; border: 0; border-radius: 8px; cursor: var(--cur-pointer, pointer);
  color: var(--cat-mono, var(--muted)); background: none;
}
.side-lock { transition: color .12s, background-color .12s; }
.side > .side-lock.side-lock:hover { color: var(--sw-hi-fg, var(--text)); background: var(--cat-hover-bg, none); }
.side-lock svg { width: 22px; height: 22px; }
/* Switching the sidebar between shown and its icon rail (Settings, the lock) glides it like the rail on hover,
   over the timeline, which changes width only once, while covered, and keeps its rows still (js/settings.js) */
@media (min-width: 761px) {
  body.side-glide.side-auto .side { transition: box-shadow .24s; }
  body.side-push .side { animation: side-glide .24s cubic-bezier(.2,.8,.2,1) both; }
  body.side-glide.side-auto .side :is(.side-h, .hint, .nm, .ct, .eye) { transition-delay: 0s; }
  /* every row, people included, moves with the sidebar: no hover pause, the glide's own timing */
  body.side-glide.side-auto .side .cats li,
  body.side-glide.side-auto .side .cats :is(li.sub .sw, .sw.portrait) {
    transition-delay: 0s; transition-duration: .24s; transition-timing-function: cubic-bezier(.2,.8,.2,1);
  }
  /* the gliding sidebar is laid out exactly like the shown one (its padding eases between the two), so nothing
     shifts when it hands over; it casts no shadow, as the shown one doesn't */
  body.side-glide .side > * { width: calc(var(--side-w, 248px) - 25px); }   /* its padding and right border */
  body.side-opening.side-glide.side-auto .side { padding: 16px 12px; box-shadow: none; }
  body.side-opening .side .cat .sw { opacity: 1; }
  /* the row rails and titles ride along with the sidebar's edge (--push-a to --push-b, set on .lanes); the title's
     own rail becomes a pseudo element so it can move too */
  /* moved by left, not translate: Safari runs translate on its compositor, whose clock starts a few ms after the
     sidebar's width, so the two drifted apart in the first frames */
  body.side-push .lane::before,
  body.side-push .lane-h::before,
  body.side-push .lane-h :is(.lane-ic, .lane-name, .lane-ct, .lane-note, .nest-btn) {
    animation: side-push .24s cubic-bezier(.2,.8,.2,1) both;
  }
  body.side-push .lane-h :is(.lane-ic, .lane-name, .lane-ct, .lane-note, .nest-btn) { position: relative; }
  body.side-push .lane-h { box-shadow: none; }
  body.side-push .lane-h::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
    background: var(--rail);
  }
}
@keyframes side-push { from { left: calc(var(--push-a) * var(--push-k, 1)); } to { left: calc(var(--push-b) * var(--push-k, 1)); } }
@keyframes side-glide { from { width: var(--w-a); padding-inline: var(--p-a); } to { width: var(--w-b); padding-inline: var(--p-b); } }

.side-lock-arrow { transition: transform .25s cubic-bezier(.2,.8,.2,1); transform-origin: 11px 9px; }
.side-lock[aria-pressed="true"] .side-lock-arrow { transform: scaleX(-1); }
@media (max-width: 760px) { .side > .side-lock.side-lock { display: none; } }

/* Category tag: slides out of a hovered icon in the collapsed sidebar and joins it into one pill (js/cattag.js).
   It draws the icon itself, over the sidebar's: the icon's shape (24%) inside the same shape grown by --pad (3px)
   all round, so the tag's left end takes the category's shape and the icon is encased top and bottom. The tag
   carries the category's shape class (shape tokens above); from the shape's middle on it is a plain bar. */
.cat-tag {
  --pad: 3px; --tint: color-mix(in srgb, var(--c) 16%, var(--surface)); --cap: calc(var(--sw) + 2 * var(--pad));
  position: fixed; z-index: 60; display: flex; align-items: center; gap: 7px;
  height: calc(var(--sh) + 2 * var(--pad)); margin-top: calc(var(--sh) / -2 - var(--pad));
  padding: 0 11px 0 0; border-radius: 0 var(--cat-tag-r, 9px) var(--cat-tag-r, 9px) 0; pointer-events: none; white-space: nowrap;
  color: color-mix(in srgb, var(--c) 70%, var(--text));
  background: linear-gradient(90deg, transparent calc(var(--cap) / 2), var(--tint) 0);
  filter: drop-shadow(8px 6px 10px rgba(0,0,0,.18));
  font-size: var(--t-caps); font-weight: 650; letter-spacing: var(--caps); text-transform: var(--label-case);
  opacity: 0; clip-path: inset(-40px calc(100% - var(--cap) / 2) -40px -12px round 0 var(--cat-tag-r, 9px) var(--cat-tag-r, 9px) 0);
  transition: clip-path .26s cubic-bezier(.2,.8,.2,1), opacity .12s ease;
}
.cat-tag.on { opacity: 1; clip-path: inset(-40px -40px -40px -12px round 0 var(--cat-tag-r, 9px) var(--cat-tag-r, 9px) 0); }
.cat-tag.glide { transition: clip-path .26s cubic-bezier(.2,.8,.2,1), opacity .12s ease, top .2s cubic-bezier(.2,.8,.2,1), height .2s, margin-top .2s; }
.cat-tag-cap {
  flex: none; display: grid; place-items: center; width: var(--cap); height: 100%;
  border-radius: var(--sr); clip-path: var(--clip, none); background: var(--tint);
}
.cat-tag-ic {
  display: grid; place-items: center; width: var(--sw); height: var(--sh); border-radius: var(--sr);
  clip-path: var(--clip, none); color: var(--c); background: color-mix(in srgb, var(--c) 24%, transparent);
}
.cat-tag-ic > svg { translate: var(--dx, 0px) var(--dy); }
.cat-tag-ct {
  padding: 1px 6px; border-radius: 6px; font-size: var(--t-tiny); letter-spacing: 0; font-variant-numeric: tabular-nums;
  background: color-mix(in srgb, var(--c) 18%, transparent);
}

/* Item card, wide form (js/cardplace.js): header, then picture | text | photos. Missing columns make it narrower. */
.detail { --wide-h: 290px; }
.detail.wide { width: min(780px, calc(100vw - 140px)); max-width: none; max-height: var(--wide-h); }
.detail.wide .d-cols { display: flex; gap: 20px; }
.detail.wide:not(:has(.d-media > .gallery)), .detail.wide:not(:has(.d-pic)) { width: min(620px, calc(100vw - 140px)); }
.detail.wide:not(:has(.d-media > .gallery)):not(:has(.d-pic)) { width: min(520px, calc(100vw - 140px)); }
.detail.wide .d-media:not(:has(.gallery)) { display: none; }
/* the picture runs to the card's left and bottom edges, under the header, with no fade. The whole card scrolls as one piece,
   so the picture keeps the height of the card's first view (its max-height) however long the text beside it runs */
.detail.wide { --hero-fade: none; }
.detail.wide .d-scroll { overscroll-behavior: contain; }
.detail.wide .d-cols { align-items: stretch; }
.detail.wide .d-media { overflow: hidden; }
.detail.wide .d-pic { flex: none; width: 170px; align-self: stretch; max-height: calc(var(--wide-h) - 40px); margin: -18px 0 -16px -18px; display: grid; }
.detail.wide .d-pic .portrait { width: 170px; height: 100%; min-height: 172px; margin: 0; }
.detail.wide .d-pic .portrait.seal { height: auto; align-content: center; padding: 0; background: linear-gradient(to right, color-mix(in srgb, var(--c) 20%, var(--surface)), var(--surface)); }
.detail.wide .d-pic .gallery.hero { width: 170px; margin: 0; }
.detail.wide .d-pic .gallery.hero :is(.g-track, .g-slide) { height: 100%; min-height: 172px; }
.detail.wide .d-pic .gallery.hero .g-dots { top: auto; bottom: 12px; right: 30px; }
.detail.wide .d-text { flex: 1 1 auto; min-width: 0; }
.detail.wide .d-text > h2 { margin: 2px 0 6px; font-size: var(--t-display); text-shadow: none; }   /* the short card: a step smaller */
/* facts in one row of columns; the place is one of them */
.detail.wide .d-facts { margin: 10px 0 0; padding: 10px 12px; border: 0; border-radius: 10px; }
.detail.wide dl { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px 14px; }
.detail.wide .d-place { grid-column: auto; margin: 0; padding: 0; border: 0; }
.detail.wide .d-about { margin-top: 12px; }
.detail.wide .d-media { flex: none; width: 250px; }
.detail.wide .d-media > .gallery { margin: 0; }

/* Tooltip (js/tooltip.js, a manual popover so it sits above the Settings panel): a small card under the control, on the surface with a hairline ring like the header pills.
   "Name: description (K)" reads as a name, a muted description and a key chip. */
.tip {
  position: fixed; z-index: 90; inset: auto; left: 0; top: 0; margin: 0; border: 0; overflow: visible; max-width: 260px; padding: 7px 11px; border-radius: 10px;
  display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; row-gap: 2px; pointer-events: none;
  background: var(--surface); color: var(--muted); font-size: var(--t-meta); line-height: 1.45;
  box-shadow: 0 0 0 1px var(--line), var(--shadow);
  opacity: 0; translate: 0 -3px; transition: opacity .14s ease, translate .14s ease;
}
.tip.above { translate: 0 3px; }
.tip.on { opacity: 1; translate: 0 0; transition: opacity .18s ease, translate .28s cubic-bezier(.2, .9, .25, 1); }
.tip b { flex: 1 1 auto; font-size: var(--t-ui); font-weight: 600; color: var(--text); letter-spacing: -.005em; }
.tip b + kbd + span, .tip b + span { flex-basis: 100%; }   /* with a name, the description gets its own line */
.tip span { flex: 1 1 auto; min-width: 0; }
.tip kbd {
  align-self: center; font: inherit; font-size: var(--t-tiny); font-weight: 600; padding: 0 6px; line-height: 17px;
  border-radius: 5px; color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent);
}
@media (prefers-reduced-motion: reduce) { .tip, .tip.on { translate: none; } }

/* ---------- Keyboard shortcuts (js/shortcuts.js, data/shortcuts.json): a modal sheet with a drawn keyboard over the list.
   A key or chip wears the theme's corner, line and accent; only colours from the theme's tokens. Lit = the solid accent. ---------- */
.keys-pop {
  inset: 0; margin: auto; width: min(1000px, calc(100vw - 32px)); height: fit-content; max-height: calc(100dvh - 32px); padding: 0;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--text); box-shadow: var(--shadow-pop);
}
.keys-pop[open] { display: flex; flex-direction: column; overflow: hidden; animation: gs-drop .42s cubic-bezier(.2, 1.25, .35, 1); }   /* the search menu's drop */
.keys-pop::backdrop { background: rgb(0 0 0 / .35); }
.keys-head { flex: none; display: flex; align-items: center; justify-content: space-between; padding: 8px 8px 0 20px; }
.keys-head h2 { margin: 0; font-size: var(--t-title); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
.keys-x {
  width: 36px; height: 36px; display: grid; place-items: center; border: 0; border-radius: var(--r-ui, 50%); background: transparent; color: var(--muted);
  cursor: var(--cur-pointer, pointer); transition: color .2s ease, background-color .2s ease;
}
.keys-x:hover { background: var(--solid, var(--accent)); color: var(--on-accent); }
.keys-draw { flex: none; padding: 4px 20px 0; }
.keys-board { display: grid; grid-template-columns: repeat(60, minmax(0, 1fr)); grid-auto-rows: 32px; gap: 4px; max-width: 720px; margin: 0 auto; }
.ks-key {
  grid-column: span 4; min-width: 0; display: grid; place-items: center; overflow: hidden; white-space: nowrap; border-radius: var(--r-ui, 6px);
  font-size: var(--t-meta); color: var(--faint); box-shadow: inset 0 0 0 var(--hair) var(--line);
  transition: background-color .14s ease, color .14s ease, box-shadow .14s ease;
}
.ks-key.has { color: var(--text); background: var(--surface-2); box-shadow: inset 0 0 0 var(--hair) var(--line-strong); }
.ks-key.lit { color: var(--on-accent); background: var(--solid, var(--accent)); box-shadow: var(--glow); }
.ks-stack { grid-column: span 4; min-width: 0; display: grid; grid-template-rows: 1fr 1fr; gap: 2px; }
.ks-stack .ks-key { grid-column: auto; font-size: var(--t-tiny); }
.keys-say { min-height: 1.5em; margin: 8px auto 0; text-align: center; font-size: var(--t-meta); line-height: 1.5; color: var(--muted); }
.keys-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 28px; padding: 10px 20px 18px;
}
.keys-list:is(:focus, :focus-visible) { outline: 0; box-shadow: none; }   /* only there so the arrow keys scroll it; it draws no ring */
.keys-col { flex: 1 1 250px; min-width: 0; }
.keys-grp { padding-bottom: 12px; }
.keys-h { margin: 0 0 4px; font-size: var(--t-meta); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text); }
.ks-rows { list-style: none; margin: 0; padding: 0; }
.ks-row { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 8px; padding: 3px 0; align-items: baseline; }
.ks-combos { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; }
.ks-combo { display: inline-flex; gap: 2px; }
.ks-alt { display: inline-flex; align-items: center; gap: 3px; }
.ks-or { font-size: var(--t-tiny); color: var(--faint); }
.ks-chip {
  font: inherit; font-size: var(--t-tiny); font-weight: 600; min-width: 18px; padding: 0 4px; line-height: 18px; text-align: center;
  border-radius: var(--r-ui, 5px); color: var(--text); background: var(--surface-2); box-shadow: inset 0 0 0 var(--hair) var(--line-strong);
  transition: background-color .14s ease, color .14s ease;
}
.ks-chip.word { font-weight: 400; color: var(--muted); background: none; box-shadow: none; padding: 0 2px; }
.ks-do { font-size: var(--t-meta); line-height: 1.4; color: var(--muted); transition: color .14s ease; }
.ks-row.on .ks-do { color: var(--text); }
.ks-row.on .ks-chip:not(.word) { background: var(--solid, var(--accent)); color: var(--on-accent); box-shadow: none; }
/* No room for the drawing on a low or narrow window: the list says it all */
@media (max-width: 620px), (max-height: 520px) { .keys-draw { display: none; } .keys-head { padding-bottom: 6px; } }
@media (prefers-reduced-motion: reduce) { .keys-pop[open] { animation: none; } }
/* Nothing to press without a keyboard: no button in the header; phones have no row in Settings either. A tablet with a keyboard finds it in Settings */
@media (hover: none), (max-width: 760px) { .hdr-cap > .hdr-keys { display: none; } }
@media (max-width: 760px) { .settings-pop .set-keys { display: none; } }

/* ---------- Relationship lines (js/ties.js) ----------
   Hovering a person draws hairline arcs from their picture to the people they were close to (green) or in conflict
   with (red); a tag on each names what the other person was to them. Everyone else fades back. */
.ties { position: absolute; inset: 0; z-index: 4; pointer-events: none; overflow: hidden; }
.ties > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.tie-line { fill: none; stroke-width: var(--tie-w); stroke-linecap: round; }
.tie-ring { fill: none; stroke-width: 1.5; opacity: 0; }
:is(.tie-line, .tie-ring).close { stroke: var(--tie-close); }
:is(.tie-line, .tie-ring).conflict { stroke: var(--tie-conflict); }
.tie-tag {
  --tc: var(--tie-close); --tm: var(--tie-close-mark); --th: var(--tie-hot-close); --thi: var(--tie-hot-close-ink); --tha: var(--tie-halo-close);
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 5px; height: 20px; padding: 0 9px 0 3px; border-radius: 10px;
  background: color-mix(in srgb, var(--tc) 9%, var(--surface));
  box-shadow: 0 0 0 var(--hair) color-mix(in srgb, var(--tc) 55%, transparent), 0 2px 8px rgba(16,24,40,.12);
  font-size: var(--t-meta); font-weight: 550; letter-spacing: -.005em; white-space: nowrap;
  color: color-mix(in srgb, var(--tc) 72%, var(--text));
}
.tie-tag.conflict { --tc: var(--tie-conflict); --tm: var(--tie-conflict-mark); --th: var(--tie-hot-conflict); --thi: var(--tie-hot-conflict-ink); --tha: var(--tie-halo-conflict); }
/* The mark: which way influence runs, in a circle, as on the bow. Its arrow lies along the line (js/draw/webgl.js tagArrow, js/ties.js); a theme
   restyles the circle with these: its size, edge, fill and ink, the arrow's width, and its head: a line (0) or a wedge (1) */
.tie-tag { --tag-d: 14px; --tag-edge: var(--hair); --tag-edge-c: var(--tm); --tag-fill: transparent; --tag-ink: var(--tm); --tag-arrow-w: 1px; --tag-head: 0; }
.tie-mark {
  flex: none; box-sizing: border-box; width: var(--tag-d); height: var(--tag-d); border-radius: 50%; display: grid; place-items: center;
  border: var(--tag-edge) solid var(--tag-edge-c); background: var(--tag-fill); color: var(--tag-ink);
}
.tie-mark svg { display: block; width: 100%; height: 100%; }
.tie-tag:not(.away) .tie-mark { order: -1; }
.lanes.ties-on .item:not(.tie, .frame) { opacity: .35; }
/* A tie to someone out of view: the line and its tag (pinned where the line leaves the screen, with an arrow
   pointing on) take a click to that person */
.lanes.tie-go { cursor: var(--cur-pointer, pointer); }   /* the new drawing: the pointer over a line or tag a click follows */
.viewport.tie-bow, .viewport.tie-bow * { cursor: none; }   /* circling a picture: the bow on the canvas is the pointer (js/draw/webgl.js) */
.viewport.tie-bow ~ .cursor.on { display: none; }   /* ...and the pointer's date line steps aside, so nothing runs through the turning lines */
.viewport.tie-bow .cursor-dates { visibility: hidden; }
.tie-hit { fill: none; stroke: transparent; stroke-width: 14; pointer-events: stroke; cursor: var(--cur-pointer, pointer); }
.tie-tag.away { pointer-events: auto; cursor: var(--cur-pointer, pointer); transition: background-color .15s, color .15s, box-shadow .15s; }
/* Hot: a click here follows the line. The tag and its line light together, the same in both drawings (js/draw/look.js reads this) */
.tie-tag:is(.hot, .away:hover:not(.touch-input *)) {
  background: var(--th); color: var(--thi); --tm: var(--thi); --tie-name: var(--thi);
  box-shadow: var(--glow, none);
}
.tie-tag :is(.tie-txt, .tie-role) { display: flex; align-items: center; gap: 5px; }
.tie-tag:not(.away) :is(.tie-av, .tie-name) { display: none; }
/* ...and grows into a small card for that person: their picture (ringed in their category's colour, as on
   their bar), their name, and what they were to the hovered person below it in the tie's colour */
.tie-tag.away { gap: 8px; height: 36px; padding: 0 8px 0 6px; border-radius: 18px; }
.tie-tag.away .tie-mark { order: 3; }
.tie-tag.away .tie-txt { flex-direction: column; align-items: flex-start; gap: 1px; line-height: 1.2; }
.tie-name { font-size: var(--t-meta); font-weight: 650; color: var(--tie-name, var(--text)); }
.tie-tag.away .tie-role { font-size: var(--t-tiny); }
.tie-av {
  position: relative; flex: none; width: 24px; height: 24px; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center; font-size: 9px; font-weight: 700; color: var(--text);
  background: color-mix(in srgb, var(--c, var(--tc)) 35%, var(--surface));
  box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.75px color-mix(in srgb, var(--c, var(--tc)) 70%, transparent);
  transition: transform .22s cubic-bezier(.2,.8,.3,1.25);
}
.tie-av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; }
.tie-tag.away:is(:hover:not(.touch-input *), .hot) .tie-av { transform: scale(1.12); }
.tie-line.hot { stroke-width: var(--tie-hot-w); }
.tie-line.hot.close { stroke: var(--tie-hot-close); }
.tie-line.hot.conflict { stroke: var(--tie-hot-conflict); }
.tie-halo { fill: none; stroke-width: var(--tie-halo-w); stroke-linecap: round; stroke-opacity: calc(1 - var(--tie-halo-soft) * .75); }
.tie-halo.close { stroke: var(--tie-halo-close); }
.tie-halo.conflict { stroke: var(--tie-halo-conflict); }
/* Rings: while lines are drawn, the half of a picture's ring facing each line takes the line's colour and the
   other half keeps its usual colour, the two blending softly (js/ties.js ringOf). It lies exactly over the
   usual ring (--ro: its outer edge, --rw: its width), repainting it in its own colour away from the lines. */
.av::after {
  content: ""; position: absolute; inset: calc(-1 * var(--ro)); padding: var(--rw); pointer-events: none;
  border-radius: 50%; background: var(--tie-ring, none);
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  opacity: 0; transition: opacity .35s ease;
}
.av { --ro: 2.75px; --rw: 1.25px; }
.item:is(:hover:not(.touch-input *), .hov) .av { --ro: 1px; --rw: .4px; }
.av.tie-lit::after { opacity: 1; }
.av.photo::after { border-radius: calc(5px + var(--ro)); }
.av.tile::after { border-radius: calc(7px + var(--ro)); }
.av.canvas::after { border-radius: calc(3px + var(--ro)); }
.av.arch::after { border-radius: calc(12.5px + var(--ro)) calc(12.5px + var(--ro)) calc(3px + var(--ro)) calc(3px + var(--ro)); }
.av.seal::after { content: none; }

/* ---------- Hover size (Settings → Controls → Enlarge on hover, --hg 0..1 from js/settings.js; 1 = full) ----------
   A hovered bar lights up (edge and glow, as always); --hg scales how much it swells: bar height, picture size and
   the name's shift. At 0 it stays in place: the picture keeps its size and the name stays put. */
.item:is(:hover:not(.touch-input *), .hov) { height: calc(26px + 4px * var(--hg, 1)); margin-top: calc(-2px * var(--hg, 1)); }
.item.lvl2:is(:hover:not(.touch-input *), .hov) { height: calc(22px + 2px * var(--hg, 1)); margin-top: calc(2px - var(--hg, 1) * 1px); }
.item.lvl3:is(:hover:not(.touch-input *), .hov) { height: calc(18px + 2px * var(--hg, 1)); margin-top: calc(4px - var(--hg, 1) * 1px); }
.item:is(:hover:not(.touch-input *), .hov) .av { transform: scale(calc(1 + 1.4 * var(--hg, 1))) !important; }   /* over every picture's own hover size, whatever its weight */
.item.parent:is(:hover:not(.touch-input *), .hov) .av { transform: scale(calc(1 + 2.2 * var(--hg, 1))) !important; }
.lanes .item.brief:not(.open-parent):is(:hover:not(.touch-input *), .hov) .av { transform: scale(calc(1 + .5 * var(--hg, 1))) !important; }
.item.has-av:is(:hover:not(.touch-input *), .hov) .lbl { transform: translateX(calc(var(--shift, 26px) * var(--hg, 1))); }
.item.has-photo:is(:hover:not(.touch-input *), .hov) .lbl { transform: translateX(calc(var(--shift, 38px) * var(--hg, 1))); }
.item.has-av:is(:hover:not(.touch-input *), .hov) { padding-right: calc(var(--shift, 26px) * var(--hg, 1)); }
.item.has-photo:is(:hover:not(.touch-input *), .hov) { padding-right: calc(var(--shift, 38px) * var(--hg, 1)); }
/* a parent's picture grows more (2.2 against 1.4), so its name shifts further with it: never under the picture */
.item.parent:where(.has-av, .has-photo):is(:hover:not(.touch-input *), .hov) { padding-right: calc(var(--shift, 26px) * 1.47 * var(--hg, 1)); }
.item.parent:where(.has-av, .has-photo):is(:hover:not(.touch-input *), .hov) .lbl { transform: translateX(calc(var(--shift, 26px) * 1.47 * var(--hg, 1))); }
.item.parent:is(:hover:not(.touch-input *), .hov) > .item.under { bottom: calc(-4px + var(--hg, 1) * -1px); }
/* ---------- Developer frame meter (?fps, js/fpsmeter.js) ---------- */
.fps-box { position: fixed; left: 12px; bottom: 12px; z-index: 100; max-width: calc(100vw - 24px); display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.fps-meter {
  min-height: 36px; max-width: 100%; padding: 6px 10px; border: 0; border-radius: 8px; text-align: left;
  background: var(--surface); color: var(--text); box-shadow: var(--shadow); font: 600 12px/1.3 var(--font-num); font-variant-numeric: tabular-nums; white-space: pre-wrap; cursor: var(--cur-pointer, pointer);
}
.fps-meter.selectable { -webkit-user-select: text; user-select: text; max-height: 60dvh; overflow: auto; }   /* copying failed: the text can be picked by hand */

/* Online copy (js/hosted.js): links to pages only the Mac's timeline server has are hidden */
[data-hosted] .local-only { display: none !important; }

/* Touch screens: every control is at least 36px to tap, and text fields use 16px type (smaller makes iOS zoom the
   page in when they get focus) */
@media (pointer: coarse) {
  .top { --hdr-c: 42px; }
  /* small marks keep their look and get a larger invisible hit area */
  .rmap-tag::before { inset: 50% auto auto -5px; width: 36px; height: 36px; margin-top: -18px; }
  .sizer, .rmap-panel .sizer { width: 36px; height: 36px; }
  .rmap-chip::after, .lane-tools::after, .nest-btn::after, .nest-tg::after, .nest-pill button::after, .cats .eye::after {
    content: ""; position: absolute; inset: -9px -2px;
  }
  .rmap-chip, .lane-tools, .nest-btn, .nest-pill button, .cats .eye { position: relative; }
  .nest-pill { overflow: visible; }
  .settings-pop .hdr-cap > button { height: 36px; }
  .lens-chip { min-height: 36px; }
  .detail .cl { width: 36px; height: 36px; }
  .d-fact { height: 36px; padding: 0 12px 0 10px; }
  .g-nav::after { content: ""; position: absolute; inset: -3px; }
  .settings-pop .settings-again { min-height: 36px; }
  .d-scroll { touch-action: pan-x pan-y; }   /* quick taps on its sides page cards (js/cardswipe.js), never zoom */
  .d-tip .d-tip-x { position: relative; }
  .d-tip .d-tip-x::after { content: ""; position: absolute; inset: -8px; }
  .gsearch input, input:not([type="checkbox"], [type="radio"], [type="range"]), select, textarea { font-size: 16px; }
}
/* Touch: the rows scroll up and down natively, with the browser's own momentum and bounce (off the main thread);
   sideways drags, pinches and taps stay ours (js/app.js onMove, fling). Sidebar rows scroll too: a long press picks a
   category up to reorder it (js/app.js) */
@media (pointer: coarse) {
  .viewport { touch-action: pan-y; }
  .lanes { overscroll-behavior: contain; }
  .cats li { touch-action: pan-x pan-y; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
}
/* Touch: the page itself never zooms, by pinch or double tap, wherever the fingers land. On every element, as iOS starts
   afresh at each scrolling area. No specificity, so the rules above win. Sliders keep the browser's own handling. While
   the page is zoomed anyway, pinches go back to the browser so they can undo it (js/nopagezoom.js) */
@media (any-pointer: coarse) {
  * { touch-action: pan-x pan-y; }
  input[type="range"] { touch-action: auto; }
  .page-zoomed, .page-zoomed * { touch-action: auto !important; }
}
.no-hover .srail { filter: none; }   /* the rail's blur as it slides in: a whole-height layer blurred anew each frame, just as a scroll begins */
/* No hover (touch): what a pointer's hover would reveal is always shown */
@media (hover: none) {
  .cats .eye, .del, .nest-btn { opacity: 1; }
  .nest-pill button:hover { background: none; }   /* a tap leaves :hover stuck, and the pill no longer clips it */
}

/* ---------- Settings: icon in front of a slider's name, swatches, the gradient angle dial ---------- */
.settings-sub .set-ic, .settings-rollup > summary .set-ic { flex: none; vertical-align: -2px; margin-right: 6px; color: var(--muted); }
.settings-rollup > summary .set-ic { color: var(--accent); }
.settings-rollup > summary { justify-content: flex-start; gap: 0; }
.settings-rollup > summary::after { margin-left: auto; }
.lab-with-ic { display: inline-flex; align-items: center; }
.lab-with-ic .set-ic { margin-right: 6px; color: var(--muted); }
.swatches { display: flex; gap: 8px; margin: 2px 0 4px; --ic-mark: color-mix(in srgb, var(--ic-1) 30%, var(--ic-2)); }   /* --ic-mark: a swatch's ring and the plus, seen on any plate */
.swatches button { width: 36px; height: 36px; display: grid; place-items: center; border: 1px solid var(--line-strong); border-radius: 50%; background: var(--ic-plate); cursor: var(--cur-pointer, pointer); }
.swatches button[aria-checked="true"] { box-shadow: 0 0 0 2px var(--accent); }
.swatches i { width: 18px; height: 18px; border-radius: 50%; background: var(--sw); box-shadow: 0 0 0 1px var(--ic-mark); }
.swatches button { position: relative; }
.swatches i.sw-custom { background: var(--ic-custom, linear-gradient(var(--ic-mark), var(--ic-mark)) center / 10px 1.5px no-repeat, linear-gradient(var(--ic-mark), var(--ic-mark)) center / 1.5px 10px no-repeat); }   /* no colour picked yet: a plus */
.swatches input[type="color"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: inherit; border: 0; padding: 0; }
.swatches i.sw-cat { background: conic-gradient(#c2452b, #b08a46, #4f8a6a, #3f7ca6, #8a5aa6, #c2452b); }

/* ---------- Theme font roles (STYLE-GUIDE.md → Theme tokens): every surface (sidebar, search, settings, card, region map,
   Data view, header) takes its lettering from the theme, so a theme sets --font-label / --label-case / --font-title /
   --font-name once and all of them follow ---------- */
:is(.side-h, .cats .cat-group, .srail-tag, .ov-lbl, .d-cat, .table th, .gs-only, .place .gs-meta, .settings-h,
  .rmap-nm > span, .rmap-tag, .rmap-tip b + span, .rmap-chip, .d-about h3, .detail dt, .cat-tag) { font-family: var(--font-label); }   /* (0,2,0): past button { font: inherit }, under a theme's own rules; their rules set the case: var(--label-case) */
.top :is(#eraSeg button, #viewSeg button, .zoom .fit, .hdr-tx, .rmap-cur, .gsearch input) { font-family: var(--font-label); }   /* buttons and the search field: past { font: inherit } */
:is(.detail h2, .sh-text h2) { font-family: var(--font-title); }
.gs-name { font-family: var(--font-name); }

/* ---------- Square chrome (STYLE-GUIDE.md → Theme tokens): a theme with squared-off controls joins this list and sets
   --r-ui; every button, switch, menu, card, chip and tag then takes that corner (pictures keep their category's shape) ---------- */
:root:is([data-skin="record"], [data-skin="atlas"]) :is(.seg, .seg-pill, .seg button, .seg button::before, .hdr-cap, .hdr-btn, .hdr-cap > button,
  .hdr-cap > button::before, .detail, .sheet-tab, .btn, .search, .table, .switch span, .nest-btn, .nest-pill, .gsearch, .gs-hl, .gs-row,
  .frame, .frame::before, .frame::after, .lane-tools, .settings-pop, .settings-tab, .settings-lab > .settings-h, .g-track,
  .rmap-btn, .rmap-panel, .rmap-rail, .rmap-pill, .rmap-tag, .rmap-stage, .rmap-tip, .rmap-chip, .d-tip, .lane-ct, .lbl .dur, .cat-tag,
  .tie-tag, .tie-kind, .tie-kind select, .cat, .side-h button, .sh-badge, .del, .gs-only, .d-map, .nest-ct, .ov-track, .d-tip-x, .srail-tag, .srail-bk,
  .side-lock, .settings-rollup, .settings-rollup > summary, .settings-labgrp, .settings-labgrp > summary, .gsearch-list, .gs-add, .gs-go, .gs-x, .gs-clear, .d-facts, .keys-pop) { border-radius: var(--r-ui); }

/* ---------- Ink hover (STYLE-GUIDE.md → Hover): a theme whose hover is only a change of ink joins this list. Every
   control (header, menus, settings, sidebar, chips, card buttons) and every bar or box keeps its resting fill, ring and
   glow when hovered; only its text and icon turn --hover-ink. Bars still swell to show their picture. ---------- */
:root:is([data-skin="atlas"], [data-skin="record"]) {
  & :is(.seg button:hover, .hdr-cap > button:hover, .hdr-cap > .seg > button:hover, #settingsBtn[aria-expanded="true"],
    .top .seg button:not([aria-checked="true"]):hover)::before { background: none; box-shadow: none; }
  & :is(.hdr-btn:hover, .rmap-btn:not([aria-expanded="true"]):hover, .btn:not(.primary):hover, .sheet-tab:hover) { background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--cap-ring); }
  & :is(.map-now button:hover, .map-credit:hover, .rel-row:hover, .rel-heir:not(.me):hover, .rel-life:not([data-kind="ego"]):hover) { background: none; color: var(--hover-ink); }
  & .rel-node:not(.ego):is(:hover, .lifted) .rel-lbl { box-shadow: none; color: var(--hover-ink); }
  & .rmap.chosen .rmap-btn:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 35%, var(--line)); }
  & :is(.detail .cl:hover, .d-tip-x:hover, .del:hover, .nest-pill button:hover, .lane-h .nest-pill button:hover,
    .cats .eye:hover, .settings-again:hover, .settings-tab:hover, .gs-add:hover, .cats li:not(.current) .cat:hover, .cats li.swept:not(.current) .cat) { background: transparent; }
  & :is(.gs-only:hover, .rmap-chip:hover) { background: color-mix(in srgb, var(--c) 14%, transparent); }
  & .lane-h .lane-tools:hover { --wash: color-mix(in srgb, var(--hd-ink) 18%, transparent); }
  & .side > .side-lock.side-lock:hover { background: none; }
  & .nest-tg:is(:hover, .hov) { box-shadow: none; }
  & .rmap-tag:not(.on):hover { scale: none; background: color-mix(in srgb, var(--surface) 82%, transparent); box-shadow: 0 0 0 1px var(--line-strong), 0 2px 6px -2px rgba(0,0,0,.18); }
  & .cats li:not(.off, .current):is(:hover, .swept) .cat .sw::before { background: var(--sw-rest, color-mix(in srgb, currentColor calc(12.5% * var(--k)), transparent)); }
  & :is(.seg button:not([aria-checked="true"]):hover, .detail .cl:hover, .d-tip-x:hover, .btn:not(.primary):hover,
    .sheet-tab:hover, .settings-tab:hover, .settings-rollup > summary:hover, .settings-labgrp > summary:hover, .nest-pill button:hover, .nest-btn:hover, .cats .eye:hover, .settings-again:hover, .gs-only:hover, .gs-add:hover,
    .lane-tools:hover, .rmap-chip:hover, .side > .side-lock.side-lock:hover, .nest-tg:is(:hover, .hov), .rmap-tag:not(.on):hover,
    .side-h button:hover, .cats li:not(.current) .cat:hover, .cats li.swept:not(.current) .cat, .cats li:not(.off, .current):is(:hover, .swept) .cat .sw) { color: var(--hover-ink); }
  & :is(.item, .frame) { --hover-glow: none; --bar-tint-hover: var(--bar-tint); }
  & .item:not(.open-parent):is(:hover:not(.touch-input *), .hov) { border-color: var(--bar-edge); border-left-color: var(--c); }
  & .item.sel:not(.open-parent):is(:hover:not(.touch-input *), .hov) { box-shadow: var(--sel-ring, 0 0 0 1px color-mix(in srgb, var(--c) 80%, transparent)); }
  & .item:is(:hover:not(.touch-input *), .hov) .lbl { color: var(--hover-ink); }
  & .frame:is(.lifted, .lit, .flash)::before { inset: 0; border-radius: inherit; border-color: var(--bar-edge); }
  & .frame:is(.lifted, .lit)::after { opacity: 0; }
}

/* Keyboard focus wears the theme's accent, never the browser's own colour */
:where(button, a, input, select, textarea, summary, [tabindex]):focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --solid: the fill of a solid tile with words on it (--on-accent), here and wherever the accent carries words. Unset it is the accent; a theme sets it when
   its accent, as ink, would glare as a fill (Record dark). */
/* ---------- Solid hover (STYLE-GUIDE.md → Hover): a theme whose hover is solid accent with reversed words joins this list; it
   builds on Ink hover above, so it stays in that list too. Every control and every bar turns solid, the same as its chosen
   state; a box too big to fill (search, an opened box) only turns its edge. ---------- */
:root:is([data-skin="record"]) {
  & :is(.seg button:not([aria-checked="true"]):hover, .hdr-cap > button:hover, .hdr-cap > .seg > button:not([aria-checked="true"]):hover,
    .top .seg button:not([aria-checked="true"]):hover, .hdr-btn:hover, .rmap-btn:not([aria-expanded="true"]):hover, .btn:not(.primary):hover, .sheet-tab:hover,
    .settings-tab:hover, .settings-again:is(:hover, :focus-visible), .nest-btn:hover, .lane-tools:hover, .lane-h .lane-tools:hover, .nest-pill button:hover,
    .lane-h .nest-pill button:hover, .side > .side-lock.side-lock:hover, .cats .eye:hover, .detail .cl:hover, .d-tip-x:hover, .del:hover, .gs-add:hover, .gs-only:hover, .gs-x:hover, .gs-clear:hover,
    .rmap-chip:hover, .lens-chip:not([aria-checked="true"], :disabled):hover, .rmap-g:is(:hover, .hot), .rmap-tag:not(.on):is(:hover, .hot), .g-nav:hover, .settings-rollup > summary:hover, .settings-labgrp > summary:hover,
    .side-h button:hover, .cats li:not(.current) .cat:hover, .cats li.swept:not(.current) .cat, .cats li:not(.off, .current):is(:hover, .swept) .cat .sw, .map-now button:hover, .map-credit:hover, .rel-row:hover, .rel-heir:not(.me):hover, .rel-chip:not([aria-pressed="true"]):hover, .rel-life:not([data-kind="ego"]):hover, .rel-node:not(.ego):is(:hover, .lifted) .rel-lbl) {
    background: var(--solid, var(--accent)); color: var(--on-accent); --wash: var(--solid, var(--accent)); box-shadow: none;
  }
  & :is(.cats li:not(.off, .current):is(:hover, .swept) .cat :is(.nm, .ct), .rmap-btn:not([aria-expanded="true"]):hover :is(.rmap-ico, .rmap-cur), .rmap-tag:not(.on):is(:hover, .hot) :is(span, .ico), .rmap-g:is(:hover, .hot) .ico, .map-now button:hover :is(.map-sub, .map-pic), .rel-row:hover :is(.rel-rsay, .rel-rmeet, .rel-rnote), .rel-heir:not(.me):hover .rel-heir-y, .rel-chip:hover :is(.rel-n, svg), .rel-life:not([data-kind="ego"]):hover .rel-life-nm, .rel-node:not(.ego):is(:hover, .lifted) .rel-role) { color: var(--on-accent); }
  & :is(.settings-rollup, .settings-labgrp) > summary:hover .set-ic { color: inherit; }
  & .lens-chip[aria-checked="true"] { background: var(--solid, var(--accent)); color: var(--on-accent); box-shadow: none; }
  & :is(.cmp-lbl:hover, .cmp-bar:hover + .cmp-lbl) { background: var(--solid, var(--accent)); color: var(--on-accent); }   /* a compared entry's name (css/compare.css) */
  & .nest-tg:is(:hover:not(.touch-input *), :focus-visible, .hov) { --hover-ink: var(--on-accent); background: var(--solid, var(--accent)); color: var(--on-accent); box-shadow: none; }
  & .table tbody tr:hover td, & .table tbody tr:hover td:first-child { background: var(--solid, var(--accent)); color: var(--on-accent); box-shadow: none; }
  & .table tbody tr:hover td :is(input, select, button, a, span) { color: var(--on-accent); }
  & .item:not(.open-parent):is(:hover:not(.touch-input *), .hov) {
    --bar-fill: var(--solid, var(--accent)); --bar-edge: var(--solid, var(--accent)); --hover-edge: var(--solid, var(--accent)); --hover-ink: var(--on-accent); --q-ink: var(--on-accent);
    border-left-color: var(--solid, var(--accent)); box-shadow: none;
  }
  & .item:not(.open-parent):is(:hover:not(.touch-input *), .hov) .lbl, & .item:not(.open-parent):is(:hover:not(.touch-input *), .hov) .lbl .nest-ct { color: var(--on-accent); }
  & .item:not(.open-parent):is(.sel, :hover:not(.touch-input *), .hov) .nest-tg:not(:hover, .hov) { color: var(--on-accent); box-shadow: inset 0 0 0 var(--hair) var(--on-accent); background: transparent; }
  & .item:not(.open-parent):is(.sel, :hover:not(.touch-input *), .hov) .nest-tg:is(:hover, .hov) { background: var(--on-accent); color: var(--solid, var(--accent)); box-shadow: none; }
  & .item:not(.open-parent):is(:hover:not(.touch-input *), .hov) .av { --ring: var(--on-accent); }
  & .gsearch:hover { box-shadow: inset 0 0 0 var(--hair) var(--accent); }
  & .gs-hl { background: var(--solid, var(--accent)); box-shadow: none; border-radius: 0; }   /* the search list's moving highlight */
  & .gs-row.place { background: transparent; border-radius: 0; }
  & .gs-row[aria-selected="true"], & .gs-row[aria-selected="true"] :is(.gs-name, .gs-name *, .gs-meta, .gs-meta *, .gs-ic) { color: var(--on-accent); }
  & .gs-row[aria-selected="true"] .gs-ic { background: none; }
  & .gs-row[aria-selected="true"] :is(.gs-go, .gs-add) { color: var(--on-accent); background: transparent; box-shadow: inset 0 0 0 var(--hair) var(--on-accent); }
  & :is(.sizer:hover, .resizing .sizer, .rmap-panel .sizer:hover, .settings-pop input[type="range"]:is(:hover, :focus-visible)) { filter: none; }
}

/* An opened parent is a box, not a bar: its heading never gets a ring or glow, selected, hovered or lit, in any theme (the .frame
   lights instead). The [data-skin] prefix and doubled class outrank every skin's .item rules, present and future. */
[data-skin] .item.open-parent.open-parent, [data-skin] .item.open-parent.open-parent:is(.sel, .hov, .flash, :hover:not(.touch-input *)) { box-shadow: none; }

/* ---------- Raise the timeline (Settings → Theme → Experimental; html.raised, wide screens only, js/settings.js). The timeline is a
   sheet lifted off the window by a soft shadow with a breath of the accent; the menu and sidebar lose their cards and lie flat on the
   back layer, in the page's own inks (each theme's dark sidebar tokens apply only without .raised). Every theme inherits it; a theme
   only sets --raise-1, --raise-2 (the shadow's colours). ---------- */
html.raised[data-skin] { --top-bg: transparent; --top-bd: 0; --side-bd: 0; }
html.raised {
  --raise-1: light-dark(rgb(16 24 32 / .12), rgb(0 0 0 / .5)); --raise-2: light-dark(rgb(16 24 32 / .24), rgb(0 0 0 / .62));
  --raise: 0 1px 3px var(--raise-1), 0 16px 48px -12px var(--raise-2), 0 0 64px -20px color-mix(in srgb, var(--accent) 34%, transparent);
}
/* It stays flat unless it has opened over the timeline: on the kept-closed rail nothing but the icon reacts */
html.raised body:not(.side-auto) .side, html.raised body.side-auto:not(.side-opening) .side:is(:not(:hover, :has(:focus-visible)), body.side-locked *, body.arriving *) { background: none; box-shadow: none; }
/* Lit or hovered, only the icon's ink changes: no wash on the row (initial: the tokens' fallback, none), and its shape stays as it was */
html.raised .side {
  --cat-current-bg: initial; --cat-hover-bg: initial;
  --nm-current: var(--text); --sw-rest: color-mix(in srgb, var(--text) 5%, transparent);
  --cat-mono: var(--muted); --sw-hi-fg: var(--accent); --sw-hi-bg: var(--sw-rest);
}
/* The name tag starts beside the lit icon, lying as flat: no shaped end or copy of the icon over it */
html.raised .cat-tag { background: linear-gradient(90deg, transparent var(--cap), var(--tint) 0); filter: none; }
html.raised .cat-tag-cap { background: none; clip-path: none; }
html.raised .cat-tag-ic { visibility: hidden; }
html.raised body.side-auto:not(.side-locked, .arriving) .side:is(:hover, :has(:focus-visible)), html.raised body.side-opening .side { background: var(--surface); box-shadow: var(--shadow); }
html.raised :is(.stage, .sheet, .mapview, .relview, .dx) { position: relative; z-index: 1; box-shadow: var(--raise); }
@media (min-width: 761px) {
  html.raised { --inset: 1; }
  /* edges off, the band left of the sheet is the rail and the sheet's --gap: the rail takes it all, so its icons are centred in the band */
  html.raised:not(.inset) { --rail-extra: var(--gap); }
  html.raised :is(.stage, .sheet, .mapview, .relview, .dx) { margin-right: var(--gap); border-right: var(--panel-bd); }
}
.gsearch :is(kbd, .kbd) { color: var(--muted); }

/* Panels draw their own thin scrollbar, where a pointer scrolls (a touch screen draws its own, which no style reaches), so its thumb speaks
   the theme: its ink, its corner. The browser's own would be a grey pill whatever the theme, and would ignore these if it had any colour or
   width set. Room for it is kept always (gutter), taken from the panel's padding, so nothing shifts when a list grows long enough to scroll.
   Not the card: its pictures and rules run to its edge, which a kept gutter would cut short; it keeps the browser's thumb, in --scroll-thumb. */
@media (hover: hover) { @supports selector(::-webkit-scrollbar) {
  :is(.gsearch-list, .settings-body, .sheet-scroll, .keys-list, .map-lens-pop, .no-rail .lanes, .dx-scroll, .dx-facet-body) {
    scrollbar-width: auto; scrollbar-color: auto;
    &::-webkit-scrollbar { display: block; width: var(--scroll-w); height: var(--scroll-w); background: transparent; }
    &::-webkit-scrollbar-track, &::-webkit-scrollbar-corner { background: transparent; }
    &::-webkit-scrollbar-track { margin-block: 6px; }
    &::-webkit-scrollbar-thumb { background: transparent; background-clip: padding-box; border: 1px solid transparent; border-radius: var(--r-ui, 3px); }
    &.is-scrolling::-webkit-scrollbar-thumb { background-color: var(--scroll-thumb); }
  }
  :is(.gsearch-list, .settings-body, .sheet-scroll, .keys-list) { scrollbar-gutter: stable; }
  .gsearch-list { padding-right: calc(6px - var(--scroll-w)); }
  .gs-hl { right: calc(6px - var(--scroll-w)); }
  .settings-body { margin-right: calc(-8px - var(--scroll-w)); }   /* its text stays where it was; the bar runs in the panel's own padding */
  .sheet-scroll, .keys-list { padding-right: calc(20px - var(--scroll-w)); }
} }

/* Show-me link (js/show.js): a short pulse ring in the theme's accent around the one thing being talked about */
.show-ring { position: fixed; z-index: 9999; pointer-events: none; border-radius: var(--r-ui, 8px); border: calc(var(--hair, 1px) * 2) solid var(--accent); box-shadow: var(--glow, none); animation: show-ring 2.4s ease-out 1 both; }
@keyframes show-ring { 0% { opacity: 0; transform: scale(1.25); } 15%, 70% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .show-ring { animation-name: show-ring-still; } @keyframes show-ring-still { 0%, 70% { opacity: 1; } 100% { opacity: 0; } } }

/* The five eras' marks (js/icons.js eraIcon): before an era's name (Data filters, search, map timebar, info card, era
   brackets). Line icons on the one pen (above); the name's own colour */
.era-ico { flex: none; vertical-align: -.16em; }
