/* Phase 3 timeline design system — palette + fonts from the mockup. */
:root {
  --paper: #f6f1e7;
  --paper-warm: #efe7d4;
  --paper-deep: #e6dcc4;
  --paper-active: #ece2c8;
  --rule: #d8cdb1;
  --rule-strong: #b8a983;
  --ink: #2a2317;
  --ink-soft: #4a3f2c;
  --ink-faded: #756749;
  --ink-ghost: #a89c7d;
  --accent-cinnabar: #a13d2c;
  --accent-cinnabar-soft: #c46d5c;

  /* Keep legacy alias so auth-shell accent refs still resolve. */
  --accent: #a13d2c;
  --accent-soft: #c46d5c;
  --green: #4a6b3a;
  --red: #9c3a30;

  --lane-hutterian:  #6b3a2e;
  --lane-elders:     #4f3d6b;
  --lane-hofs-dest:  #9c3a30;
  --lane-hofs-built: #4a6b3a;
  --lane-anabaptist: #4a637a;
  --lane-personal:   #7d5a3a;
  --lane-songs:      #6b4a7d;
  --lane-catholic:   #7d3a3a;
  --lane-turkish:    #8a6e3a;
  --lane-world:      #3a5a7d;
  --lane-moravia:    #5a3a3a;
  /* Country lanes — Czech / Habsburg core in warm earth, central-European
     neighbours in cool slate, dispersal-period lanes (Russia → North America)
     in a slightly desaturated cool palette. */
  --lane-bohemia:      #5d4a3a;
  --lane-hungary:      #7d4a3a;
  --lane-slovakia:     #6e5a3a;
  --lane-transylvania: #8a5a4a;
  --lane-bavaria:      #5a4f6b;
  --lane-switzerland:  #3a6b6b;
  --lane-poland:       #4a4f7d;
  --lane-prussia:      #3a3a5d;
  --lane-russia:       #4a3a5d;
  --lane-ukraine:      #5a4a7d;
  --lane-usa:          #2e5a6b;
  --lane-canada:       #3a5a4f;

  --font-display: 'Fraunces', 'Cormorant Garamond', Georgia, serif;
  --font-body: 'Cormorant Garamond', Georgia, serif;
  --font-mono: 'JetBrains Mono', 'Menlo', monospace;

  --shadow-paper: 0 24px 48px -16px rgba(60, 40, 12, 0.18);

  /* Width of the lane-label gutter; used by both the grid layout and the
     playhead's left-position math. Narrower on phone-class viewports below. */
  --label-gutter: 96px;

  /* Density redesign tokens */
  --closed-lane-h:    74px;        /* fixed closed-lane height = 22px caption + ~52px track */
  --lead-size:        9px;   /* was 7px — bumped for mobile readability, applies globally */
  --micro-size:       4px;   /* was 3px — bumped for mobile readability, applies globally */
  --micro-opacity:    0.55;
  --micro-gap:        2px;
  --plus-n-size:      8px;
  --lead-active-ring: 0 0 0 3px rgba(161, 61, 44, 0.25);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Mobile stability — kill rubber-band bounce (iOS Safari) and
   pull-to-refresh (Chrome/Android). Refreshing a wasm SPA reboots the
   whole app; the gesture is more accidental than useful here. Applied
   to both html and body because iOS Safari only honors the rule on
   whichever element is the actual scrolling root, and that varies. */
html, body {
  overscroll-behavior: none;
}

html, body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  min-height: 100vh;   /* fallback for browsers < iOS 15.4 / Chrome 108 */
  min-height: 100dvh;  /* live viewport, adapts as URL bar shows/hides */
}
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(circle at 20% 30%, rgba(120, 90, 50, 0.04) 0%, transparent 50%),
    radial-gradient(circle at 80% 70%, rgba(80, 60, 40, 0.05) 0%, transparent 60%);
  z-index: 0;
}
body::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.35;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.16  0 0 0 0 0.13  0 0 0 0 0.08  0 0 0 0.45 0'/></filter><rect width='220' height='220' filter='url(%23n)'/></svg>");
  mix-blend-mode: multiply;
}

button { font: inherit; cursor: pointer; }
input, textarea { font: inherit; color: var(--ink); }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── Auth shell (login / signup / not-found) ─────────────────────── */
.auth-shell {
  max-width: 420px;
  margin: 0 auto;
  padding: 56px 24px 24px;
}
.auth-shell h1 {
  font-size: 22px;
  margin: 0 0 4px;
  letter-spacing: 0.02em;
}
.auth-shell .subtitle {
  color: var(--ink-faded);
  font-style: italic;
  margin: 0 0 24px;
}
.auth-shell form { display: block; }
.auth-shell label {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 14px 0 5px;
}
.auth-shell input,
.auth-shell textarea {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--rule);
  border-radius: 5px;
  background: var(--paper-warm);
  font-size: 16px;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
  font-family: inherit;
  resize: vertical;
}
.auth-shell input:focus,
.auth-shell textarea:focus {
  border-color: var(--ink-soft);
  box-shadow: 0 0 0 3px rgba(216, 205, 177, 0.5);
}
.auth-shell button {
  margin-top: 18px;
  width: 100%;
  padding: 12px;
  background: var(--ink);
  color: var(--paper-warm);
  border: none;
  border-radius: 5px;
  font-size: 15px;
  letter-spacing: 0.02em;
  transition: background 0.15s, opacity 0.15s;
}
.auth-shell button:hover:not(:disabled) { background: #000; }
.auth-shell button:disabled { opacity: 0.5; cursor: default; }

.auth-shell .hint {
  font-size: 13px;
  color: var(--ink-faded);
  font-style: italic;
  margin: 6px 0 0;
}
.auth-shell .error {
  margin-top: 14px;
  padding: 8px 10px;
  background: rgba(161, 61, 44, 0.08);
  border-left: 3px solid var(--accent);
  color: var(--accent);
  font-size: 13.5px;
  font-style: italic;
  border-radius: 2px;
}
.auth-shell .success {
  margin-top: 14px;
  padding: 10px 12px;
  background: rgba(74, 107, 58, 0.10);
  border-left: 3px solid var(--green);
  color: var(--ink);
  font-size: 14px;
  border-radius: 2px;
}
.auth-shell .link-row {
  margin-top: 20px;
  font-size: 13.5px;
  color: var(--ink-faded);
}
.auth-shell .stat {
  margin: 40px 0;
  color: var(--ink-faded);
  font-style: italic;
  text-align: center;
}

/* ── App shell (timeline / admin) ─────────────────────────────────── */
.app-shell {
  max-width: 960px;
  margin: 0 auto;
  padding: 0 20px 32px;
}
.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 0;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 24px;
}
.topbar h1 {
  margin: 0;
  font-size: 18px;
  flex: 1;
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 14px;
}
.topbar .who {
  color: var(--ink-faded);
  font-style: italic;
  font-size: 14px;
}
.topbar-link {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.topbar-btn {
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--ink-soft);
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 13px;
}
.topbar-btn:hover { background: var(--paper-warm); }

/* ── Timeline placeholder ─────────────────────────────────────────── */
.app-shell .subtitle {
  color: var(--ink-faded);
  font-style: italic;
}
.app-shell .stat {
  font-family: var(--font-mono);
  font-size: 13px;
  padding: 12px 14px;
  background: rgba(0, 0, 0, 0.04);
  border-left: 3px solid var(--accent);
  margin: 16px 0;
}
.app-shell .error {
  color: var(--accent);
  font-style: italic;
}
ul.events {
  list-style: none;
  padding: 0;
}
ul.events li {
  padding: 8px 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
ul.events .yr {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-faded);
  margin-right: 12px;
}

/* ── Admin signup queue ───────────────────────────────────────────── */
.signup-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 12px;
}
.signup-card {
  padding: 14px 16px;
  background: var(--paper-warm);
  border: 1px solid var(--rule);
  border-radius: 6px;
}
.signup-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 4px;
}
.signup-meta .who {
  font-weight: 600;
  font-size: 16px;
}
.signup-meta .email {
  color: var(--ink-faded);
  font-size: 13px;
  font-style: italic;
}
.signup-meta-2 {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-faded);
  letter-spacing: 0.04em;
}
.signup-note {
  margin: 8px 0 0;
  padding: 8px 10px;
  background: var(--paper);
  border-left: 2px solid var(--ink-ghost);
  font-style: italic;
  font-size: 13.5px;
  color: var(--ink-soft);
}
.signup-actions {
  margin-top: 12px;
  display: flex;
  gap: 8px;
}
.btn {
  flex: 1;
  padding: 9px 12px;
  border-radius: 4px;
  font-size: 13.5px;
  border: 1px solid transparent;
  transition: opacity 0.15s, background 0.15s;
}
.btn:disabled { opacity: 0.5; cursor: default; }
.btn.approve {
  background: var(--green);
  color: var(--paper-warm);
}
.btn.approve:hover:not(:disabled) { background: #3d5930; }
.btn.reject {
  background: transparent;
  color: var(--red);
  border-color: var(--red);
}
.btn.reject:hover:not(:disabled) { background: rgba(156, 58, 48, 0.08); }

/* ──────────────────────────────────────────────────────────────
   PAGE — two phones side by side on desktop, stacked on mobile.
   ────────────────────────────────────────────────────────────── */
.stage {
  position: relative;
  z-index: 2;
  max-width: 540px;
  margin: 0 auto;
  padding: 32px 24px 80px;
}
/* On phone-class viewports the chart runs edge-to-edge; only the headers
   and prose keep a small inset so they don't kiss the screen edge. */
@media (max-width: 599px) {
  .stage {
    max-width: none;
    padding: 0 0 80px;
  }
  .page-header,
  .yearbar,
  .scrubber-frame,
  .caption {
    padding-left: 16px;
    padding-right: 16px;
  }
  .page-header {
    padding-top: 24px;
  }
  /* Chart fills the full width — no border-radius or side border on a phone,
     just rules above and below to keep it visually anchored. */
  .chart {
    border-radius: 0;
    border-left: none;
    border-right: none;
    box-shadow: none;
  }
  :root { --label-gutter: 72px; }
  .lane .label {
    padding: 8px 6px 8px 6px;
  }
  .lane .label .name { font-size: 11px; }
  .lane .label .count { font-size: 8.5px; }
  .lane .label .count-unit { display: none; }   /* "X events" → "X" on phone (gutter is narrow) */
}
/* Compact two-line header — app title + current-year/range row. */
.page-header {
  padding: 14px 0 6px;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.page-header h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  line-height: 1;
}
.gear-btn {
  background: transparent;
  border: none;
  padding: 4px;
  cursor: pointer;
  color: var(--ink-faded);
  line-height: 0;
  transition: color 0.15s ease;
}
.gear-btn:hover { color: var(--accent-cinnabar); }
.gear-btn svg { width: 18px; height: 18px; }

/* Edit panel — slides up from the bottom of the .screen, covering the
   chart while the user is configuring lanes. */
.edit-panel {
  position: absolute;
  inset: 0;
  background: var(--paper);
  z-index: 100;
  display: flex;
  flex-direction: column;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.32,0.72,0.21,1);
}
.edit-panel.open { transform: translateY(0); }
.edit-panel-header {
  padding: 14px 18px 12px;
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.edit-panel-header h2 {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
}
.edit-panel-header .done-btn {
  background: var(--accent-cinnabar);
  color: var(--paper);
  border: none;
  padding: 6px 14px;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
.edit-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0 24px;
}
.edit-panel-section-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faded);
  padding: 14px 18px 6px;
}
.edit-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  border-bottom: 1px solid rgba(216, 205, 177, 0.4);
  background: var(--paper);
  cursor: grab;
  user-select: none;
  touch-action: none;
}
.edit-row:hover { background: rgba(184, 169, 131, 0.08); }
.edit-row.dragging {
  opacity: 0.5;
  cursor: grabbing;
}
.edit-row .drag-handle {
  color: var(--ink-ghost);
  font-size: 14px;
  line-height: 1;
  width: 14px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.edit-row .drag-handle::before,
.edit-row .drag-handle::after,
.edit-row .drag-handle span {
  content: '';
  height: 2px;
  background: currentColor;
  border-radius: 1px;
}
.edit-row .swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: currentColor;
  flex-shrink: 0;
}
.edit-row .name {
  font-family: var(--font-display);
  font-size: 14px;
  color: var(--ink);
  flex: 1;
}
.edit-row .toggle {
  width: 38px;
  height: 22px;
  background: var(--rule);
  border-radius: 11px;
  position: relative;
  transition: background 0.18s ease;
  flex-shrink: 0;
}
.edit-row.on .toggle { background: var(--accent-cinnabar); }
.edit-row .toggle::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px; height: 18px;
  background: var(--paper);
  border-radius: 50%;
  transition: transform 0.18s ease;
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.edit-row.on .toggle::after { transform: translateX(16px); }

/* Yearbar — current year on the left, full data range on the right. */
.yearbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 6px 0 10px;
}
.yearbar .current-year {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 40px;
  line-height: 0.9;
  letter-spacing: -0.04em;
  color: var(--accent-cinnabar);
  font-feature-settings: 'lnum';
}
.yearbar .range {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faded);
  letter-spacing: 0.06em;
}

/* ──────────────────────────────────────────────────────────────
   CHART — shared between both phones
   ────────────────────────────────────────────────────────────── */
.chart {
  background: var(--paper-warm);
  border-radius: 6px;
  box-shadow: var(--shadow-paper);
  border: 1px solid var(--rule);
  overflow: hidden;
  position: relative;
}
.chart .lanes {
  padding: 4px 0;
}

/* Compact (closed) lane */
.lane {
  display: grid;
  grid-template-columns: var(--label-gutter) 1fr;
  align-items: stretch;
  border-bottom: 1px solid rgba(120, 95, 55, 0.32);
  position: relative;
  transition: background 0.18s ease, height 0.25s ease;
  cursor: pointer;
}
.lane:last-child { border-bottom: none; }
.lane:hover .label { background: rgba(184, 169, 131, 0.28); }
.lane.compact {
  height: var(--closed-lane-h);
}
.lane.compact .track-wrap,
.lane.compact .track {
  height: 100%;
  overflow: hidden;
}
.lane.compact .scroller {
  height: 100%;
}

.lane .label {
  /* Sticky-left is forward-looking — the chart currently pans via translateX
     on .scroller (no horizontal scroll container), so position:sticky is
     visually inert today. The z-index + paper-warm background do the real
     work of layering the label above the translated scroller content. When
     real horizontal scrolling lands, the label keeps anchored automatically. */
  position: sticky;
  left: 0;
  z-index: 2;
  align-self: stretch;             /* preserve vertical fill inside the grid row */
  background: rgb(229, 218, 188);
  padding: 8px 8px 8px 8px;
  /* Hard-clip label contents to the gutter column. Without this, long
     names like "Hofs destroyed" or "Catholic powers" can extend past
     the right border into the track area when the gutter is narrow
     (mobile breakpoint sets --label-gutter: 76px). */
  overflow: hidden;
  min-width: 0;
  /* Vertical rule separating the label gutter from the track. Without
     this, track-side captions (e.g. per-lane "X chosen as Vorsteher")
     visually bleed into the label column on the left. */
  border-right: 1px solid rgba(120, 95, 55, 0.32);
  display: flex;
  flex-direction: column;
  justify-content: center;
  transition: background 0.18s ease;
}
.lane .label .name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 13px;
  line-height: 1.15;
  color: var(--ink);
}
.lane .label .count {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-faded);
  letter-spacing: 0.06em;
  margin-top: 3px;
}

.lane .track-wrap {
  display: flex;
  flex-direction: column;
  min-width: 0;
  /* Block native gestures on the track so the Rust pan handler owns
     horizontal pointer events outright. Vertical scrolling of the lane
     stack still works because the parent `.lanes` allows `pan-y` — a
     touch starting on the label gutter (which doesn't override) hits
     the native scroller. */
  touch-action: none;
}
/* Per-lane captions live in .lane-caption above the .track inside .track-wrap. */
.lane .track {
  position: relative;
  overflow: hidden;
  min-height: 42px;
  /* Padding-block makes room for the staggered dots' top/bottom extents so the
     dot edges don't get clipped at the lane border. */
  padding-block: 6px;
}
/* Open-lane has a different structure: story panel on top, ministrip below. */
.lane.open .track {
  display: flex;
  flex-direction: column;
  min-height: 96px;
  padding: 6px 8px 4px;
  gap: 4px;
}
.story-yr {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  color: var(--ink-faded);
}
.story-ttl {
  font-family: var(--font-display);
  font-size: 13px;
  line-height: 1.25;
  color: var(--ink);
}
.story-empty {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12.5px;
  color: var(--ink-faded);
}
.ministrip {
  position: relative;
  height: 22px;
  flex: 0 0 auto;
  overflow: hidden;
}
.ministrip.split { height: 32px; }
.ministrip-dot {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  border: 1.5px solid var(--paper-warm);
  box-shadow: 0 0 0 1px currentColor;
  transform: translate(-50%, -50%);
  top: 50%;
  cursor: pointer;
  z-index: 3;
}
.ministrip-pill {
  position: absolute;
  height: 6px;
  border-radius: 3px;
  background: currentColor;
  opacity: 0.55;
  top: calc(50% - 3px);
  cursor: pointer;
}
.ministrip-dot.ghost,
.ministrip-pill.ghost { opacity: 0.35; }
/* Pannable inner scroller — width = 100% * SPAN/VIEW_YEARS; translateX moves it. */
.scroller {
  position: absolute;
  inset: 0;
  will-change: transform;
}
/* The mockup's panning midline (.lane.compact .scroller::before) was removed:
   it lived on the scroller and so panned with content, which combined with
   the paper-bordered dots produced gappy, mismatched-looking hairlines
   across lanes. Lane separators (.lane border-bottom) provide enough
   visual structure on their own. */

/* Event dot — anchored by its center to the row x-position. transform: -50% on
   X only so the dot's vertical extent stays inside its row and isn't clipped by
   neighboring-lane borders. The inline style:top resolves to a calc that already
   targets the row's midline. */
.event-dot {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  border: 1.5px solid var(--paper-warm);
  box-shadow: 0 0 0 1px currentColor;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  transform: translate(-50%, -50%);
  /* Default vertical center if no inline top is set (e.g., closed lane row 0). */
  top: 50%;
  z-index: 3;
}
.event-dot.precise { width: 11px; height: 11px; }
.event-dot.year-only { width: 7px; height: 7px; opacity: 0.78; }
/* Pierce variant: dot whose year falls inside two overlapping pills. Renders
   as a short vertical tick that visually punctures the pill underneath. */
.event-dot.pierce {
  width: 2.5px;
  height: 11px;
  border-radius: 1.5px;
  border: none;
  box-shadow: 0 0 0 1px var(--paper-warm);
}
.event-dot.pierce:hover {
  transform: translate(-50%, -50%) scaleY(1.15) scaleX(1.6);
  box-shadow: 0 0 0 1px var(--paper-warm), 0 0 0 4px rgba(161, 61, 44, 0.12);
}
.event-dot:hover {
  transform: translate(-50%, -50%) scale(1.4);
  box-shadow: 0 0 0 1.5px currentColor, 0 0 0 5px rgba(161, 61, 44, 0.12);
  z-index: 10;
}
/* Active: cursor is exactly on this event. The dot pops with a cinnabar
   ring + slight scale so the user sees what the header label refers to. */
.event-dot.active,
.ministrip-dot.active,
.ministrip-pill.active {
  box-shadow: 0 0 0 2px var(--accent-cinnabar), 0 0 0 5px rgba(161, 61, 44, 0.18);
  z-index: 11;
}
.event-dot.active,
.ministrip-dot.active {
  transform: translate(-50%, -50%) scale(1.3);
}

/* Caption strip — sits ABOVE every closed lane's track inside .track-wrap.
   Always present (no reflow on scrub). Shows the nearest event in this lane
   within ±budget_years of the cursor; silent when nothing in budget.
   Inner .scroller inherits `position: absolute; inset: 0` from the generic
   .scroller rule (line ~722) so it fills the strip; the inline width and
   transform are set by Leptos, same as .track .scroller. */
.lane-caption {
  position: relative;
  height: 22px;
  /* Clip caption text strictly to the track column. The cap-text inside
     uses `transform: translate(-50%)` so a caption anchored near the
     track's left edge extends leftward past it; without this clip the
     overflow visibly crosses into the sticky label gutter. The mask
     below still fades the leftmost few percent so clipped text doesn't
     end abruptly. */
  overflow: hidden;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 8%);
          mask-image: linear-gradient(to right, transparent 0%, #000 8%);
}
.lane-caption .cap-text {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-display);
  font-size: 12px;
  line-height: 1;
  color: var(--ink-faded);
  white-space: nowrap;
  text-shadow: 0 0 3px var(--paper-warm), 0 0 3px var(--paper-warm);
  transition: left 0.18s ease-out, opacity 0.18s ease;
  opacity: 0.85;
}
.lane-caption .cap-text::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: 4px;
  background: currentColor;
  opacity: 0.35;
}

@media (max-width: 599px) {
  .lane-caption .cap-text { font-size: 11px; }
}

/* Event pill — durational marker (start..end). Slim by default so two
   stacked pills fit cleanly in a single 42px lane row. */
.event-pill {
  position: absolute;
  top: 25%;
  height: 5px;
  background: currentColor;
  border-radius: 999px;
  transform: translateY(-50%);
  cursor: pointer;
  opacity: 0.78;
  transition: opacity 0.15s ease, height 0.15s ease, box-shadow 0.15s ease, top 0.18s ease;
}
.event-pill::before, .event-pill::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 2px;
  height: 9px;
  background: currentColor;
  transform: translateY(-50%);
  border-radius: 1px;
}
.event-pill::before { left: 0; }
.event-pill::after  { right: 0; }
.event-pill:hover {
  opacity: 1;
  height: 7px;
  box-shadow: 0 0 0 1px currentColor, 0 0 0 4px rgba(161, 61, 44, 0.12);
  z-index: 10;
}

.event-ghost {
  position: absolute;
  top: 0; bottom: 0;
  width: 1px;
  background: currentColor;
  opacity: 0.18;
  pointer-events: none;
  z-index: 0;
}

/* When a lane has any overlap, every event (pill or dot) splits across
   2 or 3 sub-rows so nothing visually stacks. */
.lane.split[data-rows="2"] .event-pill[data-row="0"],
.lane.split[data-rows="2"] .event-dot[data-row="0"]  { top: 26%; }
.lane.split[data-rows="2"] .event-pill[data-row="1"],
.lane.split[data-rows="2"] .event-dot[data-row="1"]  { top: 74%; }
/* 3-row layout: lane is taller and rows divide the space evenly. */
.lane.split[data-rows="3"] { min-height: 56px; }
.lane.split[data-rows="3"] .event-pill[data-row="0"],
.lane.split[data-rows="3"] .event-dot[data-row="0"]  { top: 18%; }
.lane.split[data-rows="3"] .event-pill[data-row="1"],
.lane.split[data-rows="3"] .event-dot[data-row="1"]  { top: 50%; }
.lane.split[data-rows="3"] .event-pill[data-row="2"],
.lane.split[data-rows="3"] .event-dot[data-row="2"]  { top: 82%; }

/* ──────────────────────────────────────────────────────────────
   OPEN LANE — scrubber-driven story pane
   One event at a time, chosen by the scrubber's current year.
   The lane height stays constant — no chart reflow when swapping.
   ────────────────────────────────────────────────────────────── */
.lane.open {
  background: var(--paper-active);
  cursor: default;
  box-shadow: inset 3px 0 0 var(--accent-cinnabar);
}
.lane.open .label {
  background: rgba(161, 61, 44, 0.04);
  border-right-color: rgba(161, 61, 44, 0.15);
}
.lane.open .label .name {
  color: var(--accent-cinnabar);
}
.lane.open .track {
  min-height: 140px;
  padding: 10px 14px 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* Fixed structure: story-pane (top, fixed 96px) + mini-strip (bottom, 18-30px).
     No flex height computation — prevents the story from bleeding over the mini-strip. */
}
.lane.open .track::before { display: none; }

/* Mini-strip beneath the story — keeps lane density visible.
   Taller when the lane needs split sub-rows so they have room to breathe. */
.ministrip {
  position: relative;
  height: 22px;
  margin-top: 2px;
  overflow: hidden;
}
.ministrip.split[data-rows="2"] { height: 32px; }
.ministrip .scroller::before {
  content: '';
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  height: 1px;
  background: rgba(184, 169, 131, 0.4);
}
.ministrip .ministrip-dot {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  border: 1.5px solid var(--paper-active);
  box-shadow: 0 0 0 1px currentColor;
  transform: translate(-50%, -50%);
  top: 50%;
  cursor: pointer;
  transition: transform 0.28s ease, box-shadow 0.28s ease, background 0.28s ease, width 0.28s ease, height 0.28s ease, top 0.18s ease;
  z-index: 3;
}
.ministrip .ministrip-dot:hover {
  transform: translate(-50%, -50%) scale(1.6);
  box-shadow: 0 0 0 1px currentColor, 0 0 0 4px rgba(161, 61, 44, 0.12);
  z-index: 6;
}
.ministrip .ministrip-dot.pierce {
  width: 2px;
  height: 9px;
  border-radius: 1px;
  border: none;
  box-shadow: 0 0 0 1px var(--paper-active);
}
.ministrip .ministrip-dot.pierce:hover {
  transform: translate(-50%, -50%) scaleY(1.2) scaleX(1.6);
}
.ministrip .ministrip-dot.pierce.focused {
  width: 3px;
  height: 11px;
  border-radius: 1.5px;
}
.ministrip .ministrip-dot.focused {
  background: var(--accent-cinnabar);
  width: 11px;
  height: 11px;
  box-shadow: 0 0 0 1.5px var(--accent-cinnabar), 0 0 0 5px rgba(161, 61, 44, 0.16);
  z-index: 5;
}

/* Ministrip pill — durational counterpart to the dot. Slim by default. */
.ministrip .ministrip-pill {
  position: absolute;
  top: 50%;
  height: 5px;
  background: currentColor;
  border-radius: 999px;
  transform: translateY(-50%);
  opacity: 0.78;
  cursor: pointer;
  transition: opacity 0.18s ease, height 0.18s ease, background 0.18s ease, box-shadow 0.18s ease, top 0.18s ease;
}
.ministrip .ministrip-pill:hover {
  opacity: 1;
  height: 6px;
  box-shadow: 0 0 0 1px currentColor;
  z-index: 6;
}
.ministrip .ministrip-pill.focused {
  background: var(--accent-cinnabar);
  height: 7px;
  opacity: 1;
  box-shadow: 0 0 0 1px var(--accent-cinnabar), 0 0 0 3px rgba(161, 61, 44, 0.16);
  z-index: 5;
}
.ministrip.split[data-rows="2"] .ministrip-pill[data-row="0"],
.ministrip.split[data-rows="2"] .ministrip-dot[data-row="0"]  { top: 28%; }
.ministrip.split[data-rows="2"] .ministrip-pill[data-row="1"],
.ministrip.split[data-rows="2"] .ministrip-dot[data-row="1"]  { top: 72%; }
/* 3-row ministrip: also taller. */
.ministrip.split[data-rows="3"] { height: 42px; }
.ministrip.split[data-rows="3"] .ministrip-pill[data-row="0"],
.ministrip.split[data-rows="3"] .ministrip-dot[data-row="0"]  { top: 22%; }
.ministrip.split[data-rows="3"] .ministrip-pill[data-row="1"],
.ministrip.split[data-rows="3"] .ministrip-dot[data-row="1"]  { top: 50%; }
.ministrip.split[data-rows="3"] .ministrip-pill[data-row="2"],
.ministrip.split[data-rows="3"] .ministrip-dot[data-row="2"]  { top: 78%; }

/* Playhead — usually fixed at the centre of the visible track, but slides
   toward the edge once the chart can't pan any further (clamped). */
.playhead {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--accent-cinnabar);
  pointer-events: none;
  z-index: 4;
  box-shadow: 0 0 0 0.5px rgba(161, 61, 44, 0.3);
  transition: left 0.18s ease-out;
  opacity: 0.55;
}
.playhead::before {
  content: '';
  position: absolute;
  top: -1px; left: 50%;
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--accent-cinnabar);
}
.playhead::after {
  content: '';
  position: absolute;
  bottom: -1px; left: 50%;
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-bottom: 5px solid var(--accent-cinnabar);
}

/* Year axis — same scroller mechanic so labels track the chart pan. */
.axis {
  display: grid;
  grid-template-columns: var(--label-gutter) 1fr;
  border-top: 1px solid var(--rule);
  background: var(--paper-deep);
}
.axis .axis-spacer { border-right: 1px solid var(--rule); }
.axis .axis-ticks {
  position: relative;
  overflow: hidden;
  height: 40px;
}
.axis .axis-ticks .scroller {
  padding: 4px 0;
}
.axis .tick {
  position: absolute;
  top: 2px;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faded);
  white-space: nowrap;
}
.axis .tick.major { font-weight: 600; color: var(--ink-soft); }
.axis .tick::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 14px;
  width: 1px;
  height: 4px;
  background: rgba(184, 169, 131, 0.55);
}
.axis .tick.major::before { height: 6px; background: rgba(184, 169, 131, 0.85); }

/* Scrubber — lives inside .axis-ticks as an absolute overlay. The track
   itself is invisible (the axis tick row IS the visual track); only the
   thumb is rendered, positioned at the bottom of the axis row so it
   reads as a "playhead" sitting on the timeline. */
.scrubber-frame {
  position: relative;
  margin-top: 6px;
  padding: 0 4px;
}
.scrubber-bracket {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  height: 18px;
  border-left:  2px solid currentColor;
  border-right: 2px solid currentColor;
  color: var(--ink-soft, #5a4632);
  opacity: 0.4;
  pointer-events: none;
  transition: left 0.15s ease, width 0.15s ease, opacity 0.15s ease;
  z-index: 2;
}
/* Hide bracket entirely when it'd be skinnier than ~6px (≈ default zoom). */
.scrubber-bracket.collapsed { opacity: 0; }
.zoom-control {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  font-family: var(--font-display, 'EB Garamond', Georgia, serif);
  font-size: 11px;
  color: var(--ink-faded, #8c7858);
}
.zoom-label { user-select: none; cursor: default; }
.zoom-slider {
  flex: 0 1 120px;
  height: 14px;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
.zoom-slider::-webkit-slider-runnable-track {
  height: 2px; background: currentColor; opacity: 0.4; border-radius: 2px;
}
.zoom-slider::-webkit-slider-thumb {
  appearance: none; width: 14px; height: 14px; border-radius: 50%;
  background: var(--ink, #2a1d10); margin-top: -6px; cursor: grab;
}
.zoom-fit {
  background: none; border: 1px solid currentColor; color: inherit;
  font: inherit; padding: 1px 6px; border-radius: 3px; cursor: pointer;
  opacity: 0.7;
}
.zoom-fit:hover { opacity: 1; }
.scrubber {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  /* Confined to bottom half of the 40px axis row so the thumb sits
     BELOW the year labels (which live near the top). */
  height: 22px;
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  outline: none;
  margin: 0;
  padding: 0;
  z-index: 3;
  cursor: grab;
}
.scrubber::-webkit-slider-runnable-track { background: transparent; height: 100%; }
.scrubber::-moz-range-track             { background: transparent; height: 100%; }
.scrubber::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--accent-cinnabar);
  cursor: grab;
  border: 2px solid var(--paper);
  box-shadow: 0 0 0 1px var(--accent-cinnabar), 0 3px 6px -1px rgba(161, 61, 44, 0.5);
  /* Sit at the vertical center of the axis ticks row (28px tall). */
  margin-top: 0;
}
.scrubber::-moz-range-thumb {
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--accent-cinnabar);
  cursor: grab;
  border: 2px solid var(--paper);
  box-shadow: 0 0 0 1px var(--accent-cinnabar), 0 3px 6px -1px rgba(161, 61, 44, 0.5);
}
/* unused — kept so any leftover JS query doesn't NPE */
.scrubber-ticks {
  display: none;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faded);
}

/* Footnote / caption */
.caption {
  margin-top: 16px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12.5px;
  color: var(--ink-faded);
  line-height: 1.5;
  padding-left: 4px;
  border-left: 2px solid var(--rule);
  padding-left: 10px;
}
.caption strong {
  color: var(--ink-soft);
  font-weight: 500;
  font-style: normal;
}

.tune-bar {
  display: flex;
  justify-content: center;
  padding: 4px 0 6px;
  border-top: 1px solid var(--rule, #b8a47e);
  background: var(--paper-warm, #f5ecd6);
}
.tune-link {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-soft, #5a4632);
  text-decoration: none;
  opacity: 0.7;
  padding: 2px 10px;
  border-radius: 3px;
}
.tune-link:hover { opacity: 1; background: rgba(0,0,0,0.05); }
.debug-panel {
  position: fixed; right: 10px; bottom: 30px;
  background: rgba(252,247,232,0.97);
  border: 1px solid var(--rule, #b8a47e);
  padding: 10px 12px; border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 11px; line-height: 1.4;
  color: var(--ink, #2a1d10);
  max-width: 300px;
  z-index: 1000;
  box-shadow: 0 4px 16px rgba(0,0,0,0.15);
}
.debug-panel[hidden] { display: none; }
.debug-row { margin-bottom: 4px; }
.debug-thresholds { font-size: 10px; margin-bottom: 6px; }
.debug-thresholds div { display: flex; justify-content: space-between; gap: 8px; }
.debug-panel button {
  margin-right: 6px; padding: 2px 8px;
  font: inherit; font-size: 10px;
  background: none; border: 1px solid currentColor;
  border-radius: 3px; cursor: pointer;
}
.dbg-slider-row {
  display: grid;
  grid-template-columns: 90px 1fr 36px;
  align-items: center;
  gap: 6px;
  margin-bottom: 3px;
  font-size: 10px;
}
.dbg-slider-row label { color: var(--ink-soft, #5a4632); }
.dbg-slider-row input[type=range] {
  width: 100%;
  height: 12px;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
.dbg-slider-row input[type=range]::-webkit-slider-runnable-track {
  height: 2px; background: currentColor; opacity: 0.4; border-radius: 2px;
}
.dbg-slider-row input[type=range]::-webkit-slider-thumb {
  appearance: none; width: 10px; height: 10px; border-radius: 50%;
  background: var(--ink, #2a1d10); margin-top: -4px;
}
.dbg-val { text-align: right; font-variant-numeric: tabular-nums; }
.dbg-warn { color: var(--accent-cinnabar, #8a3a2a); font-style: italic; font-size: 9px; }

/* ── Event tap-pill (State 1) ──
   A slim one-line label that appears on TAP of a dot/pill. Year + short
   event text + arrow. Tapping the pill itself opens the full detail
   screen (State 2). */
.event-card {
  position: absolute;
  z-index: 50;
  background: var(--paper-warm, #f5ecd6);
  border: 1px solid var(--rule, #b8a47e);
  border-radius: 999px;
  box-shadow: 0 4px 10px -3px rgba(20,12,4,0.25);
  padding: 4px 10px 4px 12px;
  max-width: 260px;
  display: flex; align-items: center; gap: 6px;
  font-family: var(--font-display, 'EB Garamond', Georgia, serif);
  font-size: 12px; line-height: 1.2;
  color: var(--ink, #2a1d10);
  transform: translate(-50%, calc(-100% - 10px));
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.event-card[hidden] { display: none; }
.event-card .ec-yr {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 10px;
  color: var(--ink-soft, #5a4632);
  flex-shrink: 0;
}
.event-card .ec-label {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.event-card .ec-arrow {
  font-size: 13px;
  color: var(--ink-soft, #5a4632);
  flex-shrink: 0;
  margin-left: 2px;
}
.event-card::after {
  content: '';
  position: absolute;
  top: 100%; left: 50%;
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid var(--rule, #b8a47e);
}

/* ── Chart shell (replaces mockup's .device > .stage chain) ─────────── */
.chart-shell {
  position: relative;
  z-index: 2;
  max-width: 540px;
  margin: 0 auto;
  padding: 0;
  overflow: hidden;
  /* Fit within the viewport so the scrubber stays visible and the lane
     stack can scroll internally if there are more lanes than fit. */
  height: calc(100vh - 16px);   /* fallback */
  height: calc(100dvh - 16px);  /* live viewport */
  display: flex;
  flex-direction: column;
}
.chart-shell .chart {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.chart-shell .lanes {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* The scroll container allows pan-y so users can vertical-scroll
     lanes from the label gutter. The track area (`.track-wrap` inside
     each lane) overrides to `touch-action: none` so the Rust pan
     handler owns horizontal gestures there without a browser fight. */
  touch-action: pan-y;
}
/* Generic page wrapper for non-chart screens. Pinned-height column-flex
   shape so the tab bar lands at the viewport bottom without overflow.
   Used by /map and /sources placeholders; /event/:slug adopts it
   later in this slice once the tab bar lands.
   No max-width — inner content sets its own cap (.event-detail-shell
   uses 920, .chart-shell uses 540; .page-shell stays neutral). */
.page-shell {
  position: relative;
  z-index: 2;
  height: calc(100vh - 16px);   /* fallback */
  height: calc(100dvh - 16px);  /* live viewport */
  display: flex;
  flex-direction: column;
}
.placeholder-body {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-soft);
  font-style: italic;
}
/* ── bottom tab bar — Timeline / Map / Sources ─────────────────────
   Mounted inside .chart-shell / .page-shell as the last flex child so
   the screen above absorbs the height. padding-bottom owns the iOS
   home-bar safe area (see also .scrubber edit in next commit). */
.tabbar {
  background: var(--paper);
  border-top: 1px solid var(--rule);
  display: flex;
  padding-top: 8px;
  padding-bottom: max(8px, env(safe-area-inset-bottom));
  flex-shrink: 0;
}
.tabbtn {
  flex: 1;
  text-align: center;
  font-size: 11px;
  color: var(--ink-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  cursor: pointer;
  text-decoration: none;
}
.tabbtn.active { color: var(--accent); }
.tabbtn svg { width: 22px; height: 22px; }
.tablabel { display: block; }
@media (max-width: 599px) {
  .chart-shell {
    max-width: none;
    padding: 0;
    height: 100vh;   /* fallback */
    height: 100dvh;  /* live viewport */
  }
}

/* Event marker: open-lane positioned absolutely inside the .lane-track.open */
.event-marker {
  position: absolute;
  transform: translate(-50%, 0);
}
.event-marker.event-pill { transform: translate(0, 0); }
.event-marker-btn {
  background: transparent; border: none; padding: 4px 6px;
  font: inherit; color: inherit; cursor: pointer;
  display: flex; align-items: center; gap: 6px;
}
.event-label {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Gear button in header */
.gear-btn {
  width: 32px; height: 32px;
  border: 1px solid var(--rule);
  background: var(--paper-warm);
  color: var(--ink-soft);
  border-radius: 50%;
  font-size: 14px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.gear-btn:hover { background: var(--paper-deep); }

/* ── Scrubber lane (bottom of chart-shell) ──────────────────────────── */
.scrub-lane {
  display: grid;
  grid-template-columns: var(--label-gutter) 1fr;
  align-items: stretch;
  border-top: 1px solid var(--rule-strong);
  background: var(--paper-warm);
  position: relative;
  /* safe-area-inset handled by .tabbar (below). */
  min-height: 56px;
  flex: 0 0 auto;
  user-select: none;
}
.scrub-label {
  padding: 8px 10px 8px 16px;
  border-right: 1px solid rgba(216, 205, 177, 0.7);
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.scrub-year {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
  color: var(--accent-cinnabar);
  font-variant-numeric: tabular-nums;
}
.scrub-track-wrap {
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
/* Top half: year-tick scale reflecting the FULL data range. Window-edge
   chevrons sit at the bottom of this row at the visible-window fractions. */
.scrub-scale {
  position: relative;
  height: 22px;
  flex: 0 0 auto;
}
.scrub-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}
.scrub-tick::before {
  content: '';
  width: 1px;
  background: var(--ink-faded);
  margin-bottom: 2px;
}
.scrub-tick-major::before { height: 7px; }
.scrub-tick-mid::before {
  height: 5px;
  opacity: 0.85;
}
.scrub-tick-minor::before {
  height: 3px;
  opacity: 0.6;
}
.scrub-tick-sub::before {
  height: 2px;
  opacity: 0.4;
}
.scrub-tick-label {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-faded);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
/* Bottom half: draggable track with axis line + handle. */
.scrub-track {
  position: relative;
  flex: 1 1 auto;
  min-height: 28px;
  cursor: ew-resize;
  touch-action: none;
}
.scrub-axis {
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  height: 2px;
  background: var(--rule-strong);
  border-radius: 1px;
  transform: translateY(-50%);
  pointer-events: none;
}
/* Draggable handle — vertical bar with circular grip. Extends through the
   year-scale row too so the cursor reads as one continuous line from the
   top of the lane stack down through the scrubber. */
.scrub-handle {
  position: absolute;
  top: -22px; /* extend up through .scrub-scale row */
  bottom: 0;
  width: 2px;
  background: var(--accent-cinnabar);
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 2;
}
.scrub-handle::after {
  content: '';
  position: absolute;
  top: calc(50% + 11px); /* center within just the .scrub-track (axis row) */
  left: 50%;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--paper-warm);
  border: 2px solid var(--accent-cinnabar);
  transform: translate(-50%, -50%);
  box-shadow: 0 1px 3px rgba(60, 40, 12, 0.18);
}

/* Visible-window indicator: translucent cinnabar band on the axis row,
   showing the current view-window's slice of the full data range. Always
   visible (clamps to min 0.5% width) so high zoom doesn't collapse it. */
.scrub-window-band {
  position: absolute;
  top: 50%;
  height: 10px;
  background: rgba(161, 61, 44, 0.18);
  border-left: 1.5px solid var(--accent-cinnabar);
  border-right: 1.5px solid var(--accent-cinnabar);
  transform: translateY(-50%);
  pointer-events: none;
  z-index: 1;
}

/* ─── Mobile (< 600px): tighter lanes + roomy label gutter ─── */
@media (max-width: 599px) {
  :root {
    --closed-lane-h: 68px;   /* was 46px — bumped to fit 22px caption band */
    --label-gutter:  72px;
    --plus-n-size:   7px;    /* new mobile override; root is 8px */
  }
  .lane.compact .col > .stack {
    padding-top: 4px;        /* mobile: tighter top-gap (vs. 6px desktop) for 46px lane */
  }
}

/* ─── Density redesign — closed lane column tokens ─── */
.lane.compact .col {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 24px;
  transform: translateX(-50%);
  cursor: pointer;
}
/* Active-column halo removed (was background: rgba(161, 61, 44, 0.10)) —
   the lead-dot active-ring and the vertical playhead line carry the focus
   cue, the halo was visually redundant and felt heavy on mobile.
   Pseudo-element kept (empty) so any future hook can re-attach. */
.lane.compact .col.active::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.lane.compact .col > .stack {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;              /* DOM order lead → micros → +N; renders top-down */
  align-items: center;
  gap: var(--micro-gap);
  justify-content: flex-start;         /* anchor cluster to top of lane (consistent lead Y) */
  padding-top: 6px;                    /* lead dot sits 6px from lane top */
}
.lane.compact .lead {
  width: var(--lead-size);
  height: var(--lead-size);
  border-radius: 50%;
  background: currentColor;
  opacity: 1;
}
.lane.compact .col.active .lead {
  box-shadow: var(--lead-active-ring);
}
.lane.compact .micros {
  display: flex;
  gap: var(--micro-gap);
}
.lane.compact .micros .m {
  width: var(--micro-size);
  height: var(--micro-size);
  border-radius: 50%;
  background: currentColor;
  opacity: var(--micro-opacity);
}
.lane.compact .more-text {
  font-family: var(--font-mono);
  font-size: var(--plus-n-size);
  color: var(--ink-faded);
  line-height: 1;
}

/* ─── Multi-event popover ─── */
.strip-popover {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--paper);
  border: 1px solid rgba(112, 50, 28, 0.30);
  border-radius: 5px;
  box-shadow: 0 8px 16px rgba(60, 40, 12, 0.15);
  z-index: 50;
  min-width: 280px;
  font-family: var(--font-body);
}
.strip-popover .pop-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px 7px;
  border-bottom: 1px solid rgba(112, 50, 28, 0.10);
  background: var(--paper-warm);
  border-radius: 4px 4px 0 0;
}
.strip-popover .pop-header .ph {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-faded);
  text-transform: uppercase;
  letter-spacing: 0.10em;
}
.strip-popover .pop-header .closebtn {
  background: transparent;
  border: 1px solid rgba(112, 50, 28, 0.20);
  border-radius: 3px;
  width: 18px;
  height: 18px;
  line-height: 14px;
  text-align: center;
  color: var(--ink-faded);
  font-family: monospace;
  cursor: pointer;
  font-size: 13px;
}
.strip-popover .pop-list { padding: 6px 8px; }
.strip-popover .pi {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 6px;
  font-size: 12px;
  line-height: 1.3;
  color: var(--ink);
  cursor: pointer;
  border-radius: 3px;
}
.strip-popover .pi:hover { background: rgba(112, 50, 28, 0.05); }
.strip-popover .pi.active {
  color: var(--accent-cinnabar);
  font-weight: 600;
  background: rgba(161, 61, 44, 0.07);
}
.strip-popover .pi .pdot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.strip-popover .pop-footer {
  border-top: 1px solid rgba(112, 50, 28, 0.10);
  padding: 6px 12px;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-faded);
  display: flex;
  justify-content: space-between;
}

/* ─── Open lane: story panel + sidebar + chronicle excerpt ─── */
/* Wrapper for the open-lane story content. Sizes to its children;
   does NOT clip. Replaces two pre-existing rules that fought over
   height (96px cap) and flex behaviour, both deleted earlier in
   this slice. */
.story-pane {
  position: relative;
}
.lane.open .open-story {
  padding: 14px 18px 6px;
}
.lane.open .open-story-main { min-width: 0; }
.lane.open .story-yr {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.10em;
  color: var(--ink-faded);
}
.lane.open .story-ttl {
  font-family: var(--font-display);
  font-size: 16.5px;
  font-weight: 600;
  line-height: 1.32;
  color: var(--ink);
  margin: 4px 0 6px;
}
.lane.open .story-body {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0 0 10px;
}
.lane.open .story-quote {
  font-style: italic;
  border-left: 2px solid rgba(112, 50, 28, 0.28);
  padding: 4px 12px;
  margin: 10px 0 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.lane.open .story-quote .quote-header {
  font-style: normal;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--accent-cinnabar);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}
.lane.open .story-quote .quote-body { margin: 0 0 5px; }
.lane.open .story-quote .quote-attr {
  font-style: normal;
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--ink-faded);
  letter-spacing: 0.04em;
  margin-top: 5px;
}
.lane.open .further {
  margin-top: 12px;
  padding: 8px 0 4px;
  border-top: 1px solid rgba(112, 50, 28, 0.10);
  font-size: 11.5px;
}
.lane.open .further a {
  color: var(--accent-cinnabar);
  text-decoration: none;
  border-bottom: 1px solid var(--accent-cinnabar-soft);
  padding-bottom: 1px;
}
.lane.open .further a:hover { border-bottom-color: var(--accent-cinnabar); }
.lane.open .nav-pill {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 0;
  margin-top: 10px;
  background: var(--paper-warm);
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faded);
  cursor: pointer;
}
.lane.open .ministrip {
  position: relative;        /* anchor for .strip-popover (absolute) */
}
.lane.open .ministrip-dot.active,
.lane.open .ministrip-pill.active {
  cursor: pointer;
}

@media (max-width: 599px) {
  .lane.open .open-story {
    padding: 10px 14px 4px;
  }
  .lane.open .story-ttl {
    font-size: 13px;
    font-weight: 400;
  }
  .lane.open .story-body {
    font-size: 11px;
  }
}

/* ─── Event detail page (/event/:slug) ─── */
.event-detail-shell {
  max-width: 920px;
  margin: 0 auto;
  padding: 28px 36px;
  background: var(--paper);
  font-family: var(--font-body);
  color: var(--ink);
}
/* When wrapped in .page-shell (event-detail page), the shell becomes the
   scrolling article container that flexes to fill the space between
   header and tab bar. Standalone, .event-detail-shell stays a plain
   padded container — these rules only apply when nested. */
.page-shell > .event-detail-shell {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.event-detail .breadcrumb {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faded);
  margin-bottom: 10px;
  letter-spacing: 0.06em;
}
.event-detail .breadcrumb a {
  color: var(--accent-cinnabar);
  text-decoration: none;
}
.event-detail h1 {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.3;
  color: var(--ink);
  margin: 0 0 4px;
  font-weight: 400;
}
.event-detail .yr-place {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-faded);
  letter-spacing: 0.06em;
  margin-bottom: 22px;
}
.event-detail-grid {
  display: grid;
  grid-template-columns: 1fr 240px;
  gap: 28px;
}
.event-detail .main h2 {
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--accent-cinnabar);
  margin: 0 0 6px;
}
.event-detail .main p {
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-soft);
  margin: 0 0 14px;
}
.event-detail .side .sb {
  background: var(--paper-warm);
  padding: 12px 14px;
  border: 1px solid rgba(112, 50, 28, 0.18);
  border-radius: 5px;
  margin-bottom: 12px;
}
.event-detail .side .sb h3 {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.10em;
  color: var(--accent-cinnabar);
  text-transform: uppercase;
  margin: 0 0 6px;
}
.event-detail .side .sb p {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-soft);
  margin: 0;
}
.event-detail-shell .error {
  margin: 14px 0;
  padding: 8px 12px;
  background: rgba(161, 61, 44, 0.08);
  border-left: 3px solid var(--accent);
  color: var(--accent);
  font-size: 13.5px;
  font-style: italic;
  border-radius: 2px;
}
@media (max-width: 599px) {
  .event-detail-shell { padding: 20px 16px; }
  .event-detail-grid { grid-template-columns: 1fr; gap: 16px; }
  .event-detail h1 {
    font-size: 13px;
  }
  .event-detail .main p {
    font-size: 11px;
  }
}

/* ─── Mini-strip year markers ─── */
.lane.open .ministrip .strip-years {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 12px;
  pointer-events: none;
}
.lane.open .ministrip .yr-mark {
  position: absolute;
  top: 0;
  font-family: var(--font-mono);
  font-size: 8.5px;
  color: var(--ink-faded);
  transform: translateX(-50%);
}
.lane.open .ministrip .yr-mark.active {
  color: var(--accent-cinnabar);
  font-weight: 700;
}

@media (max-width: 599px) {
  .lane.open .ministrip { display: none; }   /* mobile uses nav-pill instead */
}
