/* ---------- Faces -----------------------------------------------------
   Three faces, three jobs, each chosen for this project rather than for
   flavour. Self-hosted: the CSP allows no outside origins, and a silent
   fallback would undo the reason for picking them.
   -------------------------------------------------------------------- */

/* Atkinson Hyperlegible — drawn by the Braille Institute for readers with
   low vision. Chosen for a reader with macular degeneration: unambiguous
   letterforms, open apertures, no confusable I/l/1. Everything read as
   content is set in this. */
@font-face {
  font-family: "Atkinson Hyperlegible";
  src: url("/static/fonts/AtkinsonHyperlegible-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  src: url("/static/fonts/AtkinsonHyperlegible-400-italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Atkinson Hyperlegible";
  src: url("/static/fonts/AtkinsonHyperlegible-700-normal.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* Courier Prime — index cards were typed. Used only for the things that would
   have been typed onto a card: the subject line, counts, dates. */
@font-face {
  font-family: "Courier Prime";
  src: url("/static/fonts/CourierPrime-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Courier Prime";
  src: url("/static/fonts/CourierPrime-700-normal.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

/* Newsreader — the book all of this is being collected for. Headings and the
   wordmark only, never at small sizes. */
@font-face {
  font-family: "Newsreader";
  src: url("/static/fonts/Newsreader-400-normal.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  src: url("/static/fonts/Newsreader-600-normal.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}

/* ---------- Tokens ----------------------------------------------------
   Who this is for: two people in their late sixties, on iPads. One is partially
   sighted and answers by speaking into the keyboard microphone. The single job
   of a screen is to get one memory out of one person, right now.

   Warm and friendly on purpose, and light-only on purpose: following the
   device's dark preference produced a cold grey page where the card had no
   edges. Dark can come back later as a deliberate choice.
   ------------------------------------------------------------------- */

:root {
  color-scheme: light;

  --oat:         #fdf8f0;   /* the page: warm, like a good notebook */
  --oat-deep:    #f6ecdd;
  --sheet:       #ffffff;   /* the card */
  --sheet-warm:  #fffcf7;

  --ink:         #241c14;   /* 15.9:1 on white */
  --ink-mid:     #4d3f31;   /*  9.1:1 */
  --ink-soft:    #6d5c4a;   /*  5.6:1 */

  --rule:        #ece0cd;
  --rule-firm:   #c9b697;

  --teal:        #10605f;   /* 6.3:1 on white — calm, trustworthy, friendly */
  --teal-deep:   #0a4443;
  --teal-wash:   #e0eeed;
  --on-teal:     #ffffff;

  --apricot:     #a84a17;   /* 6.0:1 — warmth and emphasis */
  --apricot-wash:#fdece0;

  --sage:        #2c6a45;   /* answered */
  --sage-wash:   #e6f2ea;
  --rust:        #9c3218;   /* something went wrong */
  --rust-wash:   #fdeae5;

  --sans:     "Atkinson Hyperlegible", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --typed:    "Courier Prime", ui-monospace, Menlo, monospace;
  --wordmark: "Newsreader", ui-serif, "Iowan Old Style", Georgia, serif;

  --t-micro: 1rem;
  --t-small: 1.125rem;
  --t-body:  1.3125rem;
  --t-lead:  1.5rem;
  --t-h3:    1.4375rem;
  --t-h2:    1.75rem;
  --t-h1:    2.375rem;

  --leading: 1.7;
  --tight:   1.25;

  --s1: .25rem; --s2: .5rem; --s3: .75rem; --s4: 1rem;
  --s5: 1.5rem; --s6: 2rem;  --s7: 3rem;   --s8: 4.5rem;

  --tap: 52px;              /* thumbs, not cursors */
  --measure: 33rem;
  --column: 43rem;

  --r:    1rem;             /* rounded reads friendly; square reads clinical */
  --r-sm: .625rem;
  --r-pill: 999px;

  --lift-1: 0 1px 2px rgba(60,42,24,.06),  0 2px 6px rgba(60,42,24,.05);
  --lift-2: 0 1px 2px rgba(60,42,24,.08),  0 10px 22px -8px rgba(60,42,24,.16);
  --lift-3: 0 2px 4px rgba(60,42,24,.10),  0 20px 40px -14px rgba(60,42,24,.24);
  --lift-4: 0 4px 8px rgba(60,42,24,.12),  0 34px 60px -24px rgba(60,42,24,.30);

  --ease: cubic-bezier(.2,.6,.35,1);
}

/* Text size is the reader's to set. Everything is in rem, so one change here
   scales the whole interface — far easier to find than Safari's settings. */
:root[data-size="lg"]  { font-size: 118%; }
:root[data-size="xl"]  { font-size: 138%; }
:root[data-size="xxl"] { font-size: 160%; }

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--oat);
  color: var(--ink);
  font: var(--t-body)/var(--leading) var(--sans);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

::selection { background: var(--teal-wash); color: var(--ink); }

:focus-visible {
  outline: 3px solid var(--teal);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

.skip {
  position: absolute; left: var(--s4); top: var(--s4); z-index: 100;
  padding: var(--s3) var(--s4);
  background: var(--teal); color: var(--on-teal);
  font: 700 var(--t-small)/1.2 var(--sans);
  border-radius: var(--r-sm);
  transform: translateY(-250%);
  transition: transform 120ms var(--ease);
}
.skip:focus { transform: translateY(0); }

/* ---------- Masthead --------------------------------------------------
   One row: name, where to go, settings. It was two, with the tabs underneath,
   which cost 77px of every screen before any content and pushed the first
   question halfway down an iPad. The tabs stay big enough to hit without aiming
   and drop to a line of their own once three of them no longer fit.
   ------------------------------------------------------------------- */

.bar {
  display: flex; align-items: center;
  gap: var(--s4); flex-wrap: wrap;
  padding: var(--s4) clamp(var(--s4), 4vw, var(--s6));
  padding-top: max(var(--s4), env(safe-area-inset-top));
  background: var(--sheet);
  border-bottom: 1px solid var(--rule);
}

.bar-title {
  display: inline-flex; align-items: baseline; gap: .45rem;
  font-family: var(--wordmark);
  font-size: 1.625rem; font-weight: 600; letter-spacing: -.015em;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}

.bar-tools { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin-left: auto; }
.bar-tools form { margin: 0; }

/* Inside the bar, so it carries no padding, background or rule of its own. The
   tools are pushed to the far edge by their own margin instead of the bar's
   space-between, which would otherwise strand the tabs in the middle. */
.tabs {
  display: flex; gap: var(--s2); flex-wrap: wrap;
  margin-left: var(--s5);
}

@media (max-width: 46rem) {
  /* Its own line under the title once the three of them stop fitting, which keeps
     the tap targets full width rather than squeezing them. */
  .tabs { order: 3; flex: 1 0 100%; margin-left: 0; }
}

.tab {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--tap); padding: var(--s2) var(--s4);
  border-radius: var(--r-pill);
  color: var(--ink-mid); font: 600 var(--t-small)/1.2 var(--sans);
  text-decoration: none;
  transition: background 130ms var(--ease), color 130ms var(--ease);
}
.tab:hover { background: var(--oat-deep); color: var(--ink); }
.tab[aria-current="page"] { background: var(--teal); color: var(--on-teal); font-weight: 700; }

.page {
  max-width: var(--column);
  margin: 0 auto;
  /* Tight gutters on a phone, generous on a desktop. The old floor of 1rem each
     side still wasted space that the cards could use. */
  padding: clamp(var(--s4), 4vw, var(--s7)) clamp(var(--s3), 3.5vw, var(--s6));
  padding-bottom: max(var(--s7), calc(env(safe-area-inset-bottom) + var(--s5)));
}

@media (max-width: 30rem) {
  .page { padding-left: var(--s3); padding-right: var(--s3); }
  .panel, .card, .qrow, .answer { padding: var(--s4); }

  /* At 26px the wordmark was 164px against the 147px left over beside the tools,
     so the tools wrapped and the bar cost three rows on a phone instead of two. */
  .bar-title { font-size: 1.375rem; }

}

/* ---------- Type ---------------------------------------------------- */

h1, h2, h3 { margin: 0; font-family: var(--wordmark); line-height: var(--tight);
             letter-spacing: -.015em; text-wrap: balance; color: var(--ink); }
h1 { font-size: var(--t-h1); font-weight: 600; margin-bottom: var(--s3); }
h3 { font-size: var(--t-h3); font-weight: 600; margin-bottom: var(--s2); }

h2 {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  font-size: var(--t-h2); font-weight: 600;
  margin: var(--s7) 0 var(--s4); padding-bottom: var(--s2);
  border-bottom: 2px solid var(--rule);
}

p { margin: 0 0 var(--s4); max-width: var(--measure); }
.lede { font-size: var(--t-lead); color: var(--ink-mid); text-wrap: pretty; }

.eyebrow {
  font: 700 var(--t-micro)/1.4 var(--sans);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--apricot); margin: 0 0 var(--s2);
}

.count { font-family: var(--typed); font-size: var(--t-small); font-weight: 700;
         font-variant-numeric: tabular-nums; color: var(--ink-soft); letter-spacing: 0; }

.hint { margin-top: var(--s2); max-width: var(--measure);
        font-size: var(--t-small); line-height: 1.6; color: var(--ink-soft); }

/* ---------- Buttons ------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--tap); min-width: var(--tap);
  padding: .85rem 1.75rem;
  border: 2px solid var(--teal); border-radius: var(--r-pill);
  background: var(--teal); color: var(--on-teal);
  font: 700 1.1875rem/1.2 var(--sans);
  cursor: pointer; text-decoration: none; touch-action: manipulation;
  box-shadow: var(--lift-1);
  transition: background 130ms var(--ease), box-shadow 160ms var(--ease), transform 100ms var(--ease);
}
.btn:hover  { background: var(--teal-deep); border-color: var(--teal-deep); box-shadow: var(--lift-2); }
.btn:active { transform: translateY(1px); box-shadow: var(--lift-1); }

.btn-quiet {
  background: var(--sheet); color: var(--teal); border-color: var(--rule-firm); box-shadow: none;
}
.btn-quiet:hover { background: var(--teal-wash); border-color: var(--teal); color: var(--teal-deep); }

.btn[disabled] { opacity: .5; cursor: default; }

.btn-row { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; margin-top: var(--s5); }

.link {
  display: inline-flex; align-items: center; min-height: var(--tap);
  padding: 0; border: 0; background: none; color: var(--teal);
  font: 600 var(--t-small)/1.4 var(--sans); cursor: pointer;
  text-decoration: underline; text-underline-offset: .22em; text-decoration-thickness: 2px;
}
.link:hover { color: var(--teal-deep); text-decoration-thickness: 3px; }

/* ---------- Forms --------------------------------------------------- */

label { display: block; margin-bottom: var(--s2);
        font: 700 var(--t-body)/1.4 var(--sans); color: var(--ink); }

input[type="email"], input[type="text"], textarea, select {
  width: 100%; min-height: var(--tap);
  padding: .9rem 1.1rem;
  border: 2px solid var(--rule-firm); border-radius: var(--r-sm);
  background: var(--sheet); color: var(--ink);
  font: var(--t-body)/1.65 var(--sans);
}
input[type="email"], input[type="text"], select { margin-bottom: var(--s4); }
input::placeholder, textarea::placeholder { color: var(--ink-soft); opacity: 1; }

input:focus, textarea:focus, select:focus {
  outline: 3px solid var(--teal); outline-offset: 1px; border-color: var(--teal);
}

/* The writing surface: the largest thing on the card after the question. */
textarea { min-height: 7.5rem; resize: vertical; background: var(--sheet-warm); }

input[type="file"] {
  width: 100%; min-height: var(--tap); padding: var(--s3);
  border: 2px dashed var(--rule-firm); border-radius: var(--r-sm);
  background: var(--oat-deep); color: var(--ink-mid);
  font-size: var(--t-small); margin-bottom: var(--s4);
}

/* Speaking is how this actually gets used, so the invitation to speak is a
   real element rather than placeholder text that vanishes on first tap. */
.dictate {
  display: flex; align-items: center; gap: var(--s3);
  margin: 0 0 var(--s3); padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  background: var(--apricot-wash);
  color: var(--ink-mid);
  font-size: var(--t-small); line-height: 1.55;
  max-width: none;
}
.dictate-icon {
  flex: none;
  display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem;
  border-radius: 50%;
  background: var(--apricot); color: #fff;
  font-size: 1.25rem;
}

/* ---------- The deck ------------------------------------------------
   The signature: sheets of paper with their edges showing, so the pile reads
   as finite and physical. The old version put an empty band above the card,
   which just looked broken.
   ------------------------------------------------------------------- */

.stack { position: relative; margin-top: var(--s5); padding-bottom: 1.75rem; }

.stack-peek {
  position: absolute; z-index: 0;
  height: 5rem; bottom: 0;
  border: 1px solid var(--card-edge, var(--rule));
  border-radius: var(--r);
  background: var(--sheet-warm);
  box-shadow: var(--lift-1);
}
.stack-peek-1 { left: .85rem; right: .85rem; bottom: .85rem; }
.stack-peek-2 { left: 1.7rem; right: 1.7rem; bottom: 0; }

.card {
  position: relative; z-index: 1;
  padding: clamp(var(--s4), 4.5vw, var(--s5));
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--sheet);
  box-shadow: var(--lift-3);
  touch-action: pan-y;
  cursor: grab;
  user-select: none; -webkit-user-select: none;
}
.card textarea, .card .answer-body { user-select: text; -webkit-user-select: text; }
.card .btn { min-height: 56px; padding-inline: 1.9rem; }
.card .btn-row { margin-top: var(--s4); }
.card label { margin-bottom: var(--s2); }

.card.dragging { cursor: grabbing; transition: none; box-shadow: var(--lift-4); }
.card.settling { transition: transform 240ms cubic-bezier(.34,1.4,.5,1), opacity 200ms linear; }
.card.tossed   { pointer-events: none; }

.card[data-toss] { border-color: var(--teal); }
.card[data-toss]::after {
  content: "Let go — back in the pile";
  position: absolute; top: var(--s4);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-pill);
  background: var(--teal); color: var(--on-teal);
  font: 700 1rem/1.2 var(--sans); white-space: nowrap;
}
.card[data-toss="right"]::after { right: var(--s4); }
.card[data-toss="left"]::after  { left: var(--s4); }

.stack-peek { transition: transform 200ms var(--ease); }
.stack[data-armed] .stack-peek-1 { transform: translateY(-3px); }
.stack[data-armed] .stack-peek-2 { transform: translateY(-3px); }

@keyframes deal-in {
  from { transform: translate3d(0,12px,0) scale(.99); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.card { animation: deal-in 200ms var(--ease) both; }
.card.dragging, .card.tossed, .card.settling { animation: none; }

.card-subject {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  margin: 0 0 var(--s3); max-width: none;
  font: 700 var(--t-micro)/1.4 var(--sans);
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--apricot);
}

.card-question {
  font-family: var(--sans);
  font-size: clamp(1.5rem, 4.2vw, 1.8125rem);
  font-weight: 700; line-height: 1.3; letter-spacing: -.015em;
  text-wrap: pretty; margin: 0 0 var(--s4); max-width: none; color: var(--ink);
}

.card-badge {
  display: inline-block; padding: .1rem .6rem;
  border-radius: var(--r-pill);
  background: var(--oat-deep); color: var(--ink-mid);
  font: 700 .9375rem/1.6 var(--sans);
  letter-spacing: .01em; text-transform: none;
}

.save-state {
  min-height: 1.7rem; margin: var(--s2) 0 0; max-width: none;
  font-size: var(--t-small); color: var(--ink-soft);
}
.save-state[data-state="error"] { color: var(--rust); font-weight: 700; }

/* ---------- Banners ------------------------------------------------- */

.banner {
  padding: var(--s3) var(--s4); margin-bottom: var(--s4);
  border-radius: var(--r-sm);
  background: var(--rust-wash); color: var(--rust);
  border: 2px solid var(--rust);
  font-weight: 600; max-width: var(--measure);
}
.banner-quiet { background: var(--sage-wash); color: var(--sage); border-color: var(--sage); }

/* ---------- Pills --------------------------------------------------- */

.mode-picker { display: flex; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s5); }

.mode-picker button, .pill {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--tap); padding: var(--s2) var(--s4);
  border: 2px solid var(--rule-firm); border-radius: var(--r-pill);
  background: var(--sheet); color: var(--ink-mid);
  font: 600 1.0625rem/1.2 var(--sans);
  text-decoration: none; cursor: pointer;
  transition: background 130ms var(--ease), border-color 130ms var(--ease), color 130ms var(--ease);
}
.mode-picker button:hover, .pill:hover { border-color: var(--teal); background: var(--teal-wash); color: var(--ink); }
.mode-picker button[aria-pressed="true"], .pill[aria-pressed="true"] {
  border-color: var(--teal); background: var(--teal); color: var(--on-teal); font-weight: 700;
}
.mode-picker button[aria-pressed="true"] .count, .pill[aria-pressed="true"] .count { color: var(--on-teal); }

.filter { margin-bottom: var(--s5); }

summary {
  display: flex; align-items: center; gap: var(--s2);
  min-height: var(--tap); cursor: pointer;
  color: var(--teal); font: 700 var(--t-small)/1.4 var(--sans); list-style: none;
}
summary::-webkit-details-marker { display: none; }
summary::after { content: "+"; font-weight: 700; font-size: 1.25rem; line-height: 1; }
details[open] > summary::after { content: "–"; }

/* ---------- Display settings --------------------------------------- */

/* The panel is positioned against the whole tools group rather than against the
   button, so its right edge lines up with the edge of the bar. Anchored to the
   button it ran off the left of a phone: the button sits 100px in from the right,
   the panel is 336px wide, and 336 from there is 46px past the screen -- the word
   "Text size" arrived as "t size" with the Normal button cut in half. */
.bar-tools { position: relative; }
/* The disclosure triangle is a ::marker, not an ::after, and hiding the wrong one
   left it holding space at the start of the pill so the label sat right of centre.
   A summary only stops generating a marker once it is no longer display: list-item,
   which is what the flex here is for; the two resets below cover older engines. */
.display-settings > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  list-style: none;
  padding: var(--s2) var(--s4);
  border: 2px solid var(--rule-firm); border-radius: var(--r-pill);
  color: var(--ink-mid); white-space: nowrap;
  cursor: pointer;
}
.display-settings > summary::marker { content: ""; }
.display-settings > summary::-webkit-details-marker { display: none; }
/* Opts out of the "+" that summary::after gives every other collapsible here. The
   pill already reads as a button, and centring the label against a "+" is what put
   it off centre in the first place. content: none generates no box at all, where
   content: "" still makes an empty flex item.
   The open state needs its own selector: details[open] > summary::after swaps in a
   "–" and, at two type selectors to this rule's one, wins on specificity unless the
   [open] is matched here as well. */
.display-settings > summary::after,
.display-settings[open] > summary::after { content: none; }
.display-settings > summary:hover { border-color: var(--teal); color: var(--ink); }

.display-panel {
  position: absolute; right: 0; top: calc(100% + var(--s2)); z-index: 40;
  width: min(21rem, calc(100vw - 2rem));
  /* Belt as well as braces: whatever it is anchored to, it never starts left of
     the screen. */
  max-width: calc(100vw - var(--s4) * 2);
  padding: var(--s4);
  border: 1px solid var(--rule); border-radius: var(--r);
  background: var(--sheet); box-shadow: var(--lift-3);
}
.display-panel h3 { font-family: var(--sans); font-size: var(--t-small); margin: 0 0 var(--s2); }
.display-panel > * + h3 { margin-top: var(--s4); }

.display-row { display: flex; gap: var(--s2); flex-wrap: wrap; }
.display-row button {
  flex: 1 1 auto; min-height: var(--tap); padding: var(--s2) var(--s3);
  border: 2px solid var(--rule-firm); border-radius: var(--r-sm);
  background: var(--sheet); color: var(--ink-mid);
  font: 600 1.0625rem/1.2 var(--sans); cursor: pointer;
}
.display-row button[aria-pressed="true"] {
  border-color: var(--teal); background: var(--teal); color: var(--on-teal); font-weight: 700;
}

/* ---------- Question list ------------------------------------------ */

.qlist { list-style: none; margin: 0 0 var(--s6); padding: 0; }

.qrow {
  display: flex; flex-direction: column; gap: var(--s1);
  padding: var(--s4); margin-bottom: var(--s3);
  border: 1px solid var(--rule); border-radius: var(--r-sm);
  background: var(--sheet);
}
.qrow-done { background: var(--oat-deep); border-color: var(--rule); }

.qrow-body {
  font-size: var(--t-body); line-height: 1.5; font-weight: 600;
  color: var(--ink); text-decoration: none; text-wrap: pretty;
}
.qrow-body:hover { color: var(--teal); text-decoration: underline; text-underline-offset: .2em; }

.qrow-meta { margin: 0; max-width: none; font-size: var(--t-small); line-height: 1.55; color: var(--ink-soft); }
/* Their name, and the way to their page. Underlined only on hover: at 174 rows a
   permanent underline turns the list into a page of links, and the name is a label
   first and a route second. */
.qrow-subject { color: var(--apricot); font-weight: 700; text-decoration: none; }
a.qrow-subject:hover, a.qrow-subject:focus-visible { text-decoration: underline; }

/* Same on the group heading above them. */
.qgroup-head a { color: inherit; text-decoration: none; }
.qgroup-head a:hover, .qgroup-head a:focus-visible { text-decoration: underline; }
.qrow-done .qrow-meta { color: var(--sage); font-weight: 600; }

/* ---------- Answers ------------------------------------------------- */

.answer {
  padding: var(--s5); margin-bottom: var(--s4);
  border: 1px solid var(--rule); border-radius: var(--r);
  background: var(--sheet); box-shadow: var(--lift-1);
}
.answer-primary { border-left: 5px solid var(--teal); background: var(--sheet-warm); box-shadow: var(--lift-2); }

.answer-by {
  display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
  margin: 0 0 var(--s3); max-width: none;
  font: 700 var(--t-body)/1.4 var(--sans); color: var(--ink);
}
.answer-when { font-family: var(--typed); font-weight: 400; font-size: var(--t-small);
               color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.answer-body { white-space: pre-wrap; max-width: var(--measure);
               font-size: var(--t-body); line-height: 1.75; color: var(--ink); }

.replies { list-style: none; margin: var(--s5) 0 0; padding: 0 0 0 var(--s4);
           border-left: 3px solid var(--rule); }
.replies li { margin-bottom: var(--s4); white-space: pre-wrap; line-height: 1.65; color: var(--ink-mid); }
.reply-by { font-weight: 700; color: var(--ink); margin-right: var(--s2); }

.reply-form, .photo-form, .new-story { margin-top: var(--s5); }

/* ---------- Photographs -------------------------------------------- */

.photos { list-style: none; margin: var(--s5) 0 0; padding: 0; display: grid; gap: var(--s5); }
@media (min-width: 40rem) { .photos { grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); } }
.photos img { display: block; width: 100%; height: auto;
              border: 1px solid var(--rule); border-radius: var(--r-sm); background: var(--oat-deep); }
.photo-caption { margin: var(--s2) 0 0; max-width: none; font-size: var(--t-small); color: var(--ink-mid); }

/* ---------- Pedigree ------------------------------------------------ */

.pedigree { margin: 0 0 var(--s7); }
.tparents { margin-left: var(--s2); padding-left: var(--s4); border-left: 2px solid var(--rule); }

.tperson {
  display: block; min-height: var(--tap);
  padding: var(--s3) var(--s4); margin: var(--s2) 0;
  border: 1px solid var(--rule); border-radius: var(--r-sm);
  background: var(--sheet); color: var(--ink); text-decoration: none;
}
a.tperson:hover { border-color: var(--teal); background: var(--teal-wash); }
.tperson-told { border-left: 5px solid var(--teal); }
.tperson-plain { background: transparent; border-style: dashed; color: var(--ink-soft); }

.tname  { display: block; font-size: var(--t-body); font-weight: 700; line-height: 1.35; }
.tyears { font-family: var(--typed); font-size: var(--t-small); color: var(--ink-soft);
          font-variant-numeric: tabular-nums; }
.tcount { display: block; margin-top: .15rem; font: 700 var(--t-small)/1.4 var(--sans);
          color: var(--apricot); font-variant-numeric: tabular-nums; }

.tnode-gen0 > .tperson { background: var(--apricot-wash); border-color: var(--rule-firm); }
.tnode-gen0 > .tperson .tname { font-size: var(--t-h3); }

/* ---------- Subjects ------------------------------------------------ */

.slist { list-style: none; margin: 0; padding: 0; }
.srow {
  padding: var(--s4); margin-bottom: var(--s3);
  border: 1px solid var(--rule); border-radius: var(--r-sm); background: var(--sheet);
}
.srow-name { display: block; font-size: var(--t-body); font-weight: 700;
             color: var(--ink); text-decoration: none; }
.srow-name:hover { color: var(--teal); text-decoration: underline; text-underline-offset: .2em; }
.srow-meta { margin: var(--s1) 0 0; max-width: none; font-size: var(--t-small);
             color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.members { list-style: none; margin: 0 0 var(--s5); padding: var(--s4);
           border: 1px solid var(--rule); border-radius: var(--r-sm); background: var(--sheet-warm); }
.members li { padding: var(--s1) 0; }

/* ---------- Empty --------------------------------------------------- */

.empty {
  padding: var(--s7) var(--s5);
  border: 1px solid var(--rule); border-radius: var(--r);
  background: var(--sheet); box-shadow: var(--lift-2);
}
.empty h1 { margin-bottom: var(--s3); }

/* ---------- Print --------------------------------------------------- */

@media print {
  .bar, .tabs, .skip, .mode-picker, .filter, .btn, .btn-row,
  .reply-form, .photo-form, .new-story, .stack-peek, .dictate { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .page { max-width: none; padding: 0; }
  .card, .answer, .empty { box-shadow: none; border: 1px solid #999; break-inside: avoid; }
  a { color: #000; text-decoration: none; }
}

/* ---------- Choosing what to answer ---------------------------------
   Folded away under the card. Landing on fifteen name pills before reaching
   the question was the single least intuitive thing on the page.
   ------------------------------------------------------------------ */

.picker { margin-top: var(--s6); }

.picker > summary {
  padding: var(--s3) var(--s4);
  border: 2px solid var(--rule-firm); border-radius: var(--r-pill);
  background: var(--sheet);
  justify-content: center;
}
.picker > summary:hover { border-color: var(--teal); background: var(--teal-wash); }

.picker-body {
  margin-top: var(--s4); padding: var(--s5);
  border: 1px solid var(--rule); border-radius: var(--r);
  background: var(--sheet);
}
.picker-body h3 { font-family: var(--sans); font-size: var(--t-small); margin-bottom: var(--s3); }
.picker-body > * + h3 { margin-top: var(--s5); }
.picker-body .mode-picker { margin-bottom: 0; }
.picker-body .btn-row { margin-top: var(--s4); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* On one person's page, repeating their name on every row is noise: the
   heading already said it. */
.qlist-onesubject .qrow-subject { display: none; }
.qlist-onesubject .qrow-meta::first-letter { text-transform: capitalize; }

.lifespan {
  margin: 0 0 var(--s5);
  font-family: var(--typed);
  font-size: var(--t-lead);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------- Sidebar filters -----------------------------------------
   A rail of names beats a wall of pills: twelve pills wrapped across ten rows
   and pushed the questions off the screen. Below 62rem there is no room for a
   rail, so the groups fold up above the list instead.
   ------------------------------------------------------------------ */

.with-side { display: block; }
.with-side-main { min-width: 0; }

@media (min-width: 62rem) {
  .with-side {
    display: grid;
    grid-template-columns: 15rem minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    gap: var(--s4) var(--s6);
    align-items: start;
  }

  /* Dissolved, so the toggle and the rail become grid items and can be placed
     independently. The child selector is deliberate: a bare `.controls` here
     loses to the `.controls` rule defined later in this file. */
  .with-side > .controls { display: contents; }
  .with-side > .controls > .side-toggle { grid-column: 1; grid-row: 1 / span 2; }
  .with-side > .controls > .segmented   { grid-column: 2; grid-row: 1; margin: 0; }
  .with-side > .with-side-main          { grid-column: 2; grid-row: 2; }
  /* The first heading in the main column should line up with the sidebar. */
  .with-side-main > h2:first-child { margin-top: 0; }
}

.side { min-width: 0; }
.side-group { margin-bottom: var(--s3); }

/* A group of names that opens and closes. Smaller than the page's other headings:
   there are five of these in a column beside the questions, and at reading size
   they took more room than the names inside them. */
.side-group > summary {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-family: var(--sans);
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-mid);
  cursor: pointer;
  list-style: none;
  border-bottom: 1px solid var(--rule);
  padding: var(--s1) 0;
  margin-bottom: var(--s1);
}

.side-list { list-style: none; margin: 0; padding: 0; }

.side-link {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  min-height: var(--tap);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  color: var(--ink-mid);
  font: 600 var(--t-small)/1.35 var(--sans);
  text-decoration: none;
}
.side-link:hover { background: var(--oat-deep); color: var(--ink); }

.side-link[aria-current="true"] {
  background: var(--teal); color: var(--on-teal); font-weight: 700;
}

.side-name { min-width: 0; }

.side-count {
  flex: none;
  font-family: var(--typed); font-size: 1rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}
.side-link[aria-current="true"] .side-count { color: var(--on-teal); }
.side-link:hover .side-count { color: var(--ink-mid); }

/* ---------- Panels, bylines, chips -----------------------------------
   A question and its answers are a conversation, so they are separate cards
   with a face and a name on each — not one long column of text with rules
   between it, which read as a message board from 2004.
   ------------------------------------------------------------------ */

.panel {
  padding: var(--s5);
  margin-bottom: var(--s4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--sheet);
  box-shadow: var(--lift-1);
}

.panel-question {
  background: var(--sheet);
  border-color: var(--rule-firm);
  box-shadow: var(--lift-2);
}

.panel-title {
  font-family: var(--sans);
  font-size: clamp(1.5rem, 4vw, 1.8125rem);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -.015em;
  margin: 0 0 var(--s4);
  max-width: none;
}

/* The answer the book gets written from. */
.panel-primary { border-left: 5px solid var(--teal); background: var(--sheet-warm); box-shadow: var(--lift-2); }

.panel-empty { background: var(--oat-deep); box-shadow: none; border-style: dashed; }
.panel-empty p { margin: 0; }

.panel-compose { background: var(--sheet-warm); border-color: var(--rule-firm); }
.panel-compose .btn-row { margin-top: var(--s4); }

/* Who said it. An initial in a circle is instantly parseable — no reading
   required to see that two different people answered. */
.byline { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s4); }

.avatar {
  flex: none;
  display: grid; place-items: center;
  width: 3rem; height: 3rem;
  border-radius: 50%;
  background: var(--apricot); color: #fff;
  font: 700 1.375rem/1 var(--sans);
  text-transform: uppercase;
}
.avatar-primary { background: var(--teal); }
.avatar-you     { background: var(--ink-mid); }
.avatar-sm      { width: 2.25rem; height: 2.25rem; font-size: 1rem; }

.byline-text { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.byline-name { font: 700 var(--t-body)/1.3 var(--sans); color: var(--ink); }
.byline-meta { font-family: var(--typed); font-size: var(--t-small); color: var(--ink-soft); }

.chips { display: flex; gap: var(--s2); flex-wrap: wrap; margin: 0; max-width: none; }

.chip {
  display: inline-flex; align-items: center;
  padding: .2rem .7rem;
  border-radius: var(--r-pill);
  background: var(--oat-deep); color: var(--ink-mid);
  font: 700 1rem/1.5 var(--sans);
}
.chip-asked { background: var(--apricot-wash); color: var(--apricot); }
.chip-done  { background: var(--sage-wash);    color: var(--sage); }

/* Separates the intended person's answer from everyone else's. */
.feed-divider {
  display: flex; align-items: center; gap: var(--s3);
  margin: var(--s6) 0 var(--s4); max-width: none;
  font: 700 var(--t-micro)/1.4 var(--sans);
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft);
}
.feed-divider::before, .feed-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--rule);
}

/* Replies read as a thread hanging off the answer. */
.replies { list-style: none; margin: var(--s5) 0 0; padding: var(--s4) 0 0;
           border-top: 1px solid var(--rule); border-left: 0; }
.replies li { display: flex; align-items: flex-start; gap: var(--s3);
              margin-bottom: var(--s4); white-space: normal; }
.replies li > div { white-space: pre-wrap; line-height: 1.6; color: var(--ink-mid); min-width: 0; }
.reply-by { display: block; font-weight: 700; color: var(--ink); margin: 0 0 .1rem; }

.panel .answer-body { max-width: none; }

/* A sidebar needs room. The reading column stays narrow everywhere else, but a
   page carrying a rail is allowed to use the width it needs. */
@media (min-width: 62rem) {
  .page:has(.with-side) { max-width: 70rem; }
}

/* Wrapping a name onto two lines in a 15rem rail is ugly; let long ones sit on
   one line and shrink slightly instead. */
@media (min-width: 62rem) {
  .side-name { font-size: 1.0625rem; }
}

/* ---------- Independent column scrolling -----------------------------
   On a wide screen the rail and the list each scroll on their own, so a long
   list of questions never carries the list of people away with it.

   Only above 62rem: on an iPad the two are stacked, and locking the body would
   trap the page. Scoped with :has() so pages without a sidebar keep ordinary
   whole-page scrolling.
   ------------------------------------------------------------------ */

@media (min-width: 62rem) {
  html:has(.with-side) { height: 100%; }

  body:has(.with-side) {
    height: 100dvh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }

  /* The masthead stays put; the tabs are inside it now. */
  body:has(.with-side) > .bar { flex: none; }

  .page:has(.with-side) {
    /* No page heading on these, so the generous top gutter only pushed the first
       question down the screen. */
    padding-top: var(--s5);
    flex: 1;
    min-height: 0;              /* lets the flex child actually shrink */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding-bottom: 0;
  }

  /* The page heading and counts stay visible above both columns. */
  .page:has(.with-side) > h1,
  .page:has(.with-side) > .lede { flex: none; }

  .with-side {
    flex: 1;
    min-height: 0;
    padding-bottom: 0;
  }

  /* Only the main column here. The rail scrolls on .side-toggle instead -- see the
     62rem block below. This rule used to name .side as well, which never worked:
     .side sits inside a details element, and in Chrome that puts an auto-height
     ::details-content box between them, so height: 100% had nothing to resolve
     against and the panel grew to its full 1140px. It ended up an overflow: auto box
     that could not scroll, nested inside the one that could. */
  .with-side-main {
    height: 100%;
    overflow-y: auto;
    overscroll-behavior: contain;    /* scrolling one does not pull the page */
    scrollbar-gutter: stable;
    padding-bottom: var(--s7);       /* the last row is not flush to the edge */
  }

  .with-side-main > h2 { margin-top: 0; }
  .with-side-main > h2 ~ h2 { margin-top: var(--s6); }

  .side-group > summary {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--oat);
  }

  /* Restrained scrollbars, still wide enough to grab. */
  .side-toggle, .with-side-main { scrollbar-width: thin; scrollbar-color: var(--rule-firm) transparent; }
  .side::-webkit-scrollbar, .with-side-main::-webkit-scrollbar { width: 12px; }
  .side::-webkit-scrollbar-track, .with-side-main::-webkit-scrollbar-track { background: transparent; }
  .side::-webkit-scrollbar-thumb, .with-side-main::-webkit-scrollbar-thumb {
    background: var(--rule-firm);
    border: 3px solid var(--oat);
    border-radius: var(--r-pill);
  }
}

/* ---------- Grouped question runs ------------------------------------
   The prompts file reads as runs — everything about one relative, then one
   period of a life — and it runs roughly in order: childhood, school, college,
   work, being a parent, looking back. The list keeps that shape.
   ------------------------------------------------------------------ */

.qgroup { margin-bottom: var(--s5); }

.qgroup-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  font-family: var(--sans);
  font-size: var(--t-small);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--apricot);
  margin: 0 0 var(--s3);
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--rule);
}

.qgroup-head .count { color: var(--ink-soft); letter-spacing: 0; }

/* Only one thing pins: which run of questions you are in. Two stacked bars left
   cards sliding under both and arriving with their first line cut off. */
.qgroup-head {
  position: sticky;
  top: 0;
  z-index: 6;
  /* Opaque, and padded wide enough that a card passing underneath disappears
     cleanly instead of peeking out above the text. */
  background: var(--oat);
  padding: var(--s2) 0;
  margin: 0 0 var(--s2);
}

/* ---------- Narrow-screen filter menu --------------------------------
   A rail of a dozen names above the questions wastes the whole first screen on
   a phone or a portrait iPad, so below 62rem it folds behind a hamburger.
   ------------------------------------------------------------------ */

.side-toggle { margin-bottom: var(--s5); }

.side-toggle-btn {
  display: inline-flex; align-items: center; gap: var(--s3);
  min-height: var(--tap); padding: var(--s2) var(--s4);
  border: 2px solid var(--rule-firm); border-radius: var(--r-pill);
  background: var(--sheet);
  color: var(--ink);
  font: 700 var(--t-small)/1.2 var(--sans);
  cursor: pointer;
}
.side-toggle-btn:hover { border-color: var(--teal); background: var(--teal-wash); }
.side-toggle-btn::after { content: ""; }

/* Three bars, drawn rather than fetched. */
.burger {
  position: relative;
  width: 1.25rem; height: 2px;
  background: currentColor; border-radius: 2px;
}
.burger::before, .burger::after {
  content: ""; position: absolute; left: 0;
  width: 100%; height: 2px; background: currentColor; border-radius: 2px;
  transition: transform 160ms var(--ease);
}
.burger::before { top: -6px; }
.burger::after  { top: 6px; }

/* Open: the bars become a cross, so the control says what it will do next. */
.side-toggle[open] .burger { background: transparent; }
.side-toggle[open] .burger::before { transform: translateY(6px) rotate(45deg); }
.side-toggle[open] .burger::after  { transform: translateY(-6px) rotate(-45deg); }

.side-toggle[open] .side {
  margin-top: var(--s4);
  padding: var(--s4);
  border: 1px solid var(--rule); border-radius: var(--r);
  background: var(--sheet);
}

@media (min-width: 62rem) {
  /* No menu at this width: the rail is a column of the layout. */
  .side-toggle { margin-bottom: 0; min-width: 0; }
  .side-toggle-btn { display: none; }
  .side-toggle[open] .side {
    margin-top: 0; padding: 0; border: 0; background: none;
  }
  .with-side { grid-template-columns: 15rem minmax(0, 1fr); }

  /* The details element is the grid child, so it carries the column's height, and
     it has to be the scrolling box too. It was clipping instead: overflow: hidden
     here with overflow-y: auto on the panel inside, which never scrolled because
     nothing bounded it -- Chrome puts a ::details-content block between a details
     and its children, so the panel's height resolved against that, not against this
     box. Scrolling the details sidesteps the wrapper entirely, which also keeps
     working on the iPadOS versions that do not implement it. */
  .side-toggle {
    height: 100%;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
    /* Keeps the longer names off the scrollbar, and the last one off the edge. */
    padding-right: var(--s2);
    padding-bottom: var(--s7);
  }
}

.backlink { margin-bottom: var(--s4); }

/* ---------- Sticky context -------------------------------------------
   Scrolling a long list should never leave you wondering whose questions these
   are or which run you are in. Exactly one bar pins per screen, so nothing
   ever slides under two of them.
   ------------------------------------------------------------------ */

/* Narrow: the whole page scrolls, so everything pins to the viewport. */
@media (max-width: 61.99rem) {
  /* The controls row pins as a unit, since the two now sit side by side. */
  .controls {
    position: sticky;
    top: 0;
    z-index: 30;
    padding: var(--s3) 0;
    margin-bottom: var(--s3);
    background: var(--oat);
  }

  .qgroup-head { top: 4.75rem; }   /* clears the pinned controls row */

  /* The panel drops below the row rather than squeezing into it.
     
     And it scrolls. It is positioned against the controls row, which is sticky, so
     it travels with that row and stays near the top of the screen -- which means
     scrolling the page can never bring the rest of it into view. With four lines in
     it the panel is 1050px tall on a 844px phone, so the last 500px of it, from
     Grandparents down, could not be reached by any gesture at all.
     
     The max-height here is the floor, deliberately conservative so it is always
     scrollable even if the script below never runs; display.js measures the space
     actually available and relaxes it. */
  .side-toggle[open] .side {
    position: absolute;
    left: 0;
    right: 0;
    z-index: 29;
    margin-top: var(--s2);
    box-shadow: var(--lift-3);
    max-height: 60dvh;
    overflow-y: auto;
    /* Reaching the end of the list must not start scrolling the questions behind
       it, which on a touchscreen reads as the panel jumping away. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .side-toggle { position: static; }
}

/* A person's page: their name stays put while their questions scroll past. */
.subject-head {
  position: sticky;
  top: 0;
  z-index: 20;
  margin-bottom: var(--s5);
  padding: var(--s4) 0 var(--s3);
  background: var(--oat);
  border-bottom: 1px solid var(--rule);
}

.subject-head h1 { margin-bottom: var(--s1); }
.subject-head .eyebrow { margin-bottom: var(--s1); }
.subject-head .lifespan { margin: 0; font-size: var(--t-body); }

/* On a person's page only their name pins. The section headings there are
   passed once, and a second bar would clip the cards under it. */

/* The name block would otherwise sit over the masthead as it scrolls past. */
/* Above the page, so the text size panel hanging out of the bar paints over what
   follows it. This used to be a fight with a sibling tab row at the same z-index,
   which won on document order and covered the panel; the tabs are in the bar now. */
.bar { position: relative; z-index: 50; }

/* ---------- Drawn pedigree -------------------------------------------
   Styled entirely from the same tokens as the rest of the site, so the chart
   inherits the theme rather than carrying its own palette.
   ------------------------------------------------------------------ */

.pedigree-figure { margin: 0 0 var(--s6); }

.pedigree-caption {
  font: 700 var(--t-micro)/1.4 var(--sans);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--apricot);
  margin-bottom: var(--s3);
}

/* A pedigree is wider than any column, so it scrolls inside its own frame and
   the page never scrolls sideways. */
.pedigree-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: var(--s3);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--sheet-warm);
}

.pedigree-scroll svg { display: block; }

.pnode-box {
  fill: var(--sheet);
  stroke: var(--rule-firm);
  stroke-width: 1;
}

.pnode-name {
  font: 700 15px/1 var(--sans);
  fill: var(--ink);
}

.pnode-meta {
  font: 400 13px/1 var(--typed);
  fill: var(--ink-soft);
}

/* Marks who has already been talked about — the same signal as the list. */
.pnode-told { fill: var(--teal); }

.pnode-root .pnode-box { fill: var(--apricot-wash); stroke: var(--ink); stroke-width: 2; }

a.pnode-hit { cursor: pointer; }
a.pnode-hit:hover .pnode-box { stroke: var(--teal); stroke-width: 2; }
a.pnode-hit:hover .pnode-name { fill: var(--teal); }
a.pnode-hit:focus-visible { outline: none; }
a.pnode-hit:focus-visible .pnode-box { stroke: var(--teal); stroke-width: 3; }

/* Nobody has anything recorded for these, so they are not links. */
.pnode-flat .pnode-box { fill: none; stroke-dasharray: 4 4; }
.pnode-flat .pnode-name { fill: var(--ink-soft); font-weight: 400; }

/* A pedigree is wider than any reading column, so the page it sits on gets the
   window. The list view inside it keeps its own comfortable measure. */
@media (min-width: 62rem) {
  .page:has(.pedigree-chart) { max-width: 78rem; }
}


/* A great-grandparent couple is one box, because that is also how their
   questions are asked and how the book's chapters are organised. Solid and
   openable even with no questions yet: their page names both of them and takes
   a story. */
.pnode-couple .pnode-box { fill: var(--sheet-warm); stroke: var(--rule-firm); stroke-width: 1.5; }
.pnode-couple .pnode-name { fill: var(--ink); font-weight: 700; }

/* ---------- Whose line ----------------------------------------------- */

.line-picker { display: flex; gap: var(--s2); flex-wrap: wrap; margin: 0 0 var(--s5); }

.line-button {
  min-height: var(--tap);
  padding: var(--s2) var(--s5);
  border: 2px solid var(--rule-firm);
  border-radius: var(--r-pill);
  background: var(--sheet);
  color: var(--ink-mid);
  font: 700 var(--t-small)/1.2 var(--sans);
  cursor: pointer;
}
.line-button:hover { border-color: var(--teal); background: var(--teal-wash); color: var(--ink); }
.line-button[aria-pressed="true"] {
  border-color: var(--teal); background: var(--teal); color: var(--on-teal);
}

.pedigree-figure[hidden] { display: none; }

/* Two names have to share the width of one box, so they are set a little
   smaller than a single name. */
.pnode-couple .pnode-name { font-size: 13.5px; }
.pnode-couple .pnode-meta { font-size: 12px; }

/* ---------- Opening a person downward --------------------------------
   A pedigree has room for ancestors and nothing else, so the badge on the
   bottom edge of a box opens a second chart underneath: that person's children
   and grandchildren. The number on it is how many children, so you can see
   which boxes are worth opening before opening any.
   ------------------------------------------------------------------ */

.pnode-open { cursor: pointer; }

/* The reachable target, twice the size of the mark it surrounds. Invisible, but
   it is what a thumb actually lands on. */
.pnode-open-hit { fill: transparent; }

.pnode-open-disc {
  fill: var(--sheet);
  stroke: var(--rule-firm);
  stroke-width: 1.5;
}

.pnode-open-mark {
  font: 700 12px/1 var(--sans);
  fill: var(--ink-mid);
  /* The badge is the target, not the digit inside it. */
  pointer-events: none;
}

.pnode-open:hover .pnode-open-disc,
.pnode-open:focus-visible .pnode-open-disc { fill: var(--teal); stroke: var(--teal); }
.pnode-open:hover .pnode-open-mark,
.pnode-open:focus-visible .pnode-open-mark { fill: var(--on-teal); }
.pnode-open:focus-visible { outline: none; }
.pnode-open:focus-visible .pnode-open-disc { stroke: var(--ink); stroke-width: 3; }

.kin-panel {
  margin-top: var(--s4);
  padding: var(--s4);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--sheet);
}

.kin-panel:focus { outline: none; }

.kin-head {
  display: flex;
  gap: var(--s3);
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  margin-bottom: var(--s3);
}

.kin-title {
  margin: 0;
  font: 700 var(--t-small)/1.3 var(--sans);
  color: var(--ink);
}

.kin-close {
  min-height: var(--tap);
  padding: var(--s1) var(--s4);
  border: 1px solid var(--rule-firm);
  border-radius: var(--r-pill);
  background: var(--sheet);
  color: var(--ink-mid);
  font: 700 var(--t-small)/1.2 var(--sans);
  cursor: pointer;
}
.kin-close:hover { border-color: var(--teal); color: var(--ink); }

/* The person the opened chart is about, and the one you came in through from
   the pedigree. Without both, a row of names gives no clue where you are. */
.pnode-subject .pnode-box { fill: var(--apricot-wash); stroke: var(--ink); stroke-width: 2; }
.pnode-online .pnode-box { stroke: var(--teal); stroke-width: 2; }

/* ---------- Waiting / answered ---------------------------------------
   One section at a time. With a hundred and fifty waiting, stacking both meant
   scrolling past all of them to reach the handful that were answered.
   ------------------------------------------------------------------ */

/* The waiting/answered toggle and the filter menu share one line: two stacked
   controls cost a third of the first screen on a phone. */
.controls {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s4);
}

.controls .segmented,
.controls .side-toggle { margin: 0; }

.segmented {
  display: inline-flex;
  margin: 0 0 var(--s4);
  padding: 4px;
  border: 2px solid var(--rule-firm);
  border-radius: var(--r-pill);
  background: var(--oat-deep);
  max-width: none;
  gap: 2px;
}

.segment {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 44px;
  padding: var(--s2) var(--s4);
  border-radius: var(--r-pill);
  color: var(--ink-mid);
  font: 700 var(--t-small)/1.2 var(--sans);
  text-decoration: none;
  white-space: nowrap;
}

.segment:hover { color: var(--ink); background: var(--sheet); }

/* Two labels, one rendered: the phone block swaps them. */
.seg-short { display: none; }

.segment[aria-pressed="true"] {
  background: var(--teal);
  color: var(--on-teal);
}

.segment-count {
  font-family: var(--typed);
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  opacity: .85;
}

@media (max-width: 26rem) {
  .segmented { display: flex; width: 100%; }
  .segment { flex: 1; justify-content: center; padding-inline: var(--s2); }
}

/* ---------- Breadcrumbs ---------------------------------------------- */

.crumbs {
  display: flex; align-items: center; gap: var(--s2);
  flex-wrap: wrap;
  margin-bottom: var(--s2);
  font: 600 var(--t-small)/1.4 var(--sans);
}

.crumbs .link { min-height: 2.25rem; }
.crumbs-sep { color: var(--rule-firm); }
.crumbs-here { color: var(--ink-soft); font-weight: 400; }

/* A section heading with an action beside it, rather than the action stranded
   below a sentence below the heading. */
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  margin: var(--s7) 0 var(--s3);
}

.section-head h2 {
  margin: 0;
  padding: 0;
  border: 0;
  flex: 1 1 auto;
}

.section-head form { margin: 0; flex: none; }
.section-head + .lede { margin-bottom: var(--s4); }

@media (max-width: 30rem) {
  .section-head .btn { width: 100%; }
}

/* ---------- The note on the home page -------------------------------- */

/* No headings: the two paragraphs explain themselves, and the blocks below are
   found by their opening words rather than by a label over them. */

.note { margin-top: var(--s7); background: var(--sheet-warm); }

.note p { max-width: var(--measure); text-wrap: pretty; }

/* Not capped at --measure, unlike the paragraphs: once the blocks carry a fill they
   have to reach the panel's edge, or they end in a ragged margin of unused sheet.
   The panel is narrow enough that the lines stay short anyway. */
.note-list {
  list-style: none;
  margin: var(--s5) 0 0;
  padding: 0;
  display: grid;
  gap: var(--s3);
}

/* Under a label the list needs less room above it than it does under a paragraph,
   which is what the margin above is sized for. */
.note .eyebrow + .note-list { margin-top: var(--s3); }

/* Blocks rather than bordered lines: on a warm sheet the wash is what separates
   them from the paragraphs above, and the rule marks where each one starts. */
.note-list li {
  padding: var(--s4);
  border-left: 3px solid var(--apricot);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--apricot-wash);
  line-height: 1.65;
}

/* The opening words are the whole point of the block, so they get the serif and
   their own line rather than running on in bold. */
.note-list strong {
  display: block;
  font-family: var(--wordmark);
  font-size: var(--t-h3);
  font-weight: 600;
  color: var(--ink);
  margin-bottom: var(--s1);
}

/* ---------- Phone -----------------------------------------------------
   Last in the file on purpose. These override .segment and .side-toggle-btn,
   which are defined further up; a media query adds no specificity, so an
   earlier block would simply have lost to them.
   ------------------------------------------------------------------- */

@media (max-width: 30rem) {
  /* The toggle wanted all 366px of a phone, so it and the filter menu each took a
     row of a sticky bar. Shortening the label is what buys the row back: at this
     width "Still waiting" is 176px a side and no amount of padding closes the gap,
     whereas "Waiting" fits without making the type any smaller. */
  .seg-long  { display: none; }
  .seg-short { display: inline; }
  .segment { padding: var(--s2) var(--s3); }

  /* 38px and 24px are sized for a desktop column; on a 390px screen the heading
     and its line under it were 160px of the 440px that sat above the tree. */
  h1 { font-size: 1.875rem; }
  .lede { font-size: var(--t-body); }

  /* A phone cannot show a pedigree whole, so it gets a predictable slab of one that
     pans in both directions, rather than a 777px column of mostly empty canvas.
     The script opens it on the person the chart is about. */
  .pedigree-scroll { max-height: 60vh; }

  /* Undoes the width: 100% set at 26rem, which made sense while the toggle had the
     row to itself and is what kept it 366px wide no matter how short the label got.
     It is content-sized now so the filter button fits beside it. */
  .segmented { width: auto; }

  /* Icon alone. The word stays in the accessibility tree rather than being
     deleted, so the button keeps its name. */
  /* Square, and still a thumb's worth: without the word it came out 48px wide. */
  .side-toggle-btn {
    gap: 0;
    padding-left: var(--s3); padding-right: var(--s3);
    min-width: var(--tap); justify-content: center;
  }
  .side-toggle-label {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* A six-digit code read off a screen and typed in: spaced out, in the typed face,
   and wide enough that it never wraps at the largest text size. */
.input-code {
  font-family: var(--typed);
  font-size: var(--t-lead);
  font-variant-numeric: tabular-nums;
  letter-spacing: .3em;
  max-width: 11ch;
}

/* The "who it's about" heading, which is a label rather than a collapsible group:
   the groups underneath are the things that open and close. */
/* The generations, which belong to the heading above them. Indented, in sentence
   case, and with a rule that stops short of the edge -- a level down rather than
   another one across. */
.side-section { margin-bottom: var(--s3); }

.side-subgroup { margin-left: var(--s3); }

.side-subgroup > summary {
  font-size: var(--t-micro);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-soft);
  border-bottom-color: color-mix(in srgb, var(--rule) 60%, transparent);
}
.side-subgroup > summary:hover { color: var(--ink); }
.side-subgroup[open] > summary { color: var(--ink-mid); }

/* A count beside a group name, so a collapsed group still says how much is in it,
   pushed to the far edge where the eye can run down it. */
.side-group > summary .side-count { margin-left: auto; }

/* The marker has to be drawn here: summary stops generating its own ::marker once
   it is display: flex, and the site's global summary::after "+" was suppressed for
   these back when they never opened. A caret that turns is clearer than +/- for a
   list of names. */
.side-group > summary::before {
  content: "";
  width: .4rem; height: .4rem;
  border-right: 2px solid var(--ink-soft);
  border-bottom: 2px solid var(--ink-soft);
  transform: rotate(-45deg);
  transition: transform 120ms var(--ease);
  flex: none;
}
.side-group[open] > summary::before { transform: rotate(45deg); }
.side-group > summary::after,
.side-group[open] > summary::after { content: none; }
.side-group > summary::-webkit-details-marker { display: none; }

/* Tighter rows too, since the whole family is listed now rather than a filtered few. */
.side-group .side-list { margin: 0 0 var(--s2); }
.side-group .side-link { padding-top: var(--s1); padding-bottom: var(--s1); }

/* ---------- Footer ----------------------------------------------------
   Somebody stuck on a question at nine in the evening should not have to go
   looking for how to reach a person. Quiet, but on every page they can see.
   -------------------------------------------------------------------- */

.footer {
  padding: var(--s4);
  padding-bottom: max(var(--s4), calc(env(safe-area-inset-bottom) + var(--s2)));
}

.footer p {
  margin: 0;
  /* Paragraphs are capped at a reading measure everywhere else, which is right for
     prose and wrong here: it left this one right-aligned inside a 33rem box at the
     far left of the page rather than against the edge of the screen. */
  max-width: none;
  text-align: right;
  font-size: var(--t-micro);
  line-height: 1.5;
  color: var(--ink-soft);
}

.footer a { color: var(--teal); }

/* The full-height column pages hide their own overflow, so the footer would be
   cut off. It sits below the columns instead of inside them. */
@media (min-width: 62rem) {
  body:has(.with-side) > .footer { flex: none; }
}

/* ---------- Who should answer it ------------------------------------
   Checkboxes rather than a multiple-select: on a phone that control is a small
   scrolling box, and choosing more than one in it needs a modifier key that a
   touchscreen does not have.
   ------------------------------------------------------------------ */

.askees {
  margin: 0 0 var(--s4);
  padding: var(--s3) var(--s4) var(--s4);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
}

/* A whole line's worth of people is twenty-five names, which is a wall rather than
   a list. Capped and scrolled, with the cap in rem so it holds at any text size.
   The legend stays put while the names move under it. */
.askees-scroll {
  max-height: 17rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: var(--s2);
}

.askees legend {
  padding: 0 var(--s2);
  font: 700 var(--t-small)/1.3 var(--sans);
  color: var(--ink);
}

.askee {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--tap);
  cursor: pointer;
}

.askee input { width: 1.4rem; height: 1.4rem; accent-color: var(--teal); }

/* Says the chart goes on past the right edge, which nothing else does: the boxes
   there are clipped mid-word, which reads as a fault rather than as more chart. */
.pedigree-more {
  margin: var(--s2) 0 0;
  max-width: none;
  font-size: var(--t-micro);
  color: var(--ink-soft);
}
.pedigree-more[hidden] { display: none; }

/* A soft edge on the scroller, so the cut is obviously a window onto something
   wider rather than where the drawing stops. Only while there is something past
   it: fading the edge of a chart that fits would look like a fault. */
.pedigree-scroll[data-more="yes"] {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
}

/* One line at the top of the filter rail. The three lists below it narrow each
   other, and stacking them said nothing about that. */
.side-note {
  margin: 0 0 var(--s3);
  max-width: none;
  font-size: var(--t-micro);
  line-height: 1.4;
  color: var(--ink-soft);
}

/* ---------- Where an admin lands ------------------------------------
   Not a dashboard. Who is writing, who has not started, and the two things an
   admin actually does.
   ------------------------------------------------------------------ */

.standings {
  list-style: none;
  margin: 0 0 var(--s5);
  padding: 0;
  max-width: var(--measure);
}

.standing {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule);
}

.standing-name { font-weight: 700; color: var(--ink); }
.standing-count {
  font: 400 var(--t-small)/1.3 var(--typed);
  color: var(--ink-soft);
  white-space: nowrap;
}

/* ---------- Editing who's here --------------------------------------
   This list is read far more often than it is changed, so the controls fold
   away: a row of buttons against every name turns a page you skim into a page
   you have to be careful on.
   ------------------------------------------------------------------ */

.member + .member { margin-top: var(--s4); }

.member-edit > summary {
  display: inline-block;
  margin-top: var(--s2);
  padding: var(--s1) var(--s3);
  border-radius: var(--r-pill);
  font: 700 var(--t-micro)/1.4 var(--sans);
  color: var(--ink-soft);
  cursor: pointer;
  list-style: none;
}
.member-edit > summary::-webkit-details-marker { display: none; }
.member-edit > summary:hover { color: var(--teal); }
.member-edit[open] > summary { color: var(--ink); }

.member-edit-body {
  margin-top: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  background: var(--sheet-warm);
}
.member-edit-body form + form {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}

.btn-danger { border-color: var(--rust); color: var(--rust); background: var(--sheet); }
.btn-danger:hover { background: var(--rust); color: var(--sheet); border-color: var(--rust); }

/* ---------- Photographs ----------------------------------------------
   A photograph belongs to everybody in it rather than to one person's answer, so
   it appears on all their pages. The pins are the point: younger generations do
   not know what these people looked like, and a group photograph of fifteen faces
   says nothing unless somebody wrote down which one was their grandfather.
   ------------------------------------------------------------------ */

/* The name and, at the far end of the same line, the way to their pictures. */
.subject-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
}
.subject-title h1 { margin: 0; min-width: 0; }

/* An icon that is still a thumb's worth of target, with the count riding on it. */
.icon-btn {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  border: 2px solid var(--rule-firm);
  border-radius: var(--r-pill);
  background: var(--sheet);
  color: var(--ink-mid);
}
.icon-btn svg { width: 1.5rem; height: 1.5rem; fill: none; stroke: currentColor; stroke-width: 1.6; }
.icon-btn:hover, .icon-btn:focus-visible {
  border-color: var(--teal); background: var(--teal-wash); color: var(--teal);
}

.icon-badge {
  position: absolute;
  top: -4px; right: -4px;
  min-width: 1.35rem;
  padding: 0 .25rem;
  border-radius: var(--r-pill);
  background: var(--teal);
  color: var(--on-teal);
  font: 700 .8125rem/1.35rem var(--sans);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.collage {
  list-style: none;
  margin: 0 0 var(--s6);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: var(--s4);
}

.collage-item { margin: 0; }
.collage-item a { display: block; border-radius: var(--r-sm); overflow: hidden; }
.collage-item img {
  display: block; width: 100%; height: 12rem;
  object-fit: cover; background: var(--oat-deep);
}
.collage-item a:hover img, .collage-item a:focus-visible img { filter: brightness(1.06); }
.collage-missing {
  display: block; padding: var(--s4); text-align: center;
  font-size: var(--t-micro); color: var(--ink-soft); background: var(--oat-deep);
}
.collage-caption { margin: var(--s2) 0 0; font: 700 var(--t-small)/1.3 var(--sans); }
.collage-who { margin: 0; font: 400 var(--t-micro)/1.4 var(--typed); color: var(--ink-soft); }

/* The pins are positioned against the picture, not the figure: the figure includes
   the caption, so a percentage of its height lands below the face it means. */
.photo-full { margin: 0 0 var(--s5); }
.photo-frame { position: relative; display: block; }
.photo-full img { display: block; width: 100%; height: auto; border-radius: var(--r-sm); }
.photo-full figcaption {
  margin-top: var(--s2); font: 400 var(--t-micro)/1.4 var(--typed); color: var(--ink-soft);
}

/* A pin sits at a percentage of the width and height, so it stays on the face
   whatever size the picture is drawn at. */
.photo-pin {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  text-decoration: none;
}
.photo-pin-dot {
  width: 1.4rem; height: 1.4rem; border-radius: 50%;
  border: 2px solid var(--sheet);
  background: color-mix(in srgb, var(--teal) 75%, transparent);
  box-shadow: 0 0 0 1px rgba(0,0,0,.35);
}
/* Out of the way until you look at the picture. Fifteen dots on a photograph of
   fifteen people is a diagram, not a photograph -- so they appear when the pointer
   is on it, when something inside it has keyboard focus, or after a tap. */
.photo-frame .photo-pin { opacity: 0; transition: opacity 120ms var(--ease); }
.photo-frame:hover .photo-pin,
.photo-frame:focus-within .photo-pin,
.photo-frame[data-show-pins="yes"] .photo-pin { opacity: 1; }
.photo-frame .photo-pin-new { opacity: 1; }

/* Dragging one moves who it points at. */
.photo-pin[data-draggable] { cursor: grab; touch-action: none; }
.photo-pin[data-dragging] { cursor: grabbing; }
.photo-pin[data-dragging] .photo-pin-dot { transform: scale(1.25); }
.photo-pin[data-saving] .photo-pin-dot { opacity: .5; }

.photo-pin-name {
  padding: 1px var(--s2);
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--sheet);
  font: 700 .8125rem/1.35 var(--sans);
  white-space: nowrap;
  /* Shown on the pin you are pointing at, so fifteen names do not cover the
     picture at once. */
  opacity: 0; transition: opacity 120ms var(--ease);
}
.photo-pin:hover .photo-pin-name,
.photo-pin:focus-visible .photo-pin-name,
.photo-pin[data-dragging] .photo-pin-name { opacity: 1; }
.photo-pin-new .photo-pin-dot { background: color-mix(in srgb, var(--apricot) 80%, transparent); }
.photo-pin-new .photo-pin-name { opacity: 1; background: var(--apricot); }

.inline-form { display: inline; }
.inline-form button { font-size: var(--t-micro); }

/* ---------- A tile that adds a picture -------------------------------
   Sits with the pictures rather than under them, because adding one is the same
   kind of act as looking at one. Dotted, so it reads as a space for something
   rather than as something.
   ------------------------------------------------------------------ */

.collage-add-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  width: 100%;
  height: 12rem;              /* the height of a thumbnail */
  border: 2px dashed var(--rule-firm);
  border-radius: var(--r-sm);
  background: var(--sheet-warm);
  color: var(--ink-soft);
  font: 700 var(--t-small)/1.3 var(--sans);
  cursor: pointer;
}
.collage-add-btn:hover, .collage-add-btn:focus-visible {
  border-color: var(--teal); color: var(--teal); background: var(--teal-wash);
}
.collage-add-mark { font-size: 2rem; line-height: 1; font-weight: 400; }

/* Without scripting the tile does nothing, so it is not offered; the form is
   already on the page as a panel instead. */
html:not(.js) .collage-add { display: none; }

/* ---------- A sheet ---------------------------------------------------
   A modal where scripting is available. Where it is not, the same markup renders
   as the panel it used to be, so the only way to add a picture is never lost to a
   nicer button.
   ------------------------------------------------------------------ */

dialog.sheet {
  width: min(34rem, calc(100vw - 2rem));
  max-height: min(85dvh, 48rem);
  overflow-y: auto;
  padding: var(--s5);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--sheet);
  color: var(--ink);
  box-shadow: var(--lift-4);
}
dialog.sheet::backdrop { background: rgba(36, 28, 20, .45); }

.sheet-close-row { display: flex; justify-content: flex-end; margin: 0; }
.sheet-close {
  min-width: var(--tap); min-height: var(--tap);
  border: 0; border-radius: var(--r-pill);
  background: transparent; color: var(--ink-soft);
  font: 400 1.75rem/1 var(--sans); cursor: pointer;
}
.sheet-close:hover, .sheet-close:focus-visible { background: var(--oat-deep); color: var(--ink); }

html:not(.js) dialog.sheet {
  display: block;
  position: static;
  margin: var(--s6) 0 0;
  max-height: none;
  box-shadow: none;
}
html:not(.js) .sheet-close-row { display: none; }

/* A brother or sister of the person the chart is about. Same box, quieter edge:
   they are beside the line rather than on it. */
.pnode-sibling .pnode-box { stroke-dasharray: 5 3; }

/* Revealed by a choice above it. Without scripting nothing can reveal it, so it is
   simply there -- a field you might not need beats a field you cannot reach. */
html:not(.js) .reveal[hidden] { display: block; }

/* ---------- Who is in a picture --------------------------------------
   One line rather than a stack. Each name used to take a heading's worth of
   vertical space for itself and a remove button, with the form for pointing out a
   face standing open underneath -- most of a screen before anything anybody had
   written about the picture.
   ------------------------------------------------------------------ */

.photo-who {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  max-width: none;
  margin: 0 0 var(--s5);
}

.photo-who-label {
  font: 700 var(--t-micro)/1.4 var(--sans);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.photo-who-person {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  padding: var(--s1) var(--s1) var(--s1) var(--s3);
  border: 1px solid var(--rule-firm);
  border-radius: var(--r-pill);
  background: var(--sheet);
  font: 700 var(--t-small)/1.3 var(--sans);
}
.photo-who-person a { color: var(--ink); text-decoration: none; }
.photo-who-person a:hover, .photo-who-person a:focus-visible { text-decoration: underline; }
.photo-who-person form { margin: 0; display: inline; }
.photo-who-person button {
  min-width: 1.75rem; min-height: 1.75rem;
  border: 0; border-radius: var(--r-pill);
  background: transparent; color: var(--ink-soft);
  font: 400 1.125rem/1 var(--sans); cursor: pointer;
}
.photo-who-person button:hover, .photo-who-person button:focus-visible {
  background: var(--rust-wash); color: var(--rust);
}

/* Somebody recorded as being in it whose face nobody has pointed out yet. */
.photo-who-loose { border-style: dashed; }
.photo-who-loose a { color: var(--ink-mid); }

.photo-who-none { font-size: var(--t-small); color: var(--ink-soft); }

.photo-who-add {
  min-height: var(--tap);
  padding: var(--s1) var(--s4);
  border: 2px dashed var(--rule-firm);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink-mid);
  font: 700 var(--t-small)/1.2 var(--sans);
  cursor: pointer;
}
.photo-who-add:hover, .photo-who-add:focus-visible {
  border-color: var(--teal); color: var(--teal); background: var(--teal-wash);
}
