/* ═══════════════════════════════════════════════════════════════════════════
   components.css — every piece of interface furniture.
   Order: chrome shell → masthead → search → legend → stats → hint
        → labels → panel → sheet → intro → fatal → responsive.
   ═══════════════════════════════════════════════════════════════════════ */

/* ═══ Chrome shell ═══════════════════════════════════════════════════════
   A full-viewport grid that never intercepts pointer events; only the
   individual controls opt back in. The canvas stays fully draggable. */
.chrome {
  position: fixed;
  inset: 0;
  z-index: var(--z-chrome);
  pointer-events: none;
  padding: var(--gutter);
  display: grid;
  grid-template-rows: auto 1fr auto;
  grid-template-columns: 1fr;
  transition: opacity var(--d-slow) var(--ease-out);
}
.chrome > * { pointer-events: auto; }
.chrome[data-state='hidden'] { opacity: 0; pointer-events: none; }
.chrome[data-state='visible'] { opacity: 1; }

/* ═══ Masthead ═══════════════════════════════════════════════════════════ */
.masthead {
  grid-row: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: start;
  gap: var(--s-4);
  pointer-events: none;
}
.masthead > * { pointer-events: auto; }

/* ── Brand ──────────────────────────────────────────────────────────── */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3) var(--s-2) var(--s-2);
  margin: calc(var(--s-2) * -1);
  border-radius: var(--r-md);
  justify-self: start;
  transition: background var(--d-fast) var(--ease-out);
}
.brand:hover { background: rgba(20, 21, 26, 0.035); }
.brand:active { transform: scale(0.985); }

.brand__mark {
  width: 34px;
  height: 34px;
  flex: none;
  color: var(--bronze);
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
}
.brand__mark svg { width: 100%; height: 100%; }

.brand__text { display: flex; flex-direction: column; gap: 1px; text-align: left; }
.brand__title {
  font-family: var(--font-display);
  font-size: var(--t-md);
  letter-spacing: 0.004em;
  line-height: 1.15;
  color: var(--ink);
}
.brand__sub {
  font-size: var(--t-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-tertiary);
  font-weight: 520;
}

/* ═══ Search ═════════════════════════════════════════════════════════════ */
.search {
  position: relative;
  width: min(420px, 42vw);
  justify-self: center;
  z-index: 2;
}

.search__field {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  height: 46px;
  padding: 0 var(--s-2) 0 var(--s-4);
  border-radius: var(--r-pill);
  transition:
    box-shadow var(--d-base) var(--ease-out),
    transform var(--d-base) var(--ease-out);
}
.search[data-focused='true'] .search__field {
  box-shadow:
    inset 0 0 0 1px rgba(168, 118, 58, 0.32),
    0 0 0 4px var(--bronze-wash),
    var(--shadow-2);
}

.search__icon {
  width: 17px; height: 17px;
  flex: none;
  color: var(--ink-tertiary);
  transition: color var(--d-fast) var(--ease-out);
}
.search[data-focused='true'] .search__icon { color: var(--bronze); }

.search__input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  font-size: var(--t-base);
  letter-spacing: -0.008em;
}

.search__kbd {
  flex: none;
  margin-right: var(--s-2);
  transition: opacity var(--d-fast) var(--ease-out);
}
.search[data-focused='true'] .search__kbd,
.search[data-filled='true'] .search__kbd { opacity: 0; }

.search__clear {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--ink-tertiary);
  transition: background var(--d-fast) var(--ease-out), color var(--d-fast) var(--ease-out);
}
.search__clear svg { width: 15px; height: 15px; }
.search__clear:hover { background: rgba(20, 21, 26, 0.06); color: var(--ink); }

/* ── Results ────────────────────────────────────────────────────────── */
.search__results {
  position: absolute;
  top: calc(100% + var(--s-2));
  left: 0;
  right: 0;
  max-height: min(52vh, 430px);
  overflow-y: auto;
  overscroll-behavior: contain;
  border-radius: var(--r-lg);
  padding: var(--s-2);
  box-shadow:
    inset 0 0 0 1px var(--glass-ring),
    var(--shadow-3);
  animation: results-in var(--d-base) var(--ease-out) both;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-quaternary) transparent;
}
@keyframes results-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
.search__results::-webkit-scrollbar { width: 8px; }
.search__results::-webkit-scrollbar-thumb {
  background: var(--ink-quaternary);
  border-radius: 99px;
  border: 2px solid transparent;
  background-clip: content-box;
}

.result {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  text-align: left;
  cursor: pointer;
  transition: background var(--d-instant) linear;
}
.result[aria-selected='true'] { background: rgba(20, 21, 26, 0.055); }

.result__gen {
  font-size: 10px;
  font-weight: 620;
  letter-spacing: 0.03em;
  color: var(--ink-tertiary);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.result__name {
  font-family: var(--font-display);
  font-size: var(--t-md);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.result__name mark {
  background: none;
  color: var(--bronze);
  font-weight: 600;
}
.result__years {
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.search__empty {
  padding: var(--s-5) var(--s-4);
  text-align: center;
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
}

/* ═══ Tools ══════════════════════════════════════════════════════════════ */
.tools {
  justify-self: end;
  display: flex;
  gap: var(--s-1);
  align-items: center;
}
.tool {
  width: 38px; height: 38px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  color: var(--ink-secondary);
  transition:
    background var(--d-fast) var(--ease-out),
    color var(--d-fast) var(--ease-out),
    transform var(--d-fast) var(--ease-out);
}
.tool svg { width: 18px; height: 18px; }
.tool:hover { background: rgba(20, 21, 26, 0.055); color: var(--ink); }
.tool:active { transform: scale(0.93); }
.tool[aria-expanded='true'] { background: rgba(20, 21, 26, 0.075); color: var(--ink); }

/* ═══ View switch ════════════════════════════════════════════════════════ */
.switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: var(--r-pill);
  margin-right: var(--s-2);
}
.switch__opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px 0 10px;
  border-radius: var(--r-pill);
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
  transition:
    background var(--d-fast) var(--ease-out),
    color var(--d-fast) var(--ease-out);
}
.switch__opt svg { width: 16px; height: 16px; flex: none; }
.switch__opt:hover { color: var(--ink-secondary); }
.switch__opt[aria-selected='true'] {
  background: var(--white);
  color: var(--ink);
  font-weight: 550;
  box-shadow: 0 0 0 1px rgba(20, 21, 26, 0.05), var(--shadow-1);
}

/* ═══════════════════════════════════════════════════════════════════════
   The 2D chart
   ═══════════════════════════════════════════════════════════════════════ */
.chart {
  position: fixed;
  inset: 0;
  z-index: var(--z-labels);
  background: var(--paper);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--d-base) var(--ease-out),
    visibility var(--d-base);
  cursor: grab;
  overscroll-behavior: none;
  touch-action: none;
}
.chart[data-active='true'] { opacity: 1; visibility: visible; }
.chart[data-grabbing='true'] { cursor: grabbing; }

.chart__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* ── Generation ruler ─────────────────────────────────────────────────
   Sits above the chart in HTML and follows the horizontal pan, so the
   column headings stay legible however far down you have scrolled.   */
.chart__ruler {
  position: absolute;
  top: calc(var(--gutter) + 62px);
  left: 0;
  right: 0;
  height: 22px;
  pointer-events: none;
}
.chart__gen {
  position: absolute;
  top: 0;
  left: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  transform-origin: 0 0;
  margin-left: -14px;
  font-size: 10px;
  font-weight: 620;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-quaternary);
  transition: opacity var(--d-fast) linear;
  white-space: nowrap;
}
/* ── Column guides ────────────────────────────────────────────────── */
.chart__guide {
  stroke: var(--rule);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* ── Connectors ───────────────────────────────────────────────────── */
.chart__edge {
  fill: none;
  stroke: var(--ink-quaternary);
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
  transition: stroke var(--d-base) var(--ease-out), opacity var(--d-base) var(--ease-out);
}
.chart[data-lineage='true'] .chart__edge { opacity: 0.22; }
.chart__edge[data-lineage='true'] {
  stroke: var(--bronze);
  stroke-width: 2;
  opacity: 1;
}

/* ── Nodes ────────────────────────────────────────────────────────── */
.cnode { cursor: pointer; }
.cnode__box {
  fill: var(--white);
  stroke: var(--rule-strong);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transition: fill var(--d-fast) var(--ease-out), stroke var(--d-fast) var(--ease-out);
}
.cnode__dot { fill: var(--bronze-bright); }
.cnode__dot--living { fill: var(--evergreen-bright); }

.cnode__name {
  font-family: var(--font-display);
  font-size: 13px;
  fill: var(--ink);
  dominant-baseline: middle;
}
.cnode__years {
  font-family: var(--font-ui);
  font-size: 9.5px;
  fill: var(--ink-tertiary);
  dominant-baseline: middle;
  font-variant-numeric: tabular-nums;
}

.cnode:hover .cnode__box { stroke: var(--ink-quaternary); }
.cnode[data-hover='true'] .cnode__box { stroke: var(--ink-tertiary); }

.cnode[data-match='true'] .cnode__box {
  stroke: var(--bronze);
  stroke-width: 1.6;
  fill: #FFFCF6;
}
.cnode[data-selected='true'] .cnode__box {
  stroke: var(--ink);
  stroke-width: 1.8;
  fill: var(--white);
}
.cnode[data-selected='true'] .cnode__name { font-weight: 600; }

/* Lineage: the path to the founder stays lit, everything else recedes. */
.chart[data-lineage='true'] .cnode { opacity: 0.3; }
.chart[data-lineage='true'] .cnode[data-lineage='true'] { opacity: 1; }
.cnode[data-lineage='true'] .cnode__box { stroke: var(--bronze); }

.cnode:focus { outline: none; }
.cnode:focus-visible .cnode__box { stroke: var(--bronze); stroke-width: 2.4; }

/* ═══ Bottom-left stack ══════════════════════════════════════════════════ */
.corner--left {
  grid-row: 3;
  justify-self: start;
  align-self: end;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
}

/* ═══ Upcoming birthdays ═════════════════════════════════════════════════
   The archive's one forward-looking element: everything else here records
   what has happened. Hidden entirely when no living relative has a
   recorded day, rather than shown empty.                                 */
.upcoming {
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4) var(--s-3) var(--s-3);
  min-width: 208px;
  animation: results-in var(--d-slow) var(--ease-out) both;
}
.upcoming[hidden] { display: none; }

.upcoming__title {
  font-size: var(--t-xs);
  font-weight: 560;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin: 0 0 var(--s-2) var(--s-2);
}

.upcoming__list { display: flex; flex-direction: column; gap: 2px; }

.up-row {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr);
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-2);
  border-radius: var(--r-sm);
  text-align: left;
  transition: background var(--d-fast) var(--ease-out);
}
.up-row:hover { background: rgba(20, 21, 26, 0.05); }
.up-row:active { transform: scale(0.99); }

/* A small calendar tile — the museum-label treatment of a date. */
.up-date {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 5px 0 6px;
  border-radius: var(--r-xs);
  background: var(--evergreen-wash);
}
.up-date__day {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1;
  color: var(--evergreen);
  font-variant-numeric: lining-nums tabular-nums;
}
.up-date__month {
  font-size: 9px;
  font-weight: 620;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--evergreen);
  opacity: 0.75;
}

/* Today gets the accent inverted, so it cannot be missed. */
.up-row[data-today='true'] .up-date { background: var(--evergreen); }
.up-row[data-today='true'] .up-date__day,
.up-row[data-today='true'] .up-date__month { color: #fff; opacity: 1; }

.up-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.up-name {
  font-family: var(--font-display);
  font-size: var(--t-base);
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.up-meta {
  font-size: 11px;
  color: var(--ink-tertiary);
  font-variant-numeric: tabular-nums;
}
.up-row[data-today='true'] .up-meta { color: var(--evergreen); font-weight: 550; }

/* ═══ Legend ═════════════════════════════════════════════════════════════ */
.legend {
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  min-width: 208px;
}
.legend__row { display: flex; flex-direction: column; gap: 6px; }
.legend__label {
  font-size: var(--t-xs);
  font-weight: 560;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-tertiary);
}
.legend__scale {
  height: 5px;
  border-radius: 99px;
  /* Filled at runtime from the same ramp the shader uses — one source. */
  background: linear-gradient(90deg, var(--bronze), var(--evergreen));
}
.legend__ends {
  display: flex;
  justify-content: space-between;
  font-size: 10.5px;
  color: var(--ink-tertiary);
  font-variant-numeric: tabular-nums;
}
.legend__keys {
  display: flex;
  gap: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule);
}
.legend__key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-sm);
  color: var(--ink-secondary);
}

/* ═══ Stats ══════════════════════════════════════════════════════════════ */
/* Hidden in the chart, where it would sit on top of the outermost
   generation with no background to protect the text beneath it. */
body[data-view='chart'] .stats { display: none; }
body[data-view='chart'] .hint { display: none; }

.stats {
  grid-row: 3;
  justify-self: end;
  align-self: end;
  pointer-events: none;
}
.stats__list {
  display: flex;
  gap: var(--s-6);
  align-items: flex-end;
}
.stats__list > div { display: flex; flex-direction: column; gap: 2px; align-items: flex-end; }
.stats__value {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
}
.stats__key {
  font-size: 10px;
  font-weight: 560;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-tertiary);
}

/* ═══ Hint ═══════════════════════════════════════════════════════════════ */
.hint {
  grid-row: 2;
  align-self: end;
  justify-self: center;
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
  letter-spacing: 0.01em;
  padding-bottom: var(--s-5);
  pointer-events: none;
  transition: opacity var(--d-slow) var(--ease-out);
}
.hint[data-hidden='true'] { opacity: 0; }

/* ═══ Node labels ════════════════════════════════════════════════════════
   HTML rather than 3D text: real font rasterisation, real kerning, and
   free level-of-detail. Positioned by transform only — never top/left —
   so the compositor can move them without layout.                        */
.labels {
  position: fixed;
  inset: 0;
  z-index: var(--z-labels);
  pointer-events: none;
  overflow: hidden;
  contain: strict;
}

.label {
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform, opacity;
  transform-origin: 0 50%;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--d-fast) var(--ease-out);
}

.label__inner {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  transform: translate(14px, -50%);
  padding: 3px 9px 4px;
  border-radius: var(--r-pill);
  background: rgba(252, 252, 250, 0.66);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: 0 0 0 1px rgba(20, 21, 26, 0.05);
  transition:
    background var(--d-fast) var(--ease-out),
    box-shadow var(--d-fast) var(--ease-out),
    transform var(--d-base) var(--ease-out);
}

.label__name {
  font-family: var(--font-display);
  font-size: 13.5px;
  line-height: 1.2;
  color: var(--ink-secondary);
  letter-spacing: 0.002em;
  transition: color var(--d-fast) var(--ease-out);
}
.label__year {
  font-size: 10.5px;
  color: var(--ink-quaternary);
  font-variant-numeric: tabular-nums;
  opacity: 0;
  max-width: 0;
  overflow: hidden;
  transition: opacity var(--d-base) var(--ease-out), max-width var(--d-base) var(--ease-out);
}

/* Visible tiers, driven by labels.js */
.label[data-visible='true']  { opacity: 1; }
.label[data-visible='dim']   { opacity: 0.34; }

.label[data-emphasis='hover'] .label__inner,
.label[data-emphasis='selected'] .label__inner,
.label[data-emphasis='match'] .label__inner {
  background: var(--white);
  box-shadow: 0 0 0 1px rgba(20, 21, 26, 0.07), var(--shadow-1);
}
.label[data-emphasis='hover'] .label__name { color: var(--ink); }
.label[data-emphasis='match'] .label__name { color: var(--bronze); font-weight: 550; }
.label[data-emphasis='selected'] .label__name { color: var(--ink); font-weight: 600; }
.label[data-emphasis='selected'] .label__year,
.label[data-emphasis='hover'] .label__year {
  opacity: 1;
  max-width: 90px;
}
.label[data-emphasis='selected'] .label__inner { transform: translate(16px, -50%); }

/* Lineage mode: everything off the ancestral path recedes. */
.labels[data-lineage='true'] .label:not([data-lineage='true']) { opacity: 0.12; }

/* ═══ Detail panel ═══════════════════════════════════════════════════════ */
.panel {
  position: fixed;
  top: var(--gutter);
  right: var(--gutter);
  bottom: var(--gutter);
  width: var(--panel-w);
  max-width: calc(100vw - var(--gutter) * 2);
  z-index: var(--z-panel);
  border-radius: var(--r-xl);
  box-shadow:
    inset 0 0 0 1px var(--glass-ring),
    var(--shadow-3);
  display: flex;
  flex-direction: column;
  transform: translateX(calc(100% + var(--gutter)));
  opacity: 0;
  transition:
    transform var(--d-slow) var(--ease-out),
    opacity var(--d-base) var(--ease-out);
}
.panel[data-open='true'] { transform: none; opacity: 1; }

.panel__close {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  z-index: 2;
  width: 30px; height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--ink-tertiary);
  background: rgba(20, 21, 26, 0.04);
  transition: background var(--d-fast) var(--ease-out), color var(--d-fast) var(--ease-out);
}
.panel__close svg { width: 14px; height: 14px; }
.panel__close:hover { background: rgba(20, 21, 26, 0.09); color: var(--ink); }

.panel__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-7) var(--s-6) var(--s-6);
  scrollbar-width: thin;
  scrollbar-color: var(--ink-quaternary) transparent;
}
.panel__scroll::-webkit-scrollbar { width: 9px; }
.panel__scroll::-webkit-scrollbar-thumb {
  background: var(--ink-quaternary);
  border-radius: 99px;
  border: 3px solid transparent;
  background-clip: content-box;
}

/* Each element of the panel arrives on its own beat. */
.panel__scroll > * {
  animation: panel-rise var(--d-slow) var(--ease-out) both;
}
.panel__scroll > *:nth-child(1) { animation-delay: 40ms; }
.panel__scroll > *:nth-child(2) { animation-delay: 80ms; }
.panel__scroll > *:nth-child(3) { animation-delay: 120ms; }
.panel__scroll > *:nth-child(4) { animation-delay: 160ms; }
.panel__scroll > *:nth-child(5) { animation-delay: 200ms; }
.panel__scroll > *:nth-child(6) { animation-delay: 240ms; }
@keyframes panel-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ── Panel header ───────────────────────────────────────────────────── */
.p-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-xs);
  font-weight: 560;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding: 4px 10px 4px 8px;
  border-radius: var(--r-pill);
  margin-bottom: var(--s-4);
}
.p-status--living { color: var(--evergreen); background: var(--evergreen-wash); }
.p-status--past   { color: var(--bronze);    background: var(--bronze-wash); }

.p-name {
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  line-height: 1.1;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin-bottom: var(--s-2);
}

.p-lifespan {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  color: var(--ink-secondary);
  font-variant-numeric: lining-nums tabular-nums;
  display: flex;
  align-items: baseline;
  /* A written date is far longer than a pair of years, so the age drops
     to its own line rather than being squeezed off the edge. */
  flex-wrap: wrap;
  gap: 2px 9px;
  margin-bottom: var(--s-2);
}
.p-lifespan__dash { color: var(--ink-quaternary); }
.p-lifespan__age {
  font-family: var(--font-ui);
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
}

/* The next birthday. Living people with a recorded day only. */
.p-birthday {
  margin-top: var(--s-3);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px;
  border-radius: var(--r-pill);
  background: var(--evergreen-wash);
  color: var(--evergreen);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}
.p-birthday[data-today='true'] {
  background: var(--evergreen);
  color: #fff;
  font-weight: 550;
}

/* A quiet caveat for estimated dates. Never a placeholder — it only
   appears when the source itself hedged ("c. 1701", "1701?"). */
.p-note {
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  font-style: italic;
  color: var(--ink-quaternary);
}

.p-prose {
  font-size: var(--t-base);
  line-height: 1.65;
  color: var(--ink-secondary);
}

.p-photo {
  width: 100%;
  height: auto;
  border-radius: var(--r-md);
  display: block;
  box-shadow: var(--shadow-1);
}

.p-rule {
  height: 1px;
  background: var(--rule);
  margin: var(--s-5) 0;
}

/* ── Panel sections ─────────────────────────────────────────────────── */
.p-section { margin-bottom: var(--s-5); }
.p-section:last-child { margin-bottom: 0; }
.p-section__title {
  font-size: var(--t-xs);
  font-weight: 560;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: var(--s-3);
}

.p-facts { display: grid; gap: var(--s-2); }
.p-fact {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-4);
  font-size: var(--t-base);
}
.p-fact__k { color: var(--ink-tertiary); }
.p-fact__v { color: var(--ink); text-align: right; font-variant-numeric: tabular-nums; }

/* ── Person chips (father / sons / lineage) ─────────────────────────── */
.p-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px 6px 10px;
  border-radius: var(--r-pill);
  background: rgba(20, 21, 26, 0.04);
  font-size: var(--t-sm);
  color: var(--ink-secondary);
  transition:
    background var(--d-fast) var(--ease-out),
    color var(--d-fast) var(--ease-out),
    transform var(--d-fast) var(--ease-out);
}
.chip:hover { background: rgba(20, 21, 26, 0.08); color: var(--ink); transform: translateY(-1px); }
.chip:active { transform: none; }
.chip__name { font-family: var(--font-display); font-size: var(--t-base); }
.chip__year { font-size: 11px; color: var(--ink-tertiary); font-variant-numeric: tabular-nums; }

/* ── Lineage ladder ───────────────────────────────────────────────────
   The emotional centre of the panel: the unbroken line from the founder
   down to this person, walkable in one click.                          */
.lineage { display: flex; flex-direction: column; }
.lineage__step {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-3);
  padding: 6px var(--s-2);
  border-radius: var(--r-sm);
  text-align: left;
  width: 100%;
  transition: background var(--d-fast) var(--ease-out);
}
.lineage__step:hover { background: rgba(20, 21, 26, 0.045); }
.lineage__step[data-current='true'] { background: var(--bronze-wash); }

.lineage__rail {
  position: relative;
  height: 100%;
  min-height: 26px;
  display: grid;
  place-items: center;
}
.lineage__rail::before {
  content: '';
  position: absolute;
  top: -6px; bottom: -6px;
  width: 1px;
  background: var(--rule-strong);
}
.lineage__step:first-child .lineage__rail::before { top: 50%; }
.lineage__step:last-child  .lineage__rail::before { bottom: 50%; }
.lineage__node {
  position: relative;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ink-quaternary);
  transition: background var(--d-fast) var(--ease-out), box-shadow var(--d-fast) var(--ease-out);
}
.lineage__step[data-current='true'] .lineage__node {
  background: var(--bronze);
  box-shadow: 0 0 0 3px var(--bronze-wash);
}
.lineage__name {
  font-family: var(--font-display);
  font-size: var(--t-base);
  color: var(--ink-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lineage__step[data-current='true'] .lineage__name { color: var(--ink); font-weight: 550; }
.lineage__gen {
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink-quaternary);
  font-variant-numeric: tabular-nums;
}

/* ── Extensibility placeholder ────────────────────────────────────────
   Rendered only when the JSON carries the field. Adding photos or a
   biography later requires no code change here — see detailPanel.js.  */
.p-empty {
  font-size: var(--t-sm);
  color: var(--ink-quaternary);
  font-style: italic;
}

/* ═══ Shortcut sheet ═════════════════════════════════════════════════════ */
.sheet {
  position: fixed;
  top: calc(var(--gutter) + 52px);
  right: var(--gutter);
  z-index: var(--z-sheet);
  width: 264px;
  border-radius: var(--r-lg);
  padding: var(--s-4) var(--s-5) var(--s-5);
  box-shadow: inset 0 0 0 1px var(--glass-ring), var(--shadow-3);
  animation: results-in var(--d-base) var(--ease-out) both;
}
.sheet__title {
  font-size: var(--t-xs);
  font-weight: 560;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-tertiary);
  margin-bottom: var(--s-3);
}
.sheet__list { display: grid; gap: var(--s-2); }
.sheet__list > div {
  display: grid;
  grid-template-columns: 74px 1fr;
  align-items: center;
  gap: var(--s-3);
}
.sheet__list dt { display: flex; gap: 3px; }
.sheet__list dd { font-size: var(--t-sm); color: var(--ink-secondary); }

/* ═══ Intro ══════════════════════════════════════════════════════════════ */
.intro {
  position: fixed;
  inset: 0;
  z-index: var(--z-intro);
  display: grid;
  grid-template-rows: 1fr auto;
  pointer-events: none;
  transition: opacity var(--d-glacial) var(--ease-out);
}
.intro[data-phase='done'] { opacity: 0; visibility: hidden; }

/* A soft vignette that lifts during growth so the tree emerges from light. */
.intro__veil {
  position: absolute;
  inset: 0;
  background: var(--paper);
  transition: opacity 1600ms var(--ease-soft);
}
.intro[data-phase='loading'] .intro__veil { opacity: 1; }
.intro[data-phase='growing'] .intro__veil { opacity: 0; }
.intro[data-phase='done'] .intro__veil { opacity: 0; }

.intro__center {
  grid-row: 1;
  align-self: center;
  justify-self: center;
  text-align: center;
  padding: 0 var(--gutter);
  transition:
    opacity var(--d-glacial) var(--ease-out),
    transform var(--d-glacial) var(--ease-out),
    filter var(--d-glacial) var(--ease-out);
}
.intro[data-phase='growing'] .intro__center,
.intro[data-phase='done'] .intro__center {
  opacity: 0;
  transform: translateY(-18px);
  filter: blur(3px);
}

.intro__eyebrow {
  font-size: var(--t-xs);
  font-weight: 560;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--bronze);
  margin-bottom: var(--s-4);
  animation: intro-line 1400ms var(--ease-out) 200ms both;
}
.intro__title {
  font-family: var(--font-display);
  font-size: var(--t-3xl);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.024em;
  color: var(--ink);
  animation: intro-line 1600ms var(--ease-out) 380ms both;
}
.intro__meta {
  margin-top: var(--s-4);
  font-size: var(--t-base);
  color: var(--ink-tertiary);
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  animation: intro-line 1600ms var(--ease-out) 620ms both;
}
@keyframes intro-line {
  from { opacity: 0; transform: translateY(14px); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}

.intro__foot {
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--gutter);
  pointer-events: none;
}
.intro__progress {
  flex: 1;
  max-width: 190px;
  height: 1.5px;
  border-radius: 99px;
  background: var(--rule-strong);
  overflow: hidden;
}
.intro__progress > i {
  display: block;
  height: 100%;
  width: 0%;
  background: var(--bronze);
  border-radius: inherit;
  transition: width 220ms linear;
}
.intro__skip {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
  padding: 8px 14px;
  border-radius: var(--r-pill);
  transition: background var(--d-fast) var(--ease-out), color var(--d-fast) var(--ease-out);
}
.intro__skip:hover { background: rgba(20, 21, 26, 0.05); color: var(--ink); }
.intro[data-phase='done'] .intro__foot { opacity: 0; }
.intro__foot { transition: opacity var(--d-base) var(--ease-out); }

/* ═══ Fatal ══════════════════════════════════════════════════════════════ */
.fatal {
  position: fixed;
  inset: 0;
  z-index: var(--z-fatal);
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: var(--paper);
}
.fatal[hidden] { display: none; }
.fatal__card {
  max-width: 520px;
  border-radius: var(--r-xl);
  padding: var(--s-7);
  box-shadow: inset 0 0 0 1px var(--glass-ring), var(--shadow-3);
}
.fatal__title {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-weight: 400;
  letter-spacing: -0.012em;
  margin-bottom: var(--s-3);
}
.fatal__body { color: var(--ink-secondary); font-size: var(--t-md); line-height: 1.6; }
.fatal__code {
  margin: var(--s-4) 0 0;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  background: rgba(20, 21, 26, 0.045);
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  color: var(--ink);
  overflow-x: auto;
  white-space: pre;
}
.fatal__note {
  margin-top: var(--s-4);
  font-size: var(--t-sm);
  color: var(--ink-tertiary);
}

/* ═══════════════════════════════════════════════════════════════════════
   Responsive.
   Not a reduced mobile build — the same scene, the same interactions.
   Only the furniture rearranges so the tree keeps the room it needs.
   ═══════════════════════════════════════════════════════════════════ */

/* Laptop: the search yields width before anything else does. */
@media (max-width: 1180px) {
  .search { width: min(360px, 38vw); }
  .stats__list { gap: var(--s-5); }
}

/* Tablet: masthead becomes two rows, search spans full width beneath. */
@media (max-width: 900px) {
  .masthead {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      'brand tools'
      'search search';
    row-gap: var(--s-3);
  }
  .brand  { grid-area: brand; }
  .tools  { grid-area: tools; }
  .search { grid-area: search; width: 100%; justify-self: stretch; }

  .stats { display: none; }             /* the legend carries the summary */
  .hint  { display: none; }             /* touch users discover by touching */

  .panel {
    top: auto;
    left: var(--gutter);
    right: var(--gutter);
    bottom: var(--gutter);
    width: auto;
    max-height: 62vh;
    transform: translateY(calc(100% + var(--gutter)));
  }
  .panel[data-open='true'] { transform: none; }
  .panel__scroll { padding: var(--s-6) var(--s-5) var(--s-5); }

  .sheet { display: none; }
}

/* The chart's generation ruler sits under the masthead, which is taller
   once the search drops onto its own row. */
@media (max-width: 900px) {
  .chart__ruler { top: calc(var(--gutter) + 106px); }
}

/* Phone: the view switch is now the primary control and must keep its
   labels, so everything competing with it for that row gives way. Replay
   and the shortcut sheet are desktop affordances — one wants a keyboard,
   the other opens a panel already hidden at this width. The birthdays
   stay: they are the most useful thing on a family's phone screen. */
@media (max-width: 620px) {
  /* The archive must still say what it is, so the title stays and the
     controls give up the width instead: the two view labels tighten, and
     both remaining tools go. Nothing is lost — the brand mark already
     frames the whole chart when tapped, which is what reset did. */
  .tool { display: none; }

  .brand { padding: var(--s-2) 0; gap: var(--s-2); }
  .brand__mark { width: 28px; height: 28px; }
  .brand__title { font-size: 14px; }
  .brand__sub { display: block; font-size: 9.5px; letter-spacing: 0.07em; }

  .switch { margin-right: 0; padding: 2px; }
  .switch__opt {
    height: 32px;
    padding: 0 9px 0 7px;
    gap: 5px;
    font-size: 12px;
  }
  .switch__opt svg { width: 15px; height: 15px; }

  .legend { display: none; }
  .upcoming { min-width: 0; }
  .search__kbd { display: none; }
  .search__field { height: 44px; }
  .p-name { font-size: var(--t-xl); }
  .label__inner { padding: 2px 7px 3px; }
  .label__name { font-size: 12.5px; }
  .panel { max-height: 68vh; }
}

/* Short viewports: recover vertical room. */
@media (max-height: 620px) {
  .legend { display: none; }
  .hint { display: none; }
}
@media (max-height: 480px) {
  .upcoming { display: none; }
}

/* Coarse pointers get larger targets. */
@media (pointer: coarse) {
  .tool { width: 42px; height: 42px; }
  .up-row { padding: 10px var(--s-2); }
  .lineage__step { padding: 9px var(--s-2); }
  .chip { padding: 8px 14px 8px 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .panel__scroll > *,
  .search__results,
  .sheet,
  .intro__eyebrow,
  .intro__title,
  .intro__meta { animation: none !important; }
}
