/* axgf-cms — one stylesheet, no build step, no framework.
 *
 * The confidence system is the important part of this file. AXGF attaches a
 * 0.0–1.0 confidence to every fact, and the project requirement is that a
 * 0.98 fact and a 0.35 fact look different at a glance. That is implemented
 * three ways, all driven by a `data-band` attribute set server-side:
 *
 *   .conf-bar   a small filled bar   — used where there is room
 *   .conf-text  opacity + underline  — used inline, inside prose
 *   .conf-dot   a coloured dot       — used in dense lists and tree cards
 *
 * Bands: certain (>=0.90), high (>=0.75), medium (>=0.50), low (<0.50).
 */

:root {
  /* Every colour on :root — Study 06, the floor under every theme — is in
     static/css/tokens.css, the one file that names colours, with the river's
     palette and the four themes. What stays here is the part of :root that
     is not a colour. */

  /* ---------------------------------------------------------------------
     The presentation scale. A *style* owns these; a theme never touches them,
     and nothing in here is a colour. That division is what keeps nine
     combinations to three palettes: see `src/style.rs`, and the test that
     fails the build if a `[data-style]` block ever names a hue.

     The rhythm unit `step` is the root of it: everything spatial is a multiple
     of that one value, so a style changes one number and the page keeps its
     beat rather than drifting out of it a rule at a time. */
  --step: 0.55rem;
  /* rem, not px (the minimalism pass): 1rem is the reader’s own setting,
     16px unless they changed it, and a px value here overrode it. */
  --text: 1rem;
  --leading: 1.6;
  --measure: 74ch;
  --face: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  /* The typographic scale — the rungs.
 Nine of them, each a visible step from the next, and every
     size on the page is one of them.

     The scale existed before this and almost nothing used it: seventy-one of
     the hundred-and-one `font-size` declarations in this file set a literal,
     across twenty-nine distinct values. 0.93, 0.94 and 0.95rem all appeared;
     so did 0.72, 0.74 and 0.75. Differences that small are invisible as
     hierarchy and perfectly visible as inconsistency — which is the whole of
     why headings, labels and values read at the same weight. A heading is not
     a heading because it is 0.02rem larger than the value under it.

     Tight steps at the bottom, where a dense record needs fine distinctions
     between a label, a value and an aside; wider at the top, where the eye is
     climbing rather than scanning. A style retunes all nine and changes
     nothing else about them; `--h1-size` and friends below are names for
     rungs, not separate knobs. */
  --type-micro: 0.66rem;
  --type-label: 0.75rem;
  --type-small: 0.82rem;
  --type-minor: 0.9rem;
  --type-body: 1rem;
  --type-sub: 1.2rem;
  --type-major: 1.5rem;
  --type-title: 1.75rem;
  --type-display: 2.4rem;

  /* Four weights, the same four everywhere. `650` was a real value in this
     file and is not a real weight in most faces — it rounds to 700 in some
     and 600 in others, so the same heading was bolder on one machine than
     another. */
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semi: 600;
  --weight-strong: 700;

  /* The rungs the rest of the stylesheet asks for by role. */
  --h1-size: var(--type-title);
  --h2-size: var(--type-sub);
  --h3-size: var(--type-body);
  --h1-weight: var(--weight-strong);
  --h2-weight: var(--weight-semi);
  --label-size: var(--type-label);
  --label-track: 0.06em;
  --small-size: var(--type-small);
  /* How much room a section gives its contents, and whether it is a card or a
     ruled block. Paper sets the shadow to none and the fill to transparent,
     which is what a printer does to a card anyway. */
  /* The inline half of the section's padding on its own, so that what
     bleeds into it (a table drawn edge to edge) bleeds by exactly that much
     in every style. It was a literal 1.3rem, which under compact — whose
     sections pad 0.85rem — carried a table's header past its card's edges
     (v0.5.0, the operator's screenshot of a record in compact). */
  --section-pad-x: 1.3rem;
  --section-pad: 1.15rem var(--section-pad-x);
  --section-gap: 1.1rem;
  --section-fill: var(--surface-raised);
  --section-shadow: 0 1px 2px rgb(var(--shadow-rgb) / var(--shadow-a));
  --section-border: 1px solid var(--border-hairline);
  --section-radius: var(--radius-lg);

  --radius: 7px;
  --radius-sm: 5px;
  --radius-lg: 11px;
  /* The round of a button and of a field: Study 06's 2px, which every theme
     keeps but one. Canopée, the reference theme, carries its own (pill
     buttons, softened fields) and redefines these two and nothing else of
     the geometry a style owns (docs/adr/0004). A token rather than a literal
     so that the rule "one radius for every button" still holds, per theme. */
  --btn-radius: 2px;
  --field-radius: 2px;
  /* The one size above the display rung: the reference theme's headline on
     the overview page. Derived from the rung, so a style that retunes the
     scale retunes it too. */
  --type-hero: clamp(calc(var(--type-display) * 0.82), 4.4vw, calc(var(--type-display) * 1.4));
  /* One duration for everything that moves, so the interface has a single
     tempo rather than a different one per component. Short enough that it
     never delays a click. */
  --tempo: 130ms;
  --ease: cubic-bezier(0.2, 0, 0.2, 1);
  /* The masthead is sticky and 60px tall; anything else pinned below it
     clears that plus a little air. */
  --stick-top: 4.5rem;
  /* Room under the tree for its horizontal scrollbar to sit in, so the bar
     never lies over the oldest generation. Wider than any platform's bar. */
  --hscroll-gutter: 1rem;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* Study 06's serif: the wordmark, and the names the river writes. */
  --serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  /* The river's type, at the sizes its design fixes in pixels. */
  --type-rv-eyebrow: 0.625rem;
  --type-rv-data: 0.65625rem;
  --type-rv-seg: 0.71875rem;
  --type-rv-chip: 0.75rem;
  --type-rv-control: 0.78125rem;
}

/* The river's palette and the four themes are in static/css/tokens.css. */



* { box-sizing: border-box; }
/* What the markup hides stays hidden, whatever display a class gives it. */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------------------
   The page wash
   ---------------------------------------------------------------------------
   A soft gradient behind the page, built from the three tints each theme
   derives from its own tokens above. Three things about it are deliberate:

   IT IS THE ROOT ELEMENT'S BACKGROUND, NOT A LAYER OVER THE PAGE. The first
   version of this was a fixed pseudo-element at `z-index: -1`, which looked
   identical and was wrong: a fixed, composited layer under the whole document
   makes Chromium drop LCD subpixel antialiasing for every glyph on the page,
   so turning the wash on very slightly softened all the text. Painting into
   the root element's background instead goes into the canvas, where it costs
   the text nothing — a screenshot of the page with the wash on and off now
   differs only where the wash is, and not by one pixel of type.

   IT DOES NOT MOVE. There is no animation and no transition on it, under
   `prefers-reduced-motion` or otherwise. A gradient that drifts is a
   distraction on a page somebody reads for an hour, and the reduced-motion
   query is not the place to decide that: it is decided here, for everyone.

   IT IS OFF WHEN IT SHOULD BE. `data-wash` is written by the server from the
   theme's own flag and the reader's stored preference, already resolved
   together, so this stylesheet needs no exception for a theme that refuses
   it and no second copy of the rule that produces one.

   Content sits on solid surfaces — the tree and record split, cards, tables,
   forms — so no text is ever over the gradient. What the wash reaches is the
   page behind them and the margins beside them. */
html {
  background-color: var(--surface-page);
  /* Three gradients, then the theme's texture, tiled (v0.4.3): none on most
     themes, an inline SVG grain on a few. Same switch, same root
     background, never on a control. */
  background-repeat: no-repeat, no-repeat, no-repeat, repeat;
  /* Home, and anything without a route of its own. */
  background-image:
    radial-gradient(68% 52% at 6% calc(-8% + var(--drift)), var(--wash-1), transparent 72%),
    radial-gradient(58% 46% at 98% calc(6% - var(--drift) * 1.6), var(--wash-2), transparent 70%),
    radial-gradient(90% 55% at calc(42% + var(--drift) * 0.7) calc(108% + var(--drift) * 1.2),
      var(--wash-3), transparent 74%),
    var(--wash-texture);
  /* Fixed to the viewport rather than the document. The tree page can be six
     thousand pixels tall, and a wash that scrolled with it would be a band of
     colour at the top of a mostly flat page. iOS Safari ignores this and
     scrolls the wash instead, which is a smaller loss than the alternative. */
  background-attachment: fixed;
}
[data-wash="off"] { background-image: none; }

/* Deterministic per route: the same three tints, arranged differently, so
   moving between the tree, the import page and the admin area feels like
   moving rather than reloading. The same page always draws the same wash —
   nothing here is random, and nothing is per-visit. */
[data-route="tree"] {
  background-image:
    radial-gradient(72% 55% at 96% calc(-6% + var(--drift)), var(--wash-2), transparent 72%),
    radial-gradient(54% 44% at 4% calc(12% - var(--drift) * 1.6), var(--wash-3), transparent 70%),
    radial-gradient(86% 52% at calc(58% - var(--drift) * 0.7) calc(106% + var(--drift) * 1.2),
      var(--wash-1), transparent 74%),
    var(--wash-texture);
}
[data-route="convert"] {
  background-image:
    radial-gradient(64% 50% at 50% calc(-10% + var(--drift)), var(--wash-3), transparent 72%),
    radial-gradient(58% 48% at 2% calc(62% - var(--drift) * 1.6), var(--wash-1), transparent 70%),
    radial-gradient(70% 50% at calc(100% - var(--drift) * 0.7) calc(92% + var(--drift) * 1.2),
      var(--wash-2), transparent 72%),
    var(--wash-texture);
}
[data-route="admin"] {
  background-image:
    radial-gradient(60% 48% at 2% calc(-4% + var(--drift)), var(--wash-3), transparent 70%),
    radial-gradient(66% 52% at 100% calc(44% - var(--drift) * 1.6), var(--wash-1), transparent 72%),
    radial-gradient(80% 50% at calc(40% + var(--drift) * 0.7) calc(104% + var(--drift) * 1.2),
      var(--wash-2), transparent 72%),
    var(--wash-texture);
}
/* `[data-wash="off"]` has to win over the per-route rules above, so it is
   repeated after them rather than relying on source order alone. */
[data-wash="off"][data-route] { background-image: none; }

/* ---------------------------------------------------------------------------
   The wash drifts with the scroll, and only with the scroll
   ---------------------------------------------------------------------------
   WHAT MOVES. One number, `--drift`, feeds the centre of all three gradients
   at different rates and in different directions, so the tints slide past each
   other rather than sliding together — the same parallax a landscape has, at a
   scale nobody should be able to name. Nothing else on the page moves.

   HOW FAR. The whole travel is 5% of the viewport, which at a 1000px window is
   50 pixels — spread over the entire length of the document. On a record that
   is roughly a tenth of the distance the reader scrolls; on the tree, which
   runs to six thousand pixels, it is a fifteenth of that. It was set by
   reading a full record at it and asking whether it was noticeable while
   reading rather than while looking for it. The first value that passed was
   considerably smaller than the first value that looked right in isolation, and
   that gap is the whole hazard: a background is seen for hours, not for the ten
   seconds you spend judging it.

   IT HAS NO CLOCK. `animation-timeline: scroll(root block)` ties the keyframes
   to the scroll position, not to time. Stop scrolling and it stops — there is
   no loop, no autonomous motion, and nothing to notice out of the corner of an
   eye while reading a date. That distinction is the point of the whole feature:
   peripheral motion captures attention involuntarily, and a background that
   moves while the reader does not is the one that becomes intolerable on the
   thousandth view. The keyframes are uneven on purpose — the middle of a long
   page passes through a slightly different cast than its ends — so what
   variation there is happens because the reader travelled, not because time
   passed.

   NO SCRIPT. Not one line, and no scroll listener: the browser drives this off
   its own compositor. `@supports` means a browser without scroll-driven
   animations shows the static wash, which is exactly the right fallback rather
   than a reason to reach for JavaScript.

   WHEN IT DOES NOT RUN. `prefers-reduced-motion` removes the movement and
   leaves the wash exactly where it was — a medical setting, not a taste.
   `[data-wash="off"]` removes both, which covers a theme whose `wash` flag
   is false and any reader who turned backgrounds off, because the server
   has already resolved those two into one attribute.

   NOTHING IS UNDER TEXT. Every content surface is opaque — see the three
   levels above. What drifts is the page behind them and the margins beside
   them, and a screenshot with the wash on and off differs nowhere a glyph is
   drawn. */
@property --drift {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

@keyframes wash-drift {
  /* Uneven rather than linear: two thirds of the travel is spent in the middle
     third of the document, so a long page has a middle that reads slightly
     differently from its ends instead of a uniform slide. */
  0%   { --drift: 0%; }
  35%  { --drift: 1.2%; }
  65%  { --drift: 3.8%; }
  100% { --drift: 5%; }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    html:not([data-wash="off"]):not([data-motion="still"]) {
      animation: wash-drift linear both;
      animation-timeline: scroll(root block);
    }
  }
}

/* NOT ON THE TREE, AND THE REASON IS MEASURED. The tree page marks itself
   `data-motion="still"`; `data-route` could not be used, because it carries the
   navigation group and the person page shares "tree" with the tree page. The wash is painted into the
   canvas with `background-attachment: fixed`, so every change to it repaints
   the whole viewport. Scrolling the tree with the drift on cost a median
   18.4ms per frame against 16.6ms with it off, and 24.5ms at the 95th
   percentile against 19.2ms — frames past the 16.7ms budget, on the one page
   that is a diagram rather than prose and where following a line while panning
   is the actual task. Quantising the drift into steps was tried and recovered
   about a millisecond of the median and none of the tail.

   Paying dropped frames on a diagram for an effect measured at five parts in
   255 is not a trade worth making. The record and admin pages keep it: there
   the median cost is nil and the tail is 3ms, and scrolling is reading rather
   than tracking. The tree keeps the static wash, which is what it had. */

body {
  /* A filename, a path, an identifier: an unbroken string must break rather
     than push the page sideways, at any text size (the overflow check of the
     minimalism pass found photo captions and the journal's path doing it). */
  overflow-wrap: anywhere;
  margin: 0;
  background: transparent;
  color: var(--text-primary);
  font: var(--text) / var(--leading) var(--face);
  -webkit-text-size-adjust: 100%;
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

code, pre, .mono { font-family: var(--mono); font-size: 0.9em; }

/* ---------------------------------------------------------------------------
   The three presentation styles
   ---------------------------------------------------------------------------
   A second axis from the theme, and strictly orthogonal to it: a style changes
   a size, a space, a weight, a measure or a border, and never a colour. That
   is not a convention, it is enforced — `no_style_block_names_a_colour` reads
   this file and fails the build if one of these blocks ever contains a hex, an
   `rgb(` or a colour custom property. Three palettes under three geometries
   is nine combinations that stay measured; nine palettes would not.

   `comfortable` is the default and therefore the values already on `:root`; it
   has no block of its own. What follows is only what the other two change. */

/* Compact: for somebody working through records rather than reading one.
   The rhythm unit drops by a quarter and everything spatial follows it, so the
   page keeps its beat and simply gets closer together. The point is cutting
   the scroll between the thing just read and the thing being compared. */
[data-style="compact"] {
  --step: 0.4rem;
  --text: 0.9375rem;
  --leading: 1.5;
  --measure: 88ch;
  --face: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --type-micro: 0.64rem;
  --type-label: 0.72rem;
  --type-small: 0.79rem;
  --type-minor: 0.87rem;
  --type-body: 0.95rem;
  --type-sub: 1.08rem;
  --type-major: 1.3rem;
  --type-title: 1.45rem;
  --type-display: 2rem;
  --h1-weight: var(--weight-strong);
  --h2-weight: var(--weight-semi);
  --label-track: 0.05em;
  --section-pad-x: 0.85rem;
  --section-pad: 0.7rem var(--section-pad-x);
  --section-gap: 0.55rem;
  --section-fill: var(--surface-raised);
  --section-shadow: 0 1px 2px rgb(var(--shadow-rgb) / var(--shadow-a));
  --section-border: 1px solid var(--border-hairline);
  --section-radius: var(--radius);
}

/* Paper: for a record read once and slowly, or printed and handed to a
   relative. A serif face, a measure that stops prose running the width of a
   monitor, and rules where the other two have cards — which is what a laser
   printer does to a card anyway, so the screen stops promising an edge the
   page will not keep. */
[data-style="paper"] {
  --step: 0.7rem;
  --text: 1.0625rem;
  --leading: 1.72;
  --measure: 66ch;
  --face: Iowan Old Style, "Palatino Linotype", Palatino, Georgia,
    "Times New Roman", serif;
  --type-micro: 0.72rem;
  --type-label: 0.78rem;
  --type-small: 0.88rem;
  --type-minor: 0.96rem;
  --type-body: 1.05rem;
  --type-sub: 1.32rem;
  --type-major: 1.62rem;
  --type-title: 2.1rem;
  --type-display: 2.8rem;
  /* A serif at this size carries a heading without needing 700 to do it. */
  --h1-weight: var(--weight-semi);
  --h2-weight: var(--weight-semi);
  --label-track: 0.09em;
  --section-pad-x: 0rem;
  --section-pad: 0 0 calc(var(--step) * 2);
  --section-gap: calc(var(--step) * 2.4);
  --section-fill: transparent;
  --section-shadow: none;
  --section-border: 0;
  --section-radius: 0;
}
/* ---------- layout ---------- */

/* The masthead (Study 06, B5): 58px, the page's own ground, one hairline
   under it. Nothing in it is coloured: the wordmark is ink, the section you
   are in is underlined in ink, and amber is kept for the primary action and
   the focus ring. */
.masthead {
  border-bottom: 1px solid var(--border-hairline);
  background: var(--surface-page);
  position: sticky;
  top: 0;
  z-index: 40;
}
.masthead-inner {
  /* Wider than the page's column: the bar is the frame, and at a desktop
     width everything in it fits on one 58px row. */
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 1.25rem;
  min-block-size: 58px;
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
}
.brand {
  font-family: var(--serif);
  font-weight: var(--weight-semi);
  font-size: var(--type-sub);
  letter-spacing: 0;
  color: var(--text-primary);
  text-decoration: none;
}
/* The logo (docs/adr/0006), drawn inline by `_logo.html` so that a theme
   colours it. Everywhere but Canopée it is ink, as the wordmark beside it
   is: no tile, the wreath and the X in the text's own colour. */
.logo-mark { display: block; flex: none; }
.logo-mark .lm-tile { fill: none; }
.logo-mark .lm-stem { stroke: currentColor; }
.logo-mark .lm-leaf { fill: currentColor; }
.logo-mark stop { stop-color: currentColor; }
.brand-mark {
  inline-size: 30px;
  block-size: 30px;
  margin-inline-end: -0.7rem;
  color: var(--text-primary);
}
/* Above the sign-in form, the first thing a relative is shown. */
.login-logo { inline-size: 64px; block-size: 64px; color: var(--text-primary); margin-block-end: 0.75rem; }
.masthead nav { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.masthead nav a {
  color: var(--text-secondary);
  font-size: var(--type-minor);
  text-decoration: none;
  padding-block: 0.3rem;
  border-bottom: 2px solid transparent;
  transition: color var(--tempo) var(--ease), border-color var(--tempo) var(--ease);
}
.masthead nav a:hover { color: var(--text-primary); }
.masthead nav a[aria-current="page"] {
  color: var(--text-primary);
  border-bottom-color: var(--text-primary);
}
/* Buttons in the masthead are not section links: no underline, their own
   colours. */
.masthead nav a.btn { border-bottom: 1px solid var(--border-strong); padding-block: 0; }
.masthead .spacer { flex: 1 1 auto; }
.chrome-nav { gap: 0.9rem; }

/* The language: a native select, restyled, showing a two-letter code. */
.lang-form { display: inline-flex; align-items: center; gap: 0.4rem; margin: 0; }
.lang-form [hidden] { display: none; }
.lang-form select {
  font-family: var(--mono);
  font-size: var(--type-small);
  letter-spacing: 0.04em;
  block-size: 28px;
  padding-block: 0;
  padding-inline: 0.5rem 0.25rem;
  inline-size: auto;
  color: var(--text-primary);
  background-color: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 2px;
}

/* The reader's controls (v0.4.3): the search, then language, theme and
   density as three native selects in one row, then the way to the rest of
   the settings. One row at 390px: the search takes a row of its own there
   and the three selects keep theirs, scrolling never and wrapping never. */
.masthead-tools {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  min-inline-size: 0;
}
.reading-prefs {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  /* One row whenever one row fits, which at 390px it does at the reader's
     ordinary text size in every language. At a larger text size it cannot,
     and then the row wraps rather than pushing the whole page sideways —
     measured on every page at 390px with text at 150 %, where it used to
     overflow by up to 148px (v0.5.0). */
  flex-wrap: wrap;
}
.reading-prefs .lang-form select { max-inline-size: 9.5rem; text-overflow: ellipsis; }
/* The theme and the density are words, not codes: the interface face, not
   the mono the language's two letters are set in, so a name like "Fleuve
   d'hiver" keeps the row at 390px in every language. */
.reading-prefs .pref-form select {
  font-family: inherit;
  letter-spacing: 0;
  padding-inline: 0.4rem 0.15rem;
}
.prefs-more {
  display: inline-grid;
  place-items: center;
  inline-size: 28px;
  block-size: 28px;
  border: 1px solid var(--border-strong);
  border-radius: 2px;
  color: var(--text-secondary);
  text-decoration: none;
  font-family: var(--mono);
}
.prefs-more:hover { color: var(--text-primary); }
@media (max-width: 640px) {
  /* At phone width the bar is a small grid (the operator's review,
     2026-10-25: its pieces lay scattered, each on a line of its own, the
     account alone at the right): the logo and the name on the left and the
     account on the right on the first row; the sections spread across the
     second; the search the whole width; the three reading controls and the
     settings one even row under it. */
  .masthead .masthead-inner {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "mark brand account" "nav nav nav" "tools tools tools";
    align-items: center;
    gap: 0.65rem 0.6rem;
    padding-block: 0.6rem 0.8rem;
  }
  .masthead .masthead-inner > .brand-mark { grid-area: mark; margin-inline-end: 0; }
  .masthead .masthead-inner > .brand { grid-area: brand; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* The wordmark as a link (docs/adr/0046): the mark and the name together. */
  .masthead .masthead-inner > .brand-link { grid-column: mark-start / brand-end; grid-row: mark-start / mark-end; min-inline-size: 0; gap: 0.6rem; }
  .masthead .masthead-inner > .brand-link > .brand-mark { margin-inline-end: 0; }
  .masthead .masthead-inner > nav:not(.chrome-nav) {
    grid-area: nav;
    justify-content: space-between;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    gap: 0.9rem;
  }
  .masthead .masthead-inner > .spacer { display: none; }
  .masthead .masthead-inner > .chrome-nav { grid-area: account; justify-self: end; }
  /* The account: its initials and the menu's caret; the name is still read
     out, and shown in the menu's own page. */
  .masthead .account-name {
    position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .masthead-tools { grid-area: tools; display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.55rem; }
  .masthead-tools .masthead-search { margin-inline-start: 0; }
  .masthead-tools .search-box > input[type="search"] { flex: 1 1 auto; inline-size: auto; min-inline-size: 0; max-inline-size: none; }
  .masthead-tools .reading-prefs {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr) auto;
    gap: 0.45rem;
  }
  .masthead-tools .reading-prefs .pref-form,
  .masthead-tools .reading-prefs .pref-form select { inline-size: 100%; max-inline-size: none; }
  /* At this width the masthead wraps to five rows, a third of a phone's
     screen. Pinned, it hid that third of every page, and Canopée's
     translucent ground showed the page passing under it. Here it scrolls
     away with the page, and what is pinned below it pins to the top. */
  .masthead { position: static; }
  :root { --stick-top: 0.75rem; }
}

/* Signed in: an initials chip and the name, opening onto a short menu. */
.account { position: relative; }
.account > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  cursor: pointer;
  color: var(--text-secondary);
  font-size: var(--type-minor);
  padding-block: 0.2rem;
}
.account > summary::-webkit-details-marker { display: none; }
.account > summary::after { content: "▾"; font-size: 0.8em; color: var(--text-tertiary); }
.account > summary:hover { color: var(--text-primary); }
.initials {
  display: inline-grid;
  place-items: center;
  inline-size: 26px;
  block-size: 26px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface-raised);
  color: var(--text-primary);
  font-family: var(--mono);
  font-size: var(--type-micro);
  letter-spacing: 0.02em;
}
.menu-panel {
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: calc(100% + 8px);
  min-inline-size: 12rem;
  display: grid;
  gap: 2px;
  padding: 6px;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: 2px;
  z-index: 50;
}
.menu-panel > a, .menu-panel .btn {
  display: flex;
  align-items: center;
  inline-size: 100%;
  block-size: 32px;
  padding-inline: 0.6rem;
  color: var(--text-primary);
  font-size: var(--type-minor);
  text-decoration: none;
  border: 0;
  border-radius: var(--btn-radius);
  justify-content: flex-start;
}
.menu-panel > a:hover, .menu-panel .btn:hover { background: var(--surface-hover); }
.menu-panel > a[aria-current="page"] { border-bottom: 0; text-decoration: underline; text-decoration-thickness: 2px; }
.menu-panel .lang-form { padding: 4px 0.6rem 6px; }
.menu-panel form { margin: 0; }

main {
  max-width: 1180px;
  margin: 0 auto;
  padding: calc(var(--step) * 3.2) 1.25rem calc(var(--step) * 7);
}
/* Prose gets a measure. A section is as wide as the page; a paragraph inside
   it should not be, because a line of 180 characters is read by nobody. */
.rec-section > p,
.lede,
.sec-help p { max-inline-size: var(--measure); }
main.wide { max-width: none; padding-inline: 1rem; }

/* The footer (v0.5.0): the product and its version, who made it, and the
   format it writes in — one line on a wide screen, stacked on a phone. */
.site-footer {
  margin-block-start: 2rem;
  border-top: 1px solid var(--border-hairline);
  color: var(--text-tertiary);
  font-size: var(--type-minor);
}
.footer-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 1.25rem 1.25rem 2.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.5rem;
}
.footer-inner p { margin: 0; }
.footer-id {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--text-secondary);
  font-weight: var(--weight-semi);
}
.footer-logo { inline-size: 1.6em; block-size: 1.6em; flex: none; }
.footer-version {
  font-family: var(--mono);
  font-size: var(--type-micro);
  font-weight: var(--weight-medium);
  letter-spacing: 0.04em;
  padding: 0.15em 0.6em;
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  color: var(--text-tertiary);
}
.footer-format { margin-inline-start: auto; }
.footer-format a {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--text-secondary);
  text-decoration: none;
}
.footer-format a:hover,
.footer-format a:focus-visible { color: var(--accent); text-decoration: underline; text-underline-offset: 0.2em; }
.footer-format .ico { inline-size: 1.15em; block-size: 1.15em; flex: none; }
@media (max-width: 640px) {
  .footer-format { margin-inline-start: 0; flex-basis: 100%; }
}

/* The hierarchy, on the scale a style sets. Every vertical space here is a
   multiple of --step, so the page keeps one beat whichever style is on. */
h1 {
  font-size: var(--h1-size);
  font-weight: var(--h1-weight);
  line-height: 1.2;
  margin: 0 0 calc(var(--step) * 0.8);
  letter-spacing: -0.02em;
}
h2 {
  font-size: var(--h2-size);
  font-weight: var(--h2-weight);
  margin: calc(var(--step) * 3.6) 0 calc(var(--step) * 1.4);
  letter-spacing: -0.01em;
}
/* h3 and h4 carried no weight of their own, so they took the user agent's
   `bold` — 700 — while h2 above them is 600. The ladder ran 700, 600, 700:
   a subheading heavier than the heading it sits under, which is precisely the
   "everything at one weight" complaint, except inverted. Both are named now,
   and the ladder descends: 700, 600, 600 with the size doing the rest. */
h3 {
  font-size: var(--h3-size);
  font-weight: var(--weight-semi);
  margin: calc(var(--step) * 2.3) 0 var(--step);
}
h4 {
  font-size: var(--type-minor);
  font-weight: var(--weight-semi);
  margin: calc(var(--step) * 1.8) 0 calc(var(--step) * 0.6);
}
.lede { color: var(--text-secondary); font-size: var(--type-body); margin-top: 0; }
.small, .hint { font-size: var(--small-size); }

/* ---------------------------------------------------------------------------
   Three levels: page, section, block
   ---------------------------------------------------------------------------
   The record page used to be headings and content running down a blank sheet.
   Nothing marked where Identity stopped and Family began, so finding the third
   section meant reading the first two. Every surface in the interface now sits
   at one of exactly three levels, and each is separated from its neighbours by
   more than one signal, because any single one of them fails somewhere: shadow
   was invisible under the high-contrast theme of 0.4 (--shadow-a was 0
   there), and fill is invisible wherever --surface-page and --surface-raised
   come close.

     LEVEL 1  the page      --surface-page plus the wash. No border, no elevation.
     LEVEL 2  a section     --surface-raised fill, hairline border, --radius-lg,
                            1.15rem padding, a 1px shadow.
     LEVEL 3  a block       --surface-page fill — recessed, not raised — hairline
                            border, the smaller --radius, and NO shadow.

   WHY LEVEL 3 IS RECESSED RATHER THAN RAISED. A shadow reads as "nearer the
   reader". A block that sits inside a section and appears nearer than the
   section it is inside inverts the containment, and the eye notices that
   before it notices anything you meant. Dropping the block to the page's own
   --surface-page turns it into a well in the section's surface, which is the shape the
   thing actually has. It also costs nothing where the two fills come close:
   the hairline carries the whole boundary on its own.

   WHY THERE IS NO ACCENT BAR ON EVERY SECTION. It was the obvious option and
   it is the wrong one here: an accent rule down the leading edge is already
   how a *typed* block says what type it is — .link-card marks direction that
   way, .notice marks severity — and repeating it on the container as well
   would spend the one signal that still means something. Sections are told
   apart by their surface; blocks by their accent. Four sections down a tab
   also become a row of coloured bars, which is decoration reading as data.

   ONE RECIPE, NOT TWO. .card already carried exactly the level-2 treatment,
   so the section grows into it rather than getting a parallel system: the
   selector list below is the definition of "level 2" everywhere it appears —
   record sections, admin sections, the import report, the tree's controls,
   the dashboard's stat tiles, a notice. A boundary looks the same wherever it
   is, which is the whole point of framing them at all. */
.card,
.rec-section,
.tree-controls,
.stat,
.notice {
  background: var(--section-fill);
  border: var(--section-border);
  border-radius: var(--section-radius);
  box-shadow: var(--section-shadow);
}
.card,
.rec-section {
  padding: var(--section-pad);
}
.card > :first-child,
.rec-section > :first-child { margin-top: 0; }
.card > :last-child,
.rec-section > :last-child { margin-bottom: 0; }

/* Sections are separated by a gap rather than by the heading's top margin,
   which used to do the job and cannot any more: that margin is now inside the
   box, where it pushes the heading off its own frame. */
.rec-section { margin: 0 0 var(--section-gap); }
.rec-section .sec-head h2 { margin-top: 0; }

/* LEVEL 3 — a block inside a section, or inside a card.
   Listed by what they are rather than given a shared class, because these are
   the containers that already existed; adding a class to each template would
   change the markup to say something the nesting already says. */
.rec-section .card,
.card .card,
.rec-section .union,
.rec-section .fam-block,
.rec-section .name-block,
.rec-section .shot,
.tree-controls .field-group {
  background: var(--surface-page);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  box-shadow: none;
  padding: 0.8rem 0.9rem;
}
/* .link-card and .place-card were already built this way — a recessed fill
   and a hairline — and only need the shadow suppressed and the radius
   aligned. They keep their accent edge: that edge is the block saying what
   kind of block it is, which is level 3's job and not the section's. */
.rec-section .link-card,
.rec-section .place-card {
  box-shadow: none;
  border-radius: var(--radius);
}
.rec-section .union { margin: 0.6rem 0; }
.rec-section .union:last-child { margin-bottom: 0; }

.grid { display: grid; gap: 1rem; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* ---------- confidence: the showcase primitive ---------- */

/* The track has to read as EMPTY. It was `--border-hairline`, which in the
   high-contrast theme of 0.4 was pure black — so a 12% bar rendered as a
   mostly-dark bar and looked *more* certain than a 98% one, exactly inverting
   the signal in the theme that most needed it. The track gets its own variable, and every
   theme sets it lighter than its fills. Caught by rendering the strip; reading
   the CSS would not have shown it. */
.conf-bar {
  display: inline-block;
  vertical-align: middle;
  width: 46px;
  height: 6px;
  border-radius: 3px;
  background: var(--conf-track);
  box-shadow: inset 0 0 0 1px var(--border-strong);
  overflow: hidden;
  flex: none;
}
.conf-bar > i {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--conf-medium);
}
.conf-bar[data-band="certain"] > i { background: var(--conf-certain); }
.conf-bar[data-band="high"]    > i { background: var(--conf-high); }
.conf-bar[data-band="medium"]  > i { background: var(--conf-medium); }
.conf-bar[data-band="low"]     > i { background: var(--conf-low); }

/* Inline text confidence: certain text is solid, speculative text fades and
   picks up a dotted underline so it reads as "not settled" even in grayscale. */
.conf-text { text-decoration: none; }
.conf-text[data-band="certain"] { opacity: 1; }
.conf-text[data-band="high"]    { opacity: 0.93; }
.conf-text[data-band="medium"]  {
  opacity: 0.78;
  border-bottom: 1px dashed var(--conf-medium);
}
.conf-text[data-band="low"] {
  opacity: 0.62;
  border-bottom: 1px dotted var(--conf-low);
  font-style: italic;
}

/* The dot is a PIE filled to the confidence, not a coloured disc.
   The previous version distinguished `certain`, `high` and `medium` by hue
   alone, which is precisely what a colour-blind reader does not receive — and
   the dot is what the tree cards and the dense lists use, so it was the one
   place the whole argument leaked out. Filling it to the value encodes the
   number itself, survives every theme, and needs no legend.
   `--conf-pct` is set inline from the same figure the label reads. */
.conf-dot {
  display: inline-block;
  width: 11px; height: 11px;
  border-radius: 50%;
  vertical-align: middle;
  flex: none;
  box-sizing: border-box;
  border: 1px solid var(--conf-medium);
  background:
    conic-gradient(var(--conf-medium) 0 calc(var(--conf-pct, 50) * 1%),
                   transparent calc(var(--conf-pct, 50) * 1%) 100%);
}
.conf-dot[data-band="certain"] {
  border-color: var(--conf-certain);
  background: conic-gradient(var(--conf-certain) 0 calc(var(--conf-pct, 100) * 1%),
                             transparent calc(var(--conf-pct, 100) * 1%) 100%);
}
.conf-dot[data-band="high"] {
  border-color: var(--conf-high);
  background: conic-gradient(var(--conf-high) 0 calc(var(--conf-pct, 80) * 1%),
                             transparent calc(var(--conf-pct, 80) * 1%) 100%);
}
.conf-dot[data-band="low"] {
  border-color: var(--conf-low);
  background: conic-gradient(var(--conf-low) 0 calc(var(--conf-pct, 25) * 1%),
                             transparent calc(var(--conf-pct, 25) * 1%) 100%);
}

.conf-inline { display: inline-flex; align-items: center; gap: 0.4rem; }
.conf-pct { font-size: var(--type-small); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }

/* ---------- date honesty ---------- */

.date { }
.date[data-kind="exact"] { }
/* An approximate date reads "vers 1890" with a dotted underline (B3): the
   qualifier is part of the value, and the dots say so without a colour. */
.date[data-kind="approximate"] {
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.date[data-kind="range"] { font-style: italic; color: var(--text-secondary); }
.date[data-kind="preserved"] {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--accent-soft);
  padding: 0.05em 0.35em;
  border-radius: 3px;
}
.date[data-kind="unknown"] { color: var(--text-tertiary); }

.date-note {
  display: block;
  font-size: var(--type-small);
  color: var(--text-tertiary);
  margin-top: 0.15rem;
}
.cal-tag {
  font-size: var(--type-label);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  padding: 0 0.3em;
  margin-inline-start: 0.35em;
  white-space: nowrap;
}

/* ---------- home ---------- */

.why {
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--accent);
  background: var(--surface-raised);
  border-radius: var(--radius);
  padding: 1.1rem 1.35rem;
}
.why h2 { margin-top: 0; }
.why p { color: var(--text-secondary); }
.why p:last-child { margin-bottom: 0; }

.stat {
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  padding: 0.8rem 0.9rem;
}
.stat .n {
  font-size: var(--type-major);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.stat .k { font-size: var(--type-small); color: var(--text-tertiary); text-transform: capitalize; }

.cta-row { display: flex; gap: 0.75rem; flex-wrap: wrap; margin: 1.25rem 0; }

/* ---------------------------------------------------------------------------
   Fields (Study 06, B3)
   ---------------------------------------------------------------------------
   A field sits a shade below the page, like water below a bank: the sunken
   surface, a strong edge, radius 2, the same 36px as a button so a row of
   them lines up. Its label is mono, uppercase and always visible — never a
   placeholder (tests/fields.rs). A select is the browser's own, restyled; the
   theme's color-scheme gives its open list the right ground. */
input[type="text"], input[type="password"], input[type="email"],
input[type="search"], input[type="number"], input[type="url"],
input[type="file"], select, textarea {
  box-sizing: border-box;
  inline-size: 100%;
  min-block-size: 36px;
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--field-radius);
  background: var(--surface-sunken);
  color: var(--text-primary);
  font-family: var(--face);
  font-size: var(--type-minor);
  font-weight: var(--weight-normal);
  line-height: 1.35;
  text-transform: none;
  letter-spacing: normal;
  transition: border-color var(--tempo) var(--ease);
}
input[type="file"] { padding-block: 6px; }
textarea {
  min-block-size: auto;
  padding: 8px 10px;
  font-family: var(--mono);
  line-height: 1.5;
}

/* ---------------------------------------------------------------------------
   Buttons (Study 06, B2)
   ---------------------------------------------------------------------------
   Four variants and only four. `.btn` alone is the SECONDARY, the everyday
   button: transparent, a 1px strong edge, ink. `.primary` is filled with the
   accent and there is at most one on a page (tests/controls.rs). `.ghost` has
   no edge, for actions inside rows. `.danger` is an outline in the danger
   colour, never a fill: destructive actions should feel serious, not loud.

   36px, or 28px with `.small`, and one radius per theme — Study 06's 2px
   everywhere but in Canopée, which rounds them into pills (--btn-radius,
   docs/adr/0004). Disabled is dashed, so it reads as off in greyscale. A
   busy button says so in words (ui.js swaps in `data-busy`); under Canopée
   a loader moves beside the words, never in place of them. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  box-sizing: border-box;
  min-block-size: 36px;
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--btn-radius);
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  font-size: var(--type-minor);
  font-weight: var(--weight-medium);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--tempo) var(--ease),
              border-color var(--tempo) var(--ease),
              color var(--tempo) var(--ease);
}
.btn.small { min-block-size: 28px; padding: 0 10px; font-size: var(--type-small); }
.btn:hover { background: var(--surface-hover); }
/* A secondary that is the current one of a set — a range, a mode, the
   record type being listed — is pressed: the hover surface and full ink,
   never a colour. */
.btn[aria-current], .btn[aria-pressed="true"] {
  background: var(--surface-hover);
  border-color: var(--text-secondary);
  color: var(--text-primary);
}
.btn.primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--on-accent);
  font-weight: var(--weight-semi);
}
.btn.primary:hover { background: color-mix(in srgb, var(--accent) 86%, var(--text-primary)); }
.btn.ghost { border-color: transparent; }
.btn.ghost:hover { background: var(--surface-hover); }
.btn.danger { border-color: var(--danger); color: var(--danger); }
.btn.danger:hover { background: var(--surface-hover); }
.btn:disabled, .btn[aria-disabled="true"] {
  border-style: dashed;
  border-color: var(--border-strong);
  background: transparent;
  color: var(--text-tertiary);
  cursor: not-allowed;
}
.btn[aria-busy="true"] { cursor: progress; }
/* On a phone, a label that does not fit wraps inside its button instead of
   carrying the button past the edge of the page; the button grows, as its
   minimum height allows (measured at 390px with text at 150 %, v0.5.0). */
@media (max-width: 30rem) {
  .btn { white-space: normal; text-align: center; }
}

select {
  cursor: pointer;
  /* Room for the native arrow so the value never sits under it. */
  padding-inline-end: 1.8rem;
}
/* The open list of a select is drawn by the system from the select's own
   colours. A transparent select — the masthead's were — made Chrome on
   Windows draw its options in the theme's ink on a white list: near-white
   on white under every dark theme, so a language could not be read, let
   alone chosen (v0.5.0, the operator's screenshot). Every select has its
   theme's ground now, and every option carries it too. */
option, optgroup {
  background-color: var(--surface-raised);
  color: var(--text-primary);
}
option:checked { background-color: var(--surface-hover); }
input:hover, select:hover, textarea:hover { border-color: var(--border-strong); }

/* ---------- notices (Study 06, B7) ----------
   Each kind has its own SHAPE, so none of it needs colour to read: a ring
   for information, a disc for something done, a diamond for caution, a
   square for an error. The shape sits in a gutter of its own beside the
   text. No tinted ground, except a barely-there one under an error. A
   message says what happened, then what to do next; its actions are
   buttons underneath. */
.notice {
  position: relative;
  border: 1px solid var(--border-hairline);
  border-radius: 2px;
  /* The shape's gutter: the content keeps its own flow beside it. */
  padding: 0.8rem 1rem 0.8rem 2.4rem;
  padding-inline: 2.4rem 1rem;
  margin: 1rem 0;
  background: transparent;
  box-shadow: none;
}
.notice::before {
  content: "";
  position: absolute;
  inset-inline-start: 1rem;
  inset-block-start: calc(0.8rem + 0.45em);
  inline-size: 10px;
  block-size: 10px;
  box-sizing: border-box;
  border: 1.5px solid var(--text-secondary);
  border-radius: 50%;
}
.notice.ok::before { border: 0; background: var(--conf-certain); }
.notice.warn::before, .notice-defect::before {
  border: 0;
  background: var(--warning);
  inline-size: 8px;
  block-size: 8px;
  border-radius: 0;
  transform: rotate(45deg);
  inset-inline-start: calc(1rem + 1px);
}
.notice.error::before { border: 0; background: var(--danger); border-radius: 0; }
.notice.error { background: color-mix(in srgb, var(--danger) 5%, transparent); }
.notice p:first-child { margin-top: 0; }
.notice p:last-child { margin-bottom: 0; }
.notice .notice-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-top: 0.8rem; }

/* ---------- tables ---------- */

table { border-collapse: collapse; width: 100%; font-size: var(--type-minor); }
th, td {
  text-align: start;
  /* Taller rows: a listing is scanned down a column, and 0.5rem put the
     lines close enough that the eye kept losing its place. */
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--border-hairline);
  vertical-align: top;
}
thead th {
  position: sticky;
  top: 0;
  background: var(--surface-raised);
  z-index: 1;
  border-bottom-width: 2px;
}
th {
  font-size: var(--label-size);
  text-transform: uppercase;
  letter-spacing: var(--label-track);
  color: var(--text-tertiary);
  font-weight: var(--weight-semi);
  white-space: nowrap;
}
tbody tr { transition: background-color var(--tempo) var(--ease); }
tbody tr:hover { background: var(--surface-hover); }
tbody tr:last-child td { border-bottom: none; }
/* A column of numbers is compared digit by digit, so it is aligned to its
   end and set in figures of one width. */
td.num, th.num, .conf-pct, .tdates {
  font-variant-numeric: tabular-nums;
}
td.num, th.num { text-align: end; }
/* Positioned, so what is absolutely placed inside (the screen-reader text)
   scrolls with the table instead of widening the page. */
.table-wrap { overflow-x: auto; position: relative; }
/* At phone width a table whose cells say what they are (`data-label`) is a
   list of cards, one per row, each cell its label over its value, rather
   than columns squeezed to a few letters (2026-10-25: the notes' titles
   broke every three letters). */
@media (max-width: 640px) {
  .table-wrap > table:has(> tbody > tr > td[data-label]),
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody,
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr,
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr > td { display: block; inline-size: 100%; }
  .table-wrap > table:has(> tbody > tr > td[data-label]) > thead { display: none; }
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr {
    padding: 0.7rem max(0.75rem, var(--section-pad-x, 0.75rem));
    border-block-start: 1px solid var(--border-hairline);
  }
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr > td {
    padding: 0.15rem 0;
    border: 0;
  }
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr > td:is(:first-child, :last-child) { padding-inline: 0; }
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr > td:empty { display: none; }
  .table-wrap > table:has(> tbody > tr > td[data-label]) > tbody > tr > td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-family: var(--mono);
    font-size: var(--type-label);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-tertiary);
  }
}

/* ---------- forms ---------- */

/* The label: mono, uppercase, always above its field. A checkbox's or a
   radio's label is a sentence beside it (`label.inline`) and stays one. */
label, .field-label {
  display: block;
  margin-bottom: 0.3rem;
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--text-secondary);
}
label.inline {
  font-family: inherit;
  font-size: var(--type-minor);
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-primary);
}
label > input, label > select, label > textarea, label > .date-input { margin-top: 0.3rem; }
/* A date and its qualifier are one box: `vers ▾ 1890`. */
.date-input {
  display: flex;
  align-items: stretch;
  inline-size: 100%;
}
.date-input > .date-qualifier {
  flex: none;
  inline-size: auto;
  border-start-end-radius: 0;
  border-end-end-radius: 0;
  border-inline-end-width: 0;
  color: var(--text-secondary);
}
.date-input > input {
  flex: 1 1 auto;
  min-inline-size: 5rem;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}
.date-field { margin: 0; }
.field { margin-bottom: 1rem; }
/* Help under a field says what the field means in the source. */
.hint, .field .hint {
  font-family: var(--face);
  font-size: var(--type-small);
  text-transform: none;
  letter-spacing: normal;
  color: var(--text-tertiary);
  margin-top: 0.3rem;
}
/* The way from a record to its times (docs/adr/0034), at the record's head
   and made to be seen (the operator's request of 8 November 2026): the
   accent's own gradient and, pointed at, a hit. It jumps, a shock wave runs
   out of it, a light crosses it and its mark turns. */
.record-impact { margin: 0 0 0.9rem; }
.impact-cta {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.6rem 1.3rem 0.6rem 0.95rem; border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--accent) 60%, var(--text-primary));
  background: linear-gradient(115deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 58%, var(--hue-7)) 100%);
  color: var(--on-accent); text-decoration: none;
  font-size: var(--type-body); font-weight: var(--weight-semi); letter-spacing: 0.04em; text-transform: uppercase;
  box-shadow: 0 8px 22px -10px color-mix(in srgb, var(--accent) 80%, transparent);
}
.impact-cta .ico { inline-size: 1.3em; block-size: 1.3em; stroke-width: 2; }
.impact-cta::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(105deg, transparent 35%, var(--sheen) 50%, transparent 65%);
  transform: translateX(-110%);
}
.impact-cta:hover {
  color: var(--on-accent); text-decoration: none;
  animation: impact-hit 460ms var(--ease) both, impact-wave 1.4s var(--ease) 200ms infinite;
}
.impact-cta:hover::before { animation: impact-shine 900ms var(--ease) 80ms both; }
.impact-cta:hover .ico { animation: impact-turn 700ms var(--ease) both; }
@keyframes impact-hit {
  0% { transform: none; }
  35% { transform: translateY(-3px) scale(1.07); }
  65% { transform: translateY(0) scale(0.98); }
  100% { transform: translateY(-2px) scale(1.03); }
}
@keyframes impact-wave {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 65%, transparent), 0 8px 22px -10px color-mix(in srgb, var(--accent) 80%, transparent); }
  100% { box-shadow: 0 0 0 18px transparent, 0 8px 22px -10px color-mix(in srgb, var(--accent) 80%, transparent); }
}
@keyframes impact-shine { to { transform: translateX(110%); } }
@keyframes impact-turn { from { transform: rotate(-120deg) scale(0.7); } to { transform: none; } }
/* A file refused before it is sent: over the site's limit (ui.js). */
.upload-refused {
  display: block; margin-top: 0.3rem;
  font-family: var(--face); font-size: var(--type-small); text-transform: none; letter-spacing: normal;
  color: var(--danger);
}
fieldset { border: 1px solid var(--border-hairline); border-radius: var(--radius); padding: 1rem; margin: 0 0 1.25rem; min-inline-size: 0; }
legend { font-size: var(--type-small); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-tertiary); padding: 0 0.4rem; }

/* ---------- diagnostics ---------- */

.diag { border: 1px solid var(--border-hairline); border-radius: var(--radius); overflow: hidden; margin: 1rem 0; }
.diag-row {
  display: grid;
  grid-template-columns: minmax(180px, auto) 1fr;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border-hairline);
  font-size: var(--type-minor);
  background: var(--surface-raised);
}
.diag-row:last-child { border-bottom: none; }
.diag-code { font-family: var(--mono); font-size: var(--type-small); }
.diag-row[data-sev="error"]   .diag-code { color: var(--danger); }
.diag-row[data-sev="warning"] .diag-code { color: var(--warning); }
.diag-row[data-sev="info"]    .diag-code { color: var(--text-tertiary); }
.diag-ref { display: block; font-size: var(--type-label); color: var(--text-tertiary); font-family: var(--mono); }
.diag-explanation { margin: 0 0 0.25rem; }
.diag-message { margin: 0; color: var(--text-secondary); overflow-wrap: anywhere; }

/* An inference, marked as one.
 *
 * A dotted underline rather than a colour or an icon: it survives every theme,
 * it survives greyscale, and it is the typographic convention for "there is
 * more behind this word" that a reader already knows. The distinction between
 * what a source recorded and what this page worked out is the argument of the
 * whole product, so it is never carried by tone alone. */
.presumed {
  border-bottom: 1px dotted currentColor;
  cursor: help;
}
abbr.presumed { text-decoration: none; }

.muted { color: var(--text-tertiary); }
.small { font-size: var(--type-minor); }
.nowrap { white-space: nowrap; }

/* ---------- tree ---------- */

.tree-head {
  display: flex;
  gap: 1.5rem;
  align-items: flex-start;
  flex-wrap: wrap;
  justify-content: space-between;
}
.tree-head .lede { max-width: 60ch; }
.tree-search { min-width: 240px; }
.tree-search input { min-width: 240px; }

.tree-legend {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1rem 0 0.5rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
}
.tree-legend .lg { display: inline-flex; align-items: center; gap: 0.35rem; color: var(--text-secondary); }

/* The tree scrolls sideways and only sideways.
 *
 * A 2,078px focused tree and a 24,124px full view cannot be shown any other
 * way, so the horizontal scroll stays. The vertical one is gone: the box is as
 * tall as the canvas inside it, so the page scroll is the only vertical scroll
 * on the page and a wheel gesture means the same thing wherever the pointer
 * happens to be.
 *
 * `overflow-y: hidden` rather than `visible` because CSS does not let the two
 * axes disagree: asking for `visible` beside an `auto` would silently compute
 * back to `auto` and reinstate the scrollbar this change exists to remove.
 * Nothing is clipped by it — the height is the content's, and the gutter below
 * gives the horizontal bar somewhere to sit that is not on top of the oldest
 * generation. */
.tree-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: var(--hscroll-gutter);
  /* Painting the rows into their own layer keeps scrolling smooth on a canvas
     that can be tens of thousands of pixels wide. */
  will-change: transform;
  /* Pinned while the record beside it scrolls — see the note on .tree-split.
     `--tree-height` is the canvas height the template measured. While the tree
     fits the viewport the offset is the plain one and the tree hangs below the
     masthead; once it is taller than the viewport the second term goes
     negative, which pins the tree's *bottom* to the bottom of the screen
     instead of its top to the top. Either way a whole screen of tree stays in
     view, and the horizontal scrollbar — the last thing in the box — stays
     reachable.

     There is one narrow band, a tree within about 70px of fitting, where the
     second term lands between zero and the masthead's height and the tree's
     top edge ends up behind it. That is the lesser of the two losses: pinning
     such a tree by its top instead would put its bottom — and the horizontal
     scrollbar with it — permanently below the fold for as long as it stays
     pinned, whereas an occluded top edge is one scroll away from being read.
     A tree that close to fitting has its cards well inside the band anyway. */
  position: sticky;
  top: min(
    var(--stick-top),
    calc(100vh - var(--tree-height, 0px) - var(--hscroll-gutter) - 0.75rem)
  );
  align-self: start;
}
/* The full view has no record beside it, so there is nothing for the tree to
   be pinned against: it is the whole column and the page simply scrolls it. */
.tree-split.no-panel .tree-scroll { position: static; }

.tree-canvas { position: relative; margin-inline: auto; }

.tree-wires { position: absolute; inset: 0; pointer-events: none; }
.wire { fill: none; stroke: var(--text-tertiary); stroke-width: 1.5; }
.wire-spouse { stroke-width: 2; stroke-dasharray: none; }
.wire[data-band="low"] { stroke-dasharray: 3 3; }
.wire[data-band="medium"] { stroke-dasharray: 6 3; }
/* A crossing edge carries a hue (set inline) so it can be told apart at the
   intersection; it is drawn a touch heavier than the default ink. */
/* A crossing marker is a hue AND a dash pattern, never a hue alone. Under the
   colour-blind themes several of these hues converge; the dash is what keeps
   two lines told apart through an intersection. The patterns are chosen to
   differ in rhythm, not only in length, so they stay distinct at 2px. */
.wire-crossing { stroke-width: 2.25; }
.wire-crossing[data-hue="1"] { stroke: var(--hue-1); stroke-dasharray: none; }
.wire-crossing[data-hue="2"] { stroke: var(--hue-2); stroke-dasharray: 9 4; }
.wire-crossing[data-hue="3"] { stroke: var(--hue-3); stroke-dasharray: 2 3; }
.wire-crossing[data-hue="4"] { stroke: var(--hue-4); stroke-dasharray: 12 3 2 3; }
.wire-crossing[data-hue="5"] { stroke: var(--hue-5); stroke-dasharray: 5 5; }
.wire-crossing[data-hue="6"] { stroke: var(--hue-6); stroke-dasharray: 1 4; }
.wire-crossing[data-hue="7"] { stroke: var(--hue-7); stroke-dasharray: 16 5; }
.wire-crossing[data-hue="8"] { stroke: var(--hue-8); stroke-dasharray: 7 3 1 3; }
/* Hover raises a person's own edges to the front of the eye. */
.wire.lit { stroke-width: 3; }

/* A generation, drawn as one thing.
 *
 * Vertical position carries generation, and wrapping a wide generation onto
 * several rows would break that if the rows read as separate bands. They do
 * not, because of three things at once: the rows of one generation sit 26px
 * apart where two generations sit 78px apart; the whole band has a single
 * tinted zone behind it; and the label spans the zone rather than sitting on
 * the first row. The tint alternates so that two bands cannot be misread as
 * one, and it is very faint — this is a grouping cue, not a colour.
 */
.band-zone {
  position: absolute;
  inset-inline: 0;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--text-primary) 3%, transparent);
  pointer-events: none;
  z-index: 0;
}
.band-zone.wrapped {
  /* A band that had to wrap says so with a bracket down its leading edge, so
     the reader can see at a glance that these rows are one generation rather
     than several. */
  border-inline-start: 2px solid color-mix(in srgb, var(--text-primary) 18%, transparent);
}
.band-zone.unplaced {
  background: color-mix(in srgb, var(--conf-medium) 7%, transparent);
}

.band-label {
  position: absolute;
  inset-inline-start: 0;
  transform: translateY(-1.35rem);
  font-size: var(--type-label);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  padding-inline-start: 0.5rem;
  white-space: nowrap;
  z-index: 1;
}
.band-label strong { color: var(--text-secondary); font-weight: var(--weight-semi); }
.band-label span { margin-inline-start: 0.4rem; text-transform: none; letter-spacing: 0; }
.band-label.unplaced strong { color: var(--warning); }

/* The classic chart's columns (src/chart.rs; the operator's reviews of 27
   and 28 October 2026): one generation each, the oldest at the left, a
   faint band down the page behind its cards, the person's own tinted with
   the accent. Its name heads it in spaced capitals in the accent, the
   count under it, and the head stays in view while the page scrolls down
   the chart. */
.band-zone.relative {
  inset-inline-end: auto;
  border-radius: 14px;
  background: color-mix(in srgb, var(--text-primary) 3.5%, transparent);
}
.band-zone.relative.is-centre { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.tree-colheads {
  position: sticky;
  inset-block-start: var(--stick-top);
  z-index: 8;
  block-size: 56px; /* HEAD in chart.rs */
  pointer-events: none;
}
.tree-colhead {
  position: absolute;
  inset-block-start: 4px;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 1px;
  min-block-size: 44px;
  padding: 0.2rem 0.35rem;
  box-sizing: border-box;
  border-radius: 10px;
  background: color-mix(in srgb, var(--surface-raised) 90%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  text-align: center;
  line-height: 1.15;
}
.tree-colhead strong {
  color: var(--accent);
  font-size: var(--type-micro);
  font-weight: var(--weight-semi);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* A name breaks between its words, never inside one. */
  overflow-wrap: normal;
}
.tree-colhead span { color: var(--text-secondary); font-size: var(--type-micro); }
.tree-colhead.is-centre strong { color: var(--text-primary); }
/* The chart is read down the page (2026-10-28), and the record beside it is
   narrower, growing with the page as it always has (one page scroll, not
   three: tests/panel_layout.rs). */
.tree-scroll:has(> .tree-canvas[data-chart]) {
  position: static;
  will-change: auto;
}
.tree-split:has(.tree-canvas[data-chart]) {
  grid-template-columns: minmax(0, 1fr) clamp(300px, 24vw, 380px);
}
/* Narrower than this, the chart takes the page's width and the record
   follows it, below: beside it, the chart would be scaled too small. */
@media (max-width: 1200px) {
  .tree-split:has(.tree-canvas[data-chart]) { grid-template-columns: minmax(0, 1fr); }
}
/* Fitted by tree.js (`data-fit`): the drawing was scaled to its column or
   redrawn as a column of generations, so the column is clipped across,
   never scrolled — a card's open sheet, laid out though hidden, must not
   widen the page — and nobody in it is cut off. Down the page nothing is
   clipped, and the chart's column heads stick to the window. Without
   scripts the column scrolls instead. */
.tree-split[data-fit] .tree-scroll {
  overflow-x: clip;
  overflow-y: visible;
  padding-bottom: 0;
}
/* Scaled down, the drawing keeps an open card at its own size. */
.tree-canvas .tcard .tc-more { zoom: var(--tree-unzoom, 1); }
/* Beside the record the drawing would be scaled too small to read: the
   record moves under it, and the drawing takes the page's width. */
.tree-split[data-fit="wide"]:has(.tree-canvas) { grid-template-columns: minmax(0, 1fr); }
.tree-split[data-fit="wide"] .tree-scroll { position: static; }
/* Too many generations even for the page's width: the drawing becomes the
   phone's column of generations (below), its cards as many to a line as
   fit, a card opening over its neighbours as on the chart. Every name
   keeps its own size. */
.tree-split[data-fit="stack"] .tree-scroll { position: static; }
.tree-split[data-fit="stack"] .tree-canvas {
  inline-size: auto !important;
  block-size: auto !important;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.tree-split[data-fit="stack"] .tree-canvas :is(.tree-wires, .band-zone, .tree-colheads),
.tree-split[data-fit="stack"] .tree-canvas > .band-label { display: none; }
.tree-split[data-fit="stack"] .tree-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.5rem;
  padding: 0.6rem 0.65rem 0.7rem;
  border: 1px solid var(--border-hairline);
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface-raised) 55%, transparent);
}
.tree-split[data-fit="stack"] .tree-row.is-centre { border-color: color-mix(in srgb, var(--accent) 55%, var(--border-hairline)); }
.tree-split[data-fit="stack"] .tree-row-name {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.5rem;
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--type-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.tree-split[data-fit="stack"] .tree-row-name strong { color: var(--accent); font-weight: var(--weight-semi); }
.tree-split[data-fit="stack"] .tree-row-name .muted { letter-spacing: 0; text-transform: none; }
.tree-split[data-fit="stack"] .tree-row .tcard {
  position: relative;
  inset: auto !important;
  inline-size: auto !important;
  width: auto !important;
}
/* A generation's block (_tree_canvas.html): no box at all on a wide
   screen, where every card stands where the layout put it. */
.tree-row { display: contents; }
.tree-row-name { display: none; }
/* A phone (the operator's review, 2026-10-27: the tree ran off the screen
   sideways). The chart becomes a column of generations, in the order its
   columns read — the oldest first, the newest last — the person's own in
   the middle. Its lines, zones and column heads give way to each block's
   own name; the cards stand two to a line and the canvas takes the
   column's width, so nothing scrolls sideways. An open card is a sheet at
   the foot of the screen. */
@media (max-width: 640px) {
  .tree-scroll {
    position: static;
    overflow: visible;
    will-change: auto;
    padding-bottom: 0;
  }
  .tree-scroll .tree-canvas {
    inline-size: auto !important;
    block-size: auto !important;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
  }
  .tree-canvas :is(.tree-wires, .band-zone, .tree-colheads),
  .tree-canvas > .band-label { display: none; }
  .tree-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.5rem;
    padding: 0.6rem 0.65rem 0.7rem;
    border: 1px solid var(--border-hairline);
    border-radius: 14px;
    background: color-mix(in srgb, var(--surface-raised) 55%, transparent);
  }
  .tree-row.is-centre { border-color: color-mix(in srgb, var(--accent) 55%, var(--border-hairline)); }
  .tree-row-name {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.5rem;
    grid-column: 1 / -1;
    margin: 0;
    font-size: var(--type-label);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }
  .tree-row-name strong { color: var(--accent); font-weight: var(--weight-semi); }
  .tree-row-name .muted { letter-spacing: 0; text-transform: none; }
  .tree-row .tcard {
    position: relative;
    inset: auto !important;
    inline-size: auto !important;
    width: auto !important;
  }
  .tree-row .tcard .tc-more,
  .tree-row .tcard:is(:hover, :focus-within, .is-open) .tc-more {
    position: fixed;
    inset: auto 12px 12px 12px !important;
    inline-size: auto;
    transform: none !important;
  }
}
/* The chart's lines (the operator's review, 2026-10-28: "a boy blue, a
   girl red, an adoption dotted"). A couple's bracket and a family's comb
   are inked in the page's grey, as a drawn family chart is; the line to each
   child is in the child's colour — a son blue, a daughter red, anyone else
   grey — and, when they belong to the family other than by birth, in its
   pattern: an adoption dashed, a fostering or a guardianship dotted, a
   step-child dash and dot. A couple's knot is two rings on the bracket.
   The legend draws the same lines, with the same classes. */
/* The brackets and combs in the page's own grey, never in an accent that
   could pass for a son's blue or a daughter's red (Galaxy's violet,
   Fusion's red): only the children's own lines carry a colour. */
.tree-canvas[data-chart] .wire { stroke: color-mix(in srgb, var(--text-secondary) 85%, transparent); stroke-width: 1.8; }
:is(.tree-canvas, .kin-key) .wire.wire-tone-a { stroke: var(--kin-boy); }
:is(.tree-canvas, .kin-key) .wire.wire-tone-b { stroke: var(--kin-girl); }
:is(.tree-canvas, .kin-key) .wire.wire-tone-u { stroke: var(--kin-other); }
:is(.tree-canvas, .kin-key) .wire.wire-parent { stroke-width: 2.4; }
/* A person's own lines, under the pointer, stand out from the rest. */
.tree-canvas[data-chart] .wire.lit { stroke-width: 3.2; }
:is(.tree-canvas, .kin-key) .wire.wire-line-adoptive { stroke-dasharray: 6 4; }
:is(.tree-canvas, .kin-key) .wire:is(.wire-line-foster, .wire-line-guardianship) { stroke-dasharray: 0.1 4.6; stroke-linecap: round; stroke-width: 3; }
:is(.tree-canvas, .kin-key) .wire.wire-line-step { stroke-dasharray: 8 3 1.5 3; }
/* One marriage of a polygamous union (docs/adr/0021): the bracket in long
   and short strokes, the knot with three rings. */
:is(.tree-canvas, .kin-key) .wire.wire-spouse.wire-line-polygamous { stroke-dasharray: 11 3 3 3; stroke-width: 2.2; }
.wire-node.is-polygamous .ring { stroke-width: 1.2; }
/* A couple's node with its family's emblem (docs/adr/0022); a polygamous
   union's keeps its own pattern on the bracket and a broken ring here. */
.wire-node .knot-emblem-disc { fill: var(--surface-raised); stroke: var(--accent); stroke-width: 1.4; }
.wire-node.is-polygamous .knot-emblem-disc { stroke-dasharray: 5 2.5; }
.wire-node circle { fill: var(--surface-raised); stroke: color-mix(in srgb, var(--accent) 80%, transparent); stroke-width: 1.2; }
.wire-node .ring { fill: none; stroke: var(--accent); stroke-width: 1.3; }
.tree-legend-kin { display: inline-flex; align-items: center; gap: 0.35rem 0.9rem; flex-wrap: wrap; }
.kin-key { inline-size: 24px; block-size: 8px; overflow: visible; flex: none; }
.kin-key .wire { fill: none; }
.kin-key.kin-knot { inline-size: 16px; block-size: 16px; }
.kin-key.kin-knot-poly { inline-size: 34px; block-size: 18px; }

/* A tie of rite or custom (docs/adr/0020): a godmother, a milk brother, a
   namesake, from the same family or another. It is not descent, so it is
   drawn as no family line is: a curve, in pairs of dots, in a hue of its own
   (--kin-rite) — never the theme's accent, never a son's blue or a
   daughter's red. */
:is(.tree-canvas, .kin-key) .wire.wire-rite {
  fill: none;
  stroke: var(--kin-rite);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-dasharray: 0.1 3.4 0.1 8;
}
.tree-canvas[data-chart] .wire.wire-rite.lit { stroke-width: 3.4; }

/* The open card says what the person is to the one in the middle, and names
   their ties of rite in the same dotted frame the line has. */
.tc-kin {
  display: block;
  color: var(--accent);
  font-weight: var(--weight-semi);
  font-size: var(--type-small);
}
.tc-kin-note { color: var(--text-secondary); font-weight: var(--weight-normal); }
.tc-rites { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem; }
.tc-rite {
  padding: 0.1rem 0.5rem;
  border: 2px dotted var(--kin-rite);
  border-radius: 999px;
  font-size: var(--type-label);
}
.tc-rite-term { color: var(--text-secondary); }

/* The tree's side panel: what the selected person is to the one in the
   middle, and the way to see the link step by step. */
.panel-kin { margin: 0.25rem 0 0; font-size: var(--type-small); }
.panel-kin-term { color: var(--accent); font-weight: var(--weight-semi); }
.panel-kin-path { margin-inline-start: 0.4rem; white-space: nowrap; }

/* The record's ties of rite and custom, apart from the family and framed as
   the tree draws them: dotted, in their own hue. */
.rite-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.rite-card {
  padding: 0.75rem 0.9rem;
  border: 2px dotted var(--kin-rite);
  border-radius: 14px;
  background: color-mix(in srgb, var(--kin-rite) 6%, var(--surface-raised));
}
.rite-card p { margin: 0.15rem 0; }
.rite-term { font-size: var(--type-sub); font-weight: var(--weight-semi); }
.rite-term::first-letter { text-transform: uppercase; }
.kin-native {
  display: inline-block;
  padding: 0 0.45rem;
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  font-style: italic;
  color: var(--text-secondary);
}

/* The Kinship tab: how two people are related, step by step, and every
   relative by group. Large, plain words first — the relationship is the
   answer — and the way to it beneath. */
.kin { display: grid; gap: 1.5rem; }
.kin-title { margin: 0 0 0.35rem; font-size: var(--type-sub); }
.kin-form { display: flex; flex-wrap: wrap; align-items: end; gap: 0.6rem; margin: 0.6rem 0 0; }
.kin-field { display: grid; gap: 0.25rem; flex: 1 1 18rem; min-inline-size: 0; }
.kin-field-label { font-size: var(--type-label); color: var(--text-secondary); letter-spacing: 0.04em; }
.kin-field select { inline-size: 100%; }
.kin-result { display: grid; gap: 0.9rem; margin-block-start: 1rem; scroll-margin-block-start: 5rem; }
.kin-way {
  padding: 1rem 1.1rem;
  border: 1px solid var(--border-hairline);
  border-inline-start: 4px solid var(--accent);
  border-radius: 14px;
  background: var(--surface-raised);
}
.kin-way.is-rite { border: 2px dotted var(--kin-rite); }
.kin-way p { margin: 0.2rem 0; }
.kin-who-line { font-size: var(--type-body); }
.kin-term {
  font-size: var(--type-major);
  font-weight: var(--weight-semi);
  line-height: 1.2;
  color: var(--text-primary);
}
.kin-term::first-letter { text-transform: uppercase; }
.kin-of { color: var(--text-secondary); }
/* The way: each person a chip, the word for what they are to the one before
   above the arrow that leads to them; the shared ancestors marked. */
.kin-path {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.4rem 0.2rem;
  margin: 0.75rem 0 0.25rem;
  padding: 0;
  list-style: none;
}
.kin-step { display: inline-flex; align-items: flex-end; gap: 0.2rem; }
.kin-hop {
  display: inline-grid;
  justify-items: center;
  padding: 0 0.15rem;
  font-size: var(--type-label);
  color: var(--text-secondary);
}
.kin-hop::after { content: "→"; color: var(--text-tertiary); font-size: var(--type-small); }
[dir="rtl"] .kin-hop::after { content: "←"; }
.kin-person {
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface-page);
  font-size: var(--type-small);
  white-space: nowrap;
}
.kin-step.is-start .kin-person,
.kin-step.is-end .kin-person { border-color: var(--accent); font-weight: var(--weight-semi); }
.kin-step.is-common .kin-person {
  border-style: double;
  border-width: 3px;
  border-color: color-mix(in srgb, var(--accent) 70%, var(--border-strong));
}
.kin-person.is-hidden { font-style: italic; color: var(--text-tertiary); }
.kin-group { border-block-start: 1px solid var(--border-hairline); padding-block: 0.4rem; }
.kin-group > summary { cursor: pointer; padding-block: 0.3rem; font-weight: var(--weight-semi); }
.kin-rows {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 0.35rem 1.25rem;
  margin: 0.3rem 0 0.4rem;
  padding: 0;
  list-style: none;
}
.kin-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.1rem 0.5rem;
  padding: 0.35rem 0;
  border-block-end: 1px dashed var(--border-hairline);
}
.kin-name { font-weight: var(--weight-semi); }
.kin-years { font-size: var(--type-label); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
.kin-word { flex: 1 1 100%; color: var(--text-primary); font-size: var(--type-small); }
.kin-group-rites .kin-row { border-block-end-style: dotted; border-color: var(--kin-rite); }
.kin-also { flex: 1 1 100%; font-size: var(--type-label); color: var(--text-secondary); }
.kin-show { font-size: var(--type-label); color: var(--text-secondary); }

/* A card, folded (2026-10-28, "more compact still"): one line, the first
   given name and the family name's initial in the strongest ink on a
   plain ground, a strip of the person's colour at its start — blue, red or
   grey, as their lines are — and the confidence dot at its end. Matches
   CARD_H in tree.rs and chart.rs. */
.tcard {
  position: absolute;
  height: 30px;
  box-sizing: border-box;
  border: 1px solid var(--border-strong);
  background: color-mix(in srgb, var(--surface-raised) 90%, var(--text-primary));
  border-radius: 8px;
  color: var(--text-primary);
  z-index: 2;
  transition: box-shadow 0.1s, opacity 0.1s;
}
.tcard::before {
  content: "";
  position: absolute;
  inset-block: 6px;
  inset-inline-start: 4px;
  inline-size: 3px;
  border-radius: 3px;
  background: var(--kin-other);
  pointer-events: none;
}
.tc-face {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  block-size: 100%;
  padding-inline: 0.62rem 0.35rem;
  color: inherit;
  text-decoration: none;
  border-radius: inherit;
}
.tcard .tname {
  flex: 1 1 auto;
  min-inline-size: 0;
  font-size: var(--type-small);
  font-weight: var(--weight-semi);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
/* A name too long for its card is a size smaller, not cut (tree.js). */
.tcard .tname.is-long bdi { font-size: 0.88em; letter-spacing: -0.01em; }
.tcard .tname.is-longer bdi { font-size: 0.78em; }
.tc-conf { display: block; color: var(--text-secondary); }
.tc-conf .conf-dot { margin-inline-end: 0.3rem; vertical-align: -1px; }
/* The same person drawn a second time: dashed, and a small turning arrow. */
.tcard.is-dup { border-style: dashed; }
.tc-dup { flex: none; font-size: var(--type-small); color: var(--text-tertiary); }

/* Open: the same card grown over its neighbours — on the pointer after a
   short wait, so that moving across the chart opens nothing; at once on
   focus, or pinned by a click. The portrait in its laurels (the kit's, a
   mask in the accent), the full name, the dates, and two ways on. */
.tc-more {
  position: absolute;
  inset-block-start: -12px;
  inset-inline-start: 50%;
  inline-size: 252px;
  box-sizing: border-box;
  display: grid;
  justify-items: center;
  gap: 0.3rem;
  padding: 0.9rem 0.9rem 0.85rem;
  text-align: center;
  background: var(--surface-raised);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--border-strong));
  border-radius: 16px;
  box-shadow: 0 22px 48px -22px rgb(var(--shadow-rgb) / 0.75), 0 2px 6px rgb(var(--shadow-rgb) / 0.2);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(-50%) translateY(6px) scale(0.97);
  transition: opacity 140ms var(--ease), transform 160ms var(--ease), visibility 0s linear 160ms;
}
[dir="rtl"] .tc-more { transform: translateX(50%) translateY(6px) scale(0.97); }
.tcard:is(:hover, :focus-within, .is-open) { z-index: 30; }
.tcard:is(:hover, :focus-within, .is-open) .tc-more {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(-50%);
  transition: opacity 160ms var(--ease) 0.22s, transform 180ms var(--ease) 0.22s, visibility 0s linear 0.22s;
}
[dir="rtl"] .tcard:is(:hover, :focus-within, .is-open) .tc-more { transform: translateX(50%); }
.tcard:is(:focus-within, .is-open) .tc-more { transition-delay: 0s; }
/* Near the chart's edges the open card grows the other way (tree.js). */
.tcard.tc-up .tc-more { inset-block-start: auto; inset-block-end: -12px; }
.tcard.tc-start .tc-more,
.tcard.tc-start:is(:hover, :focus-within, .is-open) .tc-more { inset-inline-start: -10px; transform: none; }
.tcard.tc-end .tc-more,
.tcard.tc-end:is(:hover, :focus-within, .is-open) .tc-more { inset-inline-start: auto; inset-inline-end: -10px; transform: none; }
.tc-portrait {
  position: relative;
  inline-size: 88px;
  block-size: 88px;
  display: grid;
  place-items: center;
}
.tc-portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  -webkit-mask: url("/static/img/laurel-ring.png?v=1") center / contain no-repeat;
  mask: url("/static/img/laurel-ring.png?v=1") center / contain no-repeat;
  pointer-events: none;
}
.tc-portrait img,
.tc-initials {
  inline-size: 56px;
  block-size: 56px;
  border-radius: 50%;
  object-fit: cover;
  background: color-mix(in srgb, var(--accent) 16%, var(--surface-raised));
}
.tc-initials {
  display: grid;
  place-items: center;
  color: var(--accent);
  font-weight: var(--weight-semi);
  font-size: var(--type-sub);
  letter-spacing: 0.04em;
}
.tc-full {
  font-family: var(--rv-serif);
  font-size: var(--type-body);
  font-weight: var(--weight-semi);
  line-height: 1.25;
  color: var(--text-primary);
}
.tc-dates {
  display: grid;
  gap: 0.15rem;
  font-size: var(--type-small);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.tc-sym { color: var(--accent); margin-inline-end: 0.3rem; }
.tc-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  margin-block-start: 0.35rem;
}
.tc-actions a {
  font-size: var(--type-label);
  padding: 0.32rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  text-decoration: none;
  white-space: nowrap;
}
.tc-actions .tc-centre { border-color: var(--accent); color: var(--accent); }
.tc-actions a:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }

.tcard.is-root {
  box-shadow: 0 0 0 2px var(--accent);
  z-index: 3;
}
.tcard.is-root .tname { color: var(--accent); }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* A person the reader may not read. Deliberately quiet rather than alarming:
   it is a normal state of a family record, not an error. The dashed border is
   the signal — it survives every theme and needs no colour to be read, which
   matters because the colour-blind themes have to carry it too. */
.tcard.is-restricted {
  border-style: dashed;
  background: var(--sex-u);
  cursor: default;
  /* A marker, not a card. It keeps the person's place in the row — the shape
     of the family is not what is being withheld — but it does not keep a
     card's width. On a bundle where a visitor may read nobody, that is the
     difference between a 24,124px canvas of the word "Private" and a legible
     one. The dash still reads at this size; the name never fitted anyway, and
     the title and accessible name still say what the marker is. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: 0.15rem;
}
.tcard.is-restricted::before { display: none; }
.tcard.is-restricted .tdates { justify-content: center; color: var(--text-tertiary); }
.tcard.is-restricted:hover { box-shadow: none; z-index: 2; }

/* The same treatment inline, where a hidden person is named as a relative. */
.restricted {
  color: var(--text-secondary);
  font-style: italic;
  border-bottom: 1px dashed var(--border-strong);
}

.tcard[data-sex="a"]::before { background: var(--kin-boy); }
.tcard[data-sex="b"]::before { background: var(--kin-girl); }
.tcard:hover { box-shadow: 0 0 0 2px var(--accent); }

.tname {
  font-size: var(--type-small);
  font-weight: var(--weight-semi);
  line-height: 1.2;
}
.tdates {
  font-size: var(--type-label);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  white-space: nowrap;
}
.tdates .sep { opacity: 0.5; }

/* Filtering dims rather than removes, so positions stay meaningful. */
.tree-canvas.filtering .tcard.nomatch { opacity: 0.15; }
.tree-canvas.filtering .tcard.match { box-shadow: 0 0 0 2px var(--accent); z-index: 4; }
.tree-canvas.filtering .tree-wires { opacity: 0.25; }

/* The selected card, whose record is open in the panel. Distinct from the
   root ring so "centred here" and "showing here" read as two different things. */
.tcard.is-selected { box-shadow: 0 0 0 3px var(--accent); z-index: 4; }

/* ---- Tree + side panel -------------------------------------------------- */
/* A scrollable tree beside a detail panel.
   ---------------------------------------------------------------------------
   Two tracks: the tree's column takes what the record leaves, and the record
   keeps one width — a share of the split between its floor and its ceiling —
   whatever the tree draws. Until 2026-10-23 the record's width was computed
   from the tree's (`--tree-width`), so that no band of nothing opened between
   a small tree and its record; but moving through the tree changes its width
   at every step, and a record that followed it slid toward the middle of the
   page and back, which the operator read as the page breaking. A narrow tree
   now stands in the middle of its own column instead.

   `minmax(0, …)` on the tree keeps its *minimum* at zero. On the full view
   the canvas is tens of thousands of pixels wide, and an `auto` minimum would
   size the track to it and push the panel off the screen. Pinned to zero, the
   overflow stays inside `.tree-scroll`, where it scrolls.

   The layout still hands the page the canvas's size (`--tree-width`,
   `--tree-height`): the pinned tree below uses the height, and tree.js
   measures the column the tree was given so that the next tree is folded to
   it. */
/* The tree and the record are one surface, not two windows side by side. The
   background and the rounding live here, once, and neither column carries a
   frame of its own: the frames that used to be there were drawing the edge of
   a scroll region, and there is no scroll region left to draw. */
/* Registered so it computes to a real length. `tree.js` has to know the
   narrowest the record column can get in order to work out the widest the tree
   column can get, and an unregistered custom property would hand it back the
   string "clamp(320px, 26vw, 460px)". Registering it here keeps the number in
   one place rather than copying it into the script. */
@property --panel-min {
  syntax: "<length>";
  inherits: true;
  initial-value: 320px;
}

.tree-split {
  --panel-min: clamp(320px, 26vw, 460px);
  /* Past this, a line of the record's prose is uncomfortable to read. */
  --panel-max: 560px;
  --split-gap: 1.5rem;
  background: var(--surface-raised);
  border-radius: var(--radius-lg);
  padding: 1rem;
  display: grid;
  /* The record keeps its place at the side whatever the tree draws (the
     operator's review, 2026-10-23): a record that followed the tree's width
     slid toward the middle of the page every time the tree moved onto a
     smaller family, and the page read as broken. The tree's column takes
     the rest, and a narrow tree stands in the middle of it. */
  grid-template-columns:
    minmax(0, 1fr)
    clamp(var(--panel-min), calc(30% - var(--split-gap)), var(--panel-max));
  gap: var(--split-gap);
  align-items: start;
}
.tree-split.no-panel { grid-template-columns: minmax(0, 1fr); }

bdi.date { unicode-bidi: isolate; }

/* ---------------------------------------------------------------------------
   Right-to-left
   ---------------------------------------------------------------------------
   Everything above is written with logical properties, so the page mirrors on
   its own from `dir="rtl"` alone — the grid above puts the panel on the left
   in Arabic without being told to, because a grid's column order follows the
   writing direction.

   Two things cannot follow from logical properties, because they are drawn on
   a coordinate system rather than laid out:

   1. The tree canvas. Cards carry `inset-inline-start`, so they mirror; the
      SVG connecting them is drawn in absolute canvas coordinates and does not.
      Reflecting the SVG about its own centre puts every wire back under the
      card it belongs to. The SVG carries no text, so nothing ends up
      backwards.
   2. The horizontal scroll position. A mirrored canvas starts at its right
      edge, which is where an RTL reader expects the beginning to be, and the
      browser handles that once `dir` is set on the document.

   Generations still stack vertically. Time runs bottom-to-top in both
   directions, because that is a vertical axis and mirroring is horizontal. */
[dir="rtl"] .tree-wires {
  transform: scaleX(-1);
  transform-origin: center;
}

/* The record grows to its full height and the page scroll carries it. It is
   the taller of the two columns on almost every person — the operator's own
   record is 2,295px against a 978px tree — which is exactly why the *tree* is
   the one that is pinned: pinning the panel instead would do nothing, because
   the tallest item in a grid row has no room to move inside it. */
.tree-panel {
  min-width: 0;
}
.panel-head {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  align-items: flex-start;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border-hairline);
  padding-bottom: 0.6rem;
  margin-bottom: 0.7rem;
}
.panel-name { margin: 0; font-size: var(--type-sub); line-height: 1.2; }
/* The portrait beside the name, on its line (v0.5.0): the record page's
   frame at a column's size. */
.panel-who { display: flex; align-items: center; gap: 0.8rem; min-inline-size: 0; }
.panel-who-text { min-inline-size: 0; }
.panel-avatar { inline-size: 64px; block-size: 64px; border-radius: var(--radius); }
.panel-avatar.is-initials { font-size: var(--type-sub); }
.panel-head .lede { margin: 0.2rem 0 0; }
.panel-actions { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.panel-empty { color: var(--text-tertiary); padding: 1rem 0; }

/* Setting an installation up (docs/adr/0005): three doors, side by side
   where there is room, each a section of its own; and how a GEDCOM was
   read, as a short list. */
.setup-doors {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  align-items: start;
}
.setup-door form { display: grid; gap: 0.6rem; }
.setup-door .field { margin: 0; }
.setup-next { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.gedcom-reading-list { margin: 0; padding-inline-start: 1.2rem; }
.gedcom-reading-list li + li { margin-block-start: 0.3rem; }

/* The record sections, re-flowed for a narrow column.
 *
 * The panel track is at most 460px, and inside its padding that is about
 * 426px of content. Every layout below assumed a page column two to three
 * times that: two-column grids, a date column beside an event column, a label
 * column beside a timeline track beside a meta column, and five-column
 * tables. None of them are wrong at page width, so none of them are changed
 * there — each is re-declared here, scoped to the panel, as a single column.
 *
 * What is *shown* never changes. A fact hidden because the column is narrow
 * would make the panel a different record from the page, and the panel is now
 * where most reading happens.
 */
.tree-panel .rec-section { margin: 0 0 1.1rem; }
.tree-panel .rec-section h2 { font-size: var(--type-body); margin-bottom: 0.4rem; }
.tree-panel .rec-section h3 { font-size: var(--type-minor); }
.tree-panel .grid.cols-2,
.tree-panel .grid.cols-3 { grid-template-columns: 1fr; gap: 0.6rem; }
.tree-panel .gallery { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); }
.tree-panel .raw-json { max-height: 40vh; }

/* A chip is `white-space: nowrap` so a label never breaks across lines. In a
   320px column — the clamp's floor, and the narrowest the panel ever gets —
   a long one would instead push the panel's own scrollbar sideways, so here
   it wraps. */
.tree-panel .chip { white-space: normal; }

/* Life events: the date column is 9rem at its narrowest, which leaves under
   270px for what happened. Stack them instead — the date reads as a heading
   over its event, which is the order they are read in anyway. */
.tree-panel .life-row {
  grid-template-columns: 1fr;
  gap: 0.15rem;
  padding-inline-start: 0.75rem;
}
.tree-panel .life-when { flex-direction: row; align-items: center; gap: 0.4rem; flex-wrap: wrap; }

/* Occupations: label, track and meta on three lines. A 160px label column
   here left about 150px of track, which is too little to compare spans on —
   full width restores the one thing the chart is for. */
.tree-panel .tl-row {
  grid-template-columns: 1fr;
  gap: 0.3rem;
  align-items: stretch;
}
.tree-panel .tl-meta { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 0.4rem; }

/* Other relationships and unions: trim the box padding, which at this width
   is a fifth of the line. */
.tree-panel .links-section { padding: 0.8rem 0.85rem; margin: 1rem 0; }
.tree-panel .union { padding: 0.7rem 0.8rem; }

/* Sources and documents: a five-column table in a 426px column either scrolls
   sideways or shreds every cell into one word per line. Stacked, each row
   becomes a small card of labelled lines — the labels are the `th` text,
   carried on `data-label`, so the two cannot disagree. */
.tree-panel table.stackable,
.tree-panel table.stackable tbody,
.tree-panel table.stackable tr,
.tree-panel table.stackable td { display: block; width: 100%; }
.tree-panel table.stackable thead {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}
.tree-panel table.stackable tr {
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
  padding: 0.4rem 0.6rem;
  margin-bottom: 0.5rem;
}
.tree-panel table.stackable tr:hover { background: var(--surface-page); }
.tree-panel table.stackable td {
  border-bottom: none;
  padding: 0.15rem 0;
}
.tree-panel table.stackable td[data-label]::before {
  content: attr(data-label);
  display: block;
  font-size: var(--type-micro);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
}

@media (max-width: 1000px) {
  /* Below laptop width the two columns stop working, so stack the panel under
     the tree rather than squeezing it into an unreadable sliver. One column
     also retires the clamp: the panel takes the full width it is given. */
  .tree-split { grid-template-columns: 1fr; }
  .tree-panel { width: 100%; }
  /* Stacked, the tree is above the record rather than beside it, and a pinned
     tree would take the whole screen and leave nowhere to read. */
  .tree-scroll { position: static; }
}

/* The structured editors.
 *
 * A record's lists — names, partners, children, participants, attachments —
 * edited as rows rather than through the raw-JSON box. The shape is the same
 * everywhere so that learning one form teaches the rest: a bordered group per
 * subject, a grid of labelled fields inside it, and one spare blank row at the
 * end because adding an entry has to work with scripting off. */
.editor { max-inline-size: 62rem; }
.ed-group {
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  padding: 1rem 1.1rem 1.1rem;
  margin: 0 0 1.1rem;
}
.ed-group > legend {
  font-weight: var(--weight-semi);
  padding: 0 0.4rem;
  color: var(--text-primary);
}
.ed-group h3 {
  margin: 1.1rem 0 0.2rem;
  font-size: var(--type-minor);
}
.ed-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.55rem 0.9rem;
  align-items: end;
}
.ed-grid > label {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: var(--type-small);
  color: var(--text-tertiary);
  min-inline-size: 0;
}
.ed-grid > label.inline {
  flex-direction: row;
  align-items: center;
  gap: 0.4rem;
  color: var(--text-primary);
  font-size: var(--type-minor);
}
.ed-grid > label.wide { grid-column: 1 / -1; }
.ed-grid input, .ed-grid select, .ed-grid textarea { inline-size: 100%; }
.ed-grid > label.inline input { inline-size: auto; }
/* One entry in a list of them. The rule between rows is what stops eight
   fields of one name reading as one field of eight names. */
.ed-row {
  padding: 0.7rem 0 0.75rem;
  border-block-start: 1px solid var(--border-hairline);
}
.ed-row:first-of-type { border-block-start: 0; }
.ed-row .hint { margin: 0.35rem 0 0; }
.ed-rows { inline-size: 100%; }
.ed-rows th {
  text-align: start;
  font-size: var(--label-size);
  text-transform: uppercase;
  letter-spacing: var(--label-track);
  color: var(--text-tertiary);
  font-weight: var(--weight-semi);
  padding-block-end: 0.3rem;
}
.ed-rows td { padding: 0.15rem 0.4rem 0.15rem 0; vertical-align: top; }
/* The type column has to fit "religious name" without the value column
   collapsing the select to two characters, which is what happened when the
   last column simply claimed everything. */
.ed-rows td:first-child { inline-size: 12rem; }
.ed-rows td:last-child { padding-inline-end: 0; inline-size: 100%; }
.ed-rows input, .ed-rows select { inline-size: 100%; }
/* A row that names a person: the name is what the row is about, and a picker
   two characters wide showed "Tomas" for every Tomasz in a union. The column
   takes the room; the small numbers beside it take what they need. */
.ed-rows.ed-people td:first-child { inline-size: 40%; min-inline-size: 14rem; }
.ed-rows.ed-people td:last-child { inline-size: auto; }
.ed-rows.ed-people select { min-inline-size: 9rem; }
/* Removing something, behind a disclosure rather than a button that toggles a
   hidden row with script: the same three policies the admin list explains, and
   it opens with scripting off. */
.ed-delete { margin-block-start: 0.8rem; }
.ed-files { list-style: none; margin: 0; padding: 0; }
.ed-files > li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 1rem;
  padding-block: 0.6rem; border-block-end: 1px solid var(--border-hairline);
}
.ed-files > li > span:first-child { flex: 1 1 16rem; min-width: 0; overflow-wrap: anywhere; }
.ed-files .ed-delete { margin-block-start: 0; }
.ed-delete > summary { display: inline-block; list-style: none; cursor: pointer; }
.ed-delete > summary::-webkit-details-marker { display: none; }
.ed-delete > summary::marker { content: ""; }
.ed-delete form {
  margin-block-start: 0.6rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--danger);
  border-radius: var(--radius);
}

.ed-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
  margin-block-start: 0.4rem;
}
/* Where an edit actually lands, when it is not this record. Removing a spouse
   changes the family both people share, and the other person's page with it;
   saying so beside the control is cheaper than a support question. */
.ed-elsewhere {
  border-inline-start: 3px solid var(--accent);
  padding: 0.5rem 0 0.5rem 0.7rem;
  margin: 0.6rem 0 0.9rem;
  font-size: var(--type-minor);
  color: var(--text-secondary);
}

@media (max-width: 640px) {
  .ed-rows, .ed-rows tbody, .ed-rows tr, .ed-rows td { display: block; inline-size: 100%; }
  .ed-rows thead { display: none; }
  .ed-rows td::before {
    content: attr(data-label);
    display: block;
    font-size: var(--type-label);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-tertiary);
  }
  .ed-rows tr { padding-block: 0.4rem; border-block-start: 1px solid var(--border-hairline); }
  .ed-rows.ed-people td:first-child,
  .ed-rows.ed-people td:last-child { inline-size: 100%; min-inline-size: 0; }
}

/* ---------- charts ---------- */

/* Three charts at the foot of the record. Confidence is carried three ways at
 * once — the bar along each spoke, the marker's shape, the band's colour — and
 * the table under each chart says it in words, so no single channel carries
 * it alone and none of the colour-blind themes loses it. Nothing here moves:
 * there is no animation to reduce. */
.radar-cards {
  display: grid;
  gap: calc(var(--step) * 2.25);
}
.radar-card {
  margin: 0;
  padding: calc(var(--step) * 1.6);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
  min-width: 0;
}
.radar-card h3 { margin: 0 0 0.4rem; }
/* The chart beside its table where there is room for both, the table under
   it where there is not. A chart the width of a phone and a table the width of
   a third of a desktop were both illegible. */
.radar-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: calc(var(--step) * 1.5);
  align-items: start;
}
@media (min-width: 60rem) {
  .radar-body { grid-template-columns: minmax(18rem, 25rem) minmax(0, 1fr); }
  .radar-body .radar-empty { grid-column: 1; }
  .radar-body .radar-table { grid-column: 2; grid-row: 1 / span 2; }
}
.radar-box {
  position: relative;
  aspect-ratio: 460 / 360;
  max-width: 100%;
  margin-block: 0.6rem 0.4rem;
}
.radar-box svg.radar {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  overflow: visible;
}
.radar-ring { fill: none; stroke: var(--border-hairline); stroke-width: 1; }
.radar-spoke { stroke: var(--border-hairline); stroke-width: 1; }
.radar-spoke.radar-absent {
  stroke: var(--border-strong);
  stroke-width: 1.5;
  stroke-dasharray: 2 4;
}
.radar-area { fill: var(--accent); fill-opacity: 0.14; stroke: none; }
.radar-outline { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.radar-band { stroke-width: 7; stroke-linecap: round; stroke: var(--conf-medium); }
.radar-band[data-band="certain"] { stroke: var(--conf-certain); }
.radar-band[data-band="high"] { stroke: var(--conf-high); }
.radar-band[data-band="low"] { stroke: var(--conf-low); }
.radar-point { stroke: var(--text-primary); stroke-width: 2; fill: none; }
.radar-point[data-band="certain"] { fill: var(--text-primary); }
.radar-point[data-band="low"] { stroke-dasharray: 2.4 2; }
.radar-point-core { fill: var(--text-primary); stroke: none; }

/* A label takes its own width, up to the room it has. Above and below the
   chart that is 8.5rem. Beside it, it is what lies between the label's spoke
   and the box's edge: 28 % of the box (`crate::radar::to_box` puts a side
   label at 28.2 % from its edge). Wider, a label crossed the box into the
   card's padding, and past it, onto the table beside the chart.
   A word too long for that room is hyphenated where the browser knows the
   language. Only a word of 13 letters or more is: below that, hyphenation
   split words that fitted on the next line, just to fill this one
   ("sta-bility"). Failing that, the body's `overflow-wrap: anywhere` breaks
   it, as a last resort. English "Conscien-tiousness" carries its own soft
   hyphen in en.ftl: Chrome does not hyphenate a capitalised English word. */
.radar-label {
  position: absolute;
  width: max-content;
  max-width: 8.5rem;
  font-size: var(--type-small);
  line-height: 1.2;
  color: var(--text-secondary);
  hyphens: auto;
  hyphenate-limit-chars: 13 4 4;
  pointer-events: none;
}
.radar-label[data-side="top"] { transform: translate(-50%, -100%); text-align: center; }
.radar-label[data-side="bottom"] { transform: translate(-50%, 0); text-align: center; }
/* Physical, not logical: a label to the right of the chart grows rightwards
   in Arabic too, because the chart does not mirror. */
.radar-label[data-side="right"] { transform: translate(0, -50%); text-align: left; max-width: 28%; }
.radar-label[data-side="left"] { transform: translate(-100%, -50%); text-align: right; max-width: 28%; }
.radar-label[data-absent] { color: var(--text-tertiary); font-style: italic; }
.radar-num {
  display: inline-block;
  min-width: 1.2em;
  font-weight: var(--weight-strong);
  font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
}
.radar-score { font-weight: var(--weight-strong); color: var(--text-primary); font-variant-numeric: tabular-nums; }

.radar-table { width: 100%; border-collapse: collapse; font-size: var(--type-minor); margin-top: 0.4rem; }
.radar-table td, .radar-table th { overflow-wrap: anywhere; }
.radar-table th, .radar-table td {
  text-align: start;
  vertical-align: top;
  padding: 0.35rem 0.4rem 0.35rem 0;
  border-block-start: 1px solid var(--border-hairline);
}
.radar-table thead th {
  border-block-start: none;
  font-size: var(--label-size);
  text-transform: uppercase;
  letter-spacing: var(--label-track, 0.04em);
  color: var(--text-tertiary);
  font-weight: var(--weight-semi);
}
.radar-table tbody th { font-weight: var(--weight-semi); }
.radar-table tr[data-absent] th { color: var(--text-tertiary); font-weight: var(--weight-medium); }
.radar-conf { display: block; font-size: var(--type-small); color: var(--text-secondary); }
.radar-facts { margin: 0; padding: 0; list-style: none; }
.radar-facts li + li { margin-top: 0.2rem; }
.radar-fact-label { color: var(--text-secondary); }
.radar-fact-label::after { content: ":"; }
/* The marker again, in the table: the same four shapes, drawn in CSS. */
.radar-mark {
  display: inline-block;
  width: 0.7rem; height: 0.7rem;
  margin-inline-end: 0.3rem;
  vertical-align: -0.05rem;
  border-radius: 50%;
  box-sizing: border-box;
  border: 2px solid var(--text-primary);
}
.radar-mark[data-band="certain"] { background: var(--text-primary); }
.radar-mark[data-band="high"] { background: radial-gradient(circle, var(--text-primary) 0 32%, transparent 34%); }
.radar-mark[data-band="low"] { border-style: dashed; }

.radar-fold > summary {
  cursor: pointer;
  display: inline-block;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--text-primary);
}
.radar-fold[open] > summary { margin-bottom: 0.4rem; }

@media (max-width: 34rem) {
  /* At phone width the names go to the table beneath; the chart keeps each
     axis's number and score, which is what fits beside a spoke. */
  .radar-label .radar-name { display: none; }
  .radar-table thead { display: none; }
  .radar-table, .radar-table tbody, .radar-table tr,
  .radar-table th, .radar-table td { display: block; width: 100%; }
  .radar-table tr { border-block-start: 1px solid var(--border-hairline); padding-block: 0.3rem; }
  .radar-table th, .radar-table td { border: none; padding: 0.1rem 0; }
}

/* ---------- identity page ---------- */

/* The record's masthead: who this person was, at a glance.
 *
 * A grid rather than a flex row, so the avatar keeps its column and the text
 * beside it wraps within its own — with `flex-wrap` a long Polish given-name
 * chain pushed the edit button onto its own line and left the avatar orphaned
 * beside a gap. */
.person-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 1rem 1.15rem;
  margin-bottom: 1.25rem;
}
.person-head-text { min-width: 0; }
.person-head h1 { margin: 0; line-height: 1.15; }
.person-head-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }

/* The photograph, or the initials standing in for one.
 *
 * Both are the same size and shape so the heading beside them sits in the same
 * place either way — on a converted bundle most people have no photograph, and
 * a masthead that jumped depending on whether one exists would make the
 * commonest case look like the broken one. */
.person-avatar {
  inline-size: 112px;
  block-size: 112px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: grid;
  place-items: center;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  flex: none;
}
.person-avatar img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.person-avatar.is-initials {
  /* Deliberate, not a placeholder for a missing thing: the theme's own accent
     at low strength, and the person's own initials rather than a stock glyph of
     somebody who is not them. */
  background: var(--accent-soft);
  border-color: var(--border-strong);
  color: var(--accent);
  font-size: var(--type-display);
  font-weight: var(--weight-semi);
  letter-spacing: 0.02em;
  cursor: default;
}
.person-akas {
  margin: 0.25rem 0 0;
  color: var(--text-secondary);
  font-size: var(--type-minor);
}
.person-vitals { margin: 0.35rem 0 0; font-size: var(--type-body); }
.person-vitals .sep { color: var(--text-tertiary); }
/* A life's two dates stay together, each on its own line only when the
   line is too short for both (2026-10-25: at phone width the pill broke
   each date in two). */
.person-vitals .pv-dates { flex-wrap: wrap; row-gap: 0; }
.person-vitals .pv-dates > * { white-space: nowrap; }
@media (max-width: 640px) { .person-vitals .pv-dates { border-radius: 14px; } }
.person-head .lede { margin: 0.3rem 0 0; }
.person-placing {
  margin: 0.3rem 0 0;
  /* A converted place name can be a whole sentence — one of the operator's is
     "Niedźwiada w Galicji, obecnie powiat ropczycki, parafia Łączki
     Kucharskie. Ale w akcie zgonu jest Tarnów…". The masthead states where
     somebody was born; it is not the place to print the parish clerk's note,
     which the Places section shows in full. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The portraits an AI reworked (docs/adr/0009): a band of its own in the
   masthead, between who the person was and the edit button. Every picture
   carries the AI's mark and a ring that turns slowly where motion is
   allowed — the one moving thing in a masthead of facts, so that nobody
   takes these for photographs of the time. */
.person-head.has-ai { grid-template-columns: auto minmax(0, 1fr) auto auto; }
.person-ai {
  display: grid;
  justify-items: start;
  align-content: start;
  gap: 0.55rem;
  min-inline-size: 0;
  max-inline-size: 22rem;
}
.person-ai-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.ai-chip {
  padding: 0.1em 0.55em;
  border-radius: 999px;
  background: linear-gradient(120deg, var(--accent), color-mix(in srgb, var(--hue-5) 70%, var(--accent)));
  color: var(--on-accent);
  font-weight: var(--weight-strong);
  letter-spacing: 0.08em;
}
.person-ai-list { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.person-ai-shot,
.ai-frame {
  position: relative;
  display: block;
  inline-size: 88px;
  block-size: 88px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  isolation: isolate;
  background: var(--surface-sunken);
}
.ai-frame { inline-size: 140px; block-size: 140px; }
.person-ai-shot img,
.ai-frame img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
/* The ring: a conic gradient through the accent and two of the crossing
   hues, cut down to its border by a mask. */
@property --ai-turn {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.person-ai-shot::after,
.ai-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  padding: 2px;
  border-radius: inherit;
  background: conic-gradient(from var(--ai-turn), var(--accent), var(--hue-5), var(--hue-1), var(--accent));
  -webkit-mask: linear-gradient(currentColor 0 0) content-box, linear-gradient(currentColor 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(currentColor 0 0) content-box, linear-gradient(currentColor 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.ai-badge {
  position: absolute;
  inset-block-end: 5px;
  inset-inline-end: 5px;
  z-index: 2;
  padding: 0.05em 0.45em;
  border-radius: 4px;
  background: color-mix(in srgb, var(--surface-page) 72%, transparent);
  color: var(--accent);
  font-size: var(--type-micro);
  font-weight: var(--weight-strong);
  letter-spacing: 0.06em;
  backdrop-filter: blur(4px);
}
.person-ai-note { margin: 0; max-inline-size: 22rem; }
@media (prefers-reduced-motion: no-preference) {
  .person-ai-shot::after,
  .ai-frame::after { animation: ai-turn 9s linear infinite; }
  .person-ai-shot img { transition: transform var(--tempo) var(--ease); }
  .person-ai-shot:hover img,
  .person-ai-shot:focus-visible img { transform: scale(1.06); }
}
@keyframes ai-turn { to { --ai-turn: 360deg; } }

/* The page that manages them. */
.ai-grid { display: flex; flex-wrap: wrap; gap: 1rem; margin: 0 0 1rem; padding: 0; list-style: none; }
.ai-card { display: grid; gap: 0.4rem; justify-items: start; max-inline-size: 140px; overflow-wrap: anywhere; }

/* The AFG on the record (docs/adr/0029): the face restored under the band's
   upload, a photograph restored or read under its picture, the summary
   above Identity, and the notes kept up on the Médias tab. */
.person-ai-rework { margin: 0; }
/* Canva's account, in the AFG room (docs/adr/0030). */
.afg-canva { display: grid; gap: 0.6rem; margin-block-start: 1rem; padding-block-start: 0.85rem; border-block-start: 1px solid var(--border-hairline); }
.afg-canva p { margin: 0; }
.afg-canva-steps { display: grid; gap: 0.85rem; }
.afg-canva-steps form { display: grid; gap: 0.4rem; justify-items: start; margin: 0; }
.afg-canva-steps input { inline-size: 100%; }
/* A sentence, not a field's name: in the reading text's case and face. */
.afg-canva-paste { text-transform: none; letter-spacing: normal; font-family: inherit; font-size: var(--type-body); color: var(--text-primary); }
.afg-canva-connect { display: inline-block; margin: 0; }
.person-summary .sec-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.summary-text { max-inline-size: 72ch; }
.summary-text > :first-child { margin-block-start: 0; }
.summary-text > :last-child { margin-block-end: 0; }
.summary-by { margin: 0.5rem 0 0; }
.summary-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-block-start: 0.85rem; }
.summary-add { margin-block: 0.25rem 1.25rem; }
.summary-add .summary-actions { margin-block-start: 0; }
.summary-ai { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0; }
.summary-lang { display: flex; align-items: center; gap: 0.45rem; }
.summary-lang .field-label { font-size: var(--type-minor); color: var(--text-secondary); white-space: nowrap; }
.shot.is-ai > a { position: relative; display: block; }
.shot-ai { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-block-start: 0.4rem; }
.shot-ai form,
.doc-ai { margin: 0; }
.doc-ai { display: inline-block; margin-inline-start: 0.5rem; }
.note-card.is-kept {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
  transition: border-color var(--tempo, 140ms) ease;
}
.note-card.is-kept:has(.note-card-in:hover) { border-color: var(--accent); }
.note-card.is-kept .note-card-in { border: 0; border-radius: 0; background: none; }
.note-card.is-kept .note-card-in:hover { transform: none; }
.note-card-mark { display: flex; align-items: center; gap: 0.4rem; padding: 0 0.95rem 0.6rem; font-size: var(--type-minor); color: var(--text-secondary); }
.note-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-block-start: auto;
  padding: 0.6rem 0.95rem 0.8rem;
  border-block-start: 1px solid var(--border-hairline);
}
.note-card-actions form { margin: 0; }

/* Narrower, the band goes under the facts rather than squeezing them. */
@media (max-width: 1100px) {
  .person-head.has-ai { grid-template-columns: auto minmax(0, 1fr) auto; }
  .person-head.has-ai .person-ai { grid-column: 2; grid-row: 2; }
  .person-head.has-ai .person-head-actions { grid-column: 3; grid-row: 1; }
}

@media (max-width: 640px) {
  /* The actions move under the text rather than squeezing a third column into
     a phone. */
  .person-head { grid-template-columns: auto minmax(0, 1fr); }
  .person-head-actions { grid-column: 1 / -1; }
  .person-avatar { inline-size: 76px; block-size: 76px; }
  .person-avatar.is-initials { font-size: var(--type-title); }
  .person-head.has-ai { grid-template-columns: auto minmax(0, 1fr); }
  .person-head.has-ai .person-ai { grid-column: 1 / -1; grid-row: auto; }
  .person-head.has-ai .person-head-actions { grid-column: 1 / -1; grid-row: auto; }
}

/* The record's Statut tab (the operator's request of 5 November 2026): what
   is worth checking, a line each with the way to correct it, and what is
   worth knowing, as short labels. The tree's panel keeps one line. */
.status-line { margin: 0.4rem 0 1rem; font-size: var(--type-minor); }
.status-line a { color: var(--accent); }
.status-h { font-size: var(--type-sub); margin: 0.2rem 0 0.6rem; }
.status-list { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: 0.5rem; }
.status-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-raised);
}
.status-text { display: grid; gap: 0.15rem; min-width: 0; }
.status-detail { color: var(--text-secondary); font-size: var(--type-minor); overflow-wrap: anywhere; }
.status-know { margin: 0 0 1rem; padding-inline-start: 1.1rem; display: grid; gap: 0.25rem; color: var(--text-secondary); }

.chip {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--text-secondary);
  border-radius: 100px;
  padding: 0.1rem 0.6rem;
  font-size: var(--type-small);
  white-space: nowrap;
}

/* Gender, living status and visibility.
 *
 * These were a run of small-caps labels interleaved with their values, which
 * at panel width read as one string — "GENDER Female LIVING yes VISIBILITY
 * members" — because nothing separated a label from the value before it. Each
 * fact is now a bordered box with the label stacked above the value, so the
 * eye separates them without having to read them, and each box wraps as a
 * unit instead of the row becoming a paragraph.
 */
.fact-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.9rem 0 0;
}
.fact-chips .fc {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
  padding: 0.25rem 0.6rem;
}
.fact-chips dt {
  font-size: var(--type-micro);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
  line-height: 1.4;
}
.fact-chips dd { margin: 0; font-size: var(--type-minor); line-height: 1.35; }
.fact-chips .fc.wide { flex: 1 1 100%; }

ul.plain { list-style: none; padding: 0; margin: 0.25rem 0 0; }
ul.plain > li { padding: 0.35rem 0; border-bottom: 1px solid var(--border-hairline); }
ul.plain > li:last-child { border-bottom: none; }

li.rel { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* Sources, badged by how strong the evidence is. */
.src-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: var(--type-small);
  border: 1px solid var(--border-strong);
  border-radius: 100px;
  padding: 0.05rem 0.55rem;
  margin-top: 0.2rem;
  max-width: 100%;
}
.src-rel {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--type-micro);
  font-weight: var(--weight-semi);
  color: var(--text-tertiary);
}
.src-chip[data-rank="4"] { border-color: var(--conf-certain); }
.src-chip[data-rank="4"] .src-rel { color: var(--conf-certain); }
.src-chip[data-rank="3"] { border-color: var(--conf-high); }
.src-chip[data-rank="3"] .src-rel { color: var(--conf-high); }
.src-chip[data-rank="2"] { border-color: var(--conf-medium); }
.src-chip[data-rank="2"] .src-rel { color: var(--warning); }
.src-chip[data-rank="1"] { border-color: var(--conf-low); }
.src-chip[data-rank="1"] .src-rel { color: var(--conf-low); }
.src-title { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Non-family links: the clearest GEDCOM gap, so visually distinct from the
   family lists. */
.links-section {
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem;
  margin: 1.5rem 0;
  background: var(--surface-raised);
}
.links-section > h2 { margin-top: 0; }
.link-card {
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 0.7rem 0.85rem;
  background: var(--surface-page);
}
.link-card[data-dir="incoming"] { border-inline-start-style: dashed; }
.link-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.link-label { font-weight: var(--weight-semi); text-transform: capitalize; }
.link-who { margin: 0.2rem 0; }

/* Occupations as spans. */
.timeline { margin-top: 0.75rem; }
.tl-row {
  display: grid;
  grid-template-columns: minmax(160px, 220px) 1fr minmax(120px, auto);
  gap: 0.75rem;
  align-items: center;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border-hairline);
}
.tl-label { display: flex; flex-direction: column; }
.tl-track {
  position: relative;
  height: 22px;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: 4px;
  min-width: 120px;
}
.tl-bar {
  position: absolute;
  top: 3px;
  bottom: 3px;
  border-radius: 3px;
  background: var(--conf-medium);
  min-width: 3px;
}
.tl-bar[data-band="certain"] { background: var(--conf-certain); }
.tl-bar[data-band="high"] { background: var(--conf-high); }
.tl-bar[data-band="medium"] { background: var(--conf-medium); }
.tl-bar[data-band="low"] {
  background: repeating-linear-gradient(90deg,
    var(--conf-low) 0 4px, transparent 4px 8px);
}
/* An unrecorded bound fades out rather than asserting an end date. */
.tl-open { position: absolute; top: 0; bottom: 0; width: 18px; }
/* `-l` and `-r` are the *earlier* and *later* ends of the span, not the left
   and right of the screen: in an RTL layout the axis runs the other way and
   both swap with it. `to right`/`to left` in the gradient are logical in the
   same sense — they follow the writing direction, so the fade always points
   away from the bar. */
.tl-open-l {
  inset-inline-start: 0;
  background: linear-gradient(to right, var(--surface-raised), transparent);
}
.tl-open-r {
  inset-inline-end: 0;
  background: linear-gradient(to left, var(--surface-raised), transparent);
}
[dir="rtl"] .tl-open-l { background: linear-gradient(to left, var(--surface-raised), transparent); }
[dir="rtl"] .tl-open-r { background: linear-gradient(to right, var(--surface-raised), transparent); }
.tl-meta { display: flex; flex-direction: column; gap: 0.2rem; align-items: flex-start; }
.tl-axis {
  display: flex;
  justify-content: space-between;
  font-size: var(--type-label);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
  padding-top: 0.3rem;
}

.prose { white-space: pre-wrap; max-width: 72ch; }
/* The AFG's answer (docs/adr/0023): a model's Markdown drawn as a note's
   is, an address in it wrapped rather than widening the page. */
.afg-answer { overflow-wrap: anywhere; }

/* A test's result over the room (docs/adr/0024), drawn as a note's window.
   Open in place without scripting; modal, on its backdrop, with it. */
.afg-dialog {
  inline-size: min(46rem, calc(100vw - 2rem));
  max-block-size: calc(100vh - 3rem);
  padding: 0;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  color: var(--text-primary);
  box-shadow: 0 24px 60px -24px rgb(var(--shadow-rgb, 0 0 0) / 0.55);
  z-index: 50;
}
/* A window over the page leaves the page behind it dim and out of focus,
   so that what it holds is read in comfort (the operator's review of
   8 November 2026): the notes', the AFG test's and Impact's. */
:is(.note-dialog, .afg-dialog, .ip-dialog)::backdrop {
  background: rgb(var(--shadow-rgb, 0 0 0) / 0.62);
  -webkit-backdrop-filter: blur(5px) saturate(0.85);
  backdrop-filter: blur(5px) saturate(0.85);
}
.afg-dialog-body { max-block-size: calc(100vh - 8rem); overflow: auto; padding: clamp(1rem, 3vw, 1.75rem); }
.afg-dialog-foot { display: flex; justify-content: flex-end; padding: 0.75rem clamp(1rem, 3vw, 1.75rem); border-block-start: 1px solid var(--border-hairline); }
.afg-result-title { margin-block: 0 0.25rem; }
/* Success or error, said as the window's title rather than as a tag. */
.afg-result-title .lvl { font-size: 1em; }
.afg-result-title .lvl::before { inline-size: 0.85rem; block-size: 0.85rem; }
.afg-result-question { margin-block: 0.75rem 1rem; }
.afg-remembered { margin-block-start: 1rem; padding: 0.6rem 0.9rem; border-inline-start: 3px solid var(--accent); }
.afg-remembered p { margin: 0 0 0.3rem; }
.afg-remembered ul { margin: 0; padding-inline-start: 1.2rem; }

/* An account's AFG brain on its own page (docs/adr/0024 §6): a fact or an
   exchange a line, and the button that forgets it at its end. */
.brain-list { list-style: none; margin: 0.4rem 0 1.2rem; padding: 0; }
.brain-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; padding-block: 0.55rem; border-block-end: 1px solid var(--border-hairline); }
.brain-row:last-child { border-block-end: 0; }
.brain-text { min-inline-size: 0; overflow-wrap: anywhere; }
.brain-answer { margin-block-start: 0.25rem; }
.brain-answer summary { cursor: pointer; font-size: var(--type-small); color: var(--text-secondary); }
.brain-answer .note-text { margin-block-start: 0.4rem; }
.brain-forget-all { margin-block-start: 1rem; }
.brain-forget-all form { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-block-start: 0.6rem; }
.muted-prose { color: var(--text-secondary); font-size: var(--type-minor); }

@media (max-width: 700px) {
  .tl-row { grid-template-columns: 1fr; }
}

/* ---------- admin ---------- */

.filter-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
  margin: 1rem 0;
}
.filter-row input[type="text"] { max-width: 22rem; }
.filter-row .spacer { flex: 1 1 auto; }


.delete-row { background: var(--surface-page); }
.policy-list { display: flex; flex-direction: column; gap: 0.3rem; margin: 0.6rem 0; }
.policy-list label {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: var(--type-minor);
  color: var(--text-secondary);
  margin: 0;
}
.policy-list strong { color: var(--text-primary); }

label.inline { display: flex; align-items: center; gap: 0.45rem; }
label.inline input[type="checkbox"] { width: auto; margin: 0; }

input[type="range"] { width: 100%; max-width: 22rem; accent-color: var(--accent); }

.pager { display: flex; align-items: center; gap: 1rem; margin: 1.25rem 0; }

a.stat:hover { border-color: var(--accent); }

/* ---------- tree controls ---------- */

.tree-controls {
  display: flex;
  gap: 0.75rem;
  align-items: flex-end;
  flex-wrap: wrap;
  margin: 1rem 0 0.25rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
}
.tc-field { display: flex; flex-direction: column; gap: 0.2rem; }
.tc-field label {
  margin: 0;
  font-size: var(--label-size);
  text-transform: uppercase;
  letter-spacing: var(--label-track);
  color: var(--text-tertiary);
}
.tc-field select { min-width: 12rem; }
#depth { min-width: 5rem; }
/* The legend in the controls' bar (2026-10-28): one band above the chart,
   the depth at its start and the key after it, with no frame of its own. */
.tree-controls { align-items: center; gap: 0.6rem 1.5rem; }
.tree-controls .tree-legend {
  flex: 1 1 30rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  gap: 0.45rem 1rem;
}


/* ---------- completeness readout ---------- */

.completeness {
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-raised);
  padding: 1.1rem 1.25rem;
  margin: 1.5rem 0;
}
.completeness > h2 { margin-top: 0; }
.completeness > h3 { margin-top: 1.5rem; }

.comp-table td { vertical-align: top; }
.comp-table code { font-size: 0.82em; word-break: break-word; }
.comp-count { font-variant-numeric: tabular-nums; font-size: var(--type-minor); }

/* A row the bundle populates reads as normal; an empty one is dimmed, so the
   shape of what is missing is visible at a glance. */
.comp-has .comp-count { color: var(--conf-certain); font-weight: var(--weight-semi); }
.comp-empty { background: transparent; }
.comp-empty .comp-count { color: var(--text-tertiary); }

/* Under the count, on a line of its own: the cell does not wrap, so a tag
   beside the count ran past the column (v0.5.0). */
.comp-tag {
  display: block;
  inline-size: fit-content;
  margin-top: 0.25rem;
  font-size: var(--type-label);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--warning);
  border: 1px solid var(--conf-medium);
  border-radius: 3px;
  padding: 0 0.35em;
  white-space: normal;
  max-width: 11rem;
}
.comp-tag.empty-tag { color: var(--text-tertiary); border-color: var(--border-strong); }

.date-shapes .stat .k { text-transform: none; font-weight: var(--weight-semi); color: var(--text-secondary); }

/* ---------- identity page: sections ---------- */

/* The tabs.
 *
 * The page used to open with an index of anchor links — a table of contents
 * for a document nobody reads top to bottom. Somebody opening a person wants
 * their life, or their photographs, or where they sit in the family; those are
 * three questions, not three parts of one answer.
 *
 * A row of links, so a tab is a URL and the back button works. The current one
 * is marked by weight and a rule under it rather than by colour alone, because
 * colour alone is not a distinction every reader can make. */
.person-tabs {
  /* Study 06, B4: no boxes; one row that scrolls sideways and never wraps,
     whatever the width or the language. Logical properties throughout, so
     right-to-left mirrors the order and the underline stays where it is. */
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  gap: 0.25rem;
  margin: 1.25rem 0 0;
  /* The hairline is drawn inside the strip, not as its border: a strip that
     scrolls clips what hangs outside it, and the current tab's 2px rule has
     to sit over the hairline, whole. */
  box-shadow: inset 0 -1px 0 var(--border-hairline);
}
.person-tab {
  flex: none;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.55rem 0.7rem;
  white-space: nowrap;
  color: var(--text-secondary);
  text-decoration: none;
  border: 0;
  border-block-end: 2px solid transparent;
  font-size: var(--type-minor);
  /* The label is bolder when current. Reserving the bold metrics up front
     stops the row from reflowing as the reader moves between tabs. */
  font-weight: var(--weight-semi);
}
.person-tab:hover { color: var(--text-primary); }
/* Current: full ink and a 2px amber rule. */
.person-tab.is-current {
  color: var(--text-primary);
  border-block-end-color: var(--accent);
}
/* A tab with nothing in it is not drawn (v0.4.1): there is no empty
   state to style. */
.person-tabs + * { margin-top: 1.25rem; }
/* The strip's arrows and its drag (ui.js): an arrow shows on a side where
   more tabs wait, for a pointer that has no sideways wheel. */
.tabs-wrap { position: relative; margin-block-start: 1.25rem; }
.tabs-wrap > .person-tabs { margin-block-start: 0; }
.tabs-wrap + * { margin-top: 1.25rem; }
.tabs-arrow {
  position: absolute;
  inset-block: 0;
  margin-block: auto;
  inline-size: 2rem;
  block-size: 2rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--surface-raised);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--type-body);
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  box-shadow: 0 2px 8px rgb(var(--shadow-rgb) / 0.3);
}
.tabs-arrow[hidden] { display: none; }
.tabs-arrow-left { left: 0.2rem; }
.tabs-arrow-right { right: 0.2rem; }
.tabs-arrow:hover { border-color: var(--accent); color: var(--accent); }
.person-tabs.is-dragging { cursor: grabbing; user-select: none; scroll-behavior: auto; }

/* The tree tab: the same canvas the tree page draws, in a box that scrolls
   rather than one that pushes the rest of the page sideways. */
.person-tree {
  position: relative;
  margin: 1.25rem 0 0.5rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
  overflow: hidden;
}
.person-tree .tree-scroll {
  overflow: auto;
  max-height: 34rem;
  padding: 0.5rem;
}

/* Kept for the tree side panel, which still lists its sections. */
.section-index {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  margin: 1.25rem 0;
  padding: 0.6rem 0.85rem;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  font-size: var(--type-minor);
}
.section-index a { color: var(--text-secondary); text-decoration: none; }
.section-index a:hover { color: var(--accent); text-decoration: underline; }

main > section { scroll-margin-top: 1rem; }

/* A section heading, and where its explanation goes.
 *
 * The standalone page prints the explanation under the heading. The panel
 * gives the same words a `?` next to the heading and opens them on its own
 * line, because at 460px four lines of prose per section *are* the section.
 */
.sec-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.sec-head h2 { margin-bottom: calc(var(--step) * 0.55); }
/* A section heading anchors its section. It was small, grey and the same
   weight as the labels under it, so the eye had nothing to find. */
.rec-section .sec-head h2 {
  color: var(--text-primary);
  font-weight: var(--h2-weight);
}
.sec-help { font-size: var(--type-minor); }
.sec-help > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  color: var(--text-tertiary);
  font-size: var(--type-label);
  line-height: 1;
  cursor: help;
  list-style: none;
  user-select: none;
}
.sec-help > summary::-webkit-details-marker { display: none; }
.sec-help > summary:hover,
.sec-help[open] > summary { border-color: var(--accent); color: var(--accent); }
/* Opened, the text takes the full row rather than squeezing in beside the
   heading. */
.sec-help[open] { flex: 1 0 100%; }
.sec-help p { margin: 0.3rem 0 0.2rem; }

/* Names: the native script leads, the transliteration sits beside it. Two
   fields, both shown, because collapsing them is the thing GEDCOM does. */
table.names td { vertical-align: top; }
.name-native { font-weight: var(--weight-semi); }
.name-latin {
  margin-inline-start: 0.5rem;
  color: var(--text-secondary);
  font-style: italic;
}
.name-latin::before { content: "≈ "; color: var(--text-tertiary); font-style: normal; }

/* One name as a block: a heading with a small type label under it, which is
   what a single name deserves. The four-column table said one fact in four
   columns, three of them empty, and spent half the panel doing it. */
.name-stack { display: flex; flex-direction: column; margin: 0.3rem 0 0; }
.name-block > p { margin: 0.25rem 0 0; }
.name-block > p:first-child { margin-top: 0; }
.name-block + .name-block {
  border-top: 1px solid var(--border-hairline);
  margin-top: 0.65rem;
  padding-top: 0.65rem;
}
.nb-display {
  font-size: var(--type-body);
  font-weight: var(--weight-semi);
  line-height: 1.25;
  overflow-wrap: break-word;
}
.nb-latin {
  color: var(--text-secondary);
  font-style: italic;
  font-size: var(--type-minor);
  overflow-wrap: break-word;
}
.nb-latin::before { content: "≈ "; color: var(--text-tertiary); font-style: normal; }
.nb-kind {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.3rem;
}
.nb-type {
  font-size: var(--type-label);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
}
.nb-evidence {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

/* The parts of a name.
 *
 * "given name: Laura · family name: Karin" was one text run, so a narrow
 * column broke it wherever it ran out of room — including between a label and
 * the value it belongs to. Each part is now its own inline-flex box with the
 * separator inside it, so the line can only break between parts, and a
 * separator can never begin a line. */
.name-parts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.55rem;
  margin: 0.35rem 0 0;
  font-size: var(--type-minor);
}
.name-part {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  max-width: 100%;
}
.name-part:not(:last-child)::after {
  content: "·";
  color: var(--text-tertiary);
}
.np-k {
  font-size: var(--type-micro);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
  white-space: nowrap;
}
.np-v { color: var(--text-secondary); overflow-wrap: anywhere; }

/* A phrase that should wrap as a whole — "From 12 June 1919", "until 1955" —
   rather than leaving a preposition stranded on the line above its date. */
.wrap-unit { display: inline-block; }
.union-when { display: flex; flex-wrap: wrap; gap: 0.15rem 0.5rem; }

/* Life timeline: one column of dates, one of what happened. */
ol.life { list-style: none; padding: 0; margin: 0.75rem 0 0; }
.life-row {
  display: grid;
  grid-template-columns: minmax(9rem, 15rem) 1fr;
  gap: 0.5rem 1rem;
  padding-block: 0.65rem;
  padding-inline: 0.9rem 0;
  border-inline-start: 2px solid var(--border-strong);
  position: relative;
}
.life-row::before {
  content: "";
  position: absolute;
  inset-inline-start: -5px;
  top: 1.05rem;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--border-strong);
}
.life-row[data-kind="birth"]::before { background: var(--conf-certain); }
.life-row[data-kind="death"]::before { background: var(--text-tertiary); }
.life-when { display: flex; flex-direction: column; gap: 0.2rem; }
@media (max-width: 34rem) {
  .life-row { grid-template-columns: 1fr; }
}

/* A union is a block of its own: the partner, how it stood, its children. */
.union {
  margin-top: 1rem;
  padding: 0.85rem 1rem;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
}
/* One marriage of a polygamous union (docs/adr/0021): a double rule down its
   leading edge, as the tree gives it a knot of three rings. */
.union.is-polygamous { border-inline-start: 4px double var(--border-strong); }
/* A family's emblem (docs/adr/0022): a coat of arms is a whole shape, so it
   is shown whole, on the surface it was drawn for. */
.union-emblem, .masthead-emblem, .hero-emblem {
  object-fit: contain;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  flex: none;
}
.union-emblem { inline-size: 44px; block-size: 44px; float: inline-end; margin-inline-start: 0.5rem; }
.masthead-emblem { inline-size: 30px; block-size: 30px; margin-inline-start: 0.5rem; padding: 2px; }
.hero-emblem { display: block; inline-size: 104px; block-size: 104px; padding: 4px; }
/* The overview's hero is a grid of named areas: the emblem stands in its
   corner, on the side the lines end, and the title keeps clear of it. On a
   narrow screen it joins the chip's row, smaller. */
.hero .hero-emblem {
  position: absolute;
  inset-block-start: calc(var(--step) * 5);
  inset-inline-end: calc(var(--step) * 4);
  z-index: 1;
}
.hero:has(.hero-emblem) .hero-title { padding-inline-end: 120px; }
@media (max-width: 860px) {
  .hero .hero-emblem {
    position: static;
    grid-area: chip;
    justify-self: end;
    inline-size: 52px;
    block-size: 52px;
  }
  .hero:has(.hero-emblem) .hero-title { padding-inline-end: 0; }
  .hero:has(.hero-emblem) .eyebrow-chip { max-inline-size: calc(100% - 64px); }
}
/* At phone width the masthead is a grid of named areas: the archive's
   emblem takes a column of its own between the name and the account. */
@media (max-width: 640px) {
  .masthead .masthead-inner:has(> .masthead-emblem) {
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    grid-template-areas: "mark brand emblem account" "nav nav nav nav" "tools tools tools tools";
  }
  .masthead .masthead-inner > .masthead-emblem { grid-area: emblem; margin-inline-start: 0; }
}
.manifest-emblem { display: flex; align-items: center; gap: 0.75rem; }
.manifest-emblem .union-emblem { float: none; margin: 0; }
/* Two Family records for one couple.

   The bracket is what makes the two blocks one thing: without it they are two
   union cards with the same spouse in both, which reads as a second marriage —
   the wrong reading, and the first one a reader reaches. The notice inside it
   says what they actually are.

   Every value here is an existing token. `--border-strong` is the one tuned to
   the 3:1 WCAG 1.4.11 asks of a boundary, and this is a boundary; the notice
   takes `--conf-medium`, which `.notice.warn` already uses and the sweep
   already measures. Nothing new to measure. */
.union-group.is-duplicate {
  margin-top: 1rem;
  padding: 0.25rem 0.85rem 0.85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
}
.union-group.is-duplicate > .union:first-of-type { margin-top: 0; }
/* Paper has abolished cards: a section there is a rule and some room, which is
   what a printer does to a card anyway. A rounded box holding two more rounded
   boxes is the one thing on that page still pretending to be a card, so the
   bracket says the same thing in paper's own vocabulary — a rule down the
   inline-start edge. Same token, so the contrast sweep does not move. */
[data-style="paper"] .union-group.is-duplicate {
  border: 0;
  border-inline-start: 2px solid var(--border-strong);
  border-radius: 0;
  padding: 0 0 0 1rem;
}
/* The action sits under the sentence, inside the notice that explains it. */
.notice-defect p { margin: 0; }
.notice-defect .inline-form { margin-top: 0.6rem; }

.union-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.union h4 { margin: 0.85rem 0 0.25rem; font-size: var(--type-minor); color: var(--text-secondary); }
.union-status[data-status="ongoing"] { color: var(--conf-certain); }
.union-status[data-status="not recorded"] { color: var(--text-tertiary); }
.birth-order {
  display: inline-block;
  min-width: 1.4em;
  text-align: center;
  font-size: var(--type-label);
  color: var(--text-tertiary);
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  font-variant-numeric: tabular-nums;
}

/* The family across (the operator's review of 22 October 2026): rows of
   cards, a relative per card, the whole card the link. The parents and the
   siblings are two groups side by side; each union is a block whose head is
   the partner and the union, and whose children are a row of their own. */
.fam-band {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 0.6rem 1.25rem;
}
.fam-title {
  margin: 0 0 0.5rem;
  font-size: var(--type-minor);
  font-weight: var(--weight-semi);
  color: var(--text-secondary);
}
.union .fam-title { margin-block: 0.9rem 0.5rem; }
.fam-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13.5rem), 1fr));
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.fam-block-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.25rem;
}
.fam-partner { flex: 0 1 17rem; min-inline-size: min(100%, 13.5rem); grid-template-columns: 1fr; }
.fam-union-facts { flex: 1 1 14rem; display: grid; gap: 0.3rem; min-inline-size: 0; }
.fam-union-kind { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; }
/* The parents and the siblings framed like a union (the operator's review
   of 8 November 2026): each block's name on the trailing side of its head,
   a union's its kind; the parents and the siblings one under the other. */
.fam-block { padding: 0.8rem 0.9rem; background: var(--surface-page); border: 1px solid var(--border-hairline); border-radius: var(--radius); }
.fam-block > .fam-title,
.union > .fam-block-title {
  margin: 0 0 0.55rem; text-align: end;
  font-size: var(--type-label); letter-spacing: 0.06em; text-transform: uppercase;
}
.fam-list { grid-template-columns: 1fr; }
.pcard { min-inline-size: 0; }
.pcard-in {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  block-size: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
  color: inherit;
  text-decoration: none;
  box-sizing: border-box;
}
a.pcard-in:hover { border-color: var(--accent); }
a.pcard-in:hover .pcard-name { color: var(--accent); }
.pcard-face {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: 48px;
  block-size: 48px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: var(--weight-semi);
  font-size: var(--type-minor);
}
.pcard-face img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
.pcard-text { display: grid; gap: 0.15rem; min-inline-size: 0; }
.pcard-name { font-weight: var(--weight-semi); overflow-wrap: anywhere; }
.pcard-meta { font-size: var(--type-minor); color: var(--text-secondary); }
.pcard-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; }
.pcard-order { margin-inline-start: auto; align-self: flex-start; }
.pcard.is-restricted .pcard-in, .pcard.is-absent .pcard-in { color: var(--text-secondary); }
.pcard.is-restricted .pcard-face { background: var(--surface-sunken); color: var(--text-tertiary); }

/* Notes and contributions (docs/adr/0014). The family page's block sits
   on the theme's own soft picture, under a veil of its surface, so that the
   cards read on it whatever the picture; a theme with none shows the
   surface alone. */
.notes-block {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin-block: 2rem;
  padding: clamp(1rem, 3vw, 2rem);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}
.notes-block::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(color-mix(in srgb, var(--surface-raised) 52%, transparent), color-mix(in srgb, var(--surface-raised) 84%, transparent)),
    var(--notes-bg, none) center / cover no-repeat;
}
.notes-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 0.75rem 1.5rem; }
.notes-head-copy { max-inline-size: 62ch; }
.notes-head .home-h2 { margin: 0; }
.notes-intro { margin: 0.35rem 0 0; color: var(--text-secondary); }
.notes-search { display: flex; align-items: flex-end; gap: 0.5rem; margin-block: 1.1rem 0; }
.notes-search-field { display: flex; flex-direction: column; flex: 1 1 auto; min-inline-size: 0; max-inline-size: 34rem; margin: 0; }
.notes-search input[type="search"] { inline-size: 100%; }
.notes-search .btn { flex: none; white-space: nowrap; }
.notes-letters { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-block: 0.85rem 0.35rem; }
/* On a phone the letters keep to one line, which scrolls sideways. */
@media (max-width: 40rem) {
  .notes-letters { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; padding-block-end: 0.25rem; }
  .notes-letter { flex: none; }
}
.notes-letter {
  display: inline-grid;
  place-items: center;
  min-inline-size: 2rem;
  block-size: 2rem;
  padding-inline: 0.35rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--surface-raised) 85%, transparent);
  color: var(--text-primary);
  font-size: var(--type-minor);
  text-decoration: none;
}
a.notes-letter:not([aria-current]):hover { border-color: var(--accent); color: var(--accent); }
.notes-letter[aria-current] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.notes-letter.is-empty { color: var(--text-tertiary); opacity: 0.5; }
.notes-letter-all { padding-inline: 0.75rem; }
.notes-count { margin: 0.4rem 0 0; }
/* Each card as tall as it is: a card with a picture does not stretch the
   ones beside it into empty boxes. */
.notes-grid {
  display: grid;
  align-items: start;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: 0.85rem;
  margin: 0.85rem 0 0;
  padding: 0;
  list-style: none;
}
.note-card[hidden] { display: none; }
.note-card-in {
  display: flex;
  flex-direction: column;
  block-size: 100%;
  overflow: hidden;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--tempo, 140ms) ease, transform var(--tempo, 140ms) ease;
}
.note-card-in:hover { border-color: var(--accent); transform: translateY(-1px); }
.note-thumb { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--surface-sunken); }
.note-thumb img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
.note-card-body { display: grid; align-content: start; gap: 0.35rem; padding: 0.85rem 0.95rem 0.95rem; }
.note-card-top { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.6rem; }
.note-date { font-size: var(--type-minor); color: var(--text-secondary); }
.note-title { font-weight: var(--weight-semi); font-size: var(--type-body); overflow-wrap: anywhere; }
.note-summary {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--text-secondary);
  font-size: var(--type-minor);
}
.note-foot { font-size: var(--type-minor); color: var(--text-tertiary); }
.notes-person .sec-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; }
.notes-locked p { margin-block-end: 0; }

/* A note's window, and its page. */
.note-dialog {
  inline-size: min(54rem, calc(100vw - 2rem));
  max-block-size: calc(100vh - 3rem);
  padding: 0;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  color: var(--text-primary);
  box-shadow: 0 24px 60px -24px rgb(var(--shadow-rgb, 0 0 0) / 0.55);
}
.note-dialog-body { max-block-size: calc(100vh - 3rem); overflow: auto; }
.note-frame { padding: clamp(1rem, 3vw, 2rem); }
.note-page .note-frame { padding: 0; }
.note-head { padding-block-end: 0.9rem; margin-block-end: 1.1rem; border-block-end: 1px solid var(--border-hairline); }
.note-eyebrow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0 0 0.4rem; }
.note-heading { margin: 0; }
.notes-person-add { margin-block: 0.25rem 1.25rem; }
.note-about { margin: 0.45rem 0 0; color: var(--text-secondary); font-size: var(--type-minor); }
.note-text { line-height: 1.65; overflow-wrap: anywhere; }
.note-text img { display: block; max-inline-size: 100%; block-size: auto; margin: 0.9rem auto; border-radius: var(--radius); }
.note-text blockquote { margin: 1rem 0; padding-inline-start: 1rem; border-inline-start: 3px solid var(--accent); color: var(--text-secondary); }
.note-text :is(h3, h4, h5, h6) { margin-block: 1.3rem 0.4rem; }
.note-text table { border-collapse: collapse; }
.note-text :is(th, td) { padding: 0.3rem 0.6rem; border: 1px solid var(--border-hairline); }
.note-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-block-start: 1.5rem;
  padding-block-start: 1rem;
  border-block-start: 1px solid var(--border-hairline);
}
.note-close { margin-inline-start: auto; }
.note-page .note-close { display: none; }

/* Writing one. */
.note-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: 0.6rem 1rem;
}
.note-field-title { grid-column: 1 / -1; }

/* The family the archive describes (the bundle room's form). */
.manifest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 0.6rem 1rem;
}
.manifest-wide { grid-column: 1 / -1; }
.manifest-grid textarea { inline-size: 100%; }
.manifest-set { margin-block: 0.4rem 0.9rem; }
/* The account's own page: the record it says it is. */
.account-person-now { margin-block: 0.4rem 0.6rem; }
.account-person-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.6rem 0.8rem; margin-block-start: 0.8rem; }
.account-person-form .field { flex: 1 1 18rem; max-inline-size: 30rem; margin: 0; }
.manifest-span { margin-block: 0.2rem 0.9rem; }
.manifest-value { margin: 0.2rem 0 0.15rem; font-variant-numeric: tabular-nums; }
/* The licence and, beside it, what it lets others do. */
.manifest-licence { display: grid; grid-template-columns: minmax(0, 26rem) minmax(0, 1fr); gap: 0.6rem 1.2rem; align-items: start; }
.manifest-about { padding: 0.65rem 0.85rem; border: 1px solid var(--border-hairline); border-inline-start: 3px solid var(--accent); border-radius: var(--radius-sm); background: var(--surface-sunken); font-size: var(--type-minor); line-height: 1.5; }
.manifest-about p { margin: 0; }
@media (max-width: 40rem) { .manifest-licence { grid-template-columns: minmax(0, 1fr); } }
.note-people { margin-block-start: 1rem; }
.note-chosen { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 0.6rem; padding: 0; list-style: none; }
.note-person { display: inline-flex; align-items: center; gap: 0.35rem; }
.note-person-remove { padding: 0 0.15rem; border: 0; background: none; color: inherit; font-size: 1.1em; cursor: pointer; }
.note-person-add { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.note-person-add input { flex: 1 1 16rem; max-inline-size: 30rem; }
.note-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin-block-end: 0.5rem; }
.note-toolbar-end { margin-inline-start: auto; }
.note-source { inline-size: 100%; min-block-size: 22rem; font-family: var(--mono); font-size: var(--type-minor); line-height: 1.55; }
.note-preview { min-block-size: 22rem; padding: 1rem; border: 1px dashed var(--border-strong); border-radius: var(--radius); }
.note-delete { margin-block-start: 1.5rem; }

/* What a file may weigh and be (docs/adr/0041): the kinds, by family. */
.uploads-kinds > summary { margin-block-end: 0.6rem; }
.uploads-family { margin: 0 0 0.8rem; padding: 0; border: 0; min-inline-size: 0; }
.uploads-family > legend { padding: 0; margin-block-end: 0.3rem; font-family: var(--mono); font-size: var(--type-label); letter-spacing: var(--label-track); text-transform: uppercase; color: var(--text-secondary); }

/* The record's films and sounds, played on the page (docs/adr/0041). */
.clips { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr)); gap: 1rem; margin-block: 0.5rem 1.25rem; }
.clip { margin: 0; }
.clip video { display: block; inline-size: 100%; max-block-size: 70vh; border-radius: var(--radius); background: var(--surface-sunken); }
.clip audio { display: block; inline-size: 100%; }
.clip figcaption { margin-block-start: 0.35rem; }

/* A person created with what surrounds them (docs/adr/0039): rows that
   grow at need, and under each typed name what the save will make of it. */
.pn-set { margin: 0.4rem 0 1rem; padding: 0; border: 0; min-inline-size: 0; }
.pn-set > legend {
  padding: 0; margin-block-end: 0.3rem;
  font-family: var(--mono); font-size: var(--type-label); letter-spacing: var(--label-track);
  text-transform: uppercase; color: var(--text-secondary);
}
.pn-rows { display: grid; gap: 0.4rem; margin: 0 0 0.5rem; padding: 0; list-style: none; }
.pn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; }
.pn-row > input { flex: 1 1 16rem; max-inline-size: 30rem; }
.pn-row > .pn-said { flex-basis: 100%; margin: 0; }
.pn-said:empty { display: none; }
.pn-said-warn { color: var(--warning); }
.pn-preview { margin: 0.2rem 0 0.5rem; }
.pn-preview .field-label { display: inline; margin: 0; }
.pn-living { align-self: end; }
.pn-union { margin: 0 0 1rem; padding: 0.8rem 1rem 0.2rem; border: 1px solid var(--border-hairline); border-radius: var(--radius); min-inline-size: 0; }
.pn-union > legend { padding: 0 0.35rem; color: var(--text-secondary); }
.pn-text { min-block-size: 10rem; }

.place-card ul.plain > li { border-bottom: none; padding: 0.1rem 0; }

.note-block.verbatim {
  border-inline-start: 2px solid var(--conf-medium);
  padding-inline-start: 0.85rem;
}

/* The raw entity. Costs nothing to include and makes the format's
   readability checkable rather than asserted. */
details.raw {
  margin-top: 1.5rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  padding: 0.6rem 0.85rem;
  background: var(--surface-raised);
}
details.raw > summary { cursor: pointer; font-weight: var(--weight-semi); }
.raw-json {
  max-height: 32rem;
  overflow: auto;
  font-size: var(--type-small);
  background: var(--surface-page);
  padding: 0.75rem;
  border-radius: var(--radius);
}

/* ---------- attached documents ---------- */

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.85rem;
  margin: 0.75rem 0;
}
.shot { margin: 0; }
.shot img {
  display: block;
  width: 100%;
  height: 150px;
  object-fit: cover;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
}
.shot figcaption {
  margin-top: 0.3rem;
  font-size: var(--type-small);
  color: var(--text-secondary);
  display: flex;
  flex-direction: column;
}

.upload-form {
  margin-top: 1.5rem;
  padding: 0.85rem 1rem;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}
.upload-form h3 { margin-top: 0; }
.upload-form p { margin-bottom: 0; max-width: 68ch; }

/* ---------------------------------------------------------------------------
   Conflict and history
   ---------------------------------------------------------------------------
   A refused save has to be readable in the ten seconds before the editor
   decides what to do with it, so the two values are set apart by weight and a
   marker rather than by colour alone — the colour-blind themes carry these
   tables too, and "the red one is theirs" is not a distinction that survives
   them. */
/* The values in a diff are whatever the record holds — a name, a date, or the
 * whole of a 1.1 block as JSON — so the columns are fixed and the text breaks
 * inside a word if it must. Without this the table sized itself to its longest
 * unbroken value and hung 300px past the page on a phone, where nothing
 * scrolled it and the end of every long value was simply gone. */
.diff-table { table-layout: fixed; inline-size: 100%; }
.diff-table td, .diff-table th { overflow-wrap: anywhere; }
.diff-table td { vertical-align: top; }
.diff-from {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  color: var(--text-secondary);
}

/* A recorded change whose value this reader may not see. Italic and muted so
   it reads as a statement about the row rather than as the row's value — the
   one thing it must never look like is a field whose contents are the word
   "withheld". */
.diff-withheld {
  font-style: italic;
  color: var(--text-tertiary);
}
.diff-to { font-weight: var(--weight-semi); }
.diff-arrow { color: var(--text-tertiary); padding: 0 0.3rem; }

/* A field both editors touched. The left border is the signal; the tint only
   reinforces it, so the row still reads as marked without colour. */
.diff-table tr.contested > td {
  background: var(--accent-soft);
}
.diff-table tr.contested > td:first-child {
  border-inline-start: 3px solid var(--accent);
}

.history { margin: 0.5rem 0 1.5rem; padding-inline-start: 1.2rem; }
.history li { margin-bottom: 0.5rem; line-height: 1.5; }
.history details { margin-top: 0.35rem; }
.history summary { cursor: pointer; color: var(--text-secondary); }


/* ---------------------------------------------------------------------------
   Reduced motion
   ---------------------------------------------------------------------------
   `prefers-reduced-motion` is a medical setting, not a preference about taste:
   for a reader with a vestibular disorder an unexpected transition can cause
   nausea. So everything stops — including the hover transitions on tree cards,
   which fire hundreds of times as a pointer crosses a canvas of them. The
   duration is 0.01ms rather than 0 because a zero-length transition does not
   fire `transitionend`, and anything waiting on that event would hang. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ---------------------------------------------------------------------------
   Form controls
   ---------------------------------------------------------------------------
   A control that does not set its own colours inherits the user agent's, which
   are black text on a white field. Under the dark theme that produced black on
   the dark theme's --surface-raised — 1.26:1, effectively unreadable — and no amount of custom-property
   discipline elsewhere fixes it, because the rule that needs to exist was
   simply absent. Measured, not assumed: this is the pair the contrast sweep
   flagged first. */
input, select, textarea {
  color: var(--text-primary);
  background-color: var(--surface-sunken);
}
button { color: var(--text-primary); background-color: transparent; font: inherit; }
input::placeholder, textarea::placeholder { color: var(--text-tertiary); opacity: 1; }
/* Focus (B2): a 2px ring in --focus-ring at 2px offset, on every control,
   never removed. Not the browser's outline, and nothing anywhere sets
   `outline: none` without putting this back (tests/controls.rs). A field
   shows it on any focus, because typing into one is always the keyboard. */
:is(a, button, input, select, textarea, summary, [tabindex]):focus-visible,
:is(input, select, textarea):focus {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* The settings page. A heading, the way back out, and one panel per tab. */
.settings-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.settings-head h1 { margin: 0; }

/* One question per tab, so the panel is a plain block rather than a menu: it
   is the content of the page now, not something hovering over it. */
.settings-panel {
  max-width: 34rem;
  margin-top: 1.25rem;
}
.settings-panel fieldset { border: 0; padding: 0; margin: 0; }
.settings-panel + .settings-panel {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-hairline);
}
.settings-panel legend {
  font-size: var(--label-size);
  text-transform: uppercase;
  letter-spacing: var(--label-track);
  color: var(--text-tertiary);
  font-weight: var(--weight-semi);
  padding: 0;
  margin-bottom: 0.4rem;
}
.settings-panel .hint { margin: 0 0 0.6rem; }
.settings-panel .btn { margin-top: 0.9rem; }

@media (max-width: 30rem) {
  /* At this width no row fits its badge on one line, and letting some wrap and
     not others reads as ragged rather than as a list. Every badge takes its own
     line, so all eleven rows are the same shape. */
  .lang-list label .badge { flex-basis: 100%; }
  .settings-panel .btn { width: 100%; }
}

.lang-list, .theme-list { list-style: none; margin: 0; padding: 0; }
.lang-list > li, .theme-list > li { margin: 0; }
/* The whole row is the target, not the 13px radio in it. */
.lang-list label, .theme-list label {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.4rem 0.5rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background-color var(--tempo) var(--ease);
}
.lang-list label:hover, .theme-list label:hover { background: var(--accent-soft); }
/* Eleven languages, and one of them is "简体中文 · Chinese (Simplified)" beside
   a badge reading "complete, not yet reviewed". That row does not fit on one
   line in any interface language, so it wraps rather than overflowing, and the
   badge keeps its place at the end of whichever line it lands on. */
.lang-list label { flex-wrap: wrap; }
.lang-list label .badge { margin-inline-start: auto; }
.lang-list input, .theme-list input { flex: none; margin: 0; accent-color: var(--accent); }
/* Each language named in itself, which is what a speaker of it looks for. */
.lang-list label > span:nth-of-type(1) { font-weight: var(--weight-medium); color: var(--text-primary); }
.lang-list label > span:nth-of-type(2) { margin-inline-start: auto; }

.badge {
  flex: none;
  font-size: var(--type-micro);
  letter-spacing: 0.03em;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.badge.reviewed { border-color: var(--conf-certain); color: var(--conf-certain); }


/* ---------------------------------------------------------------------------
   Panels and elevation
   --------------------------------------------------------------------------- */
/* The elevation belongs to the whole surface, not to the record half of it:
   a shadow around the panel alone is the frame this pass removed, redrawn in
   grey. */
.tree-split {
  box-shadow: 0 1px 3px rgb(var(--shadow-rgb) / var(--shadow-a));
}
/* The panel's content is replaced by a fragment fetch. Without a cue the swap
   is instantaneous and it is not obvious anything happened; with one, the eye
   is told where to look. The animation is on the inserted element, so it runs
   on every replacement without a class having to be toggled. */
.tree-panel .panel-inner { animation: panel-swap 180ms var(--ease); }
@keyframes panel-swap {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.tcard {
  box-shadow: 0 1px 1px rgb(var(--shadow-rgb) / var(--shadow-a));
  transition: box-shadow var(--tempo) var(--ease),
              transform var(--tempo) var(--ease),
              opacity var(--tempo) var(--ease);
}
.tcard:hover {
  box-shadow: 0 0 0 2px var(--accent),
              0 3px 10px rgb(var(--shadow-rgb) / calc(var(--shadow-a) * 1.8));
}


/* Chips and section controls pick up the same tempo as everything else. */
.chip, .src-chip {
  border-radius: 999px;
  transition: background-color var(--tempo) var(--ease),
              border-color var(--tempo) var(--ease);
}
.sec-help > summary {
  cursor: pointer;
  border-radius: 999px;
  width: 1.15rem;
  height: 1.15rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  font-size: var(--type-label);
  list-style: none;
  transition: background-color var(--tempo) var(--ease),
              color var(--tempo) var(--ease),
              border-color var(--tempo) var(--ease);
}
.sec-help > summary::-webkit-details-marker { display: none; }
.sec-help > summary::marker { content: ""; }
.sec-help > summary:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* The self-contradiction banner names the people it is about, so it needs a
   list rather than a paragraph. Shown only to readers who can act on it. */
.contradiction-list {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.contradiction-list li { font-size: var(--type-minor); }

/* ── Place lookup ──────────────────────────────────────────────────────────
   The suggestion list is deliberately not a tidy row of equal options. Half
   of what the geocoder returns for a nineteenth-century Polish village is the
   wrong kind of object — a bus stop named after the hamlet, a street in the
   wrong country — and a list that renders those identically to a real match
   is a list that invites the wrong click. The off-target rows keep their
   position (relevance is the service's to rank) and lose their confidence. */
.geocode { margin-top: 1rem; border-top: 1px solid var(--border-hairline); padding-top: 0.9rem; }
.geocode-results { margin-top: 0.8rem; }

.suggestions {
  list-style: none;
  margin: 0.5rem 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.suggestions li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--conf-certain);
  border-radius: 6px;
  background: var(--surface-raised);
}
.suggestions li.off-target {
  border-inline-start-color: var(--conf-medium);
  background: var(--surface-page);
}
.suggestion-what { display: flex; align-items: baseline; gap: 0.45rem; flex-wrap: wrap; min-width: 14rem; flex: 1 1 20rem; }
.suggestion-what .place-name { font-weight: var(--weight-semi); }
.suggestion-take { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.suggestion-take code { font-size: var(--type-minor); color: var(--text-secondary); }

.suggestions .tag {
  font-size: var(--type-label);
  color: var(--text-tertiary);
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
  white-space: nowrap;
}
.suggestions .tag code { font-size: var(--type-label); color: inherit; background: none; padding: 0; }
.warn-tag {
  font-size: var(--type-label);
  color: var(--warning);
  text-transform: lowercase;
  white-space: nowrap;
}

/* The paste box is wide because what goes in it is a URL, not a number. */
.paste-position { margin-top: 0.9rem; }
.paste-row { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.paste-row input { flex: 1 1 22rem; min-width: 12rem; }

/* ── The place map ────────────────────────────────────────────────────────
   Sized in `rem` rather than viewport units: this sits inside a form, and a
   map that grows with the window pushes the save button off the bottom on a
   laptop. The pin is drawn in CSS so the binary carries no image for it. */
.map-shell { margin-top: 0.9rem; }
#place-map {
  height: 20rem;
  border: 1px solid var(--border-hairline);
  border-radius: 6px;
  background: var(--surface-page);
}
.map-actions {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: 0.4rem;
}
.map-actions .hint { margin: 0; }

.place-pin span {
  display: block;
  width: 14px;
  height: 14px;
  margin: 2px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--on-accent);
  box-shadow: 0 0 0 1px var(--accent);
}

/* Leaflet paints its own controls and popups; keep them inside the palette
   rather than letting the vendored stylesheet's defaults sit on the page. */
.leaflet-container { font: inherit; background: var(--surface-page); }
.leaflet-bar a,
.leaflet-control-attribution {
  background: var(--surface-raised);
  color: var(--text-primary);
}
.leaflet-bar a { border-bottom-color: var(--border-hairline); }
.leaflet-bar a:hover { background: var(--accent-soft); }
.leaflet-control-attribution { font-size: var(--type-label); }
.leaflet-control-attribution a { color: var(--accent); }

/* The person page's places map. Shorter than the editor's: this one is read,
   not aimed at, and it sits inside a section rather than being the task. */
.places-map-shell { margin: 0.75rem 0 1rem; }
#places-map {
  height: 16rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
}
#places-map .leaflet-popup-content strong { display: block; margin-bottom: 0.2rem; }

/* How much is behind a tab. Quiet: it is a hint about where to click, not a
   number anybody came to read. */
.tab-count {
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.person-tab.is-current .tab-count { color: var(--text-secondary); }

/* A table fills its section edge to edge. The section already supplies the
   frame and the padding, and a table that keeps its own inset inside that
   reads as a block within a block that is not one. */
.rec-section > .table-wrap {
  margin-inline: calc(-1 * var(--section-pad-x));
  width: calc(100% + 2 * var(--section-pad-x));
}
/* On a narrow screen, or with large text, the bleed into the section's
   padding pushed the table past the page's edge; it stays inside instead. */
@media (max-width: 40em) {
  .rec-section > .table-wrap { margin-inline: 0; width: 100%; }
  /* On a phone a UUID wraps to five lines and tells a reader nothing: the
     list keeps the name, and the record's own page keeps its identifier. */
  .admin-table .rec-id { display: none; }
}
.rec-section > .table-wrap > table { font-size: var(--type-minor); }
.rec-section > .table-wrap th:first-child,
.rec-section > .table-wrap td:first-child { padding-inline-start: max(0.75rem, var(--section-pad-x)); }
.rec-section > .table-wrap th:last-child,
.rec-section > .table-wrap td:last-child { padding-inline-end: max(0.75rem, var(--section-pad-x)); }

/* The filter row sits inside its section now, so it drops its own margin. */
.rec-section > .filter-row:last-child,
.rec-section > .cta-row:first-child { margin-top: 0; }
.rec-section > .filter-row:last-child { margin-bottom: 0; }

/* The panel is 460px of column, so a section's frame there is drawn tighter:
   the same three levels, less air. Dropping the padding to 0.85rem gives back
   roughly a word per line, which at this width is the difference between a
   date wrapping and not. Nothing is removed — see the note above .tree-panel:
   what is shown never changes between the panel and the page. */
.tree-panel .rec-section { padding: 0.85rem 0.9rem; }
.tree-panel .rec-section .card,
.tree-panel .rec-section .union,
.tree-panel .rec-section .fam-block,
.tree-panel .rec-section .name-block { padding: 0.6rem 0.7rem; }
.tree-panel .rec-section > .table-wrap {
  margin-inline: -0.9rem;
  width: calc(100% + 1.8rem);
}
.tree-panel .rec-section > .table-wrap th:first-child,
.tree-panel .rec-section > .table-wrap td:first-child { padding-inline-start: 0.9rem; }
.tree-panel .rec-section > .table-wrap th:last-child,
.tree-panel .rec-section > .table-wrap td:last-child { padding-inline-end: 0.9rem; }

/* ── Body and health ──────────────────────────────────────────────────────
   A field with one entry is a line; a field with several is a series, and the
   difference is the whole reason the entries are dated. A height at twenty and
   a height at fifty-six are two measurements of a changing body, so the series
   leads with its dates and lets the values line up under each other. */
.phys-list { display: flex; flex-direction: column; gap: 0.5rem; }
.phys-item {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
}
.phys-item.is-series { display: block; }
.phys-label {
  color: var(--text-tertiary);
  font-size: var(--type-small);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  min-width: 9rem;
}
.phys-value { font-weight: var(--weight-semi); }
.phys-series {
  list-style: none;
  margin: 0.3rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  /* The rule sits on the leading edge and mirrors in RTL with the rest. */
  border-inline-start: 2px solid var(--border-hairline);
  padding-inline-start: 0.7rem;
}
.phys-series li {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.45rem;
}
.phys-series .phys-when {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  min-width: 6.5rem;
}
.phys-item:not(.is-series) .phys-when { color: var(--text-tertiary); }

/* The editor: one table per field, a row per dated entry, a spare row at the
   end so adding one works with scripting off. */
.phys-field { margin: 0 0 1rem; }
.phys-field > legend { font-size: var(--type-minor); color: var(--text-secondary); padding-inline: 0.3rem; }
.phys-rows { width: 100%; font-size: var(--type-minor); }
.phys-rows th {
  font-size: var(--type-label);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
  text-align: start;
  font-weight: var(--weight-semi);
}
.phys-rows input[type="text"] { width: 100%; box-sizing: border-box; }
.phys-rows td { vertical-align: top; padding: 0.2rem 0.3rem; }

@media (max-width: 720px) {
  /* At phone width the five columns become five labelled lines, the same way
     every other wide table in this file stacks. */
  .phys-rows thead { display: none; }
  .phys-rows, .phys-rows tbody, .phys-rows tr, .phys-rows td { display: block; width: 100%; }
  .phys-rows tr { border: 1px solid var(--border-hairline); border-radius: var(--radius); padding: 0.4rem; margin-bottom: 0.5rem; }
  .phys-rows td::before {
    content: attr(data-label);
    display: block;
    font-size: var(--type-label);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-tertiary);
  }
  .phys-label { min-width: 0; }
}

/* ── Choosing an avatar ───────────────────────────────────────────────────
   A grid of what is actually linked to the person, with the current choice
   marked by more than colour — a border weight and a checked radio, so the
   marking survives the colour-blind themes and a greyscale print. */
.avatar-modes { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.avatar-modes label { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.avatar-initials-chip {
  display: inline-grid;
  place-items: center;
  inline-size: 2rem;
  block-size: 2rem;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: var(--weight-strong);
  font-size: var(--type-small);
}
.avatar-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 0.75rem;
  margin: 0.5rem 0;
}
.avatar-option { display: flex; flex-direction: column; gap: 0.3rem; cursor: pointer; }
.avatar-frame {
  position: relative;
  display: block;
  aspect-ratio: 1;
  overflow: hidden;
  border: 2px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
}
.avatar-option.is-chosen .avatar-frame { border-color: var(--accent); border-width: 3px; }
.avatar-frame img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
/* Where the crop is centred. Two rings rather than one colour, so it is
   visible over a light photograph and a dark one alike. */
.avatar-pin {
  position: absolute;
  inline-size: 12px;
  block-size: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  border: 2px solid var(--on-accent);
  box-shadow: 0 0 0 2px var(--accent);
  pointer-events: none;
}

/* ---------------------------------------------------------------------------
   Paper: the component overrides
   ---------------------------------------------------------------------------
   Last in the file on purpose. The token blocks near the top retune the scale,
   which is all two of the three styles need; these two rules *replace* a
   declaration the level-2 and level-3 sections make, and those are declared
   several hundred lines below the tokens. A style that loses the cascade is a
   style that does nothing. */
/* Paper draws its sections with a rule above the heading rather than a box
   around the contents. The border cannot come from --section-border, which is
   all four sides; this is the one rule a style adds rather than retunes. */
[data-style="paper"] .rec-section {
  border-block-start: 1px solid var(--border-hairline);
  padding-block-start: calc(var(--step) * 2);
}
[data-style="paper"] .rec-section:first-of-type { border-block-start: 0; }
/* The recessed inner blocks lose their fill too: on paper a second grey box
   inside a first is a photocopy artefact. */
[data-style="paper"] .rec-section .card,
[data-style="paper"] .rec-section .union,
[data-style="paper"] .rec-section .fam-block,
[data-style="paper"] .rec-section .name-block,
[data-style="paper"] .rec-section .shot {
  background: transparent;
  border-inline: 0;
  border-block-start: 0;
  border-block-end: 1px solid var(--border-hairline);
  border-radius: 0;
  padding-inline: 0;
}

/* The export's per-class choice: a row of checkboxes that wraps. */
.export-choice { margin-top: 0.8rem; display: flex; flex-wrap: wrap; align-items: end; gap: 0.6rem 1rem; }
.export-choice fieldset { margin: 0; }
.check-row { display: flex; flex-wrap: wrap; gap: 0.35rem 1.2rem; }

/* ---------------------------------------------------------------- profile */

/* The strip of fourteen groups: wraps rather than scrolls, because a group a
   reader has to scroll sideways to find is a group they will not find. */
.profile-groups {
  display: flex; flex-wrap: wrap; gap: 0.3rem 0.35rem;
  margin: 0.2rem 0 1rem; padding: 0; list-style: none;
}
.profile-group-link {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.28rem 0.6rem; border: 1px solid var(--border-hairline); border-radius: 999px;
  color: var(--text-primary); text-decoration: none; font-size: var(--type-minor); line-height: 1.3;
  background: var(--surface-raised);
}
.profile-group-link:hover { border-color: var(--accent); }
.profile-group-link.is-current {
  border-color: var(--accent); background: var(--accent); color: var(--on-accent);
}
.profile-group-link.is-current .tab-count { color: inherit; }
.profile-group-link.is-empty:not(.is-current) { color: var(--text-secondary); }
/* A small padlock drawn with borders: a shape, not a colour, and named in
   words beside it for anybody who does not see it. */
.pg-withheld {
  display: inline-block; width: 0.55rem; height: 0.45rem; margin-top: 0.25rem;
  border: 1.5px solid currentColor; border-radius: 1px; position: relative;
}
.pg-withheld::before {
  content: ""; position: absolute; left: 50%; bottom: 100%; width: 0.35rem; height: 0.3rem;
  transform: translateX(-50%); border: 1.5px solid currentColor; border-bottom: 0;
  border-radius: 0.2rem 0.2rem 0 0;
}

.profile-intro { margin-top: -0.3rem; }
.profile-facts { margin-bottom: 1rem; }
.profile-attrs { margin: 0.4rem 0 0; }
.profile-attrs .pa {
  display: grid; grid-template-columns: minmax(9rem, 14rem) 1fr; gap: 0.2rem 1rem;
  padding: 0.55rem 0; border-top: 1px solid var(--border-hairline);
}
.profile-attrs .pa:first-child { border-top: 0; }
.profile-attrs dt { font-weight: var(--weight-semi); color: var(--text-primary); }
.profile-attrs dd { margin: 0; min-width: 0; }
.profile-attrs .claims { margin: 0; padding: 0; list-style: none; }
.profile-attrs .claims li + li { margin-top: 0.3rem; }
.claim { display: inline; overflow-wrap: anywhere; }
.claim > * + * { margin-inline-start: 0.35rem; }
.claim-when { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.claim-value { font-weight: var(--weight-medium); }
.claim-parts { display: inline; }
.claim-part + .claim-part::before { content: " · "; color: var(--text-secondary); }
.claim-part-label { color: var(--text-secondary); }
.claim-part-label::after { content: ":"; }
.claim-period { color: var(--text-secondary); font-size: 0.9em; }
.pa-class, .pa-earlier { vertical-align: 0.1em; }
.profile-empty { margin-top: 0.5rem; }
@media (max-width: 640px) {
  .profile-attrs .pa { grid-template-columns: 1fr; gap: 0.1rem; }
}

/* The editor. */
.pf-attr { margin: 0 0 1rem; }
.pf-attr > legend { font-weight: var(--weight-semi); }
.pf-row { padding: 0.55rem 0 0.4rem; border-top: 1px dashed var(--border-hairline); }
.pf-row:first-of-type { border-top: 0; }
.pf-spare-note { margin: 0 0 0.25rem; }
.pf-input { min-width: 0; }
.pf-input.wide { grid-column: 1 / -1; }
.pf-input textarea { width: 100%; min-height: 2.6rem; }
.pf-req { color: var(--danger); margin-inline-start: 0.15rem; }
.pf-num { display: inline-flex; align-items: center; gap: 0.3rem; }
.pf-num input { width: 8rem; }
.pf-unit { color: var(--text-secondary); font-size: var(--type-minor); }
.pf-prov { margin-top: 0.35rem; }
.pf-prov-grid { display: grid; gap: 0.6rem 1.2rem; margin-top: 0.35rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); }
/* A date and its precision and circa flag, side by side and nowhere else. */
.pf-date.ed-grid { grid-template-columns: minmax(11rem, 3fr) minmax(8rem, 2fr); }
.pf-src.ed-grid { grid-template-columns: minmax(10rem, 2fr) minmax(6rem, 1fr); }
.pf-note { grid-column: 1 / -1; }
@media (max-width: 480px) {
  .pf-date.ed-grid { grid-template-columns: 1fr 1fr; }
  .pf-src.ed-grid { grid-template-columns: 1fr; }
}
.pf-prov > summary { cursor: pointer; font-size: var(--type-minor); color: var(--text-secondary); }
.pf-prov[open] > summary { margin-bottom: 0.3rem; }
.pf-remove { margin-top: 0.3rem; font-size: var(--type-minor); }
.pf-problems { margin: 0.2rem 0 0.4rem; padding-inline-start: 1.2rem; color: var(--text-primary); }
.pf-add { margin-top: 0.4rem; }

/* ---------------------------------------------------------------------------
 * The river (`/`). Its palette is its own and does not follow the theme: the
 * water's colour ramp is drawn into the SVG server-side, and the dark ground
 * is part of how that ramp reads. Time therefore also runs on a second
 * channel — the half-century bands — which is what survives the colour-blind
 * themes and print. Logical properties throughout, so RTL keeps working.
 * ------------------------------------------------------------------------- */
.river {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  background: var(--rv-ink-0);
  border: 1px solid var(--rv-rule-2);
  color: var(--rv-text-1);
}
.river-notice { margin-block-end: 0.75rem; }
.rv-stage {
  flex: 1 1 520px;
  min-inline-size: 0;
  display: flex;
  flex-direction: column;
  background: var(--rv-ink-1);
}
/* Two rows in a fixed order (river.html): the itinerary, then the tools.
   Before, one wrapping row put each control wherever the width left room,
   the chips over two uneven lines and the buttons under them. */
.rv-controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-block: 12px;
  padding-inline: 16px;
  border-block-end: 1px solid var(--rv-rule-1);
}
.rv-itinerary { display: flex; align-items: center; gap: 14px; min-inline-size: 0; }
/* Left, what is drawn (back, range, zoom); right, how (pause, whole). */
.rv-tools { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 22px; min-block-size: 32px; }
.rv-tools-end { display: inline-flex; align-items: center; gap: 10px; margin-inline-start: auto; }
.rv-eyebrow {
  font-family: var(--rv-mono);
  font-size: var(--type-rv-eyebrow);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rv-text-3);
}
/* The itinerary keeps to its line: a long journey scrolls sideways, and
   river.js brings the stop the reader is at into view. */
.rv-trail {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: nowrap;
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--rv-rule-3) transparent;
  margin: 0;
  padding: 2px 0;
  list-style: none;
}
.rv-trail > li { flex: none; }
/* The river's controls are ordinary secondary buttons (B2) — `.river` sets
   Study 06 on itself, so they draw on the river's ground under every theme.
   What is left here is layout: the itinerary's chips, and the joined sets
   whose current member is pressed. */
.rv-chip { font-size: var(--type-rv-chip); }
/* The range: one native select, the river's mono. */
.rv-range { display: inline-flex; align-items: center; gap: 0.4rem; margin: 0; }
.rv-range [hidden] { display: none; }
.rv-range select {
  inline-size: auto;
  block-size: 28px;
  padding-inline: 0.5rem 0.25rem;
  font-family: var(--rv-mono);
  font-size: var(--type-rv-seg);
}
/* The canvas is as tall as its drawing. Stretched to the record panel beside
   it, it pushed the legend away from the river it explains and put back, as
   page, the empty height the cropped viewBox had just taken out. */
.rv-canvas { flex: 0 0 auto; }
.rv-canvas svg { display: block; }
.rv-canvas .rv-p { cursor: pointer; }
/* The person a tap looked at (river.js, 2026-10-27): named under the
   drawing with the two ways on, kept at the foot of the screen while the
   drawing above it is in view. */
.rv-pick {
  position: sticky;
  inset-block-end: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 0.75rem;
  padding: 0.7rem 0.9rem;
  background: var(--surface-raised);
  border-block-start: 1px solid var(--border-strong);
  box-shadow: 0 -12px 26px -20px rgb(var(--shadow-rgb) / 0.7);
}
.rv-pick-who { margin: 0; min-inline-size: 0; }
.rv-pick-who strong { font-weight: var(--weight-semi); color: var(--text-primary); }
.rv-pick-years { color: var(--text-secondary); font-size: var(--type-small); font-variant-numeric: tabular-nums; }
.rv-pick-go { display: flex; flex-wrap: wrap; gap: 0.5rem; }
/* The drawing's inks. src/river.rs (and river.js, its port for frames in
   motion) mark each non-water colour with a class; the colours themselves
   are the river's palette in tokens.css. */
.rv-svg .k-bg { fill: var(--rv-ink-1); }
.rv-svg .k-halo { stroke: var(--rv-ink-1); }
/* Translucent since v0.5.0: the bands are terraces over the river's ground,
   not a floor laid on top of it (docs/adr/0004 §5). */
.rv-svg .k-band-odd { fill: var(--rv-band-odd); fill-opacity: 0.55; }
.rv-svg .k-band-live { fill: var(--rv-band-live); fill-opacity: 0.6; }
.rv-svg .k-band-rule { stroke: var(--rv-band-rule); }
.rv-svg .k-band-text { fill: var(--rv-band-text); }
.rv-svg .k-band-text-live { fill: var(--rv-band-text-live); }
.rv-svg .k-data { fill: var(--rv-data); }
.rv-svg .k-data-line { stroke: var(--rv-data); }
.rv-svg .k-name { fill: var(--rv-name); }
.rv-svg .k-centre { fill: var(--rv-centre); }
.rv-svg .k-accent { fill: var(--rv-accent); }
.rv-svg .k-accent-line { stroke: var(--rv-accent); }
.rv-svg .k-glow { fill: var(--rv-glow); }
.rv-svg .k-lit { stroke: var(--rv-lit); }
.rv-svg .k-lit-fill { fill: var(--rv-lit); }
/* The lit route and its label are drawn over the people: a second tap on a
   person goes through them to the person, to travel there. */
.rv-svg .rv-lit, .rv-svg .rv-hl { pointer-events: none; }
/* A lit word's own ground (river.js): a row is rarely a lit person's alone,
   so their name is set on the river's ink, edged in its light, and the
   person's own shorter word steps aside while they are lit. */
.rv-svg .rv-hl-box { fill: var(--rv-ink-1); fill-opacity: 0.92; stroke: var(--rv-lit); stroke-opacity: 0.35; stroke-width: 0.8; }
.rv-svg a.rv-p.rv-looked .k-name { visibility: hidden; }
/* While a person is looked at, only their full name is read: every other
   word steps aside with its ground, in every theme's drawing (the
   operator's request of 6 November 2026). The person looked at keeps their
   years in the river; the one at the centre, looked at, keeps its name. */
.rv-svg.rv-naming a.rv-p:not(.rv-keep, .rv-looked) :is(.k-name, .k-centre, .k-data, .tv-panel, .tv-leader, .tv-name, .tv-data, .gx-name, .gx-data, .sm-name, .sm-data, .gn-plate) { visibility: hidden; }
.rv-svg.rv-naming a.rv-p.rv-looked :is(.k-name, .tv-panel, .tv-leader, .tv-name, .tv-data, .gx-name, .gx-data, .sm-name, .sm-data, .gn-plate) { visibility: hidden; }
.rv-svg.rv-naming .rv-names { visibility: hidden; }
.rv-canvas .rv-p:focus-visible circle:first-child { stroke: var(--rv-accent); stroke-width: 1.5; }
/* A grid of keys in columns of one width, rather than a row that wrapped
   them wherever the line ran out; the title, the long keys and the
   compass each take a row of their own. */
.rv-legend {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13.5rem), 1fr));
  gap: 10px 24px;
  align-items: center;
  padding-block: 10px 12px;
  padding-inline: 16px;
  border-block-start: 1px solid var(--rv-rule-1);
  font-family: var(--rv-mono);
  font-size: var(--type-rv-data);
  color: var(--rv-text-2);
}
.rv-legend > span { display: flex; align-items: center; gap: 7px; }
.rv-key { display: block; inline-size: 24px; border-block-start: 3px solid var(--rv-water); }
.rv-key-d { border-block-start-style: dashed; }
.rv-key-s { border-block-start-style: dotted; }
.rv-ring {
  display: block;
  inline-size: 10px;
  block-size: 10px;
  border: 1.5px solid var(--rv-water);
  border-radius: 50%;
  box-sizing: border-box;
}
.rv-rep {
  display: block;
  inline-size: 13px;
  block-size: 13px;
  border: 1px solid var(--rv-text-3);
  border-radius: 50%;
  box-sizing: border-box;
  background: radial-gradient(circle, var(--rv-water) 0 3.2px, transparent 3.7px);
}
.rv-rep-again { border-style: dashed; }
.rv-widths { gap: 4px !important; }
.rv-widths > i { display: block; block-size: 12px; background: var(--rv-water); }
.rv-widths > i:last-of-type { margin-inline-end: 3px; }
.rv-direction { color: var(--rv-text-3); }
.rv-legend > :is(.rv-legend-title, .rv-widths, .rv-direction, .rv-hint) { grid-column: 1 / -1; }
/* The two longest keys take two columns each where there are four. */
@media (min-width: 64rem) { .rv-legend > span:has(> .rv-rep) { grid-column: span 2; } }
/* The record beside the river is the ordinary record partial, which draws
   with the theme's variables; here they are the river's, so the panel reads
   on the river's ground whichever theme the reader chose. */
.rv-record {
  --surface-page: var(--rv-ink-3); --surface-raised: var(--rv-ink-3); --section-fill: var(--rv-ink-2);
  --border-hairline: var(--rv-rule-2); --border-strong: var(--rv-rule-3);
  --text-primary: var(--rv-text-1); --text-secondary: var(--rv-text-2); --text-tertiary: var(--rv-text-3);
  --accent: var(--rv-accent); --accent-soft: var(--rv-ink-4); --on-accent: var(--rv-ink-0);
  --conf-track: var(--rv-rule-2);
  --section-border: 1px solid var(--rv-rule-2);
  --section-shadow: none;
  --section-radius: 2px;
  color: var(--rv-text-1);
  padding-block: 18px 22px;
  padding-inline: 20px;
  flex: 0 1 420px;
  min-inline-size: 300px;
  max-block-size: none;
  background: var(--rv-ink-3);
  border-inline-start: 1px solid var(--rv-rule-2);
}
@media (max-width: 860px) {
  .rv-record { flex-basis: 100%; border-inline-start: 0; border-block-start: 1px solid var(--rv-rule-2); }
}
/* Beside the drawing, the record is never taller than the stage (the
   operator's request of 8 November 2026): it takes the stage's height and
   scrolls within itself. `contain: size` keeps what it holds from
   stretching the row; under the stage, at phone width, it is as tall as
   what it holds. */
@media (min-width: 861px) {
  .rv-record { contain: size; overflow-y: auto; overscroll-behavior: contain; }
}
@media print {
  .rv-controls, .rv-record { display: none; }
}

/* At phone width the river's tools are two even rows (2026-10-25): the
   range and the zoom, then the buttons side by side, each its share of the
   width, rather than wherever the wrap left them. */
@media (max-width: 640px) {
  .rv-tools { gap: 10px 16px; }
  .rv-zoom { flex: 1 1 auto; display: inline-flex; align-items: center; gap: 0.5rem; }
  .rv-zoom input[type="range"] { flex: 1 1 auto; inline-size: auto; min-inline-size: 5rem; }
  .rv-tools-end {
    margin-inline-start: 0;
    flex: 1 1 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }
  /* Each button its share of the row, never a word broken: three that do
     not fit side by side make two rows, the last one the whole width. */
  .rv-tools-end > .btn { flex: 1 1 auto; justify-content: center; text-align: center; white-space: nowrap; }
  .rv-itinerary { gap: 10px; }
}

/* The masthead search and its suggestions. */
.masthead-search { position: relative; margin-inline-start: 1rem; }
/* Its label is visible, beside the field, like every other field's. */
.search-box { display: flex; align-items: center; gap: 0.5rem; margin: 0; }
.search-box > input[type="search"] { margin-top: 0; inline-size: 9rem; max-inline-size: 36vw; }
.search-suggest {
  position: absolute; inset-inline-start: 0; inset-block-start: 100%; z-index: 20;
  min-inline-size: 100%; margin: 2px 0 0; padding: 4px 0; list-style: none;
  background: var(--surface-raised); border: 1px solid var(--border-hairline); border-radius: 2px;
}
.search-suggest li { padding: 6px 10px; cursor: pointer; white-space: nowrap; }
.search-suggest li[aria-selected="true"], .search-suggest li:hover { background: var(--accent-soft); }
.search-suggest .search-years { color: var(--text-tertiary); font-family: var(--mono); font-size: var(--type-small); }
.search-suggest li { display: flex; align-items: center; gap: 0.5rem; }
/* The discharge class, drawn as the river draws it: a line as thick as the
   person's recorded descendants make it, 1 · 2–5 · 6–20 · 21–80 · 81+. */
.search-class {
  flex: none;
  display: inline-block;
  block-size: 14px;
  background: var(--text-secondary);
  border-radius: 1px;
}
.search-class[data-class="1"] { inline-size: 2px; }
.search-class[data-class="2"] { inline-size: 3px; }
.search-class[data-class="3"] { inline-size: 5px; }
.search-class[data-class="4"] { inline-size: 8px; }
.search-class[data-class="5"] { inline-size: 12px; }
.search-suggest li.search-foot {
  cursor: default;
  margin-top: 4px;
  padding-top: 6px;
  border-top: 1px solid var(--border-hairline);
  font-family: var(--mono);
  font-size: var(--type-label);
  color: var(--text-tertiary);
}
.search-suggest li.search-foot:hover { background: none; }
/* The offer to create the person searched for (docs/adr/0039). */
.search-suggest li.search-create { color: var(--accent); font-weight: var(--weight-semi); }
.search-suggest li.search-create:not(:first-child) { margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--border-hairline); }
.search-create { margin-block-start: 1rem; }
.search-results { list-style: none; padding: 0; }
.search-results li { padding: 6px 0; border-block-end: 1px solid var(--border-hairline); }

/* ---------------------------------------------------------------------------
   Administration tables (Study 06, B6)
   ---------------------------------------------------------------------------
   Rows separated by hairlines, no zebra; identifiers and numbers in mono. A
   selected row tints the surface and gains no coloured edge. While rows are
   selected the bulk bar takes over the header row (ui.js); without scripts
   both rows show. The filiation column draws a record's parent–child links in
   the river's own line styles: solid attested, dashed inferred, dotted
   speculative. */
.admin-table tbody tr { background: transparent; }
.admin-table tbody td { border-bottom: 1px solid var(--border-hairline); }
.admin-table .sel { inline-size: 1%; padding-inline-end: 0; }
.admin-table .sel input { margin: 0; }
.admin-table tbody tr:has(> .sel input:checked) { background: var(--surface-hover); }
.bulk-inner { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.bulk-count { font-family: var(--mono); font-size: var(--type-small); color: var(--text-primary); text-transform: none; letter-spacing: normal; }
.bulk-esc { margin-inline-start: auto; font-family: var(--mono); font-size: var(--type-label); color: var(--text-tertiary); text-transform: none; letter-spacing: normal; font-weight: var(--weight-normal); }
.bulk-bar[hidden], .bulk-head[hidden] { display: none; }
.filiation { white-space: nowrap; }
.fil {
  display: inline-block;
  inline-size: 26px;
  block-size: 0;
  vertical-align: middle;
  margin-inline-end: 4px;
  border-top: 2px solid var(--text-secondary);
}
.fil-d { border-top-style: dashed; }
.fil-s { border-top-style: dotted; border-top-width: 3px; }
.pager-range { font-family: var(--mono); font-size: var(--type-small); color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.bulk-list { list-style: none; padding: 0; margin: 1rem 0; }
.bulk-list li { padding: 0.45rem 0; border-bottom: 1px solid var(--border-hairline); }

/* The merge screen (src/routes/merge.rs): the two records side by side, one
   fieldset per disagreement, each choice a whole row to click. */
.merge-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: start; }
.merge-side h2 { margin: 0 0 0.35rem; font-size: var(--type-label); color: var(--text-secondary); }
.merge-side p { margin: 0.15rem 0; overflow-wrap: anywhere; }
.merge-swap { grid-column: 1 / -1; margin: 0; }
.merge-conflict { border: 1px solid var(--border-hairline); border-radius: var(--radius); padding: 0.6rem 0.9rem; margin: 0 0 0.9rem; }
.merge-conflict legend { padding: 0 0.35rem; font-weight: var(--weight-semi); }
/* Each choice is a whole sentence to read, not a field's caption: none of
   the form label's mono capitals. */
.merge-choice { display: flex; gap: 0.6rem; align-items: baseline; padding: 0.4rem 0.35rem; margin: 0; border-radius: var(--radius); cursor: pointer; font-family: inherit; font-size: var(--type-minor); letter-spacing: normal; text-transform: none; color: var(--text-primary); }
.merge-choice:hover, .merge-choice:has(input:checked) { background: var(--surface-hover); }
.merge-choice-label { flex: none; min-inline-size: 7rem; font-weight: var(--weight-semi); }
.merge-value { overflow-wrap: anywhere; color: var(--text-secondary); }
.merge-facts { margin: 0; }
.merge-facts > div { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; justify-content: space-between; padding: 0.35rem 0; border-bottom: 1px solid var(--border-hairline); }
.merge-facts dt { color: var(--text-secondary); overflow-wrap: normal; }
.merge-facts dd { margin: 0 0 0 auto; text-align: end; }
.merge-facts .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.merge-record { max-block-size: 24rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .merge-pair { grid-template-columns: 1fr; }
  .merge-choice { flex-wrap: wrap; }
  .merge-choice-label { min-inline-size: 0; }
}

/* The quality page's filters: two selects and the downloads, on one row. */
.quality-filter { display: flex; align-items: flex-end; gap: 1rem; flex-wrap: wrap; margin: 1rem 0; }
.quality-filter [hidden] { display: none; }
.quality-filter select { inline-size: auto; }
.quality-downloads { position: relative; }
.quality-downloads > summary { cursor: pointer; padding-block: 0.4rem; color: var(--text-secondary); }

/* A short list of actions behind one disclosure (the dashboard's create and
   export). */
.menu-disclosure { position: relative; display: inline-block; margin: 0.25rem 0; }
.menu-disclosure > summary { cursor: pointer; }
.menu-disclosure > summary::marker { color: var(--text-tertiary); }
.menu-disclosure[open] > .menu-panel { position: static; margin-top: 0.4rem; }

/* Edit mode's record rows (the minimalism pass): each record is one line
   that opens to its form; adding is one disclosure. */
.record-list { display: grid; gap: 0.5rem; margin: 0.75rem 0; }
.record-edit, .add-record, .more-fields, .raw-fieldset, .danger-zone {
  border: 1px solid var(--border-hairline);
  border-radius: 2px;
  padding: 0.6rem 0.9rem;
}
.record-edit > summary, .add-record > summary, .more-fields > summary,
.raw-fieldset > summary, .danger-zone > summary { cursor: pointer; font-weight: var(--weight-medium); }
.record-edit[open] > summary, .add-record[open] > summary, .more-fields[open] > summary,
.raw-fieldset[open] > summary, .danger-zone[open] > summary { margin-bottom: 0.6rem; }
.danger-zone > summary { color: var(--danger); }

/* The record's profile in edit mode (v0.4.1): the fourteen groups as one
   list, each a disclosure named for its group with how many of its fields
   hold a value. Rows separated by hairlines, as Study 06 draws a list; the
   count in mono, like a tab's. */
.pf-groups { margin-block: 1.5rem; }
.pf-groups > h2 { margin-block-end: 0.5rem; }
.pf-group { border-block-end: 1px solid var(--border-hairline); }
.pf-group:first-of-type { border-block-start: 1px solid var(--border-hairline); }
.pf-group > summary {
  cursor: pointer;
  padding: 0.6rem 0.25rem;
  font-weight: var(--weight-medium);
}
.pf-group > summary .sep { color: var(--text-tertiary); }
.pf-group-count {
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.pf-group-body { padding: 0.25rem 0.25rem 1rem; }
.ed-parents ul { margin: 0.25rem 0; }

/* Edit mode (the minimalism pass): a band under the masthead that says so,
   with the accent's rule under it, so nobody mistakes a form for the record.
   Not a tinted page: a 6% tint took tertiary text to 4.47:1 and control
   edges to 2.99:1 on the dark ground — under WCAG — so the band carries it. */
.edit-band {
  padding: 0.45rem 1.25rem;
  background: var(--surface-raised);
  border-block-end: 2px solid var(--accent);
  font-family: var(--mono);
  font-size: var(--type-label);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--text-primary);
}

/* A finger, not a pointer (the minimalism pass, ADR 0002 §5): on a touch
   screen every button and field reaches 44 CSS px, the target WCAG 2.5.5
   asks for. The river's people are reached by the nearest-person tap. */
@media (pointer: coarse) {
  .btn, .btn.small,
  input[type="text"], input[type="password"], input[type="email"],
  input[type="search"], input[type="number"], input[type="url"],
  select, summary { min-block-size: 44px; }
  input[type="checkbox"], input[type="radio"] { inline-size: 24px; block-size: 24px; }
}

/* ---------------------------------------------------------------------------
 * The record's editor (v0.4.2): six tabs over one form.
 *
 * Without scripting, the tab on screen is the URL's fragment (:target), and
 * the server's current tab when there is none. editor.js takes over with
 * `.js` on the panels and shows the one marked current.
 * ------------------------------------------------------------------------- */
.ed-tabs {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-page);
}
.ed-panel { display: none; padding-block: 1.25rem 0.5rem; }
.ed-panel.is-current { display: block; }
.ed-panels:has(.ed-panel:target) > .ed-panel { display: none; }
.ed-panel:target { display: block; }
.ed-panels.js > .ed-panel { display: none; }
.ed-panels.js > .ed-panel.is-current { display: block; }

/* A tab with changes not yet saved: a dot after its name, in the accent. */
.ed-mark { display: none; }
.person-tab.has-changes .ed-mark {
  display: inline-block;
  inline-size: 0.45rem;
  block-size: 0.45rem;
  border-radius: 50%;
  background: var(--accent);
  margin-inline-start: 0.1rem;
  align-self: center;
}

.ed-fields {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: 0.25rem 1.25rem;
}
.ed-fields > .field, .ed-fields > label { margin: 0; }
.ed-field { display: flex; flex-direction: column; gap: 0.3rem; min-inline-size: 0; }
.ed-field.wide { grid-column: 1 / -1; }
.ed-field .field-label {
  font-family: var(--mono);
  font-size: var(--type-label);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.ed-field input:not([type="checkbox"]), .ed-field select, .ed-field textarea { inline-size: 100%; }
.ed-fields, .ed-grid { margin-block-end: 0.75rem; }

/* A record the tab writes: a union, an event, a source, a file. Hairlines,
   no boxes, as Study 06 draws a list. */
.ed-block {
  border-block-start: 1px solid var(--border-hairline);
  padding-block: 1rem 0.5rem;
}
.ed-block:last-of-type { border-block-end: 1px solid var(--border-hairline); }
.ed-block.is-touched { box-shadow: inset 2px 0 0 var(--accent); padding-inline-start: 0.75rem; }
.ed-block-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.75rem; margin-block-end: 0.75rem; }
.ed-block-title { flex: 1 1 12rem; min-inline-size: 0; overflow-wrap: anywhere; }
.ed-block-title h3 { margin: 0; font-size: var(--type-body); }
.ed-thumb { inline-size: 3.5rem; block-size: 3.5rem; object-fit: cover; border-radius: 2px; }

/* A list inside a record — partners, children — as rows with their actions. */
.ed-list-title {
  margin: 0.75rem 0 0.25rem;
  font-family: var(--mono);
  font-size: var(--type-label);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: var(--weight-normal);
}
.ed-list { border-block-start: 1px solid var(--border-hairline); }
.ed-item {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.5rem 1rem;
  padding-block: 0.5rem;
  border-block-end: 1px solid var(--border-hairline);
}
.ed-item-name { flex: 1 1 12rem; align-self: center; min-inline-size: 0; }
.ed-item-fields { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; }
.ed-item-fields .ed-field { inline-size: 11rem; }
.ed-item:has(.ed-action input:checked) .ed-item-name,
.ed-block:has(> .ed-block-head .ed-action input:checked) .ed-block-title {
  text-decoration: line-through;
  color: var(--text-tertiary);
}

/* An action on a list: a checkbox drawn as a quiet button. */
.ed-action {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border-hairline);
  border-radius: 2px;
  font-size: var(--type-minor);
  color: var(--text-secondary);
  cursor: pointer;
  max-inline-size: 100%;
}
.ed-action:has(input:checked) { border-color: var(--danger); color: var(--danger); }

/* The picture that stands for the person (Médias): the face as it is shown
   now, beside the two answers that are not a picture. Each image below
   carries the radio that makes it the face, drawn as the quiet button an
   action is, and in the accent when it is the one. */
.ed-avatar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: 0.25rem 1rem;
}
.ed-avatar-now {
  inline-size: 64px;
  block-size: 64px;
  flex: none;
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-hairline);
}
.ed-avatar-now.is-initials {
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  border-color: var(--border-strong);
  color: var(--accent);
  font-size: var(--type-sub);
  font-weight: var(--weight-semi);
}
.ed-avatar-body { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.1rem; min-inline-size: 0; }
.ed-avatar-body h3 { flex-basis: 100%; margin: 0; font-size: var(--type-body); }
.ed-avatar-mode { display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer; }
.ed-avatar-pick {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border-hairline);
  border-radius: 2px;
  font-size: var(--type-minor);
  color: var(--text-secondary);
  cursor: pointer;
}
.ed-avatar-pick:has(input:checked) { border-color: var(--accent); color: var(--accent); }

/* "Add": a disclosure that opens onto the fields of the new thing. */
.ed-add, .ed-add-row { margin-block: 0.75rem; }
.ed-add > summary, .ed-add-row > summary {
  cursor: pointer;
  color: var(--accent);
  font-weight: var(--weight-medium);
}
.ed-add[open], .ed-add-row[open] {
  border-inline-start: 2px solid var(--accent);
  padding-inline-start: 0.75rem;
}
.ed-add[open] > summary, .ed-add-row[open] > summary { margin-block-end: 0.6rem; }

.ed-save {
  position: sticky;
  bottom: 0;
  background: var(--surface-page);
  padding-block: 0.75rem;
  border-block-start: 1px solid var(--border-hairline);
  z-index: 2;
}

/* ===========================================================================
   v0.5.0 — the reference theme, the overview page and the river's ground
   ===========================================================================
   Four parts, in this order: what every theme draws (icons, the overview
   page's frames, loaders that only Canopée shows), Canopée's own furniture
   (docs/adr/0004), the motion that answers a reader under Canopée, and the
   river's new ground, which is the river's and so every theme's. Nothing
   below names a colour: the palettes are in tokens.css. */

/* ---------- icons ----------
   Inline SVG from templates/_icons.html, stroked in the current colour:
   an icon never has a colour of its own, it takes its text's. */
.ico {
  inline-size: 1.25em;
  block-size: 1.25em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.22em;
}

/* ---------- the overview page (`/about`) ----------
   One frame per thing the page says: who the archive is (the hero), what it
   holds (the strip and the composition), what it already tells beyond names
   and dates (the cards), and what a reader can do for it (the last band).
   Under every theme these are ordinary surfaces; Canopée adds the
   photographs and the motion further down. */
.hero {
  position: relative;
  display: grid;
  /* The headline takes the whole width; the confidence card stands beside
     the lede and the two ways in, and the figures run underneath. */
  grid-template-columns: minmax(0, 1fr) minmax(15rem, 19rem);
  grid-template-areas: "chip chip" "title title" "lede card" "actions card" "strip strip";
  column-gap: calc(var(--step) * 5);
  row-gap: 0;
  align-items: start;
  padding: calc(var(--step) * 6) calc(var(--step) * 4) calc(var(--step) * 4);
  margin: 0 0 var(--section-gap);
  border: var(--section-border);
  border-radius: var(--radius-lg);
  background: var(--section-fill);
  overflow: hidden;
}
.hero-copy { display: contents; }
.hero .eyebrow-chip { grid-area: chip; justify-self: start; }
.hero-title { grid-area: title; }
.hero-lede { grid-area: lede; }
.hero-actions { grid-area: actions; }
.hero-card { grid-area: card; align-self: end; }
.hero .stat-strip { grid-area: strip; margin-block-start: calc(var(--step) * 4); }
.eyebrow-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0 0 calc(var(--step) * 2.4);
  padding: 0.3rem 0.85rem;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--mono);
  font-size: var(--type-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.eyebrow-chip .dot {
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}
.hero-title {
  font-size: var(--type-hero);
  font-weight: var(--weight-strong);
  line-height: 1.06;
  letter-spacing: -0.03em;
  margin: 0 0 calc(var(--step) * 2.2);
  text-wrap: balance;
}
.hero-title > span { display: block; }
.hero-accent { color: var(--accent); }
.hero-lede { font-size: var(--type-sub); line-height: 1.5; margin: 0 0 calc(var(--step) * 3.4); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
.hero-actions .btn { min-block-size: 44px; padding-inline: 1.3rem; font-size: var(--type-body); }
.hero-ring {
  display: inline-grid;
  place-items: center;
  inline-size: 1.9rem;
  block-size: 1.9rem;
  border-radius: 50%;
  border: 1px solid currentColor;
}
.hero-ring .ico { inline-size: 1rem; block-size: 1rem; }

/* How sure the archive is of each parentage, drawn as bars in the
   confidence ramp's own colours: each band as long as its share of the
   links. The count is printed beside the bar and is what is read; the bar
   only compares, so colour is never the carrier (the ramp's rule). */
.hero-card {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-page);
}
.hero-card h2 {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.share-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }
.share-list li { display: grid; grid-template-columns: 1fr auto; gap: 0.3rem 0.8rem; font-size: var(--type-small); }
.share-list .k { color: var(--text-secondary); }
.share-list .k::first-letter, .stat-item .k::first-letter { text-transform: uppercase; }
.share-list .v { font-variant-numeric: tabular-nums; font-weight: var(--weight-semi); }
.share-bar {
  grid-column: 1 / -1;
  block-size: 4px;
  border-radius: 999px;
  background: var(--conf-track);
  overflow: hidden;
}
.share-bar > i {
  display: block;
  block-size: 100%;
  inline-size: var(--share, 0%);
  min-inline-size: 3px;
  border-radius: inherit;
  background: var(--accent);
}
.share-bar[data-band="certain"] > i { background: var(--conf-certain); }
.share-bar[data-band="high"] > i { background: var(--conf-high); }
.share-bar[data-band="medium"] > i { background: var(--conf-medium); }
.share-bar[data-band="low"] > i { background: var(--conf-low); }
.share-bar[data-band="unrated"] > i { background: var(--text-tertiary); }

/* The strip: the counts in one row of icon, number and word. */
.stat-strip {
  grid-column: 1 / -1;
  list-style: none;
  margin: 0;
  padding: 0.9rem 1rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 0.9rem 1rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-page);
}
.stat-item { display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto; column-gap: 0.6rem; align-items: center; }
.stat-ico {
  grid-row: 1 / span 2;
  display: inline-grid;
  place-items: center;
  inline-size: 2.1rem;
  block-size: 2.1rem;
  border-radius: 50%;
  border: 1px solid var(--border-hairline);
  color: var(--accent);
}
.stat-item .n {
  font-size: var(--type-major);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
/* A word is never cut in two here: the column grows instead. */
.stat-item .k { font-size: var(--type-small); color: var(--text-secondary); overflow-wrap: normal; }

/* "Scroll to explore": a mouse whose wheel moves, and the words. It is a
   picture of an instruction, so assistive technology is spared it. */
.scroll-cue {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-end: 0.7rem;
  translate: -50% 0;
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--mono);
  font-size: var(--type-micro);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.scroll-cue > i {
  position: relative;
  inline-size: 18px;
  block-size: 28px;
  border: 1.5px solid currentColor;
  border-radius: 999px;
}
.scroll-cue > i::after {
  content: "";
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 5px;
  inline-size: 3px;
  block-size: 6px;
  translate: -50% 0;
  border-radius: 999px;
  background: currentColor;
}

/* Sections under the hero: an eyebrow, a heading, then the frames. */
.home-section { margin: calc(var(--step) * 7) 0 0; }
.eyebrow {
  margin: 0 0 0.5rem;
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
.home-h2 { margin-top: 0; font-size: var(--type-major); font-weight: var(--weight-medium); letter-spacing: -0.015em; max-inline-size: 30ch; }

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
}
.feature-card {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1.3rem 1.25rem 1.2rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}
.feature-card h3 { margin: 0.35rem 0 0; font-size: var(--type-sub); font-weight: var(--weight-semi); }
.feature-card p { margin: 0; color: var(--text-secondary); font-size: var(--type-minor); }
.feature-ico {
  display: inline-grid;
  place-items: center;
  inline-size: 2.9rem;
  block-size: 2.9rem;
  border-radius: 50%;
  border: 1px solid var(--border-hairline);
  color: var(--accent);
}
.feature-ico .ico { inline-size: 1.35rem; block-size: 1.35rem; }
.feature-go { margin-block-start: auto; padding-block-start: 0.4rem; font-size: var(--type-minor); font-weight: var(--weight-medium); }

/* The last band: what a reader can do for the archive. */
.cta-banner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1.5rem;
  align-items: center;
  margin: calc(var(--step) * 7) 0 0;
  padding: 1rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}
.cta-photo { display: none; }
.cta-copy h2 { margin: 0 0 0.4rem; font-size: var(--type-major); font-weight: var(--weight-medium); letter-spacing: -0.015em; }
.cta-copy p { margin: 0; color: var(--text-secondary); }
@media (max-width: 860px) {
  .hero {
    grid-template-columns: 1fr;
    grid-template-areas: "chip" "title" "lede" "actions" "card" "strip";
    padding: calc(var(--step) * 4) calc(var(--step) * 2.4) calc(var(--step) * 3);
  }
  .hero-card { margin-block-start: calc(var(--step) * 4); }
  .cta-banner { grid-template-columns: 1fr; }
}

/* ---------- loaders (v0.5.0, docs/adr/0004 §4) ----------
   From the reference's loader set: a ring, three dots, ripples, an
   hourglass and a river. They never replace words: a busy button still
   says what it is doing (B2), and the loader moves beside the words,
   hidden from assistive technology. Drawn in the current colour. Only
   Canopée shows them — every other theme keeps Study 06's words alone —
   and `prefers-reduced-motion` stills them like everything else. */
.ld { display: none; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .ld { display: inline-grid; }
.ld {
  place-items: center;
  inline-size: 1.1em;
  block-size: 1.1em;
  flex: none;
  color: inherit;
}
.ld-dots { grid-auto-flow: column; gap: 0.2em; inline-size: auto; }
.ld-dots > i {
  inline-size: 0.32em;
  block-size: 0.32em;
  border-radius: 50%;
  background: currentColor;
  animation: ld-bounce 900ms var(--ease) infinite;
}
.ld-dots > i:nth-child(2) { animation-delay: 120ms; }
.ld-dots > i:nth-child(3) { animation-delay: 240ms; }
@keyframes ld-bounce {
  0%, 80%, 100% { transform: translateY(0); opacity: 0.45; }
  40% { transform: translateY(-0.28em); opacity: 1; }
}
.ld-ring {
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, currentColor 22%, transparent);
  border-block-start-color: currentColor;
  animation: ld-spin 820ms linear infinite;
}
@keyframes ld-spin { to { transform: rotate(1turn); } }
.ld-ripple { position: relative; }
.ld-ripple > i {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid currentColor;
  opacity: 0;
  animation: ld-ripple 1400ms cubic-bezier(0.2, 0.6, 0.3, 1) infinite;
}
.ld-ripple > i:nth-child(2) { animation-delay: 700ms; }
@keyframes ld-ripple {
  0% { transform: scale(0.3); opacity: 0.9; }
  100% { transform: scale(1.6); opacity: 0; }
}
.ld-hourglass .ico { inline-size: 100%; block-size: 100%; animation: ld-flip 1600ms var(--ease) infinite; }
@keyframes ld-flip {
  0%, 70% { transform: rotate(0); }
  100% { transform: rotate(180deg); }
}
.ld-river .ico { inline-size: 100%; block-size: 100%; stroke-dasharray: 4 5; animation: ld-flow 900ms linear infinite; }
@keyframes ld-flow { to { stroke-dashoffset: -18; } }
/* A busy button keeps its words and moves its loader beside them. */
.btn[aria-busy="true"] .ld { margin-inline-start: 0.15rem; }

/* ===========================================================================
   Canopée — the reference theme's furniture (docs/adr/0004)
   ===========================================================================
   Every other theme is a palette. Canopée carries a shape and a tempo as
   well: pill buttons, softened fields and frames, a gold glow under what
   can be pressed, and motion that answers the reader. Everything is written
   under :is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]), so any other theme gives Study 06's
   furniture back exactly. What does not change, here or anywhere: the four
   button variants and their heights, the focus ring (2px at 2px on every
   control), labels above fields, the notice shapes, the chrome budgets. */
[data-theme="canopee"] {
  --btn-radius: 999px;
  --field-radius: 12px;
  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 18px;
  --tempo: 170ms;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  /* Its two photographs, behind the overview's hero and the sign-in, and
     beside the overview's last band (docs/adr/0004). The pictures are
     cached for a day, so one that changes takes a new `?v=`. */
  --kit-hero: url("/static/img/canopee-hero.jpg?v=2");
  --kit-cta: url("/static/img/canopee-cta.jpg?v=2");
  /* Behind the family's notes (docs/adr/0014): soft, neutral, its own. */
  --notes-bg: url("/static/img/notes-canopee.jpg");
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) ::selection { background: rgb(var(--glow-rgb) / 0.32); }

/* The masthead: the page's own ground, a hairline, the logo in its own
   colours, and the section you are in written in the accent. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .masthead {
  background: color-mix(in srgb, var(--surface-page) 92%, transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .brand {
  font-family: var(--face);
  font-size: var(--type-body);
  font-weight: var(--weight-semi);
  letter-spacing: 0.01em;
}
/* The logo as it was drawn: the green tile, the gold X, the pale wreath. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .logo-mark .lm-tile { fill: var(--logo-tile); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .logo-mark .lm-stem { stroke: var(--logo-light); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .logo-mark .lm-leaf { fill: var(--logo-light); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .logo-mark .lm-deep { stop-color: var(--logo-deep); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .logo-mark .lm-gold { stop-color: var(--logo-gold); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .logo-mark .lm-light { stop-color: var(--logo-light); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .brand-mark {
  inline-size: 34px;
  block-size: 34px;
  margin-inline-end: -0.6rem;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .login-logo {
  inline-size: 76px;
  block-size: 76px;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .masthead nav a[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
/* Pills, at the padding Study 06 measured: the three selects stay one row
   at 390px in every language (docs/adr/0002 §9), so the round costs no room. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .lang-form select { border-radius: var(--btn-radius); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .prefs-more { border-radius: 50%; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .chrome-nav > a.btn {
  background: var(--accent);
  border-color: transparent;
  color: var(--on-accent);
  font-weight: var(--weight-semi);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .chrome-nav > a.btn:hover {
  background: color-mix(in srgb, var(--accent) 86%, var(--text-primary));
  box-shadow: 0 8px 24px -10px rgb(var(--glow-rgb) / 0.75);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .initials {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .menu-panel {
  border-radius: var(--radius-lg);
  padding: 8px;
  box-shadow: 0 20px 44px -18px rgb(var(--shadow-rgb) / 0.8);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .search-box > input[type="search"] { border-radius: var(--btn-radius); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .search-suggest {
  border-radius: var(--radius);
  padding: 6px;
  box-shadow: 0 20px 44px -18px rgb(var(--shadow-rgb) / 0.8);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .search-suggest li { border-radius: var(--radius-sm); }

/* Buttons: the same four, as pills. The primary carries an arrow that
   nudges forward under the pointer (and backward in a right-to-left
   script); the arrow is decoration, so it has no text alternative. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn {
  padding-inline: 18px;
  font-weight: var(--weight-semi);
  transition: background-color var(--tempo) var(--ease),
              border-color var(--tempo) var(--ease),
              color var(--tempo) var(--ease),
              box-shadow var(--tempo) var(--ease),
              transform var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.small { padding-inline: 12px; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn:hover { border-color: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn:active { transform: translateY(1px); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.primary:hover { box-shadow: 0 8px 24px -10px rgb(var(--glow-rgb) / 0.75); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.primary:not([aria-busy="true"])::after {
  content: "→" / "";
  display: inline-block;
  transition: transform var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.primary:hover::after { transform: translateX(3px); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"])[dir="rtl"] .btn.primary:not([aria-busy="true"])::after { content: "←" / ""; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"])[dir="rtl"] .btn.primary:hover::after { transform: translateX(-3px); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.ghost:hover { border-color: transparent; color: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.danger:hover { border-color: var(--danger); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn:disabled,
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn[aria-disabled="true"] { transform: none; box-shadow: none; }

/* Focus: the ring every theme draws (2px at 2px), and under it a soft glow
   of the same gold, so the control with the keyboard reads from across a
   room. A glow, never in place of the ring. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(a, button, input, select, textarea, summary, [tabindex]):focus-visible,
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(input, select, textarea):focus {
  box-shadow: 0 0 0 6px rgb(var(--glow-rgb) / 0.2);
}

/* Fields: a softened well, and the accent on the one with the keyboard. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main :is(input[type="text"], input[type="password"], input[type="email"],
  input[type="search"], input[type="number"], input[type="url"], select, textarea) {
  padding-inline-start: 14px;
  transition: border-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(input, select, textarea):focus { border-color: var(--accent); }

/* Frames: a sheen at the top of a raised surface and a deeper shadow, so
   a card sits on the forest floor rather than on paper. Not on a block
   inside a section (level 3 stays recessed), and not in the paper style,
   which has no cards to lift. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]):not([data-style="paper"]) :is(.card, .rec-section, .tree-controls, .stat, .feature-card, .cta-banner):not(:is(.card, .rec-section) *) {
  background-image: linear-gradient(180deg, color-mix(in srgb, var(--text-primary) 3.5%, transparent), transparent 48%);
  box-shadow: 0 1px 0 rgb(var(--shadow-rgb) / 0.2), 0 18px 36px -26px rgb(var(--shadow-rgb) / 0.9);
}

/* Notices: the same four shapes, larger, each carrying its sign — a ring
   with i, a disc with ✓, a diamond with !, a square with × — so the kind
   reads twice, by shape and by mark, and never by colour alone. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .notice {
  border-radius: var(--radius);
  padding-inline: 3rem 1.1rem;
  background-color: color-mix(in srgb, var(--surface-raised) 72%, transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .notice::before {
  display: grid;
  place-items: center;
  inline-size: 20px;
  block-size: 20px;
  inset-inline-start: 1rem;
  inset-block-start: 0.85rem;
  font-family: var(--face);
  font-size: var(--type-micro);
  font-weight: var(--weight-strong);
  line-height: 1;
  content: "i" / "";
  color: var(--text-secondary);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .notice.ok::before { content: "✓" / ""; color: var(--surface-page); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .notice.warn::before,
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .notice-defect::before {
  content: "!" / "";
  inline-size: 22px;
  block-size: 22px;
  inset-inline-start: calc(1rem - 1px);
  transform: none;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  color: var(--surface-page);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .notice.error::before { content: "×" / ""; border-radius: 4px; color: var(--surface-page); }

/* The record's tabs: a segmented pill, the current one filled. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tabs {
  box-shadow: none;
  gap: 0.25rem;
  padding: 4px;
  inline-size: fit-content;
  max-inline-size: 100%;
  background: var(--surface-sunken);
  border: 1px solid var(--border-hairline);
  border-radius: var(--btn-radius);
}
/* When its tabs do not fit — in the river's side panel, in a long language —
   the pill scrolls sideways without a scrollbar, which would sit inside its
   round ends: the side where more tabs wait fades out (ui.js says which), and
   the wheel turns sideways over it. A keyboard reaches every tab as before. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tabs { scrollbar-width: none; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tabs::-webkit-scrollbar { display: none; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tabs[data-more="right"] {
  -webkit-mask-image: linear-gradient(to right, currentColor calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(to right, currentColor calc(100% - 2.5rem), transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tabs[data-more="left"] {
  -webkit-mask-image: linear-gradient(to left, currentColor calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(to left, currentColor calc(100% - 2.5rem), transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tabs[data-more="both"] {
  -webkit-mask-image: linear-gradient(to right, transparent, currentColor 2.5rem, currentColor calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(to right, transparent, currentColor 2.5rem, currentColor calc(100% - 2.5rem), transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab {
  border-block-end: 0;
  border-radius: var(--btn-radius);
  padding-block: 0.45rem;
  padding-inline: 0.95rem;
  transition: background-color var(--tempo) var(--ease), color var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab:hover { background: var(--surface-hover); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab.is-current { background: var(--accent); color: var(--on-accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab.is-current .tab-count { color: var(--on-accent); }

/* Bars and badges: round ends. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .conf-bar,
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .conf-bar > i { border-radius: 999px; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .edit-band { letter-spacing: 0.12em; }

/* The overview page under Canopée: the reference's two photographs (made
   for this theme in Canva, docs/adr/0004 §3) behind the hero and beside
   the last band, under a shade so that the text over them keeps the
   contrast the palette was measured for. A photograph is the page's, never
   a control's: the buttons over it are the ordinary buttons. The pictures
   are cached for a day, so one that changes takes a new `?v=` here, or a
   reader who saw the old one keeps it (the second pass's are `v=2`). */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .hero {
  min-block-size: min(78vh, 46rem);
  border-color: var(--border-hairline);
  background-color: var(--surface-page);
  background-image:
    var(--grain),
    linear-gradient(90deg, var(--surface-page) 0%, color-mix(in srgb, var(--surface-page) 88%, transparent) 34%, color-mix(in srgb, var(--surface-page) 25%, transparent) 72%, color-mix(in srgb, var(--surface-page) 45%, transparent) 100%),
    linear-gradient(0deg, var(--surface-page) 0%, transparent 38%),
    var(--kit-hero);
  background-size: 160px 160px, cover, cover, cover;
  background-position: 0 0, center, center, 70% 40%;
  background-repeat: repeat, no-repeat, no-repeat, no-repeat;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"])[dir="rtl"] .hero {
  background-image:
    var(--grain),
    linear-gradient(270deg, var(--surface-page) 0%, color-mix(in srgb, var(--surface-page) 88%, transparent) 34%, color-mix(in srgb, var(--surface-page) 25%, transparent) 72%, color-mix(in srgb, var(--surface-page) 45%, transparent) 100%),
    linear-gradient(0deg, var(--surface-page) 0%, transparent 38%),
    var(--kit-hero);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .eyebrow-chip {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  color: var(--accent);
  background: color-mix(in srgb, var(--surface-page) 60%, transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .hero-card,
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .stat-strip {
  background: color-mix(in srgb, var(--surface-page) 72%, transparent);
  border-color: color-mix(in srgb, var(--border-strong) 45%, transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .scroll-cue { display: flex; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .hero { padding-block-end: 4.6rem; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .stat-ico { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-ico { border: 0; }
/* Zenze's coloured pastilles, one tint per kind of detail the archive holds:
   water for relations and places, leaf for occupations, amber for dates,
   clay for sources. Each ink is measured at 4.5:1 or more on its disc. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-ico { background: var(--accent-soft); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-ico:is([data-kind="links"], [data-kind="places"]) { background: var(--icon-river); color: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-ico[data-kind="occupations"] { background: var(--icon-tree); color: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-ico:is([data-kind="uncertain"], [data-kind="preserved"]) { background: var(--icon-search); color: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-ico[data-kind="sources"] { background: var(--icon-quality); color: var(--icon-quality-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-card {
  transition: transform var(--tempo) var(--ease), border-color var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .feature-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 38%, transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .cta-banner {
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.4fr) auto;
  padding-block: 0.75rem;
  padding-inline: 0.75rem 1.5rem;
}
@media (max-width: 860px) {
  :is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .cta-banner { grid-template-columns: 1fr; padding-inline: 0.75rem; }
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .cta-photo {
  display: block;
  align-self: stretch;
  min-block-size: 9rem;
  border-radius: var(--radius);
  background: var(--surface-sunken) var(--kit-cta) center 42% / cover;
}

/* ---------- motion under Canopée ----------
   What moves, moves because the reader did: a section rises into place the
   first time it scrolls into view, a count runs up to its number once, the
   primary's arrow nudges under the pointer. Nothing loops but a loader
   while work is out, and the mouse of the scroll cue, three times. ui.js
   sets `js-motion` on the root only when `prefers-reduced-motion` allows
   it, so a page without scripts, or a reader who asked for stillness,
   gets every section in place and every number whole. */
html.js-motion:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 640ms var(--ease), transform 640ms var(--ease);
}
html.js-motion:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) [data-reveal].is-in { opacity: 1; transform: none; }
/* Paper has no scroll: a section that never scrolled into view prints all
   the same. */
@media print {
  html.js-motion[data-theme] [data-reveal] { opacity: 1; transform: none; }
}
html.js-motion:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .scroll-cue > i::after { animation: cue-wheel 1600ms var(--ease) 3; }
@keyframes cue-wheel {
  0% { transform: translateY(0); opacity: 1; }
  70% { transform: translateY(8px); opacity: 0; }
  100% { transform: translateY(0); opacity: 0; }
}

/* ===========================================================================
   The river's ground and lights (v0.5.0, docs/adr/0004 §5)
   ===========================================================================
   After the Selva Verde reference: a forest seen from above, at dusk, with
   contour lines over it; the water lit from within; terrain that follows
   every line. Wherever the river is drawn, all of this is too — except in
   print, where the ground goes back to the plain ink it always was.

   The photograph (made in Canva for the river) sits under a shade that
   keeps every ink above it at the contrast the river was measured for; the
   contour lines are a mask coloured by --rv-contour; mist lies in two
   corners; the drawing's own ground lets all of it through. Nothing on the
   ground depends on the archive: it is the same picture behind every
   person. */
.rv-canvas {
  position: relative;
  isolation: isolate;
  background-color: var(--rv-ink-1);
  background-image:
    radial-gradient(42% 34% at 0% 100%, rgb(var(--rv-mist-rgb) / 0.3), transparent 72%),
    radial-gradient(36% 28% at 100% 0%, rgb(var(--rv-mist-rgb) / 0.22), transparent 72%),
    radial-gradient(125% 92% at 50% 48%, transparent 40%, rgb(var(--rv-shade-rgb) / 0.62) 100%),
    linear-gradient(rgb(var(--rv-shade-rgb) / 0.34), rgb(var(--rv-shade-rgb) / 0.34)),
    url("/static/img/river-ground.jpg?v=7");
  background-size: cover;
  background-position: center;
}
.rv-canvas::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--rv-contour);
  opacity: 0.1;
  -webkit-mask: url("/static/img/river-contours.svg") center / cover no-repeat;
  mask: url("/static/img/river-contours.svg") center / cover no-repeat;
}
.rv-canvas svg { position: relative; }
/* The drawing's own ground is its first rect, and the even half-century
   bands are the other rects in its ground ink (src/river.rs draws both with
   `k-bg`, as river.js does; a circle in that ink is a ring's or a hollow
   dot's, and stays opaque). Translucent, so the ground shows through. */
.rv-svg > rect.k-bg:first-of-type { fill-opacity: 0.16; }
.rv-svg > rect.k-bg:not(:first-of-type) { fill-opacity: 0.08; }
/* The years and generations of the rails stand on the forest now, so they
   carry the same dark halo as the names. */
.rv-svg :is(.k-band-text, .k-band-text-live, .k-accent) {
  paint-order: stroke;
  stroke: var(--rv-ink-1);
  stroke-width: 3px;
  stroke-linejoin: round;
}
/* Zones (river.js): a translucent ground and its nested contour lines, crisp
   at every level, teal under the fixed point and ochre above it. */
.rv-svg .rv-zones { pointer-events: none; animation: rv-fade-in 700ms var(--ease) both; }
.rv-svg .rv-zone-down { fill: var(--rv-zone-down); stroke: var(--rv-zone-down); }
.rv-svg .rv-zone-up { fill: var(--rv-zone-up); stroke: var(--rv-zone-up); }
.rv-svg .rv-zone.z0 { fill-opacity: 0.13; stroke-opacity: 0.85; stroke-width: 1.1; }
.rv-svg .rv-zone.z1 { fill-opacity: 0.08; stroke-opacity: 0.5; stroke-width: 0.8; }
.rv-svg .rv-zone.z2 { fill-opacity: 0.06; stroke-opacity: 0.42; stroke-width: 0.7; }
.rv-svg .rv-zone.z3 { fill-opacity: 0; stroke-opacity: 0.36; stroke-width: 0.6; stroke-dasharray: 2.5 3; }
/* Banks: a thin dark edge under every line, so the water is sharp on the
   forest rather than soft. */
.rv-svg .rv-banks { stroke: var(--rv-ink-0); stroke-opacity: 0.62; pointer-events: none; }
.rv-svg .k-core { stroke: var(--rv-core); }
.rv-svg .k-lit-halo { stroke: var(--rv-lit-halo); }
.rv-svg .k-station { fill: var(--rv-core); }
.rv-svg .rv-cores { pointer-events: none; }
/* The glow under the cores (river.js): the lit route's warm light, faint,
   on attested water only. */
.rv-svg .rv-glow { stroke: var(--rv-lit-halo); pointer-events: none; }
/* Canopée after Selva Verde (the operator's review, 2026-10-24): the
   zones a light wash of teal and ochre with fine pale contour lines, the
   attested water a white thread of light with its glow, the banks barely
   there, and each person a dot in a ring of light. */
[data-theme="canopee"] .rv-svg .rv-zone.z0 { fill-opacity: 0.14; stroke: var(--rv-contour-hi); stroke-opacity: 0.55; stroke-width: 1; }
[data-theme="canopee"] .rv-svg .rv-zone.z1 { fill-opacity: 0.07; stroke: var(--rv-contour-hi); stroke-opacity: 0.32; stroke-width: 0.7; }
[data-theme="canopee"] .rv-svg .rv-zone.z2 { fill-opacity: 0.07; stroke: var(--rv-contour-hi); stroke-opacity: 0.28; stroke-width: 0.6; }
[data-theme="canopee"] .rv-svg .rv-zone.z3 { stroke: var(--rv-contour-hi); stroke-opacity: 0.3; stroke-width: 0.6; stroke-dasharray: 1.5 3.5; }
[data-theme="canopee"] .rv-svg .rv-banks { stroke-opacity: 0.22; }
[data-theme="canopee"] .rv-svg .k-core { stroke: var(--rv-thread); }
[data-theme="canopee"] .rv-svg .rv-glow { stroke: var(--rv-thread-glow); }
[data-theme="canopee"] .rv-svg a.rv-p circle.k-halo { stroke: var(--rv-thread); stroke-opacity: 0.85; }
[data-theme="canopee"] .rv-svg .rv-name-box { fill-opacity: 0.62; stroke: var(--rv-contour-hi); stroke-opacity: 0.45; }
/* Over the photograph the half-century bands are barely a veil, as Selva
   Verde's ground carries no stripes; their rules and years still tell the
   time. */
[data-theme="canopee"] .rv-canvas.rv-alive .rv-svg .k-band-odd { fill-opacity: 0.1; }
[data-theme="canopee"] .rv-canvas.rv-alive .rv-svg .k-band-live { fill-opacity: 0.14; }
/* The family's lines as rivers (docs/adr/0004 §13): the scene draws each
   one in the ground, with its water, its banks and its light, and the
   drawing's own lines step aside while it does; they come back for a
   travel, in print, and wherever the scene cannot draw. */
[data-theme="canopee"] .river { --rv-rivers-3d: 1; }
[data-theme="canopee"] .rv-svg > :is(path, g:not([class])),
[data-theme="canopee"] .rv-svg :is(.rv-zones, .rv-banks, .rv-glow, .rv-cores, g.rv-tail > path, g.rv-stub > path) { transition: opacity 260ms var(--ease); }
@media screen {
  [data-theme="canopee"] .rv-canvas.rv-rivers .rv-svg > :is(path, g:not([class])),
  [data-theme="canopee"] .rv-canvas.rv-rivers .rv-svg :is(.rv-banks, .rv-glow, .rv-cores, g.rv-tail > path, g.rv-stub > path) { opacity: 0; }
  /* The ground is seen at 60° under the rivers (docs/adr/0004 §17): the
     zones' contour lines, a map's seen from straight above, step aside
     with the drawing's lines; the valleys the water carved are in the
     ground itself. */
  [data-theme="canopee"] .rv-canvas.rv-rivers .rv-svg .rv-zones { opacity: 0; }
}
@media print {
  .rv-canvas { background-image: none; }
  .rv-canvas::before { display: none; }
  .rv-svg > rect.k-bg { fill-opacity: 1; }
  .rv-svg :is(.rv-zones, .rv-banks, .rv-glow, .rv-cores) { display: none; }
  .rv-canvas :is(.rv-ground, .rv-water) { display: none; }
}

/* The living ground (river-scene.js, docs/adr/0004 §11): a WebGL forest
   under the drawing and the water's light over it, both the size of the
   drawing and drawn at the screen's own resolution. While they run, the
   photograph and its contour mask are not needed; without WebGL they are
   the ground, as before. The light over the drawing never takes a pointer. */
.rv-canvas.rv-alive { background-image: none; }
.rv-canvas.rv-alive::before { display: none; }
/* The half-century bands were measured over the photograph; over the
   living ground, which is lighter and moves, they need less ink to read. */
.rv-canvas.rv-alive .rv-svg .k-band-odd { fill-opacity: 0.32; }
.rv-canvas.rv-alive .rv-svg .k-band-live { fill-opacity: 0.4; }
.rv-canvas :is(.rv-ground, .rv-water) {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  pointer-events: none;
}
.rv-canvas .rv-ground { z-index: -1; }
.rv-canvas .rv-water { z-index: 1; }
/* The camera (river.js). Zoomed, every person and every word is a map
   symbol: scaled back by the zoom about its own point, so only the
   distances grow; the contour lines and the lit route stay hairlines. */
.rv-svg[data-zoom] :is(a.rv-p, .rv-cs) {
  transform-box: view-box;
  transform: scale(var(--rv-iz, 1));
}
.rv-svg[data-zoom] :is(.rv-zone, .k-band-rule, .rv-lit path) { vector-effect: non-scaling-stroke; }
/* At rest a finger scrolls the page; zoomed, it moves the camera. */
.rv-canvas { touch-action: pan-y; }
.rv-canvas.rv-zoomed { touch-action: none; cursor: grab; }
.rv-canvas.rv-grabbing { cursor: grabbing; }
.rv-zoom { display: inline-flex; align-items: center; gap: 0.5rem; }
.rv-zoom input[type="range"] { inline-size: 7rem; accent-color: var(--rv-accent); }
/* The pause: what it does is the same word whatever its state; the state
   is aria-pressed, and the sign beside the word. */
.rv-still::before { content: "❚❚" / ""; font-size: 0.7em; letter-spacing: -0.12em; }
.rv-still[aria-pressed="true"]::before { content: "▶" / ""; letter-spacing: 0; }
.rv-hint { color: var(--rv-text-3); }

/* The lit route (river.js): a halo, the light, and a thread of light
   running along it from the centre, three times, then still. */
.rv-svg .k-flow {
  stroke: var(--rv-core);
  stroke-dasharray: 1.5 22;
  animation: rv-flow 1500ms linear 3;
}
@keyframes rv-flow { to { stroke-dashoffset: -47; } }
/* The entrance, once per page: the water and the terrain come up, then the
   people, from the first paint — CSS rather than script, so nothing that was
   drawn is hidden and redrawn. river.js takes `rv-intro` off once it has
   played, so the stills a travel lays down later arrive without it. */
@keyframes rv-fade-in { from { opacity: 0; } }
.river.rv-intro .rv-svg > :is(path, g.rv-tail, g.rv-stub, g:not([class])) { animation: rv-fade-in 700ms var(--ease) both; }
.river.rv-intro .rv-svg :is(.rv-zones, .rv-banks, .rv-glow, .rv-cores) { animation: rv-fade-in 1100ms var(--ease) both; }
.river.rv-intro .rv-svg a.rv-p { animation: rv-fade-in 800ms var(--ease) 220ms both; }
/* Arriving: the fixed point's ring breathes twice and is still. */
.river.rv-arrived .rv-svg .k-accent-line {
  transform-box: fill-box;
  transform-origin: center;
  animation: rv-breathe 2000ms var(--ease) 2;
}
@keyframes rv-breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.5); opacity: 0.2; }
}
/* "Cut" is a reader's choice about motion on the river (the settings),
   and it holds for every light the river draws, not only for travel. */
.river[data-river-motion="cut"] .rv-svg * { animation: none !important; }

/* The legend, as a cartographer's: a title in spaced capitals, a compass
   that says which way time runs, and the scale of a generation. */
.rv-legend-title {
  font-size: var(--type-rv-eyebrow);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--rv-text-3);
}
.rv-compass .ico { inline-size: 22px; block-size: 22px; color: var(--rv-text-2); }
.rv-scale > b {
  display: block;
  inline-size: 46px;
  block-size: 6px;
  border: 1px solid var(--rv-text-3);
  border-block-start: 0;
}

/* The itinerary as a journey: each person visited is a numbered stop on
   one line, 01 to 07, as the reference numbers the stations of its path. */
.rv-trail { counter-reset: stop; gap: 2px 10px; }
.rv-trail > li { counter-increment: stop; display: inline-flex; align-items: center; gap: 6px; }
.rv-trail > li::before {
  content: counter(stop, decimal-leading-zero) / "";
  font-family: var(--rv-mono);
  font-size: var(--type-rv-eyebrow);
  letter-spacing: 0.08em;
  color: var(--rv-text-3);
}
.rv-trail > li + li { padding-inline-start: 18px; position: relative; }
.rv-trail > li + li::after {
  content: "";
  position: absolute;
  inset-inline-start: 2px;
  inset-block-start: 50%;
  inline-size: 11px;
  border-block-start: 1px solid var(--rv-rule-3);
}
.rv-trail > li:has(> [aria-current])::before { color: var(--rv-accent); }

/* A loader where the river is going: ripples on the person tapped, while
   their river is fetched (river.js), under every theme — it is the river's,
   like the travel it announces. */
.rv-wait {
  position: absolute;
  z-index: 2;
  inline-size: 34px;
  block-size: 34px;
  translate: -50% -50%;
  pointer-events: none;
  color: var(--rv-lit);
}
.rv-wait > i {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  opacity: 0;
  animation: ld-ripple 1200ms cubic-bezier(0.2, 0.6, 0.3, 1) infinite;
}
.rv-wait > i:nth-child(2) { animation-delay: 600ms; }

/* ===========================================================================
   Canopée on every page (v0.5.0, second pass — the operator's review)
   ===========================================================================
   The first pass dressed the overview and the controls; the record, the tree,
   the administration and the editor were "too lightly touched". This part
   carries the kit onto every page: an icon disc on every section, the person
   as a card, figures as tiles, rows that answer the pointer, and buttons that
   move — a sheen across them under the pointer, a lift, an ink ripple from
   where they were pressed. Everything stays under :is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]), so
   every other theme is Study 06 exactly; nothing here names a colour, and
   prefers-reduced-motion stills it like everything else. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) {
  --ico-leaf: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 19c0-8.3 5.2-14 14-14 0 9-5.8 14-14 14z'/%3E%3Cpath d='M5 19l7.5-7.5'/%3E%3C/svg%3E");
  --ico-person: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='3.6'/%3E%3Cpath d='M5 20.5c0-3.9 3.1-7 7-7s7 3.1 7 7'/%3E%3C/svg%3E");
  --ico-family: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8.5' cy='8.5' r='3'/%3E%3Ccircle cx='16.5' cy='9.5' r='2.4'/%3E%3Cpath d='M3 20c0-3.3 2.5-6 5.5-6s5.5 2.7 5.5 6'/%3E%3Cpath d='M14.4 14.4c.6-.3 1.3-.4 2.1-.4 2.6 0 4.5 2.3 4.5 5.5'/%3E%3C/svg%3E");
  --ico-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='5' width='16' height='15' rx='2.5'/%3E%3Cpath d='M8 3v4M16 3v4M4 10h16'/%3E%3C/svg%3E");
  --ico-pin: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 21s-6.5-6.2-6.5-11a6.5 6.5 0 0 1 13 0C18.5 14.8 12 21 12 21z'/%3E%3Ccircle cx='12' cy='10' r='2.3'/%3E%3C/svg%3E");
  --ico-briefcase: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='7' width='18' height='13' rx='2.5'/%3E%3Cpath d='M9 7V5.5A2.5 2.5 0 0 1 11.5 3h1A2.5 2.5 0 0 1 15 5.5V7'/%3E%3Cpath d='M3 13h18'/%3E%3C/svg%3E");
  --ico-link: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 14a4 4 0 0 0 5.7 0l3-3A4 4 0 0 0 13 5.3l-1 1'/%3E%3Cpath d='M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1'/%3E%3C/svg%3E");
  --ico-pen: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 20h4l10-10-4-4L4 16z'/%3E%3Cpath d='M13.5 6.5l4 4'/%3E%3C/svg%3E");
  --ico-clock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.5'/%3E%3Cpath d='M12 7.5V12l3 2'/%3E%3C/svg%3E");
  --ico-book: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 6.5C10 5 7 4.5 4 5v13c3-.5 6 0 8 1.5 2-1.5 5-2 8-1.5V5c-3-.5-6 0-8 1.5z'/%3E%3Cpath d='M12 6.5V19'/%3E%3C/svg%3E");
  --ico-bars: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 20V11M10 20V5M15 20v-7M20 20V8'/%3E%3C/svg%3E");
  --ico-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l2.2 5.6L20 9.5l-4.5 3.8L17 19l-5-3-5 3 1.5-5.7L4 9.5l5.8-.9z'/%3E%3C/svg%3E");
  --ico-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='5' width='17' height='14' rx='2.5'/%3E%3Ccircle cx='9' cy='10' r='1.8'/%3E%3Cpath d='M4 17l5-4.5 4 3.5 3-2.5 4.5 4'/%3E%3C/svg%3E");
  --ico-river: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5c2-2 4-2 6 0s4 2 6 0 4-2 6 0'/%3E%3Cpath d='M3 15.5c2-2 4-2 6 0s4 2 6 0 4-2 6 0'/%3E%3C/svg%3E");
  --ico-mountain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 19.5 9.5 7.5l3.6 6.1 2.4-3.4 6 9.3Z'/%3E%3Cpath d='m7.4 11.1 2 1.4 2-1.5'/%3E%3C/svg%3E");
  --ico-funnel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5h18l-7 8.2v5.6l-4 2.2v-7.8Z'/%3E%3C/svg%3E");
  --ico-shield: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l7 3v5c0 4.5-3 8.3-7 10-4-1.7-7-5.5-7-10V6z'/%3E%3Cpath d='M9 12l2 2 4-4'/%3E%3C/svg%3E");
  --ico-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='6.5'/%3E%3Cpath d='M20 20l-4.2-4.2'/%3E%3C/svg%3E");
  --ico-gear: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3Cpath d='M12 2.8v2.4M12 18.8v2.4M2.8 12h2.4M18.8 12h2.4M5.5 5.5l1.7 1.7M16.8 16.8l1.7 1.7M5.5 18.5l1.7-1.7M16.8 7.2l1.7-1.7'/%3E%3C/svg%3E");
  --ico-upload: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 16V4M7 9l5-5 5 5'/%3E%3Cpath d='M4 16v3a1.5 1.5 0 0 0 1.5 1.5h13A1.5 1.5 0 0 0 20 19v-3'/%3E%3C/svg%3E");
  --ico-lock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='10.5' width='14' height='10' rx='2.5'/%3E%3Cpath d='M8 10.5V8a4 4 0 0 1 8 0v2.5'/%3E%3C/svg%3E");
  /* The console's rooms (docs/adr/0011), drawn from the line icons of
     templates/_icons.html. */
  --ico-archive: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4' width='17' height='4.5' rx='1.2'/%3E%3Cpath d='M5 8.5V19a1.5 1.5 0 0 0 1.5 1.5h11A1.5 1.5 0 0 0 19 19V8.5'/%3E%3Cpath d='M10 12.5h4'/%3E%3C/svg%3E");
  --ico-server: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='4' width='16' height='7' rx='1.8'/%3E%3Crect x='4' y='13' width='16' height='7' rx='1.8'/%3E%3Cpath d='M8 7.5h.01M8 16.5h.01M12 7.5h5M12 16.5h5'/%3E%3C/svg%3E");
  --ico-accounts: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='8.5' r='3'/%3E%3Cpath d='M3.5 19.5c0-3.3 2.5-6 5.5-6s5.5 2.7 5.5 6'/%3E%3Cpath d='M16 5.5a3 3 0 0 1 0 6'/%3E%3Cpath d='M17.5 13.8c1.9.6 3 2.7 3 5.2'/%3E%3C/svg%3E");
  --ico-connectivity: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 15l6-6'/%3E%3Cpath d='M10.5 6.5l1.4-1.4a4 4 0 0 1 5.7 5.7l-1.4 1.4'/%3E%3Cpath d='M13.5 17.5l-1.4 1.4a4 4 0 0 1-5.7-5.7l1.4-1.4'/%3E%3C/svg%3E");
  --ico-gauge: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.5 17a8 8 0 1 1 15 0'/%3E%3Cpath d='M12 13l3.5-4'/%3E%3Ccircle cx='12' cy='13.5' r='1.2'/%3E%3C/svg%3E");
  --ico-upgrade: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.5'/%3E%3Cpath d='M12 16.5v-9'/%3E%3Cpath d='M8.5 11 12 7.5l3.5 3.5'/%3E%3C/svg%3E");
  --ico-cpu: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='6.5' y='6.5' width='11' height='11' rx='1.5'/%3E%3Crect x='9.5' y='9.5' width='5' height='5' rx='.8'/%3E%3Cpath d='M10 3.5v3M14 3.5v3M10 17.5v3M14 17.5v3M3.5 10h3M3.5 14h3M17.5 10h3M17.5 14h3'/%3E%3C/svg%3E");
  --ico-disk: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cellipse cx='12' cy='6' rx='7.5' ry='2.8'/%3E%3Cpath d='M4.5 6v12c0 1.5 3.4 2.8 7.5 2.8s7.5-1.3 7.5-2.8V6'/%3E%3Cpath d='M4.5 12c0 1.5 3.4 2.8 7.5 2.8s7.5-1.3 7.5-2.8'/%3E%3C/svg%3E");
  --ico-key: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='15' r='4.5'/%3E%3Cpath d='M11.2 11.8 19.5 3.5'/%3E%3Cpath d='M16 7l2.5 2.5M14 9l1.8 1.8'/%3E%3C/svg%3E");
  --ico-download: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4v11'/%3E%3Cpath d='M7.5 10.5 12 15l4.5-4.5'/%3E%3Cpath d='M4.5 19.5h15'/%3E%3C/svg%3E");
  --ico-alert: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4 21 19.5H3z'/%3E%3Cpath d='M12 10v4.5M12 17.2h.01'/%3E%3C/svg%3E");
  --ico-pulse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12h4l2.5-6 4 12 2.5-6h5'/%3E%3C/svg%3E");
  /* A grain finer than a pixel can show, at 4.5 %: it breaks the 8-bit steps
     a dark gradient over a photograph otherwise shows as bands (v0.5.0,
     the operator's review at 1920 × 1080). Grey, so it names no colour. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0 0 0 0 0.045'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
  --ico-chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l6 6-6 6'/%3E%3C/svg%3E");
}

/* ---------- buttons, every one of them ---------- */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
/* The sheen: a band of light that crosses the button once under the pointer. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(105deg, transparent 32%, color-mix(in srgb, var(--text-primary) 18%, transparent) 50%, transparent 68%);
  transform: translateX(-130%);
  transition: transform 720ms var(--ease);
  pointer-events: none;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn:hover::before { transform: translateX(130%); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.primary::before {
  background: linear-gradient(105deg, transparent 32%, color-mix(in srgb, var(--surface-page) 22%, transparent) 50%, transparent 68%);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 20px -12px rgb(var(--shadow-rgb) / 0.9);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.primary:hover { box-shadow: 0 10px 26px -10px rgb(var(--glow-rgb) / 0.85); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn.ghost:hover { background: var(--accent-soft); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .btn:active {
  transform: translateY(0) scale(0.97);
  transition-duration: 70ms;
}
/* The ink of a press, drawn by ui.js where the pointer went down. */
.ripple-ink {
  position: absolute;
  z-index: -1;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.22;
  transform: scale(0);
  pointer-events: none;
  animation: ripple-ink 620ms var(--ease) forwards;
}
@keyframes ripple-ink { to { transform: scale(1); opacity: 0; } }
/* A secondary that leads somewhere carries its own sign: the record's way
   into the river, and the edit button beside a name. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .record-foot .btn[href^="/?p="]::after,
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.person-head-actions, .panel-actions) .btn[href*="/edit"]::after {
  content: "" / "";
  flex: none;
  inline-size: 1rem;
  aspect-ratio: 1;
  background: currentColor;
  -webkit-mask: var(--ico-river) center / contain no-repeat;
  mask: var(--ico-river) center / contain no-repeat;
  transition: transform var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.person-head-actions, .panel-actions) .btn[href*="/edit"]::after {
  -webkit-mask-image: var(--ico-pen);
  mask-image: var(--ico-pen);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.record-foot .btn[href^="/?p="], :is(.person-head-actions, .panel-actions) .btn[href*="/edit"]):hover::after { transform: translateX(2px) rotate(-6deg); }
/* Under Summit the way from the record leads up the mountain. */
[data-theme="summit"] .record-foot .btn[href^="/?p="]::after {
  -webkit-mask-image: var(--ico-mountain);
  mask-image: var(--ico-mountain);
}
/* The edit button beside a name is the record's one action: filled, as the
   kit draws a primary. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-head-actions .btn[href*="/edit"] {
  background: var(--accent);
  border-color: transparent;
  color: var(--on-accent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-head-actions .btn[href*="/edit"]:hover { box-shadow: 0 10px 26px -10px rgb(var(--glow-rgb) / 0.85); }
/* Small round controls turn under the pointer. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .prefs-more { transition: transform var(--tempo) var(--ease), border-color var(--tempo) var(--ease), color var(--tempo) var(--ease); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .prefs-more:hover { transform: rotate(90deg); border-color: var(--accent); color: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .lang-form select { transition: border-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .lang-form select:hover { border-color: var(--accent); box-shadow: 0 0 0 4px rgb(var(--glow-rgb) / 0.12); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .masthead nav a { transition: color var(--tempo) var(--ease), border-color var(--tempo) var(--ease); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .masthead nav a:not([aria-current]):hover { color: var(--accent); border-bottom-color: color-mix(in srgb, var(--accent) 45%, transparent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .account > summary::after { transition: transform var(--tempo) var(--ease); display: inline-block; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .account[open] > summary::after { transform: rotate(180deg); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab { position: relative; overflow: hidden; isolation: isolate; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab:active { transform: scale(0.97); }
/* A link in a page's text: its underline moves off the word under the
   pointer, and takes the accent. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main :is(p, li, td, dd) a:not(.btn) {
  text-underline-offset: 2px;
  transition: color var(--tempo) var(--ease), text-underline-offset var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main :is(p, li, td, dd) a:not(.btn):hover { text-underline-offset: 4px; }

/* ---------- page titles and section discs ---------- */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main h1:not(.hero-title)::after {
  content: "";
  display: block;
  inline-size: 3rem;
  block-size: 3px;
  margin-block-start: 0.55rem;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--accent), transparent);
}
/* Every section says what it holds with a disc, as the kit's cards do: a
   tinted disc with its sign cut out of it. The sign is the section's — a
   person for identity, two for family, a calendar for a life, a pin for
   places — and the leaf where a section has no sign of its own. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.sec-head h2, .rec-section > h2:first-child, .settings-panel legend) {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.sec-head h2, .rec-section > h2:first-child)::before {
  content: "";
  flex: none;
  inline-size: 2rem;
  block-size: 2rem;
  border-radius: 50%;
  background: var(--disc, var(--accent));
  -webkit-mask: var(--sign, var(--ico-leaf)) center / 1.05rem no-repeat, linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask: var(--sign, var(--ico-leaf)) center / 1.05rem no-repeat, linear-gradient(black 0 0);
  mask-composite: exclude;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #identity { --sign: var(--ico-person); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #family { --sign: var(--ico-family); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #life { --sign: var(--ico-calendar); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #places { --sign: var(--ico-pin); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #occupations { --sign: var(--ico-briefcase); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #setup-archive-door { --sign: var(--ico-upload); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #setup-scratch-door { --sign: var(--ico-person); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #setup-gedcom-door { --sign: var(--ico-book); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #relationships { --sign: var(--ico-link); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #notes { --sign: var(--ico-pen); --disc: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #history { --sign: var(--ico-clock); --disc: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #evidence { --sign: var(--ico-book); --disc: var(--icon-quality-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #charts { --sign: var(--ico-bars); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #profile-summary { --sign: var(--ico-star); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .completeness { --sign: var(--ico-bars); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .rec-section:has(.gallery) { --sign: var(--ico-image); --disc: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .rec-section:has(.diag) { --sign: var(--ico-shield); --disc: var(--icon-quality-ink); }
/* The console's rooms (docs/adr/0011): each section its own sign, as the
   record's are, rather than the leaf every one of them fell back to. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #conformity { --sign: var(--ico-shield); --disc: var(--icon-quality-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #anomalies { --sign: var(--ico-alert); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #diagnostics { --sign: var(--ico-shield); --disc: var(--icon-quality-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #bundle-facts { --sign: var(--ico-archive); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #export { --sign: var(--ico-download); --disc: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #health { --sign: var(--ico-pulse); --disc: var(--icon-quality-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #version { --sign: var(--ico-upgrade); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #volumes { --sign: var(--ico-disk); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #compute { --sign: var(--ico-cpu); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #activity { --sign: var(--ico-gauge); --disc: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #roles { --sign: var(--ico-bars); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #registration { --sign: var(--ico-accounts); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #public-address { --sign: var(--ico-connectivity); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #policy { --sign: var(--ico-lock); --disc: var(--icon-quality-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #services { --sign: var(--ico-key); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #entity-counts { --sign: var(--ico-family); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #operations { --sign: var(--ico-gear); --disc: var(--icon-search-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #recent-changes { --sign: var(--ico-clock); --disc: var(--icon-river-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #existing-accounts { --sign: var(--ico-person); --disc: var(--icon-tree-ink); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) #new-account { --sign: var(--ico-pen); --disc: var(--accent); }

/* ---------- the record ---------- */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-head {
  padding: 1.25rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(90% 140% at 0% 0%, rgb(var(--glow-rgb) / 0.1), transparent 60%),
    var(--surface-raised);
  box-shadow: 0 18px 36px -26px rgb(var(--shadow-rgb) / 0.9);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-avatar {
  border-radius: 22px;
  border: 0;
  box-shadow: 0 0 0 3px var(--surface-raised), 0 0 0 5px color-mix(in srgb, var(--accent) 70%, transparent), 0 14px 30px -14px rgb(var(--glow-rgb) / 0.7);
  transition: transform var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) a.person-avatar:hover { transform: scale(1.03); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .panel-avatar { border-radius: 16px; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-head h1 { font-size: var(--type-title); letter-spacing: -0.02em; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-vitals .pv-dates {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.1rem 0.65rem;
  border-radius: 999px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .showcase-strip {
  border-radius: var(--radius);
  background: linear-gradient(90deg, rgb(var(--glow-rgb) / 0.08), transparent 70%), var(--surface-raised);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .showcase-strip > strong {
  font-family: var(--mono);
  font-size: var(--type-label);
  font-weight: var(--weight-normal);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .chip {
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  background: color-mix(in srgb, var(--accent-soft) 70%, transparent);
  transition: border-color var(--tempo) var(--ease), background-color var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .chip:hover { border-color: color-mix(in srgb, var(--accent) 60%, transparent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .fact-chips .fc {
  border-radius: var(--radius);
  border: 1px solid var(--border-hairline);
  background: var(--surface-sunken);
  transition: border-color var(--tempo) var(--ease), transform var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .fact-chips .fc:hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent); transform: translateY(-1px); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .fact-chips dt { color: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.union, .fam-block, .place-card, .link-card, .life-row, .name-block) {
  transition: border-color var(--tempo) var(--ease), transform var(--tempo) var(--ease), background-color var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.union, .fam-block, .place-card, .link-card):hover {
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  transform: translateY(-1px);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .gallery .shot { transition: transform var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease); border-radius: var(--radius); overflow: hidden; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .gallery .shot:hover { transform: translateY(-3px); box-shadow: 0 16px 30px -18px rgb(var(--shadow-rgb) / 0.95); }

/* ---------- tables, everywhere ---------- */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) thead th { border-bottom-color: color-mix(in srgb, var(--accent) 45%, transparent); color: var(--text-secondary); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) tbody tr { transition: background-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) tbody tr:hover { box-shadow: inset 3px 0 0 var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"])[dir="rtl"] tbody tr:hover { box-shadow: inset -3px 0 0 var(--accent); }

/* ---------- the tree ---------- */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .tcard {
  border-radius: 8px;
  transition: transform var(--tempo) var(--ease), border-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .tcard:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow: 0 10px 22px -14px rgb(var(--glow-rgb) / 0.8);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .tcard.is-selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgb(var(--glow-rgb) / 0.45), 0 10px 22px -14px rgb(var(--glow-rgb) / 0.8);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .band-label {
  font-family: var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .tree-legend { border-radius: var(--radius); }

/* ---------- administration and quality ---------- */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) a.stat {
  text-decoration: none;
  transition: transform var(--tempo) var(--ease), border-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) a.stat:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  box-shadow: 0 14px 28px -18px rgb(var(--glow-rgb) / 0.7);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .stat .n { color: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .stat .k { color: var(--text-secondary); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.quality-rule, .pf-group, .ed-add, .ed-add-row, .add-record, .record-edit, .more-fields, .menu-disclosure, .sec-help, .radar-fold, .danger-zone, .raw-fieldset, .pf-prov) > summary {
  cursor: pointer;
  list-style: none;
  transition: color var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.quality-rule, .pf-group, .ed-add, .ed-add-row, .add-record, .record-edit, .more-fields, .menu-disclosure, .sec-help, .radar-fold, .danger-zone, .raw-fieldset, .pf-prov) > summary::-webkit-details-marker { display: none; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.quality-rule, .pf-group, .ed-add, .ed-add-row, .add-record, .record-edit, .more-fields, .menu-disclosure, .sec-help, .radar-fold, .danger-zone, .raw-fieldset, .pf-prov) > summary::before {
  content: "";
  display: inline-block;
  inline-size: 0.9rem;
  block-size: 0.9rem;
  margin-inline-end: 0.45rem;
  vertical-align: -0.1em;
  background: var(--accent);
  -webkit-mask: var(--ico-chev) center / contain no-repeat;
  mask: var(--ico-chev) center / contain no-repeat;
  transition: transform var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"])[dir="rtl"] :is(.quality-rule, .pf-group, .ed-add, .ed-add-row, .add-record, .record-edit, .more-fields, .menu-disclosure, .sec-help, .radar-fold, .danger-zone, .raw-fieldset, .pf-prov) > summary::before { transform: scaleX(-1); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.quality-rule, .pf-group, .ed-add, .ed-add-row, .add-record, .record-edit, .more-fields, .menu-disclosure, .sec-help, .radar-fold, .danger-zone, .raw-fieldset, .pf-prov)[open] > summary::before { transform: rotate(90deg); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.quality-rule, .pf-group) > summary:hover { color: var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .quality-rule {
  transition: border-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .quality-rule:hover { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .quality-rule[open] { border-color: color-mix(in srgb, var(--accent) 55%, transparent); box-shadow: 0 16px 32px -24px rgb(var(--glow-rgb) / 0.7); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) :is(.tab-count, .pf-group-count) {
  display: inline-grid;
  place-items: center;
  min-inline-size: 1.5rem;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .person-tab.is-current .tab-count { background: color-mix(in srgb, var(--on-accent) 16%, transparent); color: var(--on-accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .ed-save {
  background: color-mix(in srgb, var(--surface-page) 88%, transparent);
  border-block-start-color: color-mix(in srgb, var(--accent) 40%, transparent);
  box-shadow: 0 -16px 30px -24px rgb(var(--glow-rgb) / 0.6);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .ed-block { border-radius: var(--radius); transition: border-color var(--tempo) var(--ease); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .ed-block:hover { border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .edit-band {
  background: linear-gradient(90deg, rgb(var(--glow-rgb) / 0.16), transparent 75%);
  color: var(--accent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .search-results li {
  border-radius: var(--radius-sm);
  padding-inline: 0.75rem;
  transition: background-color var(--tempo) var(--ease), box-shadow var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .search-results li:hover { background: var(--surface-hover); box-shadow: inset 3px 0 0 var(--accent); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .settings-panel {
  padding: 1.25rem 1.4rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) input[type="file"] {
  padding: 0.6rem;
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--surface-sunken);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) input[type="file"]::file-selector-button {
  margin-inline-end: 0.8rem;
  padding: 0.35rem 0.9rem;
  border: 0;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: var(--weight-semi);
  cursor: pointer;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) input[type="range"] { accent-color: var(--accent); }

/* ---------- signing in ---------- */
/* The one page a visitor sees first: the hero's landscape behind it, under
   the same shade, and the form as a card in front. */
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main:has(.login-mark) {
  max-width: none;
  min-block-size: 70vh;
  display: grid;
  place-items: center;
  background:
    var(--grain) 0 0 / 160px 160px repeat,
    linear-gradient(180deg, color-mix(in srgb, var(--surface-page) 55%, transparent), var(--surface-page) 95%),
    var(--kit-hero) center 35% / cover;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main:has(.login-mark) > * { inline-size: min(28rem, 100%); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) main:has(.login-mark) .card {
  background: color-mix(in srgb, var(--surface-raised) 90%, transparent);
  box-shadow: 0 30px 60px -30px rgb(var(--shadow-rgb) / 0.95);
}

/* ===========================================================================
   Fusion, Galaxy and Summit — themes with a view of their own
   (docs/adr/0007, docs/adr/0010)
   ===========================================================================
   Each carries the kit — Canopée's furniture above, written for the four —
   in its own palette (tokens.css), with its own round, tempo and
   photographs, and each draws the landing page its own way: Fusion as a
   living tree (static/fusion.js), Galaxy as a solar system
   (static/galaxy.js), Summit as an ascent at sunrise (static/summit.js).
   What does not change: everything the kit keeps, and
   everything every theme keeps. */
[data-theme="fusion"] {
  --btn-radius: 8px;
  --field-radius: 8px;
  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 14px;
  --tempo: 140ms;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --kit-hero: url("/static/img/fusion-hero.jpg");
  --kit-cta: url("/static/img/fusion-cta.jpg");
  /* Behind the family's notes (docs/adr/0014): soft, neutral, its own. */
  --notes-bg: url("/static/img/notes-fusion.jpg");
}
[data-theme="galaxy"] {
  --btn-radius: 999px;
  --field-radius: 14px;
  --radius: 16px;
  --radius-sm: 10px;
  --radius-lg: 22px;
  --tempo: 200ms;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --kit-hero: url("/static/img/galaxy-hero.jpg");
  --kit-cta: url("/static/img/galaxy-cta.jpg");
  /* Behind the family's notes (docs/adr/0014): soft, neutral, its own. */
  --notes-bg: url("/static/img/notes-galaxy.jpg");
}
/* Summit (docs/adr/0010): rounded as the reference's weather cards are,
   and unhurried, as first light is. */
[data-theme="summit"] {
  --btn-radius: 12px;
  --field-radius: 12px;
  --radius: 14px;
  --radius-sm: 10px;
  --radius-lg: 18px;
  --tempo: 240ms;
  --ease: cubic-bezier(0.25, 0.7, 0.25, 1);
  --kit-hero: url("/static/img/summit-hero.jpg");
  --kit-cta: url("/static/img/summit-cta.jpg");
  /* Behind the family's notes (docs/adr/0014): soft, neutral, its own. */
  --notes-bg: url("/static/img/notes-summit.jpg");
}
/* Fusion's tiles: a neon edge along the top, as the reference's dashboard
   panels have, and a deeper shadow. Galaxy's: glass over the night, a
   violet edge and a glow. Not in the paper style, which draws no boxes. */
[data-theme="fusion"]:not([data-style="paper"]) :is(.rec-section, .card, .hero-card, .stat-strip, .radar-card) {
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 30%, transparent), 0 18px 40px -30px rgb(var(--shadow-rgb) / 0.9);
}
[data-theme="fusion"] .masthead { border-bottom-color: color-mix(in srgb, var(--accent) 28%, var(--border-hairline)); }
[data-theme="galaxy"]:not([data-style="paper"]) :is(.rec-section, .card, .hero-card, .stat-strip, .radar-card) {
  background-color: color-mix(in srgb, var(--surface-raised) 84%, transparent);
  border-color: color-mix(in srgb, var(--accent) 24%, var(--border-hairline));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent), 0 24px 60px -36px rgb(var(--glow-rgb) / 0.55);
}
[data-theme="galaxy"] .masthead { border-bottom-color: color-mix(in srgb, var(--accent) 26%, var(--border-hairline)); }
/* Summit's: night-blue cards with the first light along their top edge, as
   the reference's weather cards, and the titles in a book face, as its
   headline. */
[data-theme="summit"]:not([data-style="paper"]) :is(.rec-section, .card, .hero-card, .stat-strip, .radar-card) {
  background-color: color-mix(in srgb, var(--surface-raised) 90%, transparent);
  border-color: color-mix(in srgb, var(--accent) 16%, var(--border-hairline));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 22%, transparent), 0 22px 50px -34px rgb(var(--glow-rgb) / 0.45);
}
[data-theme="summit"] .masthead { border-bottom-color: color-mix(in srgb, var(--accent) 22%, var(--border-hairline)); }
[data-theme="summit"] :is(main h1, .hero h1, .cta-banner h2) { font-family: var(--serif); font-weight: var(--weight-normal); letter-spacing: 0; }

/* Clarity (docs/adr/0013): the reference dashboard's furniture on white —
   cards a hairline and a soft shadow apart from a pale ground, rounded as
   its panels are, quick as a working tool is. The kit's shadows are drawn
   for a dark ground; on this one they would be bruises, so they are
   lighter here. */
[data-theme="clarity"] {
  --btn-radius: 10px;
  --field-radius: 10px;
  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 16px;
  --tempo: 150ms;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --kit-hero: url("/static/img/clarity-hero.jpg?v=2");
  --kit-cta: url("/static/img/clarity-cta.jpg?v=2");
  /* Behind the family's notes (docs/adr/0014): soft, neutral, its own. */
  --notes-bg: url("/static/img/notes-clarity.jpg?v=2");
}
/* Clarity's frames (the operator's second look, 2026-10-22: "the theme is
   white and the background is white also … it feels like we are writing on
   paper"). The ground is a blue-grey every box stands off; a box has an
   edge, a shadow that reads, and along its top a strip of its category's
   colour — the colour of its sign; its title sits in a band over a rule; a
   pack of fields is a pale panel whose legend is a chip; each figure's tile
   wears one of five inks. Not in the paper style, which draws no boxes. */
[data-theme="clarity"]:not([data-style="paper"]) :is(.rec-section, .card, .hero-card, .stat-strip, .radar-card, .kpi, .stat, .tree-controls) {
  background-color: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08), 0 8px 20px -10px rgb(var(--shadow-rgb) / 0.18);
}
[data-theme="clarity"]:not([data-style="paper"]) .rec-section { border-top: 3px solid var(--disc, var(--cl-ink-4)); }
[data-theme="clarity"]:not([data-style="paper"]) :is(.rec-section, .card, .completeness) .stat { box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08); }
[data-theme="clarity"]:not([data-style="paper"]) :is(.rec-section > h2:first-child, .rec-section > .sec-head) {
  margin: 0 0 0.9rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--border-hairline);
}
[data-theme="clarity"]:not([data-style="paper"]) .rec-section > .sec-head h2 { margin-bottom: 0; }
/* The sign as a framed chip rather than a disc. */
[data-theme="clarity"] :is(.sec-head h2, .rec-section > h2:first-child)::before { border-radius: 10px; }
/* The figures' tiles: one ink each, along the top and in the chip. */
[data-theme="clarity"] .kpi { --kpi-ink: var(--cl-ink-1); --kpi-tint: var(--cl-tint-1); }
[data-theme="clarity"] .kpi:nth-child(5n + 2) { --kpi-ink: var(--cl-ink-2); --kpi-tint: var(--cl-tint-2); }
[data-theme="clarity"] .kpi:nth-child(5n + 3) { --kpi-ink: var(--cl-ink-3); --kpi-tint: var(--cl-tint-3); }
[data-theme="clarity"] .kpi:nth-child(5n + 4) { --kpi-ink: var(--cl-ink-4); --kpi-tint: var(--cl-tint-4); }
[data-theme="clarity"] .kpi:nth-child(5n + 5) { --kpi-ink: var(--cl-ink-5); --kpi-tint: var(--cl-tint-5); }
[data-theme="clarity"]:not([data-style="paper"]) .kpi { border-top: 3px solid var(--kpi-ink); }
[data-theme="clarity"] .kpi-icon {
  border-radius: 10px;
  border-color: transparent;
  background: var(--kpi-tint);
  color: var(--kpi-ink);
}
/* A pack of fields: a pale panel inside its white box, its legend a chip. */
[data-theme="clarity"]:not([data-style="paper"]) :is(fieldset, .export-choice, .service-form) {
  background: var(--cl-pack);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  padding: 0.9rem 1rem 1rem;
}
[data-theme="clarity"]:not([data-style="paper"]) :is(.export-choice, .service-form) fieldset {
  background: none;
  border: 0;
  padding: 0;
}
[data-theme="clarity"]:not([data-style="paper"]) legend {
  padding: 0.15rem 0.7rem;
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  background: var(--surface-raised);
  color: var(--text-secondary);
}
[data-theme="clarity"] :is(input[type="text"], input[type="password"], input[type="email"], input[type="search"], input[type="number"], input[type="url"], input[type="file"], select, textarea) {
  background-color: var(--surface-raised);
}
[data-theme="clarity"] .choice-card { background: var(--surface-raised); }
[data-theme="clarity"] .pcard-in { background: var(--surface-raised); }
[data-theme="clarity"] thead th { background: var(--surface-sunken); }
/* A notice says what kind it is by its ground as well as its sign. */
[data-theme="clarity"]:not([data-style="paper"]) .notice { background: var(--cl-tint-1); border-color: color-mix(in srgb, var(--cl-ink-1) 25%, transparent); }
[data-theme="clarity"]:not([data-style="paper"]) .notice.ok { background: var(--cl-tint-5); border-color: color-mix(in srgb, var(--cl-ink-5) 25%, transparent); }
[data-theme="clarity"]:not([data-style="paper"]) .notice.warn { background: var(--cl-tint-4); border-color: color-mix(in srgb, var(--cl-ink-4) 25%, transparent); }
[data-theme="clarity"]:not([data-style="paper"]) .notice.error { background: var(--cl-tint-3); border-color: color-mix(in srgb, var(--cl-ink-3) 25%, transparent); }
[data-theme="clarity"] .admin-nav { background: var(--surface-raised); box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08); }
[data-theme="clarity"] .person-tabs { background: var(--surface-raised); box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08); }
/* Inside a box, what sat sunk into the page is a pack: the pale blue of a
   field group, not the page's grey showing through a hole in the box. */
[data-theme="clarity"]:not([data-style="paper"]) :is(.rec-section .name-block, .rec-section .union, .rec-section .fam-block, .rec-section .shot, :is(.rec-section, .card) .card, .tree-controls .field-group, .link-card, .raw-json, .service-card, .fact-chips .fc, .raw-fieldset, .danger-zone) {
  background: var(--cl-pack);
  box-shadow: none;
}
/* A finding is a white row with its level along its edge, and each area a
   heading drawn out by a rule to the box's edge. */
[data-theme="clarity"]:not([data-style="paper"]) .check {
  background: var(--surface-raised);
  border-inline-start: 3px solid var(--conf-certain);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.06);
}
[data-theme="clarity"]:not([data-style="paper"]) .check:has(.lvl-warn) { border-inline-start-color: var(--warning); }
[data-theme="clarity"]:not([data-style="paper"]) .check:has(.lvl-fail) { border-inline-start-color: var(--danger); }
[data-theme="clarity"] .checks-area { display: flex; align-items: center; gap: 0.75rem; }
[data-theme="clarity"] .checks-area::after { content: ""; flex: 1; border-block-start: 1px solid var(--border-hairline); }
/* What the record could say is a section like the others: its strip on top
   rather than an accent down its side, and its sign in a chip. */
[data-theme="clarity"]:not([data-style="paper"]) .completeness {
  border: 1px solid var(--border-hairline);
  border-top: 3px solid var(--disc);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08), 0 8px 20px -10px rgb(var(--shadow-rgb) / 0.18);
}
[data-theme="clarity"] .completeness > h2:first-child { display: flex; align-items: center; gap: 0.7rem; }
[data-theme="clarity"]:not([data-style="paper"]) .completeness > h2:first-child {
  margin: 0 0 0.9rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--border-hairline);
}
[data-theme="clarity"] .completeness > h2:first-child::before {
  content: "";
  flex: none;
  inline-size: 2rem;
  block-size: 2rem;
  border-radius: 10px;
  background: var(--disc);
  -webkit-mask: var(--sign) center / 1.05rem no-repeat, linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask: var(--sign) center / 1.05rem no-repeat, linear-gradient(black 0 0);
  mask-composite: exclude;
}
/* The record's editor: the tab on screen is a box, its strip in the colour
   of what it writes, and the save a white bar that floats over the page. */
[data-theme="clarity"] #fiche { --ed-ink: var(--icon-river-ink); }
[data-theme="clarity"] #famille { --ed-ink: var(--icon-tree-ink); }
[data-theme="clarity"] #evenements { --ed-ink: var(--icon-search-ink); }
[data-theme="clarity"] #sources { --ed-ink: var(--icon-quality-ink); }
[data-theme="clarity"] #medias { --ed-ink: var(--cl-ink-3); }
[data-theme="clarity"] #profil { --ed-ink: var(--cl-ink-2); }
[data-theme="clarity"]:not([data-style="paper"]) .ed-panel {
  padding: 1.25rem 1.25rem 0.75rem;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-top: 3px solid var(--ed-ink, var(--cl-ink-4));
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08), 0 8px 20px -10px rgb(var(--shadow-rgb) / 0.18);
}
[data-theme="clarity"]:not([data-style="paper"]) .ed-save {
  bottom: 0.75rem;
  margin-block-start: 1rem;
  padding-inline: 1rem;
  background: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.08), 0 10px 28px -12px rgb(var(--shadow-rgb) / 0.35);
}
/* The logo's white tile keeps its shape on a white ground. */
[data-theme="clarity"] .logo-mark .lm-tile { stroke: var(--border-hairline); stroke-width: 2; }
[data-theme="clarity"] .masthead {
  background: var(--surface-raised);
  border-bottom-color: var(--border-hairline);
  box-shadow: 0 1px 3px rgb(var(--shadow-rgb) / 0.08);
}
[data-theme="clarity"] main:has(.login-mark) .card { box-shadow: 0 18px 40px -24px rgb(var(--shadow-rgb) / 0.35); }
/* The way from the record leads into the funnel. */
[data-theme="clarity"] .record-foot .btn[href^="/?p="]::after {
  -webkit-mask-image: var(--ico-funnel);
  mask-image: var(--ico-funnel);
}

/* The river's page in their palette. The river keeps Study 06's ground
   under every theme (tokens.css `.river`), because its water ramp is drawn
   for it; a view is not the river, and takes its theme's colours back. */
:is([data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .river {
  --surface-page: inherit; --surface-raised: inherit; --surface-sunken: inherit; --surface-hover: inherit;
  --border-hairline: inherit; --border-strong: inherit;
  --text-primary: inherit; --text-secondary: inherit; --text-tertiary: inherit;
  --accent: inherit; --focus-ring: inherit; --danger: inherit;
  --rv-ink-0: var(--surface-page); --rv-ink-1: var(--surface-sunken); --rv-ink-2: var(--surface-sunken);
  --rv-ink-3: var(--surface-raised); --rv-ink-4: var(--surface-hover); --rv-chip-on: var(--accent-soft);
  --rv-rule-1: var(--border-hairline); --rv-rule-2: var(--border-hairline);
  --rv-rule-3: var(--border-strong); --rv-rule-4: var(--border-strong);
  --rv-text-1: var(--text-primary); --rv-text-2: var(--text-secondary); --rv-text-3: var(--text-tertiary);
  --rv-accent: var(--accent); --rv-accent-hi: var(--focus-ring); --rv-warn: var(--danger);
  --rv-band-odd: var(--surface-sunken); --rv-band-live: var(--surface-sunken); --rv-band-rule: var(--border-hairline);
  --rv-band-text: var(--text-tertiary); --rv-band-text-live: var(--accent);
  --rv-data: var(--text-secondary); --rv-name: var(--text-primary); --rv-centre: var(--text-primary);
  --rv-core: var(--text-primary);
}
[data-theme="fusion"] .river { --rv-water: var(--tv-vein); --rv-lit: var(--tv-vein-soft); --rv-lit-halo: var(--tv-vein); --rv-glow: var(--tv-leaf-living); }
[data-theme="galaxy"] .river { --rv-water: var(--gx-link); --rv-lit: var(--gx-link-hot); --rv-lit-halo: var(--gx-link); --rv-glow: var(--gx-planet-living); }
[data-theme="summit"] .river { --rv-water: var(--sm-trail); --rv-lit: var(--sm-trail-hot); --rv-lit-halo: var(--sm-trail); --rv-glow: var(--sm-camp-living); }
[data-theme="clarity"] .river { --rv-water: var(--cl-link); --rv-lit: var(--cl-link-hot); --rv-lit-halo: var(--cl-link-hot); --rv-glow: var(--cl-living); }

/* The stage: a dashboard's grid under the tree, a nebula under the sky.
   The river's photograph and contour lines are the river's. */
.river[data-view="tree"] .rv-canvas {
  background-color: var(--surface-page);
  background-image:
    radial-gradient(55% 50% at 50% 34%, color-mix(in srgb, var(--tv-vein) 9%, transparent), transparent 72%),
    radial-gradient(70% 40% at 50% 100%, color-mix(in srgb, var(--tv-vein) 7%, transparent), transparent 70%),
    var(--tv-grid);
  background-size: 100% 100%, 100% 100%, 48px 48px;
  background-position: center, center, 0 0;
}
/* Galaxy's sky (2026-10-27): a galaxy made in Canva, its core behind the
   sun, a little darkened so that the drawing reads over its arms. */
[data-theme="galaxy"] { --gx-sky: url("/static/img/galaxy-sky.jpg?v=1"); }
.river[data-view="galaxy"] .rv-canvas {
  background-color: var(--surface-page);
  background-image:
    linear-gradient(color-mix(in srgb, var(--gx-well) 30%, transparent), color-mix(in srgb, var(--gx-well) 30%, transparent)),
    var(--gx-sky);
  background-size: 100% 100%, cover;
  background-position: center, center;
  background-repeat: no-repeat;
}
/* The dawn behind the mountain: the sky summit.js paints, for a page whose
   script has not run yet. */
.river[data-view="summit"] .rv-canvas {
  background-color: var(--surface-page);
  background-image: linear-gradient(180deg, var(--sm-sky-night) 0%, var(--sm-sky-dawn) 48%, var(--sm-sky-glow) 68%, var(--sm-valley) 82%);
  background-size: 100% 100%;
}
/* The funnel's stage: a white sheet with the faintest of grids, as the
   reference's chart panels. */
.river[data-view="funnel"] .rv-canvas {
  background-color: var(--cl-stage);
  background-image:
    radial-gradient(60% 55% at 45% 45%, color-mix(in srgb, var(--cl-slice-3) 7%, transparent), transparent 72%),
    linear-gradient(var(--border-hairline) 1px, transparent 1px),
    linear-gradient(90deg, var(--border-hairline) 1px, transparent 1px);
  background-size: 100% 100%, 56px 56px, 56px 56px;
  background-position: center, -1px -1px, -1px -1px;
}
.river:is([data-view="tree"], [data-view="galaxy"], [data-view="summit"], [data-view="funnel"], [data-view="scroll"]) .rv-canvas::before { display: none; }
/* The server's river is the page without scripts. Under a view it waits,
   unseen, for the view's drawing to replace it — and shows after a moment
   if no script ever does. */
.river:is([data-view="tree"], [data-view="galaxy"], [data-view="summit"], [data-view="funnel"], [data-view="scroll"]) .rv-canvas > svg.rv-svg:not([data-view]) {
  opacity: 0;
  animation: rv-await-view 0.25s linear 1.6s forwards;
}
@keyframes rv-await-view { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .river:is([data-view="tree"], [data-view="galaxy"], [data-view="summit"], [data-view="funnel"], [data-view="scroll"]) .rv-canvas > svg.rv-svg:not([data-view]) { opacity: 1; animation: none; }
}
/* Names (river.js `names`): the far generations' hidden while only the near
   ones are asked for; the ones a drawing left out, on a ground of their own
   with a thread to their person. */
.river.rv-names-near .rv-svg a.rv-p[data-far] :is(text, .tv-panel, .tv-leader) { display: none; }
.rv-svg .rv-names { pointer-events: none; }
.rv-svg .rv-name-box { fill: var(--surface-raised); fill-opacity: 0.92; stroke: var(--border-strong); stroke-width: 0.8; }
.rv-svg .rv-name-text { fill: var(--text-primary); font-family: var(--face); }
.rv-svg .rv-name-thread { stroke: var(--text-secondary); stroke-width: 0.8; stroke-opacity: 0.8; }
.rv-names-btn[aria-pressed="true"],
.rv-whole[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* ---- the tree in its glass (fusion.js) ---- */
.tv-svg .tv-veil { fill: var(--surface-page); fill-opacity: 0.38; }
.tv-svg .tv-glass-edge { stop-color: var(--tv-glass); stop-opacity: 0.1; }
.tv-svg .tv-glass-mid { stop-color: var(--tv-glass); stop-opacity: 0.02; }
.tv-svg .tv-wall { stroke: var(--tv-glass); stroke-opacity: 0.35; stroke-width: 1; }
.tv-svg :is(.tv-rim, .tv-floor) { fill: none; stroke: var(--tv-glass); }
.tv-svg .tv-rim.tv-ring-front { stroke-opacity: 0.55; stroke-width: 1.4; }
.tv-svg .tv-rim.tv-ring-back { stroke-opacity: 0.3; stroke-width: 1; }
.tv-svg .tv-floor.tv-ring-front { stroke-opacity: 0.42; stroke-width: 1; stroke-dasharray: 6 4; }
.tv-svg .tv-floor.tv-ring-back { stroke-opacity: 0.2; stroke-width: 0.8; stroke-dasharray: 2 5; }
.tv-svg .tv-floor-0 { stroke: var(--tv-vein); }
.tv-svg .tv-branch-glow { fill: none; stroke: var(--tv-vein); stroke-opacity: 0.14; stroke-linecap: round; }
.tv-svg .tv-branch { fill: none; stroke: var(--tv-bark-lit); stroke-opacity: 0.85; }
.tv-svg .tv-faint { opacity: 0.45; }
.tv-svg .tv-vein { fill: none; stroke: var(--tv-vein); stroke-opacity: 0.95; }
.tv-svg .tv-vein-d { stroke-opacity: 0.7; }
.tv-svg .tv-vein-s { stroke: var(--tv-vein-soft); stroke-opacity: 0.55; }
/* A branch's side of the person in the middle (2026-10-27): below, toward
   the ancestors, orange; above, toward the descendants, green. */
.tv-svg .tv-branch-glow.tv-down { stroke: var(--tv-down); stroke-opacity: 0.2; }
.tv-svg .tv-branch-glow.tv-up { stroke: var(--tv-up); stroke-opacity: 0.2; }
.tv-svg .tv-branch.tv-down { stroke: color-mix(in srgb, var(--tv-down) 42%, var(--tv-bark-lit)); }
.tv-svg .tv-branch.tv-up { stroke: color-mix(in srgb, var(--tv-up) 42%, var(--tv-bark-lit)); }
.tv-svg .tv-vein.tv-down { stroke: var(--tv-down); }
.tv-svg .tv-vein.tv-up { stroke: var(--tv-up); }
.tv-svg .tv-vein-s.tv-down { stroke: var(--tv-down-soft); }
.tv-svg .tv-vein-s.tv-up { stroke: var(--tv-up-soft); }
.tv-svg .tv-twig { fill: none; stroke: var(--tv-glass); stroke-opacity: 0.7; }
.tv-svg .tv-twig-tip { fill: var(--tv-glass); }
.tv-svg .tv-bud { stroke-dasharray: 2 2; }
.tv-svg .tv-end { fill: var(--surface-page); stroke: var(--tv-glass); stroke-width: 1.4; }
.tv-svg .tv-count .k-data { fill: var(--text-primary); }
.tv-svg .tv-leaf.tv-living { fill: var(--tv-leaf-living); }
.tv-svg .tv-leaf-glow.tv-living { fill: var(--tv-leaf-living); fill-opacity: 0.22; }
.tv-svg .tv-leaf.tv-gone { fill: var(--tv-leaf-gone); }
.tv-svg .tv-leaf-glow.tv-gone { fill: var(--tv-leaf-gone); fill-opacity: 0.16; }
.tv-svg .tv-leaf.tv-hidden { fill: var(--surface-page); stroke: var(--tv-leaf-hidden); }
.tv-svg .tv-halo { fill: var(--accent); fill-opacity: 0.16; }
.tv-svg .tv-ring { stroke: var(--accent); }
.tv-svg .tv-heart { fill: var(--accent); }
/* The panels the names are written on, as the reference's floating screens. */
.tv-svg .tv-panel { fill: var(--tv-panel); fill-opacity: 0.86; stroke: var(--tv-panel-line); stroke-opacity: 0.9; stroke-width: 1; }
.tv-svg .tv-panel-centre { stroke: var(--accent); stroke-width: 1.4; }
.tv-svg .tv-leader { stroke: var(--tv-panel-line); stroke-width: 1; stroke-opacity: 0.9; }
.tv-svg .tv-name { fill: var(--text-primary); font-family: var(--face); }
.tv-svg .tv-data { fill: var(--text-secondary); }

/* ---- the solar system (galaxy.js) ---- */
.gx-svg .gx-bg { fill: var(--surface-page); fill-opacity: 0; }
/* A dark well round the sun, under the drawing, over the galaxy's core. */
.gx-svg .gx-well-in { stop-color: var(--gx-well); stop-opacity: 0.62; }
.gx-svg .gx-well-mid { stop-color: var(--gx-well); stop-opacity: 0.42; }
.gx-svg .gx-well-out { stop-color: var(--gx-well); stop-opacity: 0; }
.gx-svg .gx-orbit { fill: none; stroke: var(--gx-orbit); stroke-opacity: 0.55; stroke-width: 1; stroke-dasharray: 3 5; }
.gx-svg .gx-orbit-0 { stroke-dasharray: 1 4; }
/* A star's field, wider as more descendants come from it. */
.gx-svg .gx-field-in { stop-color: var(--gx-field); stop-opacity: 0; }
.gx-svg .gx-field-mid { stop-color: var(--gx-field); stop-opacity: 0.03; }
.gx-svg .gx-field-out { stop-color: var(--gx-field); stop-opacity: 0.1; }
.gx-svg .gx-field-edge { fill: none; stroke: var(--gx-field); stroke-opacity: 0.38; stroke-width: 0.8; stroke-dasharray: 1.5 3.5; }
.gx-svg .gx-field-inner { stroke-opacity: 0.2; }
/* The filaments: violet toward the descendants, white toward the ancestors
   (2026-10-27), each with a halo and, when attested, a thread of light. */
.gx-svg .gx-link-halo { fill: none; stroke-opacity: 0.16; }
.gx-svg .gx-link-halo.gx-up { stroke: var(--gx-up); }
.gx-svg .gx-link-halo.gx-down { stroke: var(--gx-down); stroke-opacity: 0.1; }
.gx-svg .gx-link { stroke-opacity: 0.85; }
.gx-svg .gx-link.gx-up { stroke: var(--gx-up); }
.gx-svg .gx-link.gx-down { stroke: var(--gx-down); }
.gx-svg .gx-link-d { stroke-opacity: 0.65; }
.gx-svg .gx-link-s { stroke-opacity: 0.55; }
.gx-svg .gx-thread { fill: none; stroke: var(--gx-sun-core); stroke-opacity: 0.8; }
.gx-svg .gx-union { stroke: none; }
.gx-svg .gx-union.gx-up { fill: var(--gx-up); }
.gx-svg .gx-union.gx-down { fill: var(--gx-down); }
.gx-svg .gx-tail { fill: none; stroke: var(--gx-orbit); stroke-opacity: 0.8; stroke-width: 1.2; }
.gx-svg .gx-tail-tip { fill: var(--gx-link); }
.gx-svg .gx-stub { stroke-dasharray: 2 2; }
.gx-svg .gx-end { fill: var(--surface-page); stroke: var(--gx-planet-hidden); stroke-width: 1.4; }
/* The sun: corona, rays, a lit disc, its heart. */
.gx-svg .gx-corona { fill: var(--gx-sun); }
.gx-svg .gx-corona-3 { fill-opacity: 0.07; }
.gx-svg .gx-corona-2 { fill-opacity: 0.15; }
.gx-svg .gx-sun-rays { fill: none; stroke: var(--gx-sun); stroke-opacity: 0.6; stroke-width: 1.2; stroke-linecap: round; }
.gx-svg .gx-sun { fill: url(#gx-sun-disc); }
.gx-svg .gx-sun-hi { stop-color: var(--gx-sun-core); }
.gx-svg .gx-sun-edge { stop-color: var(--gx-sun); }
.gx-svg .gx-sun-core { fill: var(--gx-sun-core); }
/* The stars: lit spheres; the living with four rays, those gone with a
   ring. */
.gx-svg .gx-sph-hi { stop-color: var(--gx-sun-core); }
.gx-svg .gx-sph-living { stop-color: var(--gx-planet-living); }
.gx-svg .gx-sph-living-deep { stop-color: color-mix(in srgb, var(--gx-planet-living) 55%, var(--gx-well)); }
.gx-svg .gx-sph-gone { stop-color: var(--gx-planet-gone); }
.gx-svg .gx-sph-gone-deep { stop-color: color-mix(in srgb, var(--gx-planet-gone) 50%, var(--gx-well)); }
.gx-svg .gx-planet.gx-living { fill: url(#gx-sphere-living); }
.gx-svg .gx-planet-glow.gx-living { fill: var(--gx-planet-living); fill-opacity: 0.2; }
.gx-svg .gx-spikes { fill: none; stroke: var(--gx-planet-living); stroke-opacity: 0.6; stroke-width: 0.8; stroke-linecap: round; }
.gx-svg .gx-planet.gx-gone { fill: url(#gx-sphere-gone); }
.gx-svg .gx-planet-glow.gx-gone { fill: var(--gx-planet-gone); fill-opacity: 0.18; }
.gx-svg .gx-planet-ring { fill: none; stroke: var(--gx-planet-gone); stroke-opacity: 0.75; stroke-width: 0.9; }
.gx-svg .gx-planet.gx-hidden { fill: var(--surface-page); stroke: var(--gx-planet-hidden); }
.gx-svg .gx-name { fill: var(--text-primary); font-family: var(--face); }
.gx-svg .gx-data { fill: var(--text-secondary); }

/* ---- the funnel (clarity.js), redrawn 2026-10-27 after a kit made in
   Canva: rings of tinted glass apart from one another, each a thin disc
   with a light along its far side and fine rims; pearls on their fronts;
   threads of ink edged in light; names on small frosted labels. ---- */
.cl-svg .cl-bg { fill: var(--cl-stage); fill-opacity: 0; }
.cl-svg .cl-edge { stop-opacity: 0.62; }
.cl-svg .cl-mid { stop-opacity: 0.22; }
.cl-svg .cl-hi { stop-color: var(--cl-gloss); stop-opacity: 0.62; }
.cl-svg .cl-s1 { stop-color: var(--cl-slice-1); }
.cl-svg .cl-s2 { stop-color: var(--cl-slice-2); }
.cl-svg .cl-s3 { stop-color: var(--cl-slice-3); }
.cl-svg .cl-s4 { stop-color: var(--cl-slice-4); }
.cl-svg .cl-s5 { stop-color: var(--cl-slice-5); }
.cl-svg .cl-ring { stroke: none; }
.cl-svg .cl-ring-edge { fill-opacity: 0.8; stroke: var(--cl-rim); stroke-opacity: 0.55; stroke-width: 0.6; }
.cl-svg .cl-rim-1 { fill: var(--cl-slice-1); }
.cl-svg .cl-rim-2 { fill: var(--cl-slice-2); }
.cl-svg .cl-rim-3 { fill: var(--cl-slice-3); }
.cl-svg .cl-rim-4 { fill: var(--cl-slice-4); }
.cl-svg .cl-rim-5 { fill: var(--cl-slice-5); }
.cl-svg .cl-ring-light { fill: none; stroke: var(--cl-gloss); stroke-width: 1.4; stroke-opacity: 0.6; stroke-linecap: round; }
.cl-svg .cl-rim-back { fill: none; stroke: var(--cl-rim); stroke-width: 0.9; stroke-opacity: 0.7; }
.cl-svg .cl-rim-front { fill: none; stroke: var(--cl-rim); stroke-width: 1.5; }
.cl-svg .cl-leader { stroke: var(--cl-figure); stroke-width: 0.8; stroke-opacity: 0.5; }
.cl-svg .cl-leader-tip { fill: var(--cl-figure); fill-opacity: 0.7; }
.cl-svg .cl-figure { fill: var(--cl-figure); font-family: var(--face); font-variant-numeric: tabular-nums; }
.cl-svg .cl-gen { fill: var(--text-tertiary); }
.cl-svg :is(.cl-figure, .cl-gen, .cl-count .k-data) {
  paint-order: stroke;
  stroke: var(--surface-raised);
  stroke-width: 3px;
  stroke-linejoin: round;
}
.cl-svg .cl-link-halo { fill: none; stroke: var(--cl-rim); stroke-opacity: 0.6; stroke-linecap: round; }
.cl-svg .cl-link { stroke: var(--cl-link); stroke-opacity: 0.92; }
.cl-svg .cl-link-d { stroke-opacity: 0.8; }
.cl-svg .cl-link-s { stroke: var(--cl-link); stroke-opacity: 0.72; }
.cl-svg .cl-union { fill: var(--cl-bead); stroke: var(--cl-link); stroke-width: 1.3; }
.cl-svg .cl-tail { fill: none; stroke: var(--cl-link); stroke-opacity: 0.8; stroke-width: 1.2; }
.cl-svg .cl-tail-tip { fill: var(--cl-link); stroke: var(--cl-bead); stroke-width: 1.1; }
.cl-svg .cl-stub { stroke-dasharray: 2 2; }
.cl-svg .cl-end { fill: var(--cl-bead); stroke: var(--cl-link); stroke-width: 1.4; }
/* A count on the glass: the figure's own ink, the page's ground around it. */
.cl-svg .cl-count .k-data { fill: var(--cl-figure); font-weight: var(--weight-semi); }
/* The pedestal of light under the narrow end. */
.cl-svg .cl-base-glow { fill: var(--cl-gloss); fill-opacity: 0.22; }
.cl-svg .cl-base-ring { fill: none; stroke: var(--cl-gloss); stroke-width: 1; stroke-opacity: 0.75; }
.cl-svg .cl-base { fill: var(--cl-gloss); fill-opacity: 0.3; stroke: var(--cl-gloss); stroke-opacity: 0.95; stroke-width: 1.2; }
/* The people: pearls lit from the upper left, ringed by what is known of
   them — living, gone, or not to be shown. */
.cl-svg .cl-pearl-0 { stop-color: var(--cl-gloss); }
.cl-svg .cl-pearl-1 { stop-color: var(--cl-bead); }
.cl-svg .cl-pearl-2 { stop-color: var(--cl-slice-3); }
.cl-svg .cl-bead { fill: url(#cl-pearl); }
.cl-svg .cl-bead.cl-living { stroke: var(--cl-living); }
.cl-svg .cl-bead-glow.cl-living { fill: var(--cl-living); fill-opacity: 0.2; }
.cl-svg .cl-bead.cl-gone { stroke: var(--cl-gone); }
.cl-svg .cl-bead.cl-hidden { stroke: var(--cl-hidden); }
.cl-svg .cl-halo { fill: var(--cl-centre); fill-opacity: 0.16; }
.cl-svg .cl-centre { fill: url(#cl-pearl); stroke: var(--cl-centre); stroke-width: 3; }
.cl-svg .cl-centre-core { fill: var(--cl-centre); }
/* A name on its small frosted label. */
.cl-svg .cl-pill { fill: var(--surface-raised); fill-opacity: 0.82; stroke: var(--cl-rim); stroke-width: 0.8; }
.cl-svg .cl-pill-centre { fill-opacity: 0.9; stroke: var(--cl-centre); stroke-opacity: 0.55; stroke-width: 1; }
.cl-svg .cl-name { fill: var(--text-primary); font-family: var(--face); }
.cl-svg .cl-data { fill: var(--text-secondary); }
.cl-svg .cl-label-thread { stroke: var(--cl-link); stroke-width: 0.8; stroke-opacity: 0.55; }
/* A name the river adds wears the same label. */
.cl-svg .rv-name-box { rx: 7.5px; fill: var(--surface-raised); fill-opacity: 0.82; stroke: var(--cl-rim); stroke-width: 0.8; }
.cl-svg .rv-name-text { fill: var(--text-primary); }
.cl-svg .rv-name-thread { stroke: var(--cl-link); stroke-opacity: 0.55; }

/* ---- Clarity, the prism (the operator's review of 2026-10-27) ----
   "White on white is ugly; stylise the pictures, give the theme contrast."
   The page stands on a vivid prism of glass (clarity-prism.jpg), softened
   by a veil that keeps every word on the page readable; every box is
   frosted glass over it, a white that now stands off its ground; the
   funnel's stage shows the prism whole, and the funnel is crystal on it:
   pale tinted slices you see the colours through, lit rims, an ink mesh
   edged in light. The paper style keeps its own, boxless page. */
[data-theme="clarity"] { --cl-prism: url("/static/img/clarity-prism.jpg?v=2"); }
/* As specific as the wash's own switches ([data-wash="off"][data-route]),
   which would otherwise take the prism away with the wash. */
html[data-theme="clarity"]:is([data-route], :not([data-route])) {
  background-color: var(--surface-page);
  /* The operator's review of 9 November 2026: the prism's colours were too
     violent under half a veil; four fifths of one leave it a hint. */
  background-image:
    linear-gradient(color-mix(in srgb, var(--surface-raised) 80%, transparent), color-mix(in srgb, var(--surface-raised) 80%, transparent)),
    var(--cl-prism);
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  background-attachment: fixed, fixed;
}
html[data-theme="clarity"][data-style="paper"]:is([data-route], :not([data-route])) {
  background-image:
    linear-gradient(color-mix(in srgb, var(--surface-raised) 90%, transparent), color-mix(in srgb, var(--surface-raised) 90%, transparent)),
    var(--cl-prism);
}
[data-theme="clarity"] .masthead {
  background: color-mix(in srgb, var(--surface-raised) 72%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(1.05);
  backdrop-filter: blur(18px) saturate(1.05);
  border-bottom-color: color-mix(in srgb, var(--surface-raised) 55%, transparent);
  box-shadow: 0 8px 28px -18px rgb(var(--shadow-rgb) / 0.5);
}
[data-theme="clarity"]:not([data-style="paper"]) :is(.rec-section, .card, .hero-card, .stat-strip, .radar-card, .kpi, .stat, .tree-controls) {
  background-color: color-mix(in srgb, var(--surface-raised) 84%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.02);
  backdrop-filter: blur(16px) saturate(1.02);
  border-color: color-mix(in srgb, var(--surface-raised) 65%, transparent);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.1), 0 22px 44px -24px rgb(var(--shadow-rgb) / 0.5);
}
/* The funnel's stage: the prism behind a veil, so that the glass and its
   names stand on soft colours rather than loud ones (9 November 2026). */
.river[data-view="funnel"] .rv-canvas {
  background-color: var(--surface-page);
  background-image:
    linear-gradient(color-mix(in srgb, var(--surface-raised) 58%, transparent), color-mix(in srgb, var(--surface-raised) 58%, transparent)),
    var(--cl-prism);
  background-size: cover;
  background-position: center;
}

/* ---- the ascent (summit.js) ---- */
.sm-svg .sm-range-far { fill: var(--sm-range-far); }
.sm-svg .sm-range-mid { fill: var(--sm-range-mid); }
.sm-svg .sm-range-snow { fill: var(--sm-snow-shade); fill-opacity: 0.55; }
.sm-svg .sm-rock { fill: var(--sm-rock); }
.sm-svg .sm-lit-0 { stop-color: var(--sm-rock-lit); stop-opacity: 0; }
.sm-svg .sm-lit-1 { stop-color: var(--sm-rock-lit); stop-opacity: 0.85; }
.sm-svg .sm-snow-0 { stop-color: var(--sm-snow); stop-opacity: 0.95; }
.sm-svg .sm-snow-1 { stop-color: var(--sm-snow-shade); stop-opacity: 0.8; }
.sm-svg .sm-snow-2 { stop-color: var(--sm-snow-shade); stop-opacity: 0.45; }
.sm-svg .sm-glow-0 { stop-color: var(--sm-snow-glow); stop-opacity: 0; }
.sm-svg .sm-glow-1 { stop-color: var(--sm-snow-glow); stop-opacity: 0.7; }
.sm-svg .sm-contour { stroke: var(--sm-contour); stroke-width: 1; stroke-dasharray: 2 5; }
.sm-svg .sm-ridge { fill: none; stroke: var(--sm-rock-lit); stroke-opacity: 0.3; stroke-width: 1.2; stroke-linecap: round; }
.sm-svg .sm-valley { fill: var(--sm-valley); }
.sm-svg .sm-pines { fill: var(--sm-pine); }
.sm-svg .sm-mist-0 { stop-color: var(--sm-mist); stop-opacity: 0; }
.sm-svg .sm-mist-1 { stop-color: var(--sm-mist); stop-opacity: 0.12; }
.sm-svg .sm-trail { stroke: var(--sm-trail); stroke-opacity: 0.85; }
.sm-svg .sm-trail-d { stroke-opacity: 0.6; }
.sm-svg .sm-trail-s { stroke: var(--sm-trail-hot); stroke-opacity: 0.5; }
.sm-svg .sm-union { fill: var(--sm-trail); fill-opacity: 0.85; }
.sm-svg .sm-tail { fill: none; stroke: var(--sm-contour); stroke-width: 1.2; }
.sm-svg .sm-tail-tip { fill: var(--sm-trail); }
.sm-svg .sm-stub { stroke-dasharray: 2 2; }
.sm-svg .sm-end { fill: var(--surface-page); stroke: var(--sm-camp-hidden); stroke-width: 1.4; }
.sm-svg .sm-pole { stroke: var(--sm-snow-shade); stroke-width: 1.3; fill: none; }
.sm-svg .sm-flag { fill: var(--accent); }
.sm-svg .sm-pole-camp { stroke: var(--text-primary); stroke-width: 1.6; }
.sm-svg .sm-camp-halo { fill: var(--sm-sun); fill-opacity: 0.1; }
.sm-svg .sm-camp-halo-2 { fill-opacity: 0.2; }
.sm-svg .sm-camp-core { fill: var(--sm-sun-core); stroke: var(--accent); stroke-width: 2; }
.sm-svg .sm-camp.sm-living { fill: var(--sm-camp-living); }
.sm-svg .sm-camp-glow.sm-living { fill: var(--sm-camp-living); fill-opacity: 0.22; }
.sm-svg .sm-camp.sm-gone { fill: var(--sm-camp-gone); }
.sm-svg .sm-camp-glow.sm-gone { fill: var(--sm-camp-gone); fill-opacity: 0.12; }
.sm-svg .sm-camp.sm-hidden { fill: var(--surface-page); stroke: var(--sm-camp-hidden); }
.sm-svg .sm-name { fill: var(--text-primary); font-family: var(--face); }
.sm-svg .sm-data { fill: var(--text-secondary); }
.sm-svg .sm-alt { fill: var(--text-tertiary); }

/* ---- a view's dashboard: figures read from the drawing on screen ----
   A strip of tiles between the controls and the drawing, as the reference's
   dashboard panels sit around its tree: never over a person. */
.rv-hud {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0.5rem;
  margin: 0;
  padding: 0.6rem 0.9rem;
  border-block-end: 1px solid var(--border-hairline);
  background: color-mix(in srgb, var(--surface-page) 55%, transparent);
}
.rv-hud-figures { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; }
.rv-hud-figures > div {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-inline-size: 6.5rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border-hairline));
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
}
.rv-hud dt {
  font-family: var(--mono);
  font-size: var(--type-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.rv-hud dd {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--type-sub);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.rv-hud-gauge dd { display: flex; align-items: center; gap: 0.45rem; }
.rv-hud-ring { inline-size: 1.5em; block-size: 1.5em; flex: none; }
.rv-hud-track { fill: none; stroke: var(--border-hairline); stroke-width: 3.5; }
.rv-hud-arc { fill: none; stroke: var(--accent); stroke-width: 3.5; stroke-linecap: round; }
.rv-hud-bars {
  flex: 1 1 9rem;
  display: flex;
  align-items: flex-end;
  gap: 3px;
  min-block-size: 2.6rem;
  padding: 0.4rem 0.6rem;
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border-hairline));
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
}
.rv-hud-bars i { flex: 1 1 0; min-inline-size: 4px; border-radius: 2px 2px 0 0; opacity: 0.85; }
.river[data-view="tree"] .rv-bar-root { background: var(--tv-down); }
.river[data-view="tree"] .rv-bar-trunk { background: var(--tv-vein); }
.river[data-view="tree"] .rv-bar-crown { background: var(--tv-up); }
.river[data-view="galaxy"] .rv-bar-root { background: var(--gx-down); }
.river[data-view="galaxy"] .rv-bar-trunk { background: var(--gx-sun); }
.river[data-view="galaxy"] .rv-bar-crown { background: var(--gx-up); }
/* Which way is which, in the colours of the two sides (Fusion, Galaxy). */
.rv-dir i { display: inline-block; inline-size: 14px; block-size: 3px; border-radius: 2px; vertical-align: middle; margin-inline-end: 0.35rem; }
.river[data-view="tree"] .rv-dir-down i { background: var(--tv-down); }
.river[data-view="tree"] .rv-dir-up i { background: var(--tv-up); }
.river[data-view="galaxy"] .rv-dir-down i { background: var(--gx-down); }
.river[data-view="galaxy"] .rv-dir-up i { background: var(--gx-up); }
.river[data-view="funnel"] .rv-bar-root { background: var(--cl-ink-3); }
.river[data-view="funnel"] .rv-bar-trunk { background: var(--cl-centre); }
.river[data-view="funnel"] .rv-bar-crown { background: var(--cl-ink-1); }
/* Summit's profile of the climb, valley to summit, as the reference's
   temperature trend: a line in the first light, its ground under it, and
   the camp marked on it. */
.rv-hud-bars.sm-profile { align-items: stretch; padding: 0.3rem 0.5rem; }
.sm-prof { inline-size: 100%; block-size: 2.4rem; display: block; overflow: visible; }
.sm-prof-area { fill: color-mix(in srgb, var(--accent) 22%, transparent); }
.sm-prof-line { fill: none; stroke: var(--accent); stroke-width: 1.6; }
.sm-prof-dot { fill: var(--accent); }
.sm-prof-here { fill: var(--sm-sun-core); stroke: var(--accent); stroke-width: 1; }
@media (max-width: 40rem) {
  .rv-hud-bars { display: none; }
  .rv-hud-figures > div { min-inline-size: 0; flex: 1 1 40%; }
}
@media print {
  .rv-hud { display: none; }
}

/* ---------------------------------------------------------------------------
   The administration console (v0.5.0, docs/adr/0011)
   ---------------------------------------------------------------------------
   Five rooms behind one strip: the overview, the bundle, the machine, the
   accounts and the ways in. Every page in it is built from four pieces, so
   that a reader who has learnt one page has learnt them all:

   * the strip (`.admin-nav`), the record's tabs again with an icon each;
   * the tiles (`.kpis`), one figure each, the dashboard's stat grown a label
     and a state;
   * the state (`.lvl`): green, warning or critical, always a word beside the
     dot, because a colour alone is not a distinction every reader can make;
   * the meters (`.meter`) and the facts (`.facts`), for the sizes and the
     particulars. */
.admin-nav {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  gap: 0.25rem;
  margin: 0 0 1.25rem;
  box-shadow: inset 0 -1px 0 var(--border-hairline);
}
.admin-nav-item {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 0.75rem;
  white-space: nowrap;
  color: var(--text-secondary);
  text-decoration: none;
  border-block-end: 2px solid transparent;
  font-size: var(--type-minor);
  font-weight: var(--weight-semi);
}
.admin-nav-item .ico { inline-size: 1.05em; block-size: 1.05em; }
.admin-nav-item:hover { color: var(--text-primary); }
.admin-nav-item[aria-current="page"] {
  color: var(--text-primary);
  border-block-end-color: var(--accent);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .admin-nav {
  box-shadow: none;
  padding: 4px;
  inline-size: fit-content;
  max-inline-size: 100%;
  background: var(--surface-sunken);
  border: 1px solid var(--border-hairline);
  border-radius: var(--btn-radius);
  scrollbar-width: none;
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .admin-nav::-webkit-scrollbar { display: none; }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .admin-nav-item {
  border-block-end: 0;
  border-radius: var(--btn-radius);
  padding-block: 0.45rem;
  padding-inline: 0.9rem;
  transition: background-color var(--tempo) var(--ease), color var(--tempo) var(--ease);
}
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .admin-nav-item:hover { background: var(--surface-hover); }
:is([data-theme="canopee"], [data-theme="fusion"], [data-theme="galaxy"], [data-theme="summit"], [data-theme="clarity"], [data-theme="genealogy"]) .admin-nav-item[aria-current="page"] {
  background: var(--accent);
  color: var(--on-accent);
}

/* The tiles: a label, a figure, what it means, and its state. A tile that
   goes somewhere is a link, whole. */
.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr));
  gap: 1rem;
  margin: 0 0 var(--section-gap);
}
/* The overview's five, one per room: narrower, so they hold one row on a
   desktop. A tile there is a way in, and says so on hover. */
.kpis-five { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
a.kpi:hover { border-color: var(--accent); }
a.kpi:hover .kpi-label { color: var(--accent); }
.kpi {
  display: grid;
  grid-template-columns: 1fr auto;
  align-content: start;
  gap: 0.2rem 0.75rem;
  padding: 1rem 1.1rem;
  color: inherit;
  text-decoration: none;
  background: var(--section-fill);
  border: var(--section-border);
  border-radius: var(--section-radius);
  box-shadow: var(--section-shadow);
}
a.kpi:hover { border-color: var(--border-strong); }
.kpi-label {
  font-size: var(--type-small);
  color: var(--text-tertiary);
}
.kpi-icon {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  inline-size: 2.3rem;
  block-size: 2.3rem;
  border-radius: 50%;
  background: var(--surface-sunken);
  border: 1px solid var(--border-hairline);
  color: var(--text-secondary);
}
.kpi-icon .ico { inline-size: 1.15rem; block-size: 1.15rem; }
.kpi-value {
  font-size: var(--type-major);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  /* Whole words: "Administrateurs" broken after its r read as two. */
  overflow-wrap: break-word;
  hyphens: auto;
}
/* A value that is words rather than a figure: one size down, so a French or
   German phrase holds in the tile. */
.kpi-value-text { font-size: var(--type-sub); }
.kpi-note {
  grid-column: 1 / -1;
  font-size: var(--type-small);
  color: var(--text-secondary);
}

/* The state of a thing: a dot and a word. */
.lvl {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
  font-size: var(--type-small);
  font-weight: var(--weight-semi);
}
.lvl::before {
  content: "";
  flex: none;
  inline-size: 0.6rem;
  block-size: 0.6rem;
  border-radius: 50%;
  background: var(--text-tertiary);
}
.lvl-ok::before { background: var(--conf-certain); }
.lvl-warn::before { background: var(--warning); }
.lvl-fail::before { background: var(--danger); }
.lvl-fail { color: var(--danger); }
/* Said under a field, a state may be a sentence: it wraps, its dot on its
   first line. */
.hint .lvl {
  white-space: normal;
  align-items: baseline;
}

/* How many of each, as one bar: green, warning, critical. */
.health-bar {
  display: flex;
  block-size: 0.6rem;
  margin: 0.6rem 0 0.4rem;
  border-radius: 999px;
  overflow: hidden;
  background: var(--conf-track);
}
.health-bar > i { display: block; block-size: 100%; }
.health-bar .hb-ok { background: var(--conf-certain); }
.health-bar .hb-warn { background: var(--warning); }
.health-bar .hb-fail { background: var(--danger); }

/* What this site keeps on its partition, part by part: one hue in
   decreasing strength, because the parts are shares of one thing and not
   states — a warning colour here would read as a warning. */
.vol-bar {
  display: flex;
  block-size: 0.8rem;
  margin: 0.6rem 0 0.8rem;
  border-radius: 999px;
  overflow: hidden;
  background: var(--conf-track);
}
.vol-bar > i { display: block; block-size: 100%; background: var(--accent); }
.vol-swatch {
  display: inline-block;
  inline-size: 0.7rem;
  block-size: 0.7rem;
  margin-inline-end: 0.45rem;
  border-radius: 3px;
  background: var(--accent);
  vertical-align: baseline;
}
.vol-1 { opacity: 1; }
.vol-2 { opacity: 0.78; }
.vol-3 { opacity: 0.6; }
.vol-4 { opacity: 0.45; }
.vol-5 { opacity: 0.32; }
.vol-6 { opacity: 0.2; }
.vol-bar > i + i { box-shadow: inset 1px 0 0 var(--surface-raised); }

/* A check, its finding, and — opened — its plan. */
.checks { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.6rem; }
.check {
  background: var(--surface-page);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
}
.check > summary {
  display: grid;
  grid-template-columns: 7.5rem 1fr;
  gap: 0.3rem 0.9rem;
  align-items: baseline;
  padding: 0.7rem 0.9rem;
  cursor: pointer;
}
.check > summary::-webkit-details-marker { display: none; }
.check-title { font-weight: var(--weight-semi); }
.check-finding {
  grid-column: 2;
  color: var(--text-secondary);
  font-size: var(--type-minor);
}
.check-plan {
  padding: 0 0.9rem 0.9rem;
  padding-inline-start: calc(7.5rem + 1.8rem);
  font-size: var(--type-minor);
}
.check-plan h4 { margin: 0 0 0.3rem; font-size: var(--type-small); color: var(--text-tertiary); }
.check-plan p { margin: 0.3rem 0; }
.checks-area { margin: 1.1rem 0 0.5rem; font-size: var(--type-small); color: var(--text-tertiary); letter-spacing: 0.06em; text-transform: uppercase; }

/* A command or a helper's log in the console: wrapped within the page, not
   a line that widens it (the upgrade command at phone width). */
:is(.notice, .rec-section) pre {
  max-inline-size: 100%;
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A size, against what it could be. */
.meter {
  display: grid;
  grid-template-columns: minmax(8rem, 13rem) 1fr minmax(5.5rem, auto);
  gap: 0.75rem;
  align-items: center;
  margin: 0.45rem 0;
}
.meter-label { font-size: var(--type-minor); }
.meter-track {
  block-size: 0.7rem;
  border-radius: 999px;
  background: var(--conf-track);
  overflow: hidden;
}
.meter-track > i { display: block; block-size: 100%; border-radius: 999px; background: var(--accent); }
.meter-track > i.is-warn { background: var(--warning); }
.meter-track > i.is-fail { background: var(--danger); }
.meter-value {
  font-size: var(--type-minor);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* Particulars: a term, its value. */
.facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 1.25rem;
  margin: 0;
}
.facts dt { color: var(--text-tertiary); font-size: var(--type-small); align-self: baseline; }
.facts dd { margin: 0; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.facts code { font-size: 0.92em; }

/* A choice among a few, each a sentence: the registration policy. */
.choice-cards { display: grid; gap: 0.6rem; margin: 0.6rem 0 1rem; }
.choice-card {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem 0.7rem;
  /* A choice is a sentence, not a field's label: not the kit's mono capitals. */
  margin: 0;
  font-family: inherit;
  font-size: var(--type-minor);
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-primary);
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
  cursor: pointer;
}
.choice-card:has(input:checked) { border-color: var(--accent); }
.choice-card input { grid-row: 1 / span 2; margin-top: 0.2rem; }
.choice-card strong { font-weight: var(--weight-semi); }
.choice-card span { grid-column: 2; color: var(--text-secondary); font-size: var(--type-small); }

/* The export's two ways and each sign-in service: one disclosure a line,
   never a row of them. */
.menu-disclosure.export-disclosure { display: block; margin: 0.6rem 0; }
.service-card {
  padding: 0.65rem 0.9rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-page);
}
.service-card > summary { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; font-weight: var(--weight-semi); }
.service-card[open] > summary { margin-bottom: 0.6rem; }
.service-form { display: grid; gap: 0.2rem; max-inline-size: 44rem; }
.service-form .field { margin-bottom: 0.4rem; }
.service-form .facts { margin: 0.4rem 0 0.6rem; }

/* A provider's sign-in button carries its name, never its brand colours: the
   theme draws it, as it draws every button. */
.sso-buttons { display: grid; gap: 0.5rem; margin: 0 0 1rem; }
.sso-buttons .btn { justify-content: center; }
.or-rule {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0;
  color: var(--text-tertiary);
  font-size: var(--type-small);
}
.or-rule::before, .or-rule::after { content: ""; flex: 1; border-top: 1px solid var(--border-hairline); }

@media (max-width: 40rem) {
  .facts { grid-template-columns: 1fr; }
  .facts dd { margin-bottom: 0.45rem; }
  .meter { grid-template-columns: 1fr auto; }
  .meter-track { grid-column: 1 / -1; grid-row: 2; }
  .check > summary { grid-template-columns: 1fr; }
  .check-finding { grid-column: 1; }
  .check-plan { padding-inline-start: 0.9rem; }
}

/* Genealogy (docs/adr/0016): the operator's second screen — a dark green
   ground with antique vines climbing its edges, cream cards with an oval
   portrait and a double rule, titles in a book face and in gold — rounded
   as old card stock is, and unhurried. */
[data-theme="genealogy"] {
  --btn-radius: 999px;
  --field-radius: 8px;
  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 16px;
  --tempo: 220ms;
  --ease: cubic-bezier(0.25, 0.7, 0.25, 1);
  --kit-hero: url("/static/img/genealogy-hero.jpg");
  --kit-cta: url("/static/img/genealogy-cta.jpg");
  /* Behind the family's notes (docs/adr/0014): soft, neutral, its own. */
  --notes-bg: url("/static/img/notes-genealogy.jpg");
  /* The page's frame: an antique vine along each edge, the middle the
     page's own green. A frame, as Canopée's photographs are, not a texture:
     nothing is ever written over the vines, and on a narrow screen, where
     the middle is all there is, they are left out. Each vine is a tall strip
     drawn at a third of its size or less, so it stays sharp on a dense
     screen (the first, one wide picture stretched over the window, was
     blurred; 2026-10-23). */
  --gn-vine-l: url("/static/img/genealogy-vine-l.jpg");
  --gn-vine-r: url("/static/img/genealogy-vine-r.jpg");
}
[data-theme="genealogy"] body {
  /* As tall as the window, so that a short page has its vines too. Faded
     into the page's moss (the operator's review of 9 November 2026: their
     pinks and golds were too loud beside the words). */
  min-block-size: 100vh;
  background-color: var(--surface-page);
  background-image:
    linear-gradient(color-mix(in srgb, var(--surface-page) 34%, transparent), color-mix(in srgb, var(--surface-page) 34%, transparent)),
    var(--gn-vine-l), var(--gn-vine-r);
  background-size: 100% 100vh, auto 100vh, auto 100vh;
  background-position: left top, left top, right top;
  background-repeat: no-repeat;
  background-attachment: fixed;
  /* The strips' own dark ground gives way to the page's moss (lighten), so
     that only the vines show, and no band of an older green beside them. */
  background-blend-mode: normal, lighten, lighten;
}
@media (max-width: 760px) {
  [data-theme="genealogy"] body { background-image: none; }
}
/* A page that takes the whole width (the tree's) has no margin for them. */
[data-theme="genealogy"] body:has(main.wide) { background-image: none; }
/* The cards: cream stock in the page's green. Every colour token takes its
   `--gn-l-` twin on a card (tokens.css), so whatever a card holds — text,
   fields, buttons, chips, a chart — reads as it would on a light theme.
   Not in the paper style, which draws no cards; nor in the record beside
   the tree, which keeps the page's green and gold as its other blocks do
   (the operator's review, 2026-10-24: a cream block there did not match). */
[data-theme="genealogy"]:not([data-style="paper"]) :is(.card, .rec-section, .tree-controls, .stat, .feature-card, .cta-banner, .hero-card, .stat-strip, .kpi, .radar-card, .notice, .completeness, .notes-block, .menu-panel, .search-suggest, dialog):not(.tree-panel *) {
  color-scheme: light;
  --surface-page: var(--gn-l-surface-page);
  --surface-raised: var(--gn-l-surface-raised);
  --border-hairline: var(--gn-l-border-hairline);
  --border-strong: var(--gn-l-border-strong);
  --text-primary: var(--gn-l-text-primary);
  --text-secondary: var(--gn-l-text-secondary);
  --text-tertiary: var(--gn-l-text-tertiary);
  --surface-sunken: var(--gn-l-surface-sunken);
  --surface-hover: var(--gn-l-surface-hover);
  --focus-ring: var(--gn-l-focus-ring);
  --accent: var(--gn-l-accent);
  --accent-soft: var(--gn-l-accent-soft);
  --on-accent: var(--gn-l-on-accent);
  --conf-certain: var(--gn-l-conf-certain);
  --conf-high: var(--gn-l-conf-high);
  --conf-medium: var(--gn-l-conf-medium);
  --conf-low: var(--gn-l-conf-low);
  --conf-track: var(--gn-l-conf-track);
  --danger: var(--gn-l-danger);
  --warning: var(--gn-l-warning);
  --sex-a: var(--gn-l-sex-a);
  --sex-a-line: var(--gn-l-sex-a-line);
  --sex-b: var(--gn-l-sex-b);
  --sex-b-line: var(--gn-l-sex-b-line);
  --sex-u: var(--gn-l-sex-u);
  --sex-u-line: var(--gn-l-sex-u-line);
  --hue-1: var(--gn-l-hue-1);
  --hue-2: var(--gn-l-hue-2);
  --hue-3: var(--gn-l-hue-3);
  --hue-4: var(--gn-l-hue-4);
  --hue-5: var(--gn-l-hue-5);
  --hue-6: var(--gn-l-hue-6);
  --hue-7: var(--gn-l-hue-7);
  --hue-8: var(--gn-l-hue-8);
  --icon-river: var(--gn-l-icon-river);
  --icon-tree: var(--gn-l-icon-tree);
  --icon-search: var(--gn-l-icon-search);
  --icon-quality: var(--gn-l-icon-quality);
  --icon-river-ink: var(--gn-l-icon-river-ink);
  --icon-tree-ink: var(--gn-l-icon-tree-ink);
  --icon-search-ink: var(--gn-l-icon-search-ink);
  --icon-quality-ink: var(--gn-l-icon-quality-ink);
  color: var(--text-primary);
}
/* A card's double rule, as old card stock is printed: an edge, a cream
   gap and a hairline of the ink inside it, and a shadow on the green. */
[data-theme="genealogy"]:not([data-style="paper"]) :is(.rec-section, .card, .hero-card, .stat-strip, .radar-card, .kpi, .stat, .tree-controls):not(.tree-panel *) {
  background-color: var(--surface-raised);
  border: 1px solid var(--border-hairline);
  box-shadow: inset 0 0 0 4px var(--surface-raised), inset 0 0 0 5px color-mix(in srgb, var(--border-strong) 24%, transparent), 0 12px 24px -18px rgb(var(--shadow-rgb) / 0.55);
}
/* In the record beside the tree, a block is the page's own: its raised
   green, edged in a hairline of gold. */
[data-theme="genealogy"]:not([data-style="paper"]) .tree-panel :is(.rec-section, .card) {
  background-color: var(--surface-raised);
  border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--border-hairline));
}
/* A notice is a card of its own: opaque cream, tinted by its kind, rather
   than the kit's veil, which over this green reads as grey. */
[data-theme="genealogy"]:not([data-style="paper"]) .notice { background-color: color-mix(in srgb, var(--accent-soft) 45%, var(--surface-raised)); }
[data-theme="genealogy"]:not([data-style="paper"]) .notice.warn { background-color: color-mix(in srgb, var(--warning) 9%, var(--surface-raised)); }
[data-theme="genealogy"]:not([data-style="paper"]) .notice.error { background-color: color-mix(in srgb, var(--danger) 8%, var(--surface-raised)); }
[data-theme="genealogy"]:not([data-style="paper"]) .notice.ok { background-color: color-mix(in srgb, var(--conf-certain) 9%, var(--surface-raised)); }
[data-theme="genealogy"] .masthead { border-bottom-color: color-mix(in srgb, var(--accent) 24%, var(--border-hairline)); }
[data-theme="genealogy"] :is(main h1, .hero h1, .cta-banner h2, .rec-section > h2:first-child, .sec-head h2) { font-family: var(--serif); font-weight: var(--weight-normal); letter-spacing: 0; }
/* The overview's hero: its picture is light and gold where the others are
   dark, so the green holds further across it, and the title keeps to the
   side the green covers. */
[data-theme="genealogy"] .hero {
  background-image:
    var(--grain),
    linear-gradient(90deg, var(--surface-page) 0%, color-mix(in srgb, var(--surface-page) 94%, transparent) 48%, color-mix(in srgb, var(--surface-page) 30%, transparent) 84%, color-mix(in srgb, var(--surface-page) 45%, transparent) 100%),
    linear-gradient(0deg, var(--surface-page) 0%, transparent 38%),
    var(--kit-hero);
}
[data-theme="genealogy"][dir="rtl"] .hero {
  background-image:
    var(--grain),
    linear-gradient(270deg, var(--surface-page) 0%, color-mix(in srgb, var(--surface-page) 94%, transparent) 48%, color-mix(in srgb, var(--surface-page) 30%, transparent) 84%, color-mix(in srgb, var(--surface-page) 45%, transparent) 100%),
    linear-gradient(0deg, var(--surface-page) 0%, transparent 38%),
    var(--kit-hero);
}
@media (min-width: 861px) {
  [data-theme="genealogy"] .hero-title { max-inline-size: 60%; }
}
/* The portraits in oval frames, as the screen's cards hold them. */
[data-theme="genealogy"] :is(.person-avatar, .panel-avatar) {
  border-radius: 50%;
  block-size: auto;
  aspect-ratio: 5 / 6;
}

/* ---- the tree, climbed (genealogy.js) ---- */
[data-theme="genealogy"] .river { --rv-water: var(--gn-gold); --rv-lit: var(--gn-lit); --rv-lit-halo: var(--gn-lit-halo); --rv-glow: var(--gn-living); }
/* The stage's dark, a little light where the scroll stands: what the page
   shows before its script has drawn the picture, and round it. */
.river[data-view="scroll"] .rv-canvas {
  background-color: var(--gn-stage);
  background-image: radial-gradient(38% 55% at 50% 50%, var(--gn-stage-lit), var(--gn-stage) 100%);
  background-size: 100% 100%;
}
.river[data-view="scroll"] .rv-bar-root { background: var(--gn-bar-root); }
.river[data-view="scroll"] .rv-bar-trunk { background: var(--gn-gold-hi); }
.river[data-view="scroll"] .rv-bar-crown { background: var(--gn-bar-crown); }
.gn-svg .gn-gen { fill: var(--gn-gen); stroke: var(--gn-gen-halo); stroke-width: 3; stroke-linejoin: round; }
.gn-svg .gn-gen-0 { fill: var(--gn-gen-0); }
/* A line: the parchment's light, then the ink, as sure as the parentage is
   known; a knot of gold ringed in ink where a family's lines meet. */
.gn-svg .gn-wire-halo { fill: none; stroke: var(--gn-wire-halo); stroke-width: 3.6; stroke-opacity: 0.7; stroke-linejoin: round; stroke-linecap: round; }
.gn-svg .gn-wire { fill: none; stroke: var(--gn-wire); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.gn-svg .gn-wire-d { stroke-dasharray: 5 3.5; }
.gn-svg .gn-wire-s { stroke-dasharray: 0.1 4.5; stroke-width: 2.2; }
.gn-svg .gn-node { fill: var(--gn-node); stroke: var(--gn-node-rim); stroke-width: 1.1; }
.gn-svg .gn-tip { fill: var(--gn-wire); }
.gn-svg .gn-end { fill: var(--gn-wire-halo); stroke: var(--gn-wire); stroke-width: 1.2; }
.gn-svg .gn-count .k-data { fill: var(--gn-wire); }
.gn-svg .gn-count .k-halo { stroke: var(--gn-wire-halo); }
.gn-svg .gn-card { fill: var(--gn-card); stroke: var(--gn-card-line); stroke-width: 0.9; }
.gn-svg .gn-card-centre { stroke: var(--gn-gold); stroke-width: 1.6; }
.gn-svg .gn-card-shade { fill: var(--gn-card-shade); fill-opacity: 0.28; }
.gn-svg .gn-name { fill: var(--gn-ink); font-family: var(--serif); }
.gn-svg .gn-centre { fill: var(--gn-centre); }
.gn-svg .gn-years { fill: var(--gn-ink-2); }
/* A portrait: a sepia print in a cream mount with a fine gold edge, as on
   the reference; the living's edge green, the gone's gold, the hidden's an
   empty mount. */
.gn-svg .gn-mount { fill: var(--gn-mount); stroke: var(--gn-mount-line); stroke-width: 1.1; }
.gn-svg .gn-mount-centre { stroke: var(--gn-gold); stroke-width: 2.4; }
.gn-svg .gn-living { stroke: var(--gn-living); stroke-width: 1.6; }
.gn-svg .gn-gone { stroke: var(--gn-gone); }
.gn-svg .gn-hidden { fill: var(--gn-hidden-fill); stroke: var(--gn-hidden); stroke-width: 1.2; }
.gn-svg .gn-print-hi { stop-color: var(--gn-print-hi); }
.gn-svg .gn-print-lo { stop-color: var(--gn-print); }
.gn-svg .gn-sparse { fill: var(--gn-print-sparse); }
.gn-svg .gn-initials { fill: var(--gn-initials); font-family: var(--serif); letter-spacing: 0.04em; pointer-events: none; }
.gn-svg .gn-halo { fill: var(--gn-gold-hi); fill-opacity: 0.28; }
.gn-svg .gn-again { stroke: var(--gn-wire); }
.gn-svg a.rv-p:hover .gn-mount { stroke: var(--gn-gold); stroke-width: 2; }
.gn-svg a.rv-p:focus-visible .gn-card { stroke: var(--focus-ring); stroke-width: 2; }
/* The names river.js adds, on cards like the drawing's own. */
.gn-svg .rv-name-box { fill: var(--gn-card); fill-opacity: 0.96; stroke: var(--gn-card-line); }
.gn-svg .rv-name-text { fill: var(--gn-ink); font-family: var(--serif); }
.gn-svg .rv-name-thread { stroke: var(--gn-wire); }
.gn-svg .rv-hl-box { fill: var(--gn-card); fill-opacity: 0.96; stroke: var(--gn-lit); stroke-opacity: 0.6; }
.gn-svg .rv-hl .k-halo { stroke: var(--gn-lit-halo); }
/* While only the near generations are named, a far person keeps their
   portrait and its initials, and their card waits for the pointer. */
.river.rv-names-near .rv-svg a.rv-p[data-far] .gn-plate { display: none; }
.river.rv-names-near .rv-svg a.rv-p[data-far] text.gn-initials { display: inline; }

/* ---------- My personal assistant (docs/adr/0026) ----------
   The account's projects and their conversations with the AFG, in the
   codes of a chat: a column of projects, a thread, a composer that stays
   at hand. Every colour is the theme's; every movement stops for a reader
   who asked for less of it. */
.as-shell {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}
.as-main { min-width: 0; }
.as-side {
  position: sticky;
  top: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  max-height: calc(100vh - 2rem);
  overflow: auto;
}
.as-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.5rem;
  border-radius: var(--radius);
  color: var(--text-primary);
  font-weight: var(--weight-semi);
  text-decoration: none;
}
.as-brand .ico { color: var(--accent); }
.as-brand[aria-current="page"] { background: var(--accent-soft); }
.as-side-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.as-side-list a {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius);
  color: var(--text-secondary);
  text-decoration: none;
  transition: background 160ms var(--ease), color 160ms var(--ease);
}
.as-side-list a:hover { background: var(--surface-hover); color: var(--text-primary); }
.as-side-list a[aria-current="page"] { background: var(--accent-soft); color: var(--text-primary); }
.as-side-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.as-new { justify-content: flex-start; gap: 0.4rem; }
.as-av {
  flex: none;
  border-radius: 30%;
  object-fit: cover;
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.25);
}
.as-av-sm { width: 1.4rem; height: 1.4rem; }
.as-av-lg { width: 2.8rem; height: 2.8rem; }
.as-av-xl { width: 4rem; height: 4rem; }

.as-hero h1 { margin-bottom: 0.25rem; }
.as-grid {
  list-style: none;
  margin: 1.25rem 0 2rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 0.75rem;
}
.as-card a {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 0.8rem;
  align-items: center;
  padding: 0.9rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  color: var(--text-primary);
  text-decoration: none;
  transition: transform 180ms var(--ease), box-shadow 180ms var(--ease), border-color 180ms var(--ease);
}
.as-card a:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: 0 12px 26px -16px rgb(var(--shadow-rgb) / 0.6);
}
.as-card .as-av { grid-row: 1 / span 2; width: 3.5rem; height: 3.5rem; }
.as-card-name { font-weight: var(--weight-semi); overflow-wrap: anywhere; }
.as-empty { text-align: center; padding: 1.5rem 1rem 0.5rem; }
.as-empty-art { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1rem; }
.as-empty-art img { border-radius: 30%; animation: as-float 4.6s var(--ease) infinite; }
.as-empty-art img:nth-child(2n) { animation-delay: -1.5s; }
.as-empty-art img:nth-child(3n) { animation-delay: -3s; }
.as-newproject {
  margin-top: 1.5rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}
.as-newproject h2 { margin-top: 0; }
.as-form { display: grid; gap: 0.75rem; padding-bottom: 0.9rem; }
.as-form .btn { justify-self: start; }

.as-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.25rem; }
.as-head h1 { margin: 0; }
.as-head-text { min-width: 0; }
.as-head-text h1 { overflow-wrap: anywhere; }
.as-head-conv { flex-direction: column; align-items: stretch; gap: 0.6rem; }
.as-back {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  width: fit-content;
  color: var(--text-secondary);
  font-size: var(--type-small);
  text-decoration: none;
}
.as-back:hover { color: var(--text-primary); }
.as-head-row { display: flex; align-items: center; gap: 0.75rem; }
.as-conv-h1 { flex: 1; min-width: 0; font-size: var(--type-major); overflow-wrap: anywhere; }
.as-menu { position: relative; }
.as-menu > summary {
  list-style: none;
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  color: var(--text-secondary);
  cursor: pointer;
}
.as-menu > summary::-webkit-details-marker { display: none; }
.as-menu > summary:hover, .as-menu[open] > summary { background: var(--surface-hover); color: var(--text-primary); }
.as-menu-panel {
  position: absolute;
  inset-inline-end: 0;
  top: calc(100% + 0.4rem);
  z-index: 20;
  width: min(26rem, 88vw);
  padding: 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  box-shadow: 0 18px 40px -20px rgb(var(--shadow-rgb) / 0.8);
}

.as-convs-wrap { margin-top: 1.5rem; }
/* The conversations, each a bubble that opens it (the operator's request of
   5 November 2026), here and on a person's page (docs/adr/0031): a speech
   bubble's shape, its tail at the foot of the side the text starts on, a
   raised surface and its border, and an arrow that says it opens. */
.as-convs { list-style: none; margin: 0.6rem 0; padding: 0; display: grid; gap: 0.55rem; }
.as-convs a {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.8rem;
  padding: 0.6rem 0.85rem 0.6rem 0.65rem;
  border: 1px solid var(--border-hairline);
  border-radius: 1.2rem;
  border-end-start-radius: 0.3rem;
  background: var(--surface-raised);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / 0.12);
  color: var(--text-primary);
  text-decoration: none;
  transition: transform 160ms var(--ease), background 160ms var(--ease),
    border-color 160ms var(--ease), box-shadow 160ms var(--ease);
}
.as-convs a:hover {
  background: var(--surface-hover);
  border-color: var(--accent);
  box-shadow: 0 6px 16px -8px rgb(var(--shadow-rgb) / 0.45);
  transform: translateY(-1px);
}
.as-convs a:active { transform: none; }
.as-conv-icon {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
}
.as-conv-text { display: grid; gap: 0.1rem; min-width: 0; }
.as-conv-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--weight-semi); }
.as-conv-go {
  display: grid;
  place-items: center;
  color: var(--text-secondary);
  transform: scaleX(-1);
  transition: transform 160ms var(--ease), color 160ms var(--ease);
}
.as-convs a:hover .as-conv-go { color: var(--accent); transform: scaleX(-1) translateX(-3px); }
[dir="rtl"] .as-conv-go { transform: none; }
[dir="rtl"] .as-convs a:hover .as-conv-go { transform: translateX(-3px); }
@media (prefers-reduced-motion: reduce) {
  .as-convs a, .as-conv-go { transition: none; }
  .as-convs a:hover { transform: none; }
  .as-convs a:hover .as-conv-go { transform: scaleX(-1); }
  [dir="rtl"] .as-convs a:hover .as-conv-go { transform: none; }
}

.as-panels { display: grid; gap: 0.6rem; margin-top: 1.75rem; }
.as-panel {
  padding: 0 0.95rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
}
.as-panel > summary {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.75rem 0;
  font-weight: var(--weight-semi);
  list-style: none;
  cursor: pointer;
}
.as-panel > summary::-webkit-details-marker { display: none; }
.as-panel[open] > summary { margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-hairline); }
.as-panel textarea { width: 100%; resize: vertical; }
.as-danger { margin: 0.25rem 0 1rem; }
.as-danger > summary { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--danger); cursor: pointer; }
.as-danger form { display: grid; gap: 0.6rem; margin-top: 0.6rem; }

.as-picker > summary { color: var(--text-secondary); font-size: var(--type-small); cursor: pointer; }
.as-picker-grid {
  display: grid;
  gap: 0.6rem;
  max-height: 22rem;
  margin: 0;
  padding: 0.6rem 0.1rem 0.2rem;
  border: 0;
  overflow: auto;
}
.as-pick-domain { display: grid; gap: 0.25rem; }
.as-pick-row { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.as-pick { position: relative; display: inline-block; cursor: pointer; }
.as-pick input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.as-pick img {
  display: block;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 30%;
  transition: transform 140ms var(--ease), box-shadow 140ms var(--ease);
}
.as-pick:hover img { transform: scale(1.07); }
.as-pick input:checked + img { box-shadow: 0 0 0 3px var(--surface-raised), 0 0 0 5px var(--accent); }
.as-pick input:focus-visible + img { box-shadow: 0 0 0 3px var(--focus-ring); }
.as-pick-auto span {
  display: inline-block;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  font-size: var(--type-small);
}
.as-pick-auto input:checked + span { border-color: var(--accent); background: var(--accent-soft); }

/* The thread: the member's words on one side, the AFG's on the other. */
.as-thread { display: flex; flex-direction: column; gap: 1.15rem; min-height: 35vh; padding: 0.5rem 0 1.25rem; }
.as-msg {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  transition: opacity 260ms var(--ease), transform 260ms var(--ease);
}
.as-msg.as-enter { opacity: 0; transform: translateY(10px); }
.as-msg-user { justify-content: flex-end; }
.as-bubble {
  max-width: min(42rem, 85%);
  padding: 0.65rem 0.95rem;
  border: 1px solid var(--border-hairline);
  border-radius: 1.15rem 1.15rem 0.35rem 1.15rem;
  background: var(--accent-soft);
  color: var(--text-primary);
}
[dir="rtl"] .as-bubble { border-radius: 1.15rem 1.15rem 1.15rem 0.35rem; }
.as-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.as-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid var(--border-hairline);
  border-radius: 50%;
  background: var(--surface-raised);
  color: var(--accent);
}
.as-mark-error { color: var(--danger); }
.as-mark-live .ico { animation: as-turn 2.6s linear infinite; }
.as-answer { flex: 1; min-width: 0; max-width: 46rem; padding-top: 0.2rem; }
.as-md { line-height: 1.65; overflow-wrap: anywhere; }
.as-md pre { overflow-x: auto; }
.as-md > :first-child { margin-top: 0; }
.as-meta { margin-top: 0.35rem; }
.as-kept { margin-top: 0.35rem; }
.as-kept > summary { display: inline-flex; align-items: center; gap: 0.35rem; color: var(--text-secondary); cursor: pointer; }
.as-kept ul { margin: 0.35rem 0 0; padding-inline-start: 1.25rem; }
.as-error { padding-inline-start: 0.85rem; border-inline-start: 3px solid var(--danger); }
.as-error-text {
  max-height: 12rem;
  margin: 0.35rem 0 0.6rem;
  overflow: auto;
  color: var(--text-secondary);
  font-size: var(--type-small);
  white-space: pre-wrap;
}
/* Operations the AFG proposed (docs/adr/0045 §2): what they would do, and
   the member's choice to apply them or set them aside. */
.as-ops {
  margin: 0.85rem 0;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-raised);
}
.as-ops-refused { border-inline-start-color: var(--danger); }
.as-ops-applied, .as-ops-aside { border-inline-start-color: var(--border-strong); }
.as-ops-head { display: flex; align-items: center; gap: 0.45rem; margin-bottom: 0.4rem; color: var(--text-primary); }
.as-ops-lines { margin: 0 0 0.65rem; padding-inline-start: 1.2rem; }
.as-ops-lines li { margin: 0.15rem 0; }
.as-ops-form { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.as-ops-note { margin: 0.45rem 0 0; }
.as-ops-why { margin: 0 0 0.65rem; color: var(--text-primary); }
.as-ops-state { display: flex; align-items: center; gap: 0.35rem; margin: 0; color: var(--text-primary); }
.as-ops-json { margin-top: 0.5rem; }
.as-ops-json > summary { cursor: pointer; color: var(--text-secondary); }
.as-ops-json pre { max-height: 16rem; margin: 0.35rem 0 0; overflow: auto; font-family: var(--mono); }
.as-ops-preparing { display: flex; align-items: center; gap: 0.35rem; margin: 0.35rem 0 0; color: var(--text-secondary); }
.as-mark-done { color: var(--conf-certain); }
.as-done { padding-inline-start: 0.85rem; border-inline-start: 3px solid var(--border-strong); color: var(--text-secondary); }
.as-orphan { display: flex; align-items: center; justify-content: flex-end; gap: 0.6rem; margin: -0.5rem 0 1rem; }
/* The wordmark as the way back to the family's page, when the sections' row
   is not drawn (docs/adr/0046): it reads as the wordmark, not as a link. */
.brand-link { display: inline-flex; align-items: center; gap: inherit; color: inherit; text-decoration: none; }
/* Who may open each part of the site, in the Connectivity room. */
.doors-edit { margin-top: 0.75rem; }
.doors-edit > summary { cursor: pointer; color: var(--text-secondary); }
.doors-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: 0.25rem 1rem; align-items: end; margin-top: 0.75rem; }
.doors-form .btn { justify-self: start; }
/* The guide to keeping the archive through the AFG (docs/adr/0045 §6): its
   chapters folded, each request a quote to copy. */
.as-guide { max-width: 46rem; margin-top: 2rem; }
.as-guide-lead { margin-bottom: 0.85rem; line-height: 1.65; }
.as-guide-chapter { border-top: 1px solid var(--border-hairline); }
.as-guide-chapter:last-of-type { border-bottom: 1px solid var(--border-hairline); }
.as-guide-chapter > summary { padding: 0.7rem 0; cursor: pointer; color: var(--text-primary); font-weight: var(--weight-semi); }
.as-guide-body { padding: 0 0 0.9rem; line-height: 1.65; }
.as-guide-body blockquote {
  margin: 0.55rem 0;
  padding: 0.55rem 0.85rem;
  border: 1px solid var(--border-hairline);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: var(--text-primary);
}
.as-guide-body blockquote p { margin: 0; }
.as-guide-body code { overflow-wrap: anywhere; }
.as-files { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.45rem; }
.as-file-img img {
  display: block;
  max-width: 14rem;
  max-height: 12rem;
  border-radius: 0.75rem;
  object-fit: cover;
}
.as-file {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: var(--text-primary);
  font-size: var(--type-small);
  text-decoration: none;
}
.as-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.as-file-size { color: var(--text-tertiary); }
.as-typing { display: inline-flex; gap: 0.3rem; padding: 0.7rem 0.2rem 0.5rem; }
.as-typing i {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--text-tertiary);
  animation: as-dot 1.2s var(--ease) infinite;
}
.as-typing i:nth-child(2) { animation-delay: 0.15s; }
.as-typing i:nth-child(3) { animation-delay: 0.3s; }
.as-caret {
  display: inline-block;
  width: 0.5em;
  height: 1.05em;
  margin-inline-start: 0.12em;
  border-radius: 2px;
  background: var(--accent);
  vertical-align: text-bottom;
  animation: as-blink 1s steps(2, start) infinite;
}

/* The composer: at hand at the foot of a conversation. */
.as-composer { margin-top: 1rem; }
.as-shell[data-screen="conversation"] .as-composer {
  position: sticky;
  bottom: 0.75rem;
  z-index: 5;
}
.as-shell[data-screen="conversation"] .as-composer::before {
  content: "";
  position: absolute;
  inset: -1.5rem -0.5rem -0.75rem;
  z-index: -1;
  background: linear-gradient(to bottom, transparent, var(--surface-page) 45%);
}
.as-composer-label {
  display: block;
  margin: 0 0 0.35rem 0.3rem;
  color: var(--text-secondary);
  font-size: var(--type-label);
}
.as-composer-box {
  padding: 0.45rem 0.45rem 0.4rem;
  border: 1px solid var(--border-strong);
  border-radius: 1.4rem;
  background: var(--surface-raised);
  box-shadow: 0 14px 34px -20px rgb(var(--shadow-rgb) / 0.75);
  transition: border-color 160ms var(--ease), box-shadow 160ms var(--ease), background 160ms var(--ease);
}
.as-composer-box:focus-within { border-color: var(--accent); }
.as-composer-box.as-drop { border-style: dashed; border-color: var(--accent); background: var(--accent-soft); }
/* The words go straight into the box: no well of their own inside it. The
   focus ring stays, as on every field. */
.as-composer .as-composer-box textarea {
  display: block;
  width: 100%;
  min-height: 2.7rem;
  max-height: 40vh;
  padding: 0.5rem 0.6rem;
  border: 0;
  border-radius: 1rem;
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  line-height: 1.5;
  resize: none;
}
.as-composer-bar { display: flex; align-items: center; gap: 0.5rem; padding: 0.15rem 0.15rem 0; }
.as-attach {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.7rem;
  border-radius: 999px;
  color: var(--text-secondary);
  font-size: var(--type-small);
  cursor: pointer;
}
.as-attach:hover { background: var(--surface-hover); color: var(--text-primary); }
.as-attach:focus-within { box-shadow: 0 0 0 2px var(--focus-ring); }
.as-file-input { position: absolute; inset: 0; width: 100%; opacity: 0; cursor: pointer; }
.as-composer-note { flex: 1; min-width: 0; }
.as-send { display: inline-flex; align-items: center; gap: 0.35rem; border-radius: 999px; }
/* Its own arrow points up, the way a message goes: not the themes' arrow
   after a primary button as well. */
.as-composer .as-send.btn.primary::after { content: none; }
.as-busy .as-send { opacity: 0.55; }
.as-composer-hint { margin: 0.35rem 0.3rem 0; }
.as-chips { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 0.35rem; padding: 0 0.25rem; }
.as-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 16rem;
  padding: 0.2rem 0.3rem 0.2rem 0.25rem;
  border: 1px solid var(--border-hairline);
  border-radius: 999px;
  background: var(--surface-sunken);
  font-size: var(--type-small);
  animation: as-pop 200ms var(--ease);
}
.as-chip img { width: 1.6rem; height: 1.6rem; border-radius: 50%; object-fit: cover; }
.as-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.as-chip-size { color: var(--text-tertiary); }
.as-chip-x {
  padding: 0 0.3rem;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text-secondary);
  font-size: 1.1em;
  line-height: 1;
  cursor: pointer;
}
.as-chip-x:hover { color: var(--danger); }
.as-jump {
  position: fixed;
  bottom: 8rem;
  inset-inline-end: 1.25rem;
  z-index: 30;
  width: 2.6rem;
  height: 2.6rem;
  padding: 0;
  border-radius: 50%;
  box-shadow: 0 10px 24px -12px rgb(var(--shadow-rgb) / 0.8);
}
.as-shut { max-width: 34rem; margin: 2rem auto; text-align: center; }
/* Its door on the account page. */
.as-entry {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: 0 0 1.25rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: linear-gradient(120deg, var(--accent-soft), var(--surface-raised) 70%);
  color: var(--text-primary);
  text-decoration: none;
  transition: transform 180ms var(--ease), box-shadow 180ms var(--ease);
}
.as-entry:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -18px rgb(var(--shadow-rgb) / 0.7); }
.as-entry-mark {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
}
.as-entry-text { display: grid; flex: 1; gap: 0.1rem; }
.as-entry-go { color: var(--accent); transform: rotate(90deg); }
[dir="rtl"] .as-entry-go { transform: rotate(-90deg); }
.as-shut-art { border-radius: 30%; animation: as-float 4.6s var(--ease) infinite; }

@keyframes as-dot { 0%, 80%, 100% { opacity: 0.35; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-4px); } }
@keyframes as-blink { to { visibility: hidden; } }
@keyframes as-pop { from { opacity: 0; transform: scale(0.9); } }
@keyframes as-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes as-turn { to { transform: rotate(360deg); } }

@media (max-width: 860px) {
  .as-shell { grid-template-columns: minmax(0, 1fr); gap: 0.75rem; }
  .as-side {
    position: static;
    flex-direction: row;
    align-items: center;
    max-height: none;
    padding: 0.45rem;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .as-side-list { flex-direction: row; }
  .as-side-list a, .as-brand, .as-new { white-space: nowrap; }
  .as-side-name { max-width: 9rem; }
  .as-convs a { grid-template-columns: auto minmax(0, 1fr); }
  .as-conv-meta { display: none; }
  .as-bubble { max-width: 92%; }
  .as-file-img img { max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .as-typing i, .as-caret, .as-empty-art img, .as-shut-art, .as-mark-live .ico, .as-chip { animation: none; }
  .as-msg, .as-card a, .as-pick img { transition: none; }
}

/* ── The AFG on a person's page (docs/adr/0031) ──────────────────────────
   The assistant's thread and composer (docs/adr/0026), in the record's
   column: the reader's own conversations about the person. */
.pa { max-width: 48rem; }
.pa-head h2 { display: flex; align-items: center; gap: 0.5rem; margin: 0 0 0.4rem; }
.pa-head .lede { margin: 0 0 0.4rem; }
.pa-thread { min-height: 0; }
.pa-thread ~ .as-composer {
  position: sticky;
  bottom: 0.75rem;
  z-index: 5;
}
.pa-thread ~ .as-composer::before {
  content: "";
  position: absolute;
  inset: -1.5rem -0.5rem -0.75rem;
  z-index: -1;
  background: linear-gradient(to bottom, transparent, var(--surface-page) 45%);
}
.pa-convs { margin-top: 1.5rem; }
.pa-convs h3 { margin: 0 0 0.4rem; }
.pa-delete-all { margin-top: 0.75rem; }

/* ---- Impact: the Universal Synoptic Timeline (docs/adr/0034) -----------
   The world's history on a vertical axis, the family's lives beside it.
   Every position is computed in crate::impact and written as a style; the
   grid holds the columns side by side, and the axis and the headings stay
   pinned while the timeline scrolls in its own frame. No colour is named
   here: each kind of landmark takes one of the theme's hues. */
.ip-head { margin-block-end: 1rem; }
.ip-spans { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.6rem 0; }
.ip-spans a {
  padding: 0.25rem 0.8rem; border: 1px solid var(--border-strong); border-radius: 999px;
  color: var(--text-primary); text-decoration: none; font-size: var(--type-small);
}
.ip-spans a[aria-current="page"] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.ip-legend {
  display: flex; flex-wrap: wrap; gap: 0.3rem 1rem; list-style: none; margin: 0.5rem 0 0; padding: 0;
  font-size: var(--type-small); color: var(--text-secondary);
}
.ip-legend li::before {
  content: ""; display: inline-block; inline-size: 0.7rem; block-size: 0.7rem; border-radius: 2px;
  background: var(--ip-k); margin-inline-end: 0.35rem; vertical-align: -0.05em;
}
/* The legend's kinds, each one a switch once impact.js runs (the operator's
   request of 8 November 2026): a kind switched off is hidden everywhere on
   the timeline — its landmarks, their bars, and on a person's view its
   column — until it is switched on again. The reader's choice is kept in
   their browser. */
.ip-legend .ip-kind {
  padding: 0.1rem 0.55rem; border: 1px solid var(--border-hairline); border-radius: 999px;
  background: transparent; color: var(--text-secondary); font: inherit; cursor: pointer;
}
.ip-legend .ip-kind:hover { border-color: var(--ip-k); color: var(--text-primary); }
.ip-legend .ip-kind[aria-pressed="false"] { opacity: 0.5; text-decoration: line-through; }
.ip-h-kind { border-block-start: 3px solid var(--ip-k); }
.ip-hide-era :is(.ip-ev.ip-k-era, .ip-bar.ip-k-era, .ip-kcol-era, .ip-h-kind.ip-k-era),
.ip-hide-war :is(.ip-ev.ip-k-war, .ip-bar.ip-k-war, .ip-kcol-war, .ip-h-kind.ip-k-war),
.ip-hide-epidemic :is(.ip-ev.ip-k-epidemic, .ip-bar.ip-k-epidemic, .ip-kcol-epidemic, .ip-h-kind.ip-k-epidemic),
.ip-hide-migration :is(.ip-ev.ip-k-migration, .ip-bar.ip-k-migration, .ip-kcol-migration, .ip-h-kind.ip-k-migration),
.ip-hide-event :is(.ip-ev.ip-k-event, .ip-bar.ip-k-event, .ip-kcol-event, .ip-h-kind.ip-k-event),
.ip-hide-economy :is(.ip-ev.ip-k-economy, .ip-bar.ip-k-economy, .ip-kcol-economy, .ip-h-kind.ip-k-economy),
.ip-hide-disaster :is(.ip-ev.ip-k-disaster, .ip-bar.ip-k-disaster, .ip-kcol-disaster, .ip-h-kind.ip-k-disaster),
.ip-hide-invention :is(.ip-ev.ip-k-invention, .ip-bar.ip-k-invention, .ip-kcol-invention, .ip-h-kind.ip-k-invention),
.ip-hide-discovery :is(.ip-ev.ip-k-discovery, .ip-bar.ip-k-discovery, .ip-kcol-discovery, .ip-h-kind.ip-k-discovery) { display: none; }
.ip-k-era { --ip-k: var(--hue-1); }
.ip-k-war { --ip-k: var(--danger); }
.ip-k-epidemic { --ip-k: var(--hue-3); }
.ip-k-migration { --ip-k: var(--hue-4); }
.ip-k-event { --ip-k: var(--accent); }
.ip-k-economy { --ip-k: var(--hue-6); }
.ip-k-disaster { --ip-k: var(--hue-7); }
.ip-k-invention { --ip-k: var(--hue-5); }
.ip-k-discovery { --ip-k: var(--hue-8); }
.ip-held { margin-block-start: 0.6rem; font-size: var(--type-small); }
.ip-held ul { margin: 0.4rem 0 0; padding-inline-start: 1.1rem; }

.ip-scroll {
  overflow: auto; max-block-size: calc(100vh - 8rem);
  border: 1px solid var(--border-hairline); border-radius: var(--radius); background: var(--surface-raised);
}
.ip-grid {
  display: grid; grid-template-columns: 7rem var(--ip-a) var(--ip-b) var(--ip-c);
  inline-size: max-content; min-inline-size: 100%;
}
.ip-h {
  position: sticky; inset-block-start: 0; z-index: 4; padding: 0.55rem 0.6rem;
  background: var(--surface-raised); border-block-end: 1px solid var(--border-strong);
  font-size: var(--type-label); font-weight: var(--weight-semi); color: var(--text-secondary); white-space: nowrap;
}
.ip-h-axis { inset-inline-start: 0; z-index: 5; }
.ip-time { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; position: relative; }
.ip-axis {
  position: sticky; inset-inline-start: 0; z-index: 3;
  background: var(--surface-raised); border-inline-end: 1px solid var(--border-hairline);
}
.ip-tick {
  position: absolute; inset-inline-end: 0.5rem; transform: translateY(-50%); white-space: nowrap;
  font-size: var(--type-micro); color: var(--text-tertiary); font-variant-numeric: tabular-nums;
}
.ip-rule { position: absolute; inset-inline: 0; border-block-start: 1px dashed var(--border-hairline); pointer-events: none; }
.ip-col { position: relative; border-inline-end: 1px solid var(--border-hairline); }
.ip-bar { position: absolute; inline-size: 4px; border-radius: 2px; background: var(--ip-k); opacity: 0.55; }
.ip-thread { position: absolute; inline-size: 0; border-inline-start: 1px dotted var(--text-tertiary); }
.ip-ev {
  position: absolute; inline-size: calc(var(--ip-lane) - 10px); block-size: 20px; overflow: hidden;
  padding: 0 0.35rem; border-inline-start: 3px solid var(--ip-k); border-radius: 3px;
  background: var(--surface-page); color: var(--text-primary);
  font-size: var(--type-micro); line-height: 20px; white-space: nowrap; text-overflow: ellipsis;
}
.ip-ev-name { font-weight: var(--weight-semi); }
.ip-ev-when { margin-inline-start: 0.4em; color: var(--text-secondary); }
.ip-ev-note { display: none; }
.ip-ev:hover {
  block-size: auto; z-index: 6; white-space: normal;
  box-shadow: 0 4px 14px rgba(var(--shadow-rgb), 0.25);
}
.ip-ev:hover .ip-ev-note { display: block; color: var(--text-secondary); }
/* A person's view: cards, two lines on the world's side, three on their
   country's, the note in full under the pointer. */
.ip-wide .ip-ev, .ip-near .ip-ev {
  display: flex; flex-direction: column; padding: 0.2rem 0.45rem; line-height: 1.3; white-space: normal;
}
.ip-wide .ip-ev { block-size: 42px; }
.ip-near .ip-ev { block-size: 62px; }
.ip-wide .ip-ev-name, .ip-near .ip-ev-name, .ip-wide .ip-ev-when, .ip-near .ip-ev-when {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin: 0;
}
.ip-near .ip-ev-note {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  color: var(--text-secondary);
}
.ip-wide .ip-ev:hover, .ip-near .ip-ev:hover { block-size: auto; }
/* A kind's column on a person's view (the operator's request of
   8 November 2026): narrow, so that several sit side by side, the name on
   two lines before it is cut; their country's a little narrower too. */
.ip-kcol .ip-ev { block-size: 54px; }
.ip-kcol .ip-ev-name { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.ip-near .ip-ev { block-size: 72px; }
.ip-near .ip-ev-name { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.ip-h-kind { white-space: normal; line-height: 1.2; }
.ip-near .ip-ev:hover .ip-ev-note { display: block; -webkit-line-clamp: unset; }
/* The band of a life: the landmarks it crossed stand out, the others step
   back. A person's view is crossed from the start. */
.ip-time[data-from] .ip-ev:not(.ip-hit), .ip-time.ip-crossing .ip-ev:not(.ip-hit) { opacity: 0.4; }
.ip-time[data-from] .ip-bar:not(.ip-hit), .ip-time.ip-crossing .ip-bar:not(.ip-hit) { opacity: 0.18; }
.ip-band {
  position: absolute; inset-inline: 0; pointer-events: none;
  background: var(--accent-soft); border-block: 1px solid var(--accent); opacity: 0.6;
}
.ip-band-own { opacity: 0.4; }
.ip-tip {
  position: absolute; z-index: 7; margin: 0; padding: 0.25rem 0.5rem; pointer-events: none; white-space: nowrap;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface-page);
  font-size: var(--type-small); box-shadow: 0 4px 14px rgba(var(--shadow-rgb), 0.25);
}
/* Lives: always a window (docs/adr/0036). An end recorded only about a
   year fades a little; an end no date gives, estimated, fades over much of
   the window. */
.ip-life {
  --ip-fade-a: 0px; --ip-fade-b: 0px;
  position: absolute; inline-size: 10px; border-radius: 3px; background: var(--accent); opacity: 0.75;
  mask-image: linear-gradient(to bottom, transparent, currentColor var(--ip-fade-a), currentColor calc(100% - var(--ip-fade-b)), transparent);
}
.ip-life:hover, .ip-life.ip-lit { opacity: 1; }
.ip-life.ip-living { background: var(--hue-2); }
.ip-life.ip-fuzzy-from { --ip-fade-a: 14px; }
.ip-life.ip-fuzzy-to { --ip-fade-b: 14px; }
.ip-life.ip-open-from { --ip-fade-a: 40%; }
.ip-life.ip-open-to { --ip-fade-b: 40%; }
.ip-life-name { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
.ip-own .ip-life-own { inset-inline-start: 1rem; inline-size: 14px; opacity: 1; }
/* The family's column fits the room the page has: fourteen pixels a lane
   where there is room, thinner where there is not, never under a pixel;
   the life nearest the pointer is the one pointed at (impact.js). */
.ip-grid.ip-fit {
  grid-template-columns: 7rem var(--ip-a) var(--ip-b) minmax(min(16rem, var(--ip-c)), var(--ip-c));
  inline-size: 100%; min-inline-size: min-content;
}
.ip-lives .ip-life {
  inset-inline-start: calc(min(14px, 100% / var(--ip-n, 1)) * var(--l, 0));
  inline-size: max(1px, calc(min(14px, 100% / var(--ip-n, 1)) * 0.72));
}
.ip-lives .ip-life.ip-lit { z-index: 2; inline-size: max(4px, calc(min(14px, 100% / var(--ip-n, 1)) * 0.72)); }
.ip-stone {
  position: absolute; inset-inline: 2.4rem 0.5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--type-micro); line-height: 18px; color: var(--text-secondary);
}
/* The graduation (docs/adr/0035): a line across the columns for each year
   where a year has room, every ten stronger; a mark on the axis for each. */
.ip-gl { position: absolute; inset-inline: 0; border-block-start: 1px solid var(--border-hairline); opacity: 0.35; pointer-events: none; }
.ip-gl-strong { opacity: 0.8; }
.ip-mt { position: absolute; inset-inline-end: 0; inline-size: 0.35rem; border-block-start: 1px solid var(--text-tertiary); pointer-events: none; }
.ip-mt-strong { inline-size: 0.7rem; }
/* What is pointed at: its years on the axis and across the timeline, its
   bar lit. */
.ip-axis-span {
  position: absolute; inset-inline: 0.3rem 0; z-index: 1; border-radius: 3px;
  background: var(--accent-soft); border-inline-end: 3px solid var(--accent);
}
.ip-axis-from {
  position: absolute; inset-block-start: 0; inset-inline-start: 0; padding: 0 0.25rem;
  border-radius: 3px; background: var(--accent); color: var(--on-accent);
  font-size: var(--type-micro); font-weight: var(--weight-semi); white-space: nowrap;
}
.ip-band-ev { opacity: 0.45; border-block-color: var(--ip-k, var(--accent)); }
.ip-bar.ip-lit { opacity: 1; inline-size: 6px; }
.ip-ev { cursor: pointer; }
.ip-ev.ip-lit { z-index: 6; box-shadow: 0 0 0 1px var(--accent), 0 4px 14px rgba(var(--shadow-rgb), 0.25); }
.ip-more { display: none; }
.ip-hint { margin: 0.35rem 0 0; }
/* A landmark's window. */
.ip-dialog {
  max-inline-size: min(36rem, calc(100vw - 2rem)); padding: 1rem 1.25rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface-raised); color: var(--text-primary);
  box-shadow: 0 12px 40px rgba(var(--shadow-rgb), 0.35);
}
.ip-dialog-close { float: inline-end; margin: 0 0 0.5rem 0.75rem; }
.ip-more-name { margin: 0 0 0.2rem; font-size: var(--type-sub); }
.ip-more-when { margin: 0 0 0.6rem; color: var(--text-secondary); font-size: var(--type-small); }
.ip-dialog-body h3 { margin: 0.8rem 0 0.2rem; font-size: var(--type-label); font-weight: var(--weight-semi); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
.ip-dialog-body p { margin: 0 0 0.4rem; }
/* A country's flag beside its name, on the timeline and on a record. */
/* A country's flag (docs/adr/0035), crisp at every screen scale (the
   operator's review of 8 November 2026: its edges were jagged). The picture
   is 16 by 12 pixels, whole at 100, 125, 150 and 200 %, where 1.25em fell
   between pixels. Its frame is a border round it, which the browser keeps
   to whole pixels and which shows a white band on a light page, where a
   shadow's spread drew a ring smoothed unevenly; an outline is the focus
   ring's alone (tests/controls.rs). */
.flag {
  display: inline-block; box-sizing: content-box; inline-size: 16px; block-size: 12px; object-fit: cover;
  vertical-align: -3px; border: 1px solid color-mix(in srgb, var(--text-primary) 28%, transparent); border-radius: 3px;
}
