/* ── Tokens ─────────────────────────────────────────────────────────────── */
:root {
  --ground:      #f5f4f1;
  --surface:     #ffffff;
  --surface-2:   #eceae5;
  --ink:         #191b1e;
  --ink-2:       #4c525b;
  --ink-3:       #7d848e;
  --line:        #dcd9d3;
  --line-2:      #c8c4bc;
  --edge:        #a59d8f;  /* tree connector lines -- darker than the hairline
                              tokens above, which read fine on panel borders
                              but vanish as 1-2px strokes on the canvas */
  --shadow:      0 1px 2px rgba(25,27,30,.06), 0 6px 18px rgba(25,27,30,.07);
  --shadow-lift: 0 2px 6px rgba(25,27,30,.10), 0 18px 44px rgba(25,27,30,.14);

  --accent:      #9d3f2a;
  --accent-soft: #9d3f2a1a;
  --accent-ink:  #ffffff;
  --unknown:     #c23b3b;  /* flags a name/date we know is missing, not just
                              never displayed -- distinct from --accent so it
                              doesn't get mistaken for a branch color */

  --font-name: "Iowan Old Style", Charter, "Palatino Linotype", Palatino, Georgia, serif;
  --font-ui:   "Avenir Next", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-data: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  --topbar-h: 51px;   /* measured height of .topbar / .stage-bar -- the */
  --stagebar-h: 45px; /* desktop sticky offsets below depend on these   */
  --rail-w: 286px;
  --detail-w: 372px;
  --radius: 9px;
}

:root[data-branch="roy"]         { --accent: #9d3f2a; --accent-soft: #9d3f2a1a; }
:root[data-branch="brendlinger"] { --accent: #8a6410; --accent-soft: #8a64101a; }
:root[data-branch="boyd"]        { --accent: #2c6b74; --accent-soft: #2c6b741a; }
:root[data-branch="roberts"]     { --accent: #42549b; --accent-soft: #42549b1a; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:    #16181b;
    --surface:   #1e2126;
    --surface-2: #272b31;
    --ink:       #e9e7e3;
    --ink-2:     #a8aeb7;
    --ink-3:     #7b828c;
    --line:      #333840;
    --line-2:    #454c56;
    --edge:      #626c7a;
    --shadow:      0 1px 2px rgba(0,0,0,.4), 0 6px 18px rgba(0,0,0,.34);
    --shadow-lift: 0 2px 6px rgba(0,0,0,.46), 0 18px 44px rgba(0,0,0,.5);
    --accent-ink: #14161a;
  }
  :root:not([data-theme="light"])[data-branch="roy"]         { --accent: #e2795c; --accent-soft: #e2795c22; }
  :root:not([data-theme="light"])[data-branch="brendlinger"] { --accent: #d5a33c; --accent-soft: #d5a33c22; }
  :root:not([data-theme="light"])[data-branch="boyd"]        { --accent: #63b8c2; --accent-soft: #63b8c222; }
  :root:not([data-theme="light"])[data-branch="roberts"]     { --accent: #8b9ceb; --accent-soft: #8b9ceb22; }
}

:root[data-theme="dark"] {
  --ground:    #16181b;
  --surface:   #1e2126;
  --surface-2: #272b31;
  --ink:       #e9e7e3;
  --ink-2:     #a8aeb7;
  --ink-3:     #7b828c;
  --line:      #333840;
  --line-2:    #454c56;
  --edge:      #626c7a;
  --shadow:      0 1px 2px rgba(0,0,0,.4), 0 6px 18px rgba(0,0,0,.34);
  --shadow-lift: 0 2px 6px rgba(0,0,0,.46), 0 18px 44px rgba(0,0,0,.5);
  --accent-ink: #14161a;
}
:root[data-theme="dark"][data-branch="roy"]         { --accent: #e2795c; --accent-soft: #e2795c22; }
:root[data-theme="dark"][data-branch="brendlinger"] { --accent: #d5a33c; --accent-soft: #d5a33c22; }
:root[data-theme="dark"][data-branch="boyd"]        { --accent: #63b8c2; --accent-soft: #63b8c222; }
:root[data-theme="dark"][data-branch="roberts"]     { --accent: #8b9ceb; --accent-soft: #8b9ceb22; }

/* ── Base ───────────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }
html, body { height: 100%; }
/* The hidden attribute must always win, even over a class that sets its own
   display (this is what let the relationship panel bleed into other modes). */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
button, input, select { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ── Top bar ────────────────────────────────────────────────────────────── */
.topbar {
  flex: none;
  display: flex;
  align-items: center;
  /* No site name lives here, so nothing needs pushing to the far end --
     flex-start puts the Focus picker and theme toggle at the left edge,
     with the branch switcher (hidden on a single-branch deployment, the
     normal case) taking whatever room it needs beside them. */
  justify-content: flex-start;
  gap: 18px;
  padding: 9px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  z-index: 5;
}
.switch { display: flex; align-items: stretch; gap: 14px; }
.switch-group { display: flex; flex-direction: column; gap: 3px; }
.switch-label {
  font-size: 9.5px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 600; padding-left: 2px;
}
.switch-buttons { display: flex; gap: 4px; }
.switch-divider { width: 1px; background: var(--line); margin: 4px 0; }
.switch-btn {
  font-family: var(--font-name);
  font-size: 14.5px;
  padding: 4px 13px 5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  transition: background .14s, color .14s, border-color .14s;
}
.switch-btn:hover { border-color: var(--line-2); color: var(--ink); }
.switch-btn[aria-current="true"] {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-ink); font-weight: 600;
}
.topbar-end { display: flex; align-items: center; gap: 8px; }

.focus-picker { position: relative; display: flex; align-items: center; }
.focus-btn { display: flex; align-items: baseline; gap: 6px; }
.focus-label {
  font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
}
.focus-name {
  font-family: var(--font-name); font-size: 13.5px; font-weight: 600; color: var(--ink);
  max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.focus-input {
  width: 240px; padding: 6px 11px;
  border: 1px solid var(--accent); border-radius: 7px;
  background: var(--surface); color: var(--ink); font-family: var(--font-name); font-size: 13.5px;
}
.focus-input:focus { outline: none; }

.ghost-btn {
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--ink-2);
  font-size: 12.5px;
  cursor: pointer;
  transition: border-color .14s, color .14s, background .14s;
}
.ghost-btn:hover { border-color: var(--line-2); color: var(--ink); background: var(--surface-2); }
.icon-btn { display: grid; place-items: center; width: 30px; height: 28px; padding: 0; }

/* ── Shell ──────────────────────────────────────────────────────────────── */
.shell {
  flex: 1;
  min-height: 0;
  display: flex;
}
.detail { flex: none; width: var(--detail-w); }

/* ── Left rail (People) ─────────────────────────────────────────────────── */
/* Starts collapsed to a thin bar -- #rail-toggle expands/collapses it. Width
   (not display) is what animates, so the toggle button stays put and content
   just fades/clips rather than reflowing everything else on the page. */
.rail {
  position: relative;
  flex: none;
  width: var(--rail-w);
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--line);
  overflow: hidden;
  transition: width .16s ease;
}
.rail.collapsed { width: 46px; }
.rail.collapsed .rail-head,
.rail.collapsed .people {
  opacity: 0;
  pointer-events: none;
  transition: opacity .1s ease;
}
.rail-head { padding: 14px 14px 10px; border-bottom: 1px solid var(--line); }
.wordmark {
  margin: 0;
  font-family: var(--font-name);
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.01em;
}
.rail-sub { margin: 1px 0 11px; font-size: 12px; color: var(--ink-3); }
.rail-sub b { color: var(--accent); font-weight: 600; }

.search-wrap { position: relative; display: flex; align-items: center; }
.search-icon { position: absolute; left: 9px; color: var(--ink-3); pointer-events: none; }
#search {
  width: 100%;
  padding: 7px 10px 7px 29px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--ground);
  font-size: 13px;
}
#search::placeholder { color: var(--ink-3); }
#search:focus { border-color: var(--accent); outline: none; }

.rail-filters {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 9px;
}
.chk { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ink-2); cursor: pointer; }
.chk input { accent-color: var(--accent); margin: 0; }
.rail-count { font-family: var(--font-data); font-size: 10.5px; color: var(--ink-3); white-space: nowrap; }
.rail-timeline-hint {
  margin: 10px 0 0; padding: 7px 9px; border-radius: 7px;
  background: var(--accent-soft); color: var(--accent); font-size: 13.5px; font-weight: 600;
}

.people { flex: 1; min-height: 0; overflow-y: auto; margin: 0; padding: 5px; list-style: none; }
.person-row {
  display: flex; align-items: baseline; gap: 7px;
  width: 100%; text-align: left;
  padding: 6px 9px;
  border: 0; border-radius: 6px;
  background: transparent; cursor: pointer;
  border-left: 2px solid transparent;
}
.person-row:hover { background: var(--surface-2); }
.person-row[aria-selected="true"] {
  background: var(--accent-soft);
  border-left-color: var(--accent);
}
.person-row .pv {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  object-fit: cover; align-self: center;
  background: var(--surface-2);
}
.person-row .pn {
  font-family: var(--font-name); font-size: 13.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.person-row[aria-selected="true"] .pn { color: var(--accent); font-weight: 600; }
.person-row .py {
  margin-left: auto; flex: none;
  font-family: var(--font-data); font-size: 10.5px;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.rail-empty { padding: 22px 14px; color: var(--ink-3); font-size: 12.5px; text-align: center; }

/* Timeline mode: rail rows become "add to timeline" toggles instead of the
   normal select-a-person behavior. */
.row-add-mark {
  flex: none; width: 16px; height: 16px; display: grid; place-items: center;
  border-radius: 999px; border: 1px solid var(--line-2); font-size: 10px;
  line-height: 1; color: var(--ink-3);
}
.person-row.is-added { background: var(--accent-soft); }
.person-row.is-added .pn { color: var(--accent); font-weight: 600; }
.person-row.is-added .row-add-mark {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}

/* ── Stage ──────────────────────────────────────────────────────────────── */
.stage { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.stage-bar {
  flex: none;
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.seg { display: flex; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }
.seg-btn {
  padding: 4px 12px; border: 0; background: transparent;
  color: var(--ink-2); font-size: 12.5px; cursor: pointer;
}
.seg-btn + .seg-btn { border-left: 1px solid var(--line); }
.seg-btn[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); font-weight: 600; }

/* min-width is a real floor (not 0) so this wraps onto its own line once the
   row gets tight, rather than the flex item being crushed to invisibility --
   the bug this replaced: at ~820-1050px with the detail panel open, the mode
   toggle + direction/depth controls left too little room and this label
   collapsed to 0 width with its text overlapping the controls next to it. */
.stage-root { flex: 1 1 220px; min-width: 220px; display: flex; align-items: baseline; gap: 8px; }
.stage-root .rl {
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 600; flex: none;
}
.stage-root .rn {
  font-family: var(--font-name); font-size: 15px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stage-tools { display: flex; align-items: center; gap: 9px; flex: none; }
.depth { display: flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--ink-3); }
.depth select {
  padding: 3px 5px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--ground); font-size: 12px;
}

.taper-chk { font-size: 11.5px; color: var(--ink-3); white-space: nowrap; }

/* Export button + its size menu */
.export-wrap { position: relative; display: flex; }
.export-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
  width: 264px; max-height: 80vh; overflow-y: auto; padding: 6px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lift);
}
.export-menu[hidden] { display: none; }
.export-group {
  margin: 6px 8px 3px; font-family: var(--font-ui); font-size: 9.5px;
  font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3);
}
.export-group:first-child { margin-top: 2px; }
.export-item {
  display: flex; flex-direction: column; gap: 1px;
  width: 100%; padding: 7px 8px; border: 0; border-radius: 6px;
  background: none; color: var(--ink); font: inherit; font-size: 12.5px;
  text-align: left; cursor: pointer;
}
.export-item:hover, .export-item:focus-visible { background: var(--accent-soft); color: var(--accent); }
.export-item .ex-sub {
  font-family: var(--font-data); font-size: 10.5px; color: var(--ink-3);
}
.export-item:hover .ex-sub, .export-item:focus-visible .ex-sub { color: var(--accent); }

.canvas-wrap { flex: 1; min-height: 0; position: relative; overflow: hidden; background: var(--ground); }
#tree {
  width: 100%; height: 100%; display: block; cursor: grab; touch-action: none;
  user-select: none; -webkit-user-select: none;
}
#tree.dragging { cursor: grabbing; }

/* A zero-height rail pinned to the bottom of the window. The canvas grows
   taller than the viewport on deep trees (syncCanvasHeight) and the document
   is what scrolls, so anchoring the controls to the canvas's own bottom edge
   put them below the fold from depth 5 on. Sticky keeps them on screen, the
   same approach .stage-bar and .detail already use. Height 0 and a
   position:absolute child mean this adds nothing to the layout the tree is
   measured and fitted against. */
.zoom-dock {
  position: sticky; bottom: 0; height: 0; flex: none; z-index: 3;
  pointer-events: none;   /* only the buttons should catch clicks */
}
.zoom-tools {
  position: absolute; right: 14px; bottom: 14px;
  pointer-events: auto;
  display: flex; gap: 6px; align-items: center;
  padding: 5px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface); box-shadow: var(--shadow);
}
.zoom-tools .icon-btn { width: 34px; height: 32px; font-size: 17px; }
.zoom-tools .ghost-btn { border-color: transparent; }
.zoom-tools .ghost-btn:hover { border-color: var(--line-2); }
.empty-note {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  margin: 0; max-width: min(440px, 84%);
  text-align: center; padding: 9px 15px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); box-shadow: var(--shadow);
  color: var(--ink-2); font-size: 12.5px; pointer-events: none;
}

/* ── Relationship-path finder ───────────────────────────────────────────── */
.relate-panel {
  position: absolute; inset: 0; overflow: auto;
  padding: 40px 28px; display: flex; flex-direction: column; align-items: center;
}
.relate-intro {
  max-width: 520px; text-align: center; margin: 0 0 26px;
  color: var(--ink-2); font-size: 13.5px; line-height: 1.55;
}
.relate-pickers {
  display: flex; align-items: flex-end; gap: 12px;
  width: 100%; max-width: 620px;
}
.relate-field { position: relative; flex: 1; display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.relate-field label {
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
}
.relate-field input {
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface); color: var(--ink); font-family: var(--font-name); font-size: 14px;
}
.relate-field input:focus { outline: none; border-color: var(--accent); }
.relate-swap { flex: none; margin-bottom: 1px; font-size: 15px; }

/* Custom person-search dropdown -- replaces native <input list=datalist>,
   which renders as unstylable browser/OS chrome and positions itself
   inconsistently (sometimes to the side of the field rather than below it,
   especially in Safari and in narrow flex layouts). Anchored via
   position:relative on each field's own wrapper. */
.ac-list {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 30;
  margin: 4px 0 0; padding: 4px; list-style: none;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  max-height: 240px; overflow-y: auto; box-shadow: var(--shadow-lift);
}
.ac-item {
  display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left;
  padding: 7px 9px; border: 0; border-radius: 6px;
  background: transparent; color: var(--ink); font-family: var(--font-name); font-size: 13.5px;
  cursor: pointer;
}
.ac-item:hover, .ac-item.is-active { background: var(--accent-soft); color: var(--accent); }
.ac-item .ac-yrs {
  margin-left: auto; flex: none; font-family: var(--font-data); font-size: 10.5px; color: var(--ink-3);
}
.ac-empty { padding: 8px 9px; color: var(--ink-3); font-size: 12.5px; }

.relate-result { width: 100%; max-width: 620px; margin-top: 30px; }
.relate-hint { color: var(--ink-3); font-size: 13px; text-align: center; margin: 0; }
.relate-headline {
  font-family: var(--font-name); font-size: 19px; text-align: center;
  line-height: 1.5; margin: 0 0 8px;
}
.relate-headline b { font-weight: 700; }
.relate-label { color: var(--accent); font-weight: 700; }

.relate-chain { margin-top: 28px; }
.relate-chain h4 {
  margin: 0 0 10px; font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-3); text-align: center;
}
.relate-chain-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.relate-chain-list li {
  display: flex; align-items: baseline; gap: 8px; justify-content: center;
  padding: 5px 0; border-left: 2px solid var(--line); padding-left: 12px;
  margin-left: 14px;
}
.relate-chain-list .ry { font-family: var(--font-data); font-size: 10.5px; color: var(--ink-3); }

.rt-scroll { max-width: 100%; overflow-x: auto; overflow-y: hidden; padding: 8px 4px 4px; }
.rt-svg { display: block; margin: 0 auto; max-height: 60vh; }

/* ── Timeline mode ───────────────────────────────────────────────────────── */
.timeline-panel {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  padding: 12px 16px; background: var(--surface); border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.timeline-controls { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.timeline-add { position: relative; display: flex; gap: 8px; flex: 1; min-width: 220px; }
.timeline-add input {
  flex: 1; padding: 7px 10px; border: 1px solid var(--line); border-radius: 7px;
  background: var(--ground); color: var(--ink); font-family: var(--font-name); font-size: 13px;
}
.timeline-add input:focus { outline: none; border-color: var(--accent); }
.timeline-pres-toggle { flex: none; white-space: nowrap; }
.timeline-chips {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 7px;
}
.timeline-chip {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 6px 5px 10px; border-radius: 999px;
  background: var(--accent-soft); font-size: 12px; color: var(--ink);
}
.timeline-chip .chip-name { font-family: var(--font-name); }
.timeline-chip .ry { font-family: var(--font-data); font-size: 10.5px; color: var(--ink-3); }
.chip-remove {
  border: none; background: none; color: var(--ink-3); font-size: 15px; line-height: 1;
  cursor: pointer; padding: 2px 4px; border-radius: 999px;
}
.chip-remove:hover { color: var(--ink); background: var(--surface-2); }

/* ── Stories mode ────────────────────────────────────────────────────────── */
.stories-panel {
  position: absolute; inset: 0; overflow: auto;
  padding: 32px 28px;
}
.stories-intro {
  max-width: 520px; margin: 0 auto 22px; text-align: center;
  color: var(--ink-2); font-size: 13.5px; line-height: 1.55;
}
.stories-list {
  list-style: none; margin: 0 auto; padding: 0; max-width: 780px;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px;
}
.stories-item { display: contents; }
.stories-link {
  display: flex; flex-direction: column; gap: 10px;
  padding: 14px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface); text-decoration: none; color: inherit;
}
.stories-link:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.stories-photo {
  width: 100%; aspect-ratio: 1; border-radius: 8px; object-fit: cover;
  background: var(--surface-2);
}
.stories-photo-empty { display: block; }
.stories-name {
  font-family: var(--font-name); font-size: 15px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stories-life {
  margin-top: 2px; font-family: var(--font-data); font-size: 11px;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.stories-empty {
  grid-column: 1 / -1; padding: 22px 14px; text-align: center;
  color: var(--ink-3); font-size: 13px; list-style: none;
}

.tl-tick { stroke: var(--line); stroke-width: 1; }
.tl-tick-label { font-family: var(--font-data); font-size: 10px; fill: var(--ink-3); text-anchor: middle; }
.tl-baseline { stroke: var(--line-2); stroke-width: 1.2; }
.tl-band { stroke: rgba(0,0,0,.14); stroke-width: 1; }
.tl-cat-war { fill: #9c4740; }
.tl-cat-pandemic { fill: #8a7a1f; }
.tl-cat-economic { fill: #35618f; }
.tl-cat-other { fill: #6b5a91; }
.tl-band-label {
  font-family: var(--font-ui); font-size: 10px; font-weight: 700; letter-spacing: .01em;
  fill: #fff; pointer-events: none;
}
/* Set inside the (fixed-color, theme-independent) lifebar itself, same
   treatment as .tl-band-label on the historical-event pills -- white
   regardless of theme, since the bar colors underneath don't change either. */
.tl-person-label {
  font-family: var(--font-ui); font-size: 10.5px; font-weight: 700; letter-spacing: .01em;
  fill: #fff; pointer-events: none;
}
/* No birth year on file -- no bar to sit inside of, so this one stands
   alone and needs to read against the plain chart background instead. */
.tl-person-label.tl-person-label-bare { font-family: var(--font-name); font-weight: 400; fill: var(--ink); }
.tl-lifebar { fill: var(--line-2); }
.tl-lifebar.sex-M { fill: #5b7fa6; }
.tl-lifebar.sex-F { fill: #a8607f; }
.tl-living-dot { fill: var(--accent); }
.tl-row-caption {
  font-family: var(--font-ui); font-size: 9.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; fill: var(--ink-3);
}
.tl-pres { stroke: var(--ground); stroke-width: 1; }
/* Name + term number + years as one vertical line running down inside the
   bar -- the bar itself is sized tall enough (presBarH) to fully enclose
   it, so the color, not the plain chart background, is what's behind the
   text the whole way down. */
.tl-pres-label {
  font-family: var(--font-ui); font-size: 12px; font-weight: 700; fill: #fff; pointer-events: none;
  /* Drawn as ordinary horizontal text, then rotated 90deg around its own
     start point (see the `transform` set in app.js) -- writing-mode:
     vertical-rl was tried first but depends on the font having real
     vertical metrics, which var(--font-ui) doesn't, so glyphs hugged one
     side of the bar instead of centering. Rotating plain horizontal text
     sidesteps that: text-anchor:start pins the reading-start at the pivot
     (so it only grows downward after rotation), dominant-baseline:central
     centers the glyphs' thickness on the pivot (so it's centered on the
     bar's x after rotation too). */
  text-anchor: start;
  dominant-baseline: central;
}
.tl-party-r { fill: #a6413f; }
.tl-party-d { fill: #3f5fa6; }
.tl-party-f { fill: #7a5c3e; }
.tl-party-w { fill: #5c8a5c; }
.tl-party-o { fill: #8a8a8a; }

/* nodes drawn as SVG */
.node { cursor: pointer; }
.node .box {
  fill: var(--surface); stroke: var(--line-2); stroke-width: 1;
  transition: stroke .12s;
}
.node:hover .box { stroke: var(--accent); }
.node.is-root .box { stroke: var(--accent); stroke-width: 2; }
.node.is-sel  .box { stroke: var(--accent); stroke-width: 2; }
.node.is-sel  .plate { fill: var(--accent-soft); }
.node .plate { fill: transparent; }
.node .nm { font-family: var(--font-name); font-size: 16px; font-weight: 700; fill: var(--ink); }
.node .yr { font-family: var(--font-data); font-size: 11.5px; fill: var(--ink-3); }
.node .nm.unk, .node .yr.unk { fill: var(--unknown); }
.node .pl { font-size: 9.5px; fill: var(--ink-3); }
.node .sexbar { fill: var(--line-2); }
.node.sex-M .sexbar { fill: #5b7fa6; }
.node.sex-F .sexbar { fill: #a8607f; }
/* Portrait placeholder -- a monogram in a soft, sex-tinted circle for the
   common case of no FamilySearch photo, so every card keeps the same
   header shape instead of some having a blank gap up top. */
.node .avatar-fallback { fill: var(--surface-2); stroke: var(--line-2); stroke-width: 1; }
.node.sex-M .avatar-fallback { fill: #5b7fa61a; stroke: #5b7fa64d; }
.node.sex-F .avatar-fallback { fill: #a8607f1a; stroke: #a8607f4d; }
.node .avatar-initial {
  font-family: var(--font-name); font-size: 21px; font-weight: 600;
  fill: var(--ink-3); pointer-events: none;
}
.node.sex-M .avatar-initial { fill: #5b7fa6; }
.node.sex-F .avatar-initial { fill: #a8607f; }
.link {
  fill: none; stroke: var(--edge); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* The marriage bar between two ancestor boxes -- the children's drop line
   hangs from its middle, so it carries weight and stays solid. */
.link.couple { stroke-width: 2; }
.link.spouse { stroke: var(--accent); stroke-width: 1.6; stroke-dasharray: 3 3; opacity: .7; }
/* Collateral wiring (revealed siblings and their lines) stays readable but
   clearly secondary to the direct line. */
.link.collateral { stroke-dasharray: 5 4; opacity: .65; }
.gen-label { font-family: var(--font-data); font-size: 10px; fill: var(--ink-3); }
.gen-rule { stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 5; }

/* collateral (revealed sibling/cousin) branches read as clearly secondary to
   the direct line -- lighter box, dashed connecting lines */
.node.is-collateral .box { stroke-dasharray: 2.5 2; opacity: .82; }
.node.is-collateral .nm, .node.is-collateral .yr { opacity: .88; }

/* Direct Relative filter active: in-laws (no shared ancestor with the Focus
   person, reachable only through a marriage edge) are dimmed rather than
   removed -- removing them would orphan the family unit they're shown
   alongside (e.g. a child's other parent). */
.node.is-not-relative .box { opacity: .4; }
.node.is-not-relative .nm, .node.is-not-relative .yr, .node.is-not-relative .sexbar { opacity: .5; }

.expand-badge { cursor: pointer; }
.expand-badge circle {
  fill: var(--surface); stroke: var(--edge); stroke-width: 1.4;
  transition: fill .12s, stroke .12s;
}
.expand-badge text {
  font-family: var(--font-ui); font-size: 13px; font-weight: 700;
  fill: var(--ink-2); pointer-events: none;
}
/* The lead-in from the family rail to a rail-mounted sibling badge -- reads
   as the rail continuing toward the not-yet-shown branch. */
.expand-badge .badge-stub { stroke: var(--edge); stroke-width: 2; stroke-linecap: round; }
.expand-badge.is-open .badge-stub { stroke-dasharray: 5 4; opacity: .65; }
.expand-badge:hover circle { fill: var(--accent); stroke: var(--accent); }
.expand-badge:hover text { fill: var(--accent-ink); }
/* Open (collapse) badges stay quiet -- with one on every drawn card they'd
   otherwise scatter accent dots across the whole tree. The accent is saved
   for hover and for closed badges hiding something. */
.expand-badge.is-open circle { stroke: var(--line-2); }
.expand-badge.is-open text { fill: var(--ink-3); }
.expand-badge.eb-siblings:not(.is-open) circle,
.expand-badge.eb-spouses:not(.is-open) circle { stroke: var(--accent); }
.expand-badge.eb-siblings:not(.is-open) text,
.expand-badge.eb-spouses:not(.is-open) text { fill: var(--accent); }

/* ── Detail panel ───────────────────────────────────────────────────────── */
.detail {
  min-height: 0; overflow-y: auto;
  background: var(--surface);
  border-left: 1px solid var(--line);
  padding: 16px 16px 40px;
}
.d-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 3px; }
.d-portrait {
  flex: none; width: 80px; height: 80px; border-radius: 10px;
  object-fit: cover; border: 1px solid var(--line-2);
  background: var(--surface-2);
}
.d-name { margin: 0; font-family: var(--font-name); font-size: 21px; font-weight: 600; line-height: 1.2; text-wrap: balance; }
.d-name .unk { color: var(--unknown); }
.d-close { margin-left: auto; flex: none; }
.d-life { font-family: var(--font-data); font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.d-life.unk { color: var(--unknown); }
.d-rel { display: inline-block; margin-top: 7px; padding: 2px 9px; border-radius: 999px;
         background: var(--accent-soft); color: var(--accent); font-size: 11.5px; font-weight: 600; }
/* The clickable variant (a real computed relationship, not the focus
   person's own "Focus" chip -- that one stays a plain span, nothing to jump
   to) needs button reset on top of the shared pill look. */
button.d-rel {
  border: 0; font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer;
}
button.d-rel:hover, button.d-rel:focus-visible { background: var(--accent); color: var(--accent-ink); }
.d-actions { display: flex; gap: 7px; margin: 13px 0 4px; }

.d-sec { margin-top: 18px; }
.d-sec > h3 {
  margin: 0 0 7px;
  font-size: 10px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 700;
}
.d-grid { display: grid; grid-template-columns: 80px minmax(0,1fr); gap: 5px 11px; font-size: 12.5px; }
.d-grid dt { color: var(--ink-3); }
.d-grid dd { margin: 0; overflow-wrap: anywhere; }
.d-grid dd.mono { font-family: var(--font-data); font-size: 11.5px; }

.photo-gallery { display: flex; flex-wrap: wrap; gap: 6px; }
.photo-thumb {
  width: 64px; height: 64px; border-radius: 8px; object-fit: cover;
  border: 1px solid var(--line-2); background: var(--surface-2);
}
.photo-thumb:hover { border-color: var(--accent); }

.rel-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.rel-item { display: flex; align-items: baseline; gap: 7px; font-size: 12.5px; }
.rel-item .rk { flex: none; width: 62px; color: var(--ink-3); font-size: 11px; }
.person-link {
  background: none; border: 0; padding: 1px 3px; margin-left: -3px;
  border-radius: 4px;
  font-family: var(--font-name); font-size: 13.5px;
  color: var(--accent); cursor: pointer; text-align: left;
}
.person-link:hover { background: var(--accent-soft); text-decoration: underline; }
.person-link .unk { color: var(--unknown); }
.rel-item .ry { font-family: var(--font-data); font-size: 10.5px; color: var(--ink-3); }
.rel-item .ry.unk { color: var(--unknown); }
.rel-item .rm { font-size: 11px; color: var(--ink-3); font-style: italic; }
.xbadge {
  flex: none; padding: 1px 7px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 9.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
}
.plain-name { font-family: var(--font-name); font-size: 13.5px; color: var(--ink-2); }
.plain-name::after { content: " (not in tree)"; font-family: var(--font-ui); font-size: 10px; color: var(--ink-3); font-style: italic; }

.bio { font-family: var(--font-name); font-size: 14px; line-height: 1.6; color: var(--ink-2); white-space: pre-wrap; overflow-wrap: anywhere; margin: 0; }
.src-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.src-item { font-size: 12px; border-left: 2px solid var(--line); padding-left: 9px; }
.src-item .sid { font-family: var(--font-data); font-size: 10.5px; color: var(--accent); font-weight: 600; }
.src-item .st { display: block; color: var(--ink); }
.src-item .sg { display: block; color: var(--ink-3); font-size: 11.5px; margin-top: 2px; }
.src-item a { display: block; margin-top: 2px; color: var(--accent); word-break: break-all; font-size: 11px; }
.fs-link { color: var(--accent); font-family: var(--font-data); font-size: 11.5px; }


@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ── Desktop: let the canvas grow past the window ───────────────────────── */
/* By default the app is locked to exactly the window height, so the tree
   never gets more room than the screen happens to give it. On desktop the
   canvas instead gets a drag handle (bottom-right corner) and the page
   scrolls once it grows past the viewport. The rail, the detail panel and
   both header bars stay pinned so the controls don't scroll away with it. */
@media (min-width: 821px) {
  html, body { height: auto; }
  /* `visible`, not `auto`: an overflow value on body would make body itself
     the scroll container, and the sticky bars below would then pin to that
     box instead of the viewport -- which reads as the header floating in
     mid-page. Leaving it visible lets the document do the scrolling. */
  body { overflow: visible; }

  .topbar { position: sticky; top: 0; }
  .shell { min-height: calc(100vh - var(--topbar-h)); height: auto; align-items: flex-start; }

  /* Sticky needs a definite self-alignment, otherwise the default stretch
     makes these full-height and there is nothing left to stick within. */
  .rail, .detail {
    position: sticky; top: var(--topbar-h); align-self: flex-start;
    height: calc(100vh - var(--topbar-h));
  }
  .stage-bar { position: sticky; top: var(--topbar-h); z-index: 4; }

  /* flex:none so the height syncCanvasHeight() sets wins over flex sizing.
     Deliberately not `resize: vertical` -- the native grip lands in the same
     corner as the zoom controls, and the height is chosen from the tree's
     own proportions anyway. */
  .canvas-wrap {
    flex: none;
    height: calc(100vh - var(--topbar-h) - var(--stagebar-h));
    min-height: 340px;
  }
}

/* ── Mobile (iPhone / iPad portrait) ────────────────────────────────────── */
@media (max-width: 820px) {
  .shell { flex-direction: column; }
  .rail {
    flex: none;
    width: 100%;
    max-height: 70vh;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    transition: max-height .16s ease;
  }
  /* Collapsing means something different once the rail is a horizontal
     strip on top instead of a sidebar -- shrink its height, not its width.
     Its own toggle button now lives in .stage-bar instead of inside the
     rail itself (see index.html), so a collapsed rail has nothing left
     worth showing here at all -- full collapse to nothing, not just a
     shrink. */
  .rail.collapsed { width: 100%; max-height: 0; border-bottom: 0; }
  .stage { flex: 1; min-height: 0; }
  .rail-head { padding: 10px 12px 8px; }
  .wordmark { font-size: 17px; }
  .rail-sub { margin-bottom: 7px; }

  /* The person record becomes a bottom sheet over the tree rather than a
     third column there is no room for. */
  .detail {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    width: auto;
    max-height: 58vh;
    z-index: 30;
    border-left: 0;
    border-top: 1px solid var(--line);
    border-radius: 14px 14px 0 0;
    box-shadow: var(--shadow-lift);
  }

  .topbar { flex-wrap: wrap; gap: 10px; padding: 8px 12px; }
  .stage-bar { flex-wrap: wrap; gap: 8px; row-gap: 6px; padding: 8px 10px; }
  .stage-root { flex-basis: 100%; order: 3; }
  .relate-pickers { flex-direction: column; align-items: stretch; }
  .relate-swap { align-self: center; transform: rotate(90deg); }
  .relate-panel { padding: 24px 14px; }
  .stories-panel { padding: 24px 14px; }
  .timeline-controls { gap: 10px; }
  .zoom-tools { right: 10px; bottom: 10px; }
}

/* On a phone-width screen the bottom sheet is pinned to exactly the lower
   third rather than growing with its content -- a long record (lots of
   sources, a bio) would otherwise cover most of the tree behind it. Content
   scrolls inside that fixed band instead (.detail already has
   overflow-y: auto from the base rule). iPad-width devices keep the more
   generous, content-sized sheet from the 820px block above. */
@media (max-width: 480px) {
  .detail {
    height: 34vh;
    max-height: 34vh;
  }
}

/* ── Print ──────────────────────────────────────────────────────────────── */
@media print {
  /* Always print on a light ground regardless of the on-screen theme --
     the selector list matches every theme state at equal-or-higher
     specificity than the theme blocks above, and being last in the file it
     wins the cascade. */
  :root,
  :root:not([data-theme="light"]),
  :root[data-theme="dark"] {
    --ground:    #ffffff;
    --surface:   #ffffff;
    --surface-2: #f2f0ec;
    --ink:       #191b1e;
    --ink-2:     #3c4149;
    --ink-3:     #6a707a;
    --line:      #cfccc5;
    --line-2:    #b5b1a9;
    --shadow: none;
    --shadow-lift: none;
    --accent-ink: #ffffff;
  }

  .topbar, .rail, .detail, .stage-bar, .zoom-dock, .zoom-tools, .empty-note,
  .timeline-controls, .relate-swap {
    display: none !important;
  }

  html, body { height: auto; overflow: visible; }
  .shell,
  .shell:has(.detail:not([hidden])) { display: block; height: auto; }
  .stage { display: block; }
  .canvas-wrap {
    position: relative;
    width: 100%;
    height: 92vh;
    overflow: hidden;
    background: #ffffff;
  }
  .relate-panel { position: static; overflow: visible; padding: 12px 0; }
  .timeline-panel { position: static; border: 0; box-shadow: none; padding: 0 0 8px; }

  /* Tree export: the live app is replaced wholesale by the generated
     sheets, so the on-screen layout never has to be coerced into paper
     shape -- each sheet is its own exactly-sized block. */
  body.printing-tiles > .shell { display: none !important; }
  body.printing-tiles #print-tiles { display: block; }
}

/* Built only while exporting, and removed again on afterprint. */
#print-tiles { display: none; }
.print-tile {
  display: flex; flex-direction: column;
  /* Page-relative, not a fixed inch size: setExportPageStyle sets only
     @page's orientation (custom @page sizes are a hint browsers routinely
     ignore), so the tile has to fill whatever paper is actually loaded --
     100vw/100vh is the printed page's own content box in this layout. */
  width: 100vw; height: 100vh;
  break-after: page; page-break-after: always;
  overflow: hidden;
}
.print-tile:last-child { break-after: auto; page-break-after: auto; }
/* One sheet's slice, cut into bands so wide content fills the paper
   instead of shrinking to a strip across the middle of it. */
.print-bands { flex: 1 1 auto; display: flex; min-width: 0; min-height: 0; }
.print-bands.axis-x { flex-direction: column; }
.print-bands.axis-y { flex-direction: row; }
.print-bands > svg { flex: 1 1 0; width: 100%; height: 100%; min-width: 0; min-height: 0; }
.print-bands.axis-x > svg + svg { border-top: 1px dashed #cfccc5; margin-top: .07in; padding-top: .07in; }
.print-bands.axis-y > svg + svg { border-left: 1px dashed #cfccc5; margin-left: .07in; padding-left: .07in; }
.print-tile-cap {
  flex: 0 0 auto; padding-top: 5px; text-align: center;
  font-family: var(--font-ui); font-size: 8.5px; letter-spacing: .04em;
  color: #6a707a;
}
