/* ============================================================
   Kallas Family Archive

   Mobile first. Nearly every visit arrives as a link tapped in a
   WhatsApp message, and for a good part of the family the phone is
   the only device they will ever use.

   Body text is 18px rather than the usual 16, tap targets are 44px,
   and contrast stays high throughout. Readers here run from teenagers
   to people in their nineties.
   ============================================================ */

:root {
  /* How wide the page is allowed to get.
  
     Was 68rem, which on a large monitor left the content in a column down
     the middle with the chat rail beside it and a great deal of nothing
     either side. Written once here because it was written out three times
     and they would have drifted. */
  --wide: 84rem;
  --paper:      #F3F1EB;
  --paper-warm: #EAE6DC;
  --ink:        #1E2A33;
  --ink-soft:   #4A5860;
  --ink-faint:  #7A868D;
  --rule:       #D6D1C4;

  --spring:     #2F6E63;   /* the water spring the village is named for */
  --spring-dim: #E2EDEA;
  --airmail:    #2A4E7E;   /* the blue of an airletter */
  --seal:       #9C3A2C;

  --measure: 34rem;
  --gap: 1.5rem;

  /* Georgia for the headings, and the system's own sans for everything
     else.
  
     The design Mike liked uses no downloaded fonts at all: Georgia for
     display and Inter-or-whatever-the-system-has for the rest. That is
     most of why it looks settled — Georgia was drawn for screens, it is
     on virtually every machine, and it renders the same on a phone in
     São Paulo as on a desktop in Beirut.
  
     Dropping Fraunces and Source Sans 3 removes two downloads from every
     page and the flash of unstyled text that comes with them. For a
     family archive read on whatever device somebody happens to have,
     that is worth more than a distinctive typeface. */
  --font-display: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  --font-body: Inter, 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;

  /* The two details that make their type look deliberate: headings
     tracked slightly tight, and small uppercase labels tracked wide. */
  --track-tight: -.01em;
  --track-label: .08em;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.65;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.15;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
}

h1 { font-size: 2.25rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.2rem; }

p { margin: 0 0 1em; max-width: var(--measure); }

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

:focus-visible {
  outline: 3px solid var(--spring);
  outline-offset: 2px;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 1rem; top: 1rem;
  z-index: 10;
  background: var(--ink);
  color: var(--paper);
  padding: 0.75rem 1rem;
  border-radius: 4px;
}

/* ---------- header ---------- */

.site-header {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.25rem 1.25rem 1rem;
  border-bottom: 1px solid var(--rule);
}

.brand { text-decoration: none; color: inherit; display: block; }

.brand-name {
  display: block;
  font-family: var(--font-display);
  font-size: 1.5rem;
  letter-spacing: 0.02em;
}

.brand-sub {
  display: block;
  font-size: 0.8rem;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  /* Left, and staying there. Anything that pushes items to the right edge
     makes their position depend on how wide the window is and on whether
     the chat column is showing, so a link is never twice in the same
     place. */
  justify-content: flex-start;
  gap: 0.25rem 1.25rem;
}

.site-nav a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: var(--ink-soft);
  font-weight: 600;
  font-size: 0.95rem;
}

.site-nav a:hover { color: var(--seal); }
.nav-signin, .nav-account { color: var(--spring); }

/* ---------- hero and the clock strip ---------- */

.hero {
  padding: 2.5rem 1.25rem 2rem;
}

.eyebrow {
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.5rem;
}

.hero h1 { max-width: 12ch; }

/* .hero-lede went with the hero it belonged to — the sentence it held is
   now .creed, further down, with a shape of its own. */

/* The signature element. Four cities, live, updating every minute.
   The strip answers the question this family actually asks daily:
   is it a reasonable hour to call them? Cities in the dark are dimmed. */

.clocks {
  list-style: none;
  margin: 1.75rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: 6px;
  overflow: hidden;
}

.clock {
  background: var(--paper-warm);
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  transition: background 0.4s ease, color 0.4s ease;
}

.clock-city {
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.clock-time {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.clock-day {
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.clock--night {
  background: var(--ink);
  color: var(--paper);
}
.clock--night .clock-city,
.clock--night .clock-day { color: #8A99A3; }

/* ---------- bands and sections ---------- */

.band {
  background: var(--spring-dim);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: 2rem 1.25rem;
}

.band-lede { font-size: 1.05rem; }

.section {
  padding: 2rem 1.25rem;
  border-bottom: 1px solid var(--rule);
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.section-head h2 { margin: 0; }

.section-more {
  font-size: 0.9rem;
  font-weight: 600;
  white-space: nowrap;
}

/* ---------- buttons ---------- */

.band-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.25rem;
  max-width: none;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0.6rem 1.25rem;
  background: var(--spring);
  color: #fff;
  border: 1px solid var(--spring);
  border-radius: 4px;
  font-weight: 600;
  font-size: 1rem;
  text-decoration: none;
  cursor: pointer;
}

.button:hover { background: #255A51; color: #fff; }

.button--quiet {
  background: transparent;
  color: var(--spring);
}
.button--quiet:hover { background: var(--spring-dim); color: var(--spring); }

/* ---------- stories ---------- */

.story-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1rem;
}

/* ------------------------------------------------------------------
   A story card
   ------------------------------------------------------------------

   The card was a single anchor stretched to fill its cell, so anything
   added afterwards — the audience line, the buttons — sat on top of it
   and of whatever came next. A card with things to do on it cannot be
   one big link.

   Now the card is the box, the link is the part you read, and the footer
   is where the doing happens. The card is a column, so the footer sits at
   the bottom whatever length the summary is and every card in a row lines
   up along the same edge.
   ------------------------------------------------------------------ */

.story-card {
  display: flex;
  flex-direction: column;
  background: var(--paper-warm);
  border: 1px solid var(--rule);
  border-radius: 6px;
  overflow: hidden;
  transition: border-color .12s ease;
}
.story-card:hover { border-color: var(--spring); }

.story-open {
  display: block;
  flex: 1;
  padding: 1.25rem 1.25rem 1rem;
  text-decoration: none;
  color: inherit;
}
.story-open:hover h3 { text-decoration: underline; }

.story-foot {
  padding: .7rem 1.25rem 1rem;
  border-top: 1px solid var(--rule);
}

/* Side by side, wrapping only when there is genuinely no room. They were
   stacking because the form around Delete is a block element; display:
   contents lets its button be a flex child of this row directly. */
.story-acts { display: flex; flex-wrap: wrap; gap: .4rem; margin: .5rem 0 0; }
.story-acts form { display: contents; }
.story-acts .button { font-size: .8rem; padding: .3rem .7rem; }

.story-card a:hover { border-color: var(--spring); }
.story-card h3 { margin-bottom: 0.25rem; }

.story-by {
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.6rem;
}

.story-summary { font-size: 0.95rem; color: var(--ink-soft); margin: 0; }

/* ---------- events ---------- */

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

.event {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: baseline;
}
.event:last-child { border-bottom: 0; }
.event-who { font-weight: 600; }

/* ---------- gates and empty states ---------- */

.gate, .empty {
  background: var(--paper-warm);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 1.5rem 1.25rem;
}

.empty--inline { background: transparent; border-style: dashed; }

.gate-counts { font-size: 1rem; color: var(--ink-soft); }
.gate-counts strong { font-family: var(--font-display); font-size: 1.15rem; }

.gate-note { font-size: 0.9rem; color: var(--ink-faint); margin-top: 1rem; margin-bottom: 0; }

.section--help { background: var(--paper-warm); }

/* ---------- footer ---------- */

.site-footer {
  padding: 2rem 1.25rem 3rem;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

.footer-meta { color: var(--ink-faint); font-size: 0.9rem; }

/* ---------- wider screens ---------- */

@media (min-width: 40rem) {
  body { font-size: 19px; }
  h1 { font-size: 3rem; }
  h2 { font-size: 2rem; }

  .site-header {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: 1.5rem 2.5rem;
  }

  .clocks { grid-template-columns: repeat(4, 1fr); }

  .hero, .band, .section, .site-footer { padding-left: 2.5rem; padding-right: 2.5rem; }
  .hero { padding-top: 4rem; padding-bottom: 3rem; }
}

@media (min-width: 64rem) {
  .hero, .band-inner, .section, .site-footer {
    max-width: var(--wide);
    margin-inline: auto;
  }
  .band { padding-left: 0; padding-right: 0; }
  .band-inner { padding-inline: 2.5rem; }
}

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

/* ============================================================
   Package 002 — components added with the new pages
   ============================================================ */

.narrow { padding: 2rem 1.25rem; max-width: 42rem; }
.narrow .lede { font-size: 1.1rem; color: var(--ink-soft); }
.section-block { padding: 2rem 1.25rem; border-bottom: 1px solid var(--rule); }
.prose p { max-width: var(--measure); }
.prose h2 { margin-top: 1.75rem; }

/* ---------- a story is for reading ----------
 *
 * --measure is 34rem, which at the story's 19px works out around 50
 * characters a line. That is right for a caption beside a photograph and
 * mean for a thousand words: on a wide screen the paragraphs sat in a
 * narrow ribbon hard against the left of a container the full width of
 * the window, which is where "it takes half the width" comes from — the
 * container was not the problem, the paragraph inside it was.
 *
 * 44rem at 19px is about 65 characters, which is the middle of the range
 * everyone who has measured this lands on. The column is centred and
 * keeps its padding, so the margins are the readability margins rather
 * than a hard left edge.
 *
 * Only the story body. A person's page and a place's page are reference,
 * read in glances, and a short measure suits them. */
.story .prose {
  max-width: 48rem;
  margin-inline: auto;
}
.story .prose p,
.story .prose li,
.story .prose blockquote { max-width: none; }
.story .prose li { margin-bottom: .4em; }

/* The heading and the byline line up with the column rather than with
   the window, or the title starts somewhere the text does not. */
@media (min-width: 64rem) {
  .story .story-head,
  .story .prose,
  .story .story-head + .lede { max-width: 48rem; margin-inline: auto; }

  /* Everything that belongs to the story lines up with the story. The
     "know something about this story?" box stayed the full width of the
     window while the words it refers to sat in a column half that wide,
     which reads as two pages stapled together. */
  .story .correct,
  .story .sources,
  .story .factblock { max-width: 48rem; margin-inline: auto; }
}
.muted { color: var(--ink-faint); }
.visually-hidden { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
.hp { position:absolute; left:-9999px; }

/* ---------- the portrait band on the front page ---------- */

.portrait { border-bottom: 1px solid var(--rule); }
.portrait-link { display: block; background: var(--ink); }
.portrait-link img { display: block; width: 100%; height: auto; }
.portrait-caption { padding: 1.5rem 1.25rem; background: var(--paper-warm); }
.portrait-caption h2 { margin-bottom: .35rem; font-size: 1.4rem; }
.portrait-note { color: var(--ink-soft); font-size: .95rem; }

/* ---------- flash and notices ---------- */

/* What just happened, and it must not be possible to miss it.
   ------------------------------------------------------------------

   Two faults, both found the same day: every decision on the curator's
   desk redirects to /admin/#corrections, which scrolls the page straight
   past a message sitting at the top of it — so a correction that could
   not be filed said so where nobody was looking. And "warn", the kind
   used by every refusal on that desk, had no rule at all: unstyled black
   text on the page background, above the fold nobody was at.

   Sticky, so it stays in view wherever the anchor lands, and every kind
   it is called with has a colour. */
.flash {
  position: sticky; top: 0; z-index: 30;
  padding: .9rem 1.25rem; font-weight: 600;
  border-bottom: 1px solid var(--rule);
  box-shadow: 0 2px 10px rgba(30, 42, 51, .12);
}
.flash--ok   { background: var(--spring-dim); color: #204F47; }
.flash--bad  { background: #F7E9E7; color: var(--seal); }
.flash--warn { background: #FBF0D9; color: #7A5A12; border-bottom-color: #E3CE9B; }

.notice { padding: 1rem 1.25rem; border-radius: 6px; border: 1px solid var(--rule); background: var(--paper-warm); }
.notice p:last-child { margin-bottom: 0; }
.notice--ok   { background: var(--spring-dim); border-color: var(--spring); }
.notice--bad  { background: #F7E9E7; border-color: var(--seal); color: var(--seal); }
.notice--flag { background: #FBF3DF; border-color: #C9A227; }

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

.stack { display: flex; flex-direction: column; gap: .4rem; max-width: 32rem; margin-top: 1.5rem; }
.stack label { font-weight: 600; margin-top: .9rem; }
.stack label:first-of-type { margin-top: 0; }
.stack .opt { font-weight: 400; color: var(--ink-faint); font-size: .9rem; }
.stack--tight { margin-top: 1rem; }

input[type=text], input[type=email], input[type=tel], input[type=search],
input[type=file], select, textarea {
  font: inherit; font-size: 1rem;
  padding: .7rem .8rem; min-height: 44px;
  border: 1px solid var(--ink-faint); border-radius: 4px;
  background: #fff; color: var(--ink); width: 100%;
}
textarea { min-height: 7rem; resize: vertical; }
input:focus, select:focus, textarea:focus { border-color: var(--spring); }

.stack .button { align-self: flex-start; margin-top: 1.25rem; }
.check { display: flex; align-items: center; gap: .5rem; font-weight: 400 !important;
         margin-top: .35rem !important; font-size: .95rem; color: var(--ink-soft); }
.check input { width: auto; min-height: 0; }
.fineprint { font-size: .9rem; color: var(--ink-faint); margin-top: 1.25rem; }
.or { color: var(--ink-faint); margin: .75rem 0; }
.button--google { background: #fff; color: var(--ink); border-color: var(--ink-faint); }
.button--google:hover { background: var(--paper-warm); color: var(--ink); }

.searchbar { display: flex; gap: .5rem; padding: 0 1.25rem 1rem; max-width: 34rem; }
.searchbar input { flex: 1; }

/* ---------- facet chips ---------- */

.facets { display: flex; flex-wrap: wrap; gap: .5rem; padding: 0 1.25rem 1.25rem; }
.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  min-height: 40px; padding: .35rem .8rem;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--paper-warm); color: var(--ink-soft);
  font-size: .95rem; font-weight: 600; text-decoration: none;
}
.chip:hover { border-color: var(--spring); color: var(--spring); }
.chip--on { background: var(--spring); border-color: var(--spring); color: #fff; }
.chip--on:hover { color: #fff; }
.chip--flag { background: #FBF3DF; border-color: #C9A227; color: #6B5410; }
.chip-n { font-weight: 400; opacity: .7; font-variant-numeric: tabular-nums; }

/* ---------- photo grids ---------- */

.tiles {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .5rem;
}
.tiles--gallery { padding: 0 1.25rem 2rem; }
.tile a { display: block; position: relative; background: var(--ink); border-radius: 4px; overflow: hidden; }
.tile img { display: block; width: 100%; height: auto; }
.tile-meta {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: space-between; gap: .5rem;
  padding: .5rem .6rem; font-size: .8rem; color: #fff;
  background: linear-gradient(to top, rgba(0,0,0,.75), rgba(0,0,0,0));
}
.tile-flag { background: #C9A227; color: #241C00; padding: 0 .35rem; border-radius: 3px; font-weight: 600; }

.photo-full { margin: 0; background: var(--ink); }
.photo-full img { display: block; width: 100%; height: auto; }
.photo-meta { padding-top: 1.5rem; }

/* ---------- person and place pages ---------- */

.person, .place, .story, .photo-page { padding-bottom: 1rem; }
.person-head, .place-head, .story-head { padding: 2rem 1.25rem 0; }
.person-portrait { display: block; width: 100%; max-width: 22rem; height: auto; border-radius: 6px; margin-bottom: 1.25rem; }
.person-dates, .place-now { color: var(--ink-soft); font-size: 1.05rem; }
.person-rel { background: var(--spring-dim); border-left: 3px solid var(--spring);
              padding: .7rem .9rem; border-radius: 0 4px 4px 0; }
.person-rel span { color: var(--ink-soft); }

.facts { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1rem; margin: 1.25rem 0; }
.facts dt { font-weight: 600; color: var(--ink-faint); font-size: .9rem; }
.facts dd { margin: 0; }

.inline-clock { font-variant-numeric: tabular-nums; background: var(--spring-dim);
                padding: .1rem .4rem; border-radius: 3px; font-size: .9rem; }
.inline-clock--night { background: var(--ink); color: var(--paper); }

.factblock, .sources { padding: 1.5rem 1.25rem; background: var(--paper-warm); border-top: 1px solid var(--rule); }
.sources { font-size: .95rem; color: var(--ink-soft); }
.sources h2, .sources h3 { font-size: 1rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-faint); }
.maplink { margin-left: .5rem; font-size: .9rem; }

.tree { padding: 1.5rem 1.25rem; border-top: 1px solid var(--rule); }
.tree-row { margin-bottom: 1.25rem; }
.tree-row h3 { font-size: .85rem; letter-spacing: .08em; text-transform: uppercase;
               color: var(--ink-faint); font-family: var(--font-body); margin-bottom: .4rem; }
.tree-people { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
.tree-people li { background: var(--paper-warm); border: 1px solid var(--rule);
                  border-radius: 4px; padding: .5rem .8rem; min-height: 44px;
                  display: flex; align-items: center; gap: .4rem; }
.tree-row--self .tree-people li { background: var(--spring-dim); border-color: var(--spring); }
.tree-self { font-weight: 600; }
.tree-join { font-size: .85rem; color: var(--ink-faint); }
.tag { font-size: .75rem; letter-spacing: .05em; text-transform: uppercase;
       background: var(--rule); color: var(--ink-soft); padding: .1rem .4rem; border-radius: 3px; }
.tag--pending { background: #FBF3DF; color: #6B5410; }
.tag--revoked { background: #F7E9E7; color: var(--seal); }

.pcards, .placelist { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .5rem; }
.pcard a, .placecard { display: flex; flex-direction: column; gap: .1rem;
  padding: .8rem 1rem; background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 4px; text-decoration: none; color: inherit; min-height: 44px; }
.pcard a:hover { border-color: var(--spring); }
.pcard-name, .placecard-name { font-weight: 600; }
.pcard-dates, .pcard-where, .placecard-country { font-size: .9rem; color: var(--ink-faint); }
.placecard { flex-direction: row; align-items: center; justify-content: space-between; }
.placecard-clock { font-variant-numeric: tabular-nums; font-family: var(--font-display); }
.placecard-clock--night, .dcard-clock--night { color: var(--ink-faint); }

/* ---------- directory ---------- */

.dir { list-style: none; margin: 0; padding: 0 1.25rem 2rem;
       display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: .5rem; }
.dcard { display: flex; align-items: center; gap: .75rem; padding: .8rem 1rem;
         background: var(--paper-warm); border: 1px solid var(--rule); border-radius: 4px; }
.dcard-main { flex: 1; display: flex; flex-direction: column; text-decoration: none; color: inherit; }
.dcard-name { font-weight: 600; }
.dcard-rel { font-size: .85rem; color: var(--spring); }
.dcard-where { font-size: .9rem; color: var(--ink-faint); }
.dcard-clock { display: flex; flex-direction: column; align-items: flex-end;
               font-variant-numeric: tabular-nums; }
.dcard-clock .clock-time { font-family: var(--font-display); font-size: 1.2rem; }
.dcard-clock .clock-day { font-size: .75rem; color: var(--ink-faint); }
.dcard-contact { display: flex; flex-direction: column; gap: .2rem; font-size: .85rem; }

/* ---------- stories ---------- */

.story-prompt { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--spring); }
.byline { color: var(--ink-soft); }
.byline-told, .byline-where, .byline-when { color: var(--ink-faint); }
.story-figure, .place-figure { margin: 1.5rem 0; }
.story-figure img, .place-figure img { display: block; width: 100%; height: auto; }
.story-figure figcaption, .place-figure figcaption {
  padding: .6rem 1.25rem; font-size: .9rem; color: var(--ink-faint); background: var(--paper-warm); }
.story .prose, .person .prose, .place .prose, .narrow .prose { padding: 0 1.25rem; }
.story-head + .lede { padding: 0 1.25rem; font-size: 1.15rem; }

.prompts { list-style: none; margin: 1.25rem 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .75rem; }
.prompts li { background: var(--paper-warm); border: 1px solid var(--rule); border-radius: 6px; padding: 1rem; }
.prompts h3 { font-size: 1.05rem; margin-bottom: .25rem; }
.prompts p { font-size: .95rem; color: var(--ink-soft); margin-bottom: .4rem; }
.prompt-count { font-size: .85rem; color: var(--spring); font-weight: 600; }
.prompt-count--empty { color: var(--ink-faint); font-weight: 400; }

/* ---------- events ---------- */

.event { flex-direction: column; align-items: flex-start; gap: .2rem; }
.event-kind { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); }
.event-what { font-weight: 600; }
.event-body { color: var(--ink-soft); font-size: .95rem; }
.event-when { font-size: .85rem; color: var(--ink-faint); }

/* ---------- corrections prompt ---------- */

.correct { margin: 2rem 1.25rem; padding: 1.25rem; background: var(--paper-warm);
           border: 1px dashed var(--rule); border-radius: 6px; }
.correct p { margin-bottom: .75rem; color: var(--ink-soft); }

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

.queue { list-style: none; margin: 0; padding: 0; }
.qitem { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .75rem;
         padding: 1rem 0; border-bottom: 1px solid var(--rule); }
.qbody { flex: 1 1 16rem; display: flex; flex-direction: column; gap: .3rem; }
.qbody p { margin: .25rem 0; }
.qwhen { font-size: .85rem; color: var(--ink-faint); }
.qactions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.qactions form { display: flex; gap: .5rem; align-items: center; }
.qactions .button { min-height: 40px; padding: .4rem .9rem; font-size: .9rem; }
.qactions select { width: auto; min-height: 40px; }
.qthumb { border-radius: 4px; }
.qitem--revoked { opacity: .55; }
.count, .badge { display: inline-block; background: var(--seal); color: #fff;
                 font-size: .8rem; font-family: var(--font-body); font-weight: 600;
                 padding: .05rem .45rem; border-radius: 999px; vertical-align: middle; }
.nav-admin { color: var(--seal); }

.pager { display: flex; align-items: center; justify-content: center; gap: 1rem; padding: 1rem 1.25rem 2.5rem; }
.pager-at { color: var(--ink-faint); font-size: .9rem; }

/* ---------- wider screens ---------- */

@media (min-width: 40rem) {
  .narrow, .section-block, .facets, .searchbar, .dir, .tiles--gallery,
  .person-head, .place-head, .story-head, .tree, .factblock, .sources, .correct {
    padding-left: 2.5rem; padding-right: 2.5rem;
  }
  .correct { margin-left: 2.5rem; margin-right: 2.5rem; padding: 1.25rem; }
  .story .prose, .person .prose, .place .prose, .narrow .prose,
  .story-head + .lede { padding-left: 2.5rem; padding-right: 2.5rem; }
  .portrait { display: grid; grid-template-columns: 3fr 2fr; align-items: stretch; }
  .portrait-link { height: 100%; }
  .portrait-link img { height: 100%; object-fit: cover; }
  .portrait-caption { display: flex; flex-direction: column; justify-content: center; padding: 2.5rem; }
  .tiles { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
  .person-head { display: grid; grid-template-columns: 22rem 1fr; gap: 2rem; align-items: start; }
}

@media (min-width: 64rem) {
  .narrow { margin-inline: auto; }
  .section-block, .facets, .dir, .tiles--gallery { max-width: var(--wide); margin-inline: auto; }

  /* A section carrying both classes must read as narrow.
  
     Without this the two rules disagree by exactly thirteen rem — 42 for
     .narrow, 68 for .section-block — and because .section-block is
     written second it wins. So a page whose opening sits in a .narrow and
     whose body sits in a .section-block narrow has its heading and its
     content measuring from different edges, about two hundred pixels
     apart. It looks like a mistake because it is one, and it is on every
     page that uses both. */
  /* A section that says narrow is narrow — 42rem, not the 68 that
     .section-block would otherwise give it.
  
     This was written to stop a heading and its table measuring from
     different edges, which it did. What it also did was shrink every
     front-page section that happened to carry both classes, because I
     had put "narrow" on sections that were never meant to be. The rule
     is right; the markup was wrong, and the markup is what changed. */
  .section-block.narrow { max-width: 42rem; }
}

/* ============================================================
   Package 003 — the tree
   ============================================================ */

.treebar { padding: 1.5rem 1.25rem 0; }
.searchbar--bare { padding: 0; margin: 0 0 1rem; }

/* ---------- lineage breadcrumb ---------- */

.lineage {
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem;
  padding: .75rem 1.25rem;
  background: var(--paper-warm);
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  font-size: .95rem;
}
.lineage a { text-decoration: none; font-weight: 600; }
.lineage a:hover { text-decoration: underline; }
.lineage-arrow { color: var(--ink-faint); }
.lineage-here { color: var(--ink-faint); }

/* ---------- the focus person ---------- */

.focus-head { padding: 2rem 1.25rem 1rem; }
.focus-head h1 { margin-bottom: .25rem; }
.focus-spouse { color: var(--ink-soft); font-size: 1.05rem; }
.unsure {
  font-family: var(--font-body); font-size: .5em; vertical-align: super;
  color: var(--ink-faint); margin-left: .15em; cursor: help;
}
.focus-stats { display: flex; flex-wrap: wrap; gap: .35rem 1.25rem;
  color: var(--ink-soft); font-size: .95rem; max-width: none; }
.focus-stats strong { font-family: var(--font-display); font-size: 1.15em; }
.focus-note { font-style: italic; color: var(--ink-soft); }
.focus-marker {
  font-size: .95rem; color: var(--ink-soft);
  background: #FBF3DF; border-left: 3px solid #C9A227;
  padding: .6rem .9rem; border-radius: 0 4px 4px 0; max-width: var(--measure);
}

.focus-side { padding: 0 1.25rem 1rem; }
.focus-side h2 { font-size: .85rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint); font-family: var(--font-body); }
.chips-people { list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .4rem; }
.chips-people a {
  display: inline-flex; align-items: center; min-height: 40px;
  padding: .35rem .8rem; background: var(--paper-warm);
  border: 1px solid var(--rule); border-radius: 999px;
  text-decoration: none; color: var(--ink); font-size: .95rem;
}
.chips-people a:hover { border-color: var(--spring); color: var(--spring); }

/* ---------- the branch view ---------- */

.branch { padding: 1rem 1.25rem 2rem; border-top: 1px solid var(--rule); }
.depthpick { display: inline-flex; align-items: center; gap: .3rem; }
.depthpick .chip { min-height: 34px; padding: .2rem .6rem; }
.depthlabel { font-size: .85rem; color: var(--ink-faint); margin-left: .3rem; }

.branchlist { list-style: none; margin: 0; padding: 0; }
.branchlist--l1, .branchlist--l2, .branchlist--l3 {
  margin-left: .75rem; padding-left: .9rem;
  border-left: 2px solid var(--rule);
}
.branchnode { margin: .15rem 0; }
.bn-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
  padding: .5rem .6rem; border-radius: 4px; min-height: 44px;
}
.bn-row:hover { background: var(--paper-warm); }
.bn-name { font-weight: 600; text-decoration: none; }
.bn-name:hover { text-decoration: underline; }
.bn-count { font-size: .85rem; color: var(--ink-faint); }
.bn-mark {
  font-size: .8rem; color: #6B5410; background: #FBF3DF;
  border-radius: 3px; padding: 0 .3rem; cursor: help;
}
.branchlist--l0 > .branchnode > .bn-row { border-bottom: 1px solid var(--rule); }

@media (min-width: 40rem) {
  .treebar, .lineage, .focus-head, .focus-side, .branch {
    padding-left: 2.5rem; padding-right: 2.5rem;
  }
}
@media (min-width: 64rem) {
  .lineage, .focus-head, .focus-side, .branch, .treebar {
    max-width: var(--wide); margin-inline: auto;
  }
}

/* ============================================================
   Package 004 — codes, contacts, suggestions
   ============================================================ */

.codebox {
  font: 700 1.9rem/1.2 var(--font-display);
  letter-spacing: .3em; text-align: center;
  font-variant-numeric: tabular-nums;
}
.codetoggle { margin-top: 1.75rem; max-width: 32rem; }
.codetoggle summary {
  cursor: pointer; font-weight: 600; color: var(--airmail);
  min-height: 44px; display: flex; align-items: center;
}

.codelist { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; }
.codecard { background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 6px; padding: 1rem; }
.codecard-who { display: flex; flex-direction: column; margin-bottom: .5rem; }
.codecard-code {
  font: 700 2.2rem/1 var(--font-display); letter-spacing: .18em;
  font-variant-numeric: tabular-nums; color: var(--spring);
  background: var(--spring-dim); border-radius: 4px;
  padding: .5rem; text-align: center; margin-bottom: .6rem;
}
.codecard-msg { width: 100%; font-size: .85rem; font-family: inherit;
  background: #fff; border: 1px solid var(--rule); border-radius: 4px; padding: .5rem; }

.pickerlist { list-style: none; margin: 0 0 1rem; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: .35rem; }
.picker { display: flex; flex-direction: column; gap: .1rem;
  padding: .6rem .8rem; background: var(--paper-warm);
  border: 1px solid var(--rule); border-radius: 4px; cursor: pointer; min-height: 44px; }
.picker:hover { border-color: var(--spring); }
.picker input { width: auto; min-height: 0; margin-bottom: .2rem; }
.picker-name { font-weight: 600; }
.picker-meta { font-size: .85rem; color: var(--ink-faint); }

.contactlist { list-style: none; margin: 0 0 1.25rem; padding: 0; }
.contactrow { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .7rem 0; border-bottom: 1px solid var(--rule); }
.ctype { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint); }
.cvalue { font-weight: 600; }
.inline { display: inline; }

.contactchips { list-style: none; margin: 1rem 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .5rem; max-width: none; }
.contactchips li a, .contactchips li {
  display: inline-flex; flex-direction: column; gap: .05rem;
  padding: .4rem .8rem; background: var(--spring-dim);
  border: 1px solid var(--rule); border-radius: 4px;
  text-decoration: none; color: var(--ink); min-height: 44px; justify-content: center;
}
.contactchips li a:hover { border-color: var(--spring); }

.journeys { list-style: none; margin: 1rem 0; padding: 0; }
.journeys li { padding: .4rem 0 .4rem .9rem; border-left: 2px solid var(--spring);
  margin-bottom: .3rem; }
.jreason { color: var(--ink-faint); font-size: .9rem; }

.rel-weight { color: var(--spring); }

/* ============================================================
   Package 005 — the explorer
   ============================================================ */

.exp-wrap { display: flex; flex-direction: column; height: calc(100vh - 8rem); min-height: 30rem; }

.exp-controls {
  display: flex; flex-wrap: wrap; gap: .75rem; align-items: center;
  padding: .75rem 1.25rem; border-bottom: 1px solid var(--rule);
  background: var(--paper-warm);
}
.exp-search { position: relative; flex: 1 1 16rem; max-width: 22rem; }
.exp-search input { width: 100%; min-height: 42px; }
.exp-results {
  position: absolute; z-index: 20; top: 100%; left: 0; right: 0;
  list-style: none; margin: .25rem 0 0; padding: 0;
  background: #fff; border: 1px solid var(--rule); border-radius: 6px;
  box-shadow: 0 8px 24px rgba(30,42,51,.14); max-height: 20rem; overflow: auto;
}
.exp-results button {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  padding: .6rem .8rem; font: inherit; cursor: pointer; min-height: 44px;
  border-bottom: 1px solid var(--rule); color: var(--ink);
}
.exp-results button:hover { background: var(--spring-dim); }
.exp-results span { color: var(--ink-faint); font-size: .85rem; }

.exp-buttons { display: flex; flex-wrap: wrap; gap: .4rem; }
.exp-buttons .chip { border: 1px solid var(--rule); background: #fff; cursor: pointer;
  font: inherit; font-size: .9rem; font-weight: 600; color: var(--ink-soft); }
.exp-buttons .chip:hover { border-color: var(--spring); color: var(--spring); }

.exp-stage { position: relative; flex: 1; overflow: hidden; background: var(--paper); }
#explorer { width: 100%; height: 100%; display: block; touch-action: none; cursor: grab; }

.exp-tip {
  position: absolute; z-index: 15; pointer-events: none;
  background: var(--ink); color: var(--paper);
  padding: .35rem .6rem; border-radius: 4px; font-size: .85rem; max-width: 22rem;
}
.exp-status {
  position: absolute; left: 1rem; bottom: 1rem; margin: 0;
  color: var(--ink-faint); font-size: .85rem; pointer-events: none;
}

.exp-panel {
  position: absolute; right: 0; top: 0; bottom: 0; width: min(22rem, 82vw);
  background: var(--paper-warm); border-left: 1px solid var(--rule);
  padding: 1.5rem 1.25rem; overflow: auto;
}
.exp-panel h2 { font-size: 1.4rem; margin-bottom: .4rem; }
.exp-gen { font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); margin-bottom: .2rem; }
.exp-count { color: var(--ink-soft); margin-bottom: .3rem; }
.exp-count strong { font-family: var(--font-display); font-size: 1.2em; }
.exp-line { font-size: .9rem; color: var(--ink-soft); line-height: 1.9; }
.exp-line span { color: var(--ink-faint); }
.exp-mark { font-size: .9rem; background: #FBF3DF; border-left: 3px solid #C9A227;
  padding: .5rem .7rem; border-radius: 0 4px 4px 0; }
.exp-actions { margin-top: 1rem; }

/* The instruction halves live inside .exp-foot now and carry no box
   of their own — the old one was four lines of body text and took a
   fifth of a small laptop screen away from the tree. */

/* ------------------------------------------------------------------
   print.php — a branch on paper
   ------------------------------------------------------------------
   Designed for a cheap printer and a pen, not for a screen. Everything
   here is black on white with room to write; what is already known is
   printed faintly so that the empty boxes are what the eye goes to.
   ------------------------------------------------------------------ */

.sheet { max-width: 52rem; margin: 0 auto 4rem; padding: 0 1.25rem; }
.sheet-head { border-bottom: 2px solid var(--ink); padding-bottom: .6rem; margin-bottom: .8rem; }
.sheet-head h2 { font-family: var(--font-display); font-size: 1.5rem; margin: 0 0 .3rem; }
.sheet-line { font-size: .82rem; color: var(--ink-soft); margin: 0 0 .25rem; }
.sheet-when { font-size: .78rem; color: var(--ink-soft); margin: 0; }

.sheet-ask { font-size: .9rem; margin: 0 0 1rem; line-height: 1.5; }
.sheet-controls { display: flex; flex-wrap: wrap; gap: .75rem; align-items: end; margin: 1rem 0; }
.sheet-controls label { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; }

.sheet-table { width: 100%; border-collapse: collapse; font-size: .88rem; }
.sheet-table th {
  text-align: left; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .06em; font-weight: 600; color: var(--ink-soft);
  border-bottom: 1px solid var(--ink); padding: .3rem .4rem;
}
.sheet-table td { padding: .34rem .4rem; vertical-align: top; border-bottom: 1px solid #cfcabd; }
.sheet-table .c-no    { width: 3.4rem; font-size: .74rem; color: var(--ink-soft); }
.sheet-table .c-born,
.sheet-table .c-died  { width: 7.5rem; }
.sheet-table .c-where { width: 8rem; }
.sheet-table .nm { display: inline-block; }
.sheet-table .sp { display: block; font-size: .76rem; color: var(--ink-soft); padding-left: 1rem; }

/* Already recorded. Faint on purpose — the gaps are the point. */
.sheet-table .had { color: #8d8778; font-size: .8rem; }

.sheet-extra { margin-top: 1.6rem; }
.sheet-extra h3 { font-family: var(--font-display); font-size: 1.05rem; margin: 0 0 .2rem; }
.sheet-hint { font-size: .8rem; color: var(--ink-soft); margin: 0 0 .6rem; }
.ruled { border-bottom: 1px solid #cfcabd; height: 1.7rem; }

.sheet-foot { margin-top: 2rem; border-top: 1px solid var(--ink); padding-top: .7rem; font-size: .82rem; }
.sheet-fine { margin-top: 1.4rem; color: var(--ink-soft); }

@media print {
  /* Nothing from the website itself goes on the paper. */
  .site-header, .site-footer, .noprint, .skip, .whoami, .flash { display: none !important; }

  body { background: #fff; color: #000; font-size: 11pt; }
  .sheet { max-width: none; margin: 0; padding: 0; }

  .sheet-table { font-size: 10pt; }
  .sheet-table td { border-bottom: 1px solid #999; }
  .sheet-table .had { color: #777; }

  /* A name and its box must not be split across two sheets of paper. */
  .sheet-table tr, .ruled, .sheet-extra { page-break-inside: avoid; break-inside: avoid; }
  .sheet-table thead { display: table-header-group; }
  .sheet-foot { page-break-inside: avoid; break-inside: avoid; }

  /* Boxes big enough to write a year in with a biro. */
  .sheet-table .c-born, .sheet-table .c-died { height: 1.5rem; }

  a { text-decoration: none; color: #000; }
  a[href]:after { content: ""; }
}

/* A reply nobody has read yet. Quiet, not shouting — this is family
   correspondence, not an alert. */
.card--new { border-left: 3px solid var(--btn, #2F6E63); }

/* The tree, on paper. Indentation carries the generation, exactly as the
   step to the right does on the canvas, so somebody who knows the screen
   recognises the page. Text rather than a picture: it stays sharp at any
   size, and a pen still works on it. */
.branch-tree { font-size: .84rem; line-height: 1.5; }
.bt-row {
  position: relative; display: flex; align-items: baseline; gap: .5rem;
  padding-top: .12rem; padding-bottom: .12rem;
  border-bottom: 1px dotted #d6d1c4;
}
.bt-rule {
  position: absolute; left: 0; top: 0; bottom: 0; width: 1px;
  background: #ded9cc;
}
.bt-name { font-weight: 600; }
.bt-sp { color: var(--ink-soft); font-size: .82em; }
.bt-dates { margin-left: auto; color: var(--ink-soft); font-size: .82em; white-space: nowrap; }
.bt-blank { letter-spacing: .04em; }
.bt-no { color: #a9a496; font-size: .72em; width: 3.2rem; text-align: right; }

@media print {
  .branch-tree { font-size: 8.5pt; }
  .bt-row { border-bottom: 1px dotted #aaa; page-break-inside: avoid; break-inside: avoid; }
  .bt-rule { background: #bbb; }
  .bt-dates, .bt-sp { color: #555; }
  .bt-no { color: #888; }
}

/* ------------------------------------------------------------------
   messages.php — what you sent, and what came back
   ------------------------------------------------------------------
   A list that reads as a list: one row per thing, unread ones marked at
   a glance, and anything long folded away so six paragraphs from a
   cousin do not become a wall nobody reads.
   ------------------------------------------------------------------ */

.msg-filters { display: flex; flex-wrap: wrap; gap: .4rem; margin: .8rem 0 1.1rem; }
.msg-tab {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .34rem .7rem; border: 1px solid var(--rule); border-radius: 999px;
  font-size: .84rem; text-decoration: none; color: var(--ink-soft); background: var(--paper);
}
.msg-tab:hover { border-color: var(--ink-soft); color: var(--ink); }
.msg-tab--on { background: var(--ink); border-color: var(--ink); color: #fff; }
.msg-tab-n { font-size: .76rem; opacity: .75; }
.msg-tab--new { border-color: #2F6E63; color: #2F6E63; font-weight: 600; }
.msg-tab--new.msg-tab--on { background: #2F6E63; border-color: #2F6E63; color: #fff; }
.msg-tab-dot { width: 7px; height: 7px; border-radius: 50%; background: #2F6E63; }
.msg-tab--on .msg-tab-dot { background: #fff; }

.msg-route {
  margin: .2rem 0 .6rem; padding: .5rem .7rem;
  background: #f6f4ee; border-left: 2px solid var(--rule);
  font-size: .88rem;
}
.hint { display: block; font-size: .78rem; color: var(--ink-soft); margin-top: .15rem; }

.msg-list { list-style: none; margin: 0; padding: 0; }
.msg {
  border: 1px solid var(--rule); border-radius: 6px;
  padding: .85rem 1rem; margin-bottom: .7rem; background: var(--paper);
}
.msg--unread { border-left: 3px solid #2F6E63; background: #fbfaf6; }

.msg-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; }
.msg-dot { width: 7px; height: 7px; border-radius: 50%; background: transparent; }
.msg--unread .msg-dot { background: #2F6E63; }
.msg-kind { font-weight: 600; }
.msg-about, .msg-when { color: var(--ink-soft); font-size: .84rem; }
.msg-when { margin-left: auto; }
.msg-state {
  font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
  padding: .1rem .45rem; border-radius: 3px; border: 1px solid var(--rule);
  color: var(--ink-soft);
}
.msg-state--waiting  { border-color: #c8b98a; color: #8a7a45; }
.msg-state--accepted { border-color: #9dbfad; color: #2F6E63; }
.msg-state--declined { border-color: #d3b3ac; color: #9C3A2C; }
.msg-new {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
  background: #2F6E63; color: #fff; padding: .1rem .4rem; border-radius: 3px;
}

.msg-body { margin: .55rem 0 0; }
.msg-body summary { cursor: pointer; color: var(--ink); }
.msg-more { color: var(--ink-soft); font-size: .8rem; white-space: nowrap; }
details.msg-body[open] summary .msg-more,
.msg-reply details[open] summary .msg-more { display: none; }

.msg-reply {
  margin: .7rem 0 0; padding: .6rem .8rem;
  border-left: 2px solid var(--rule); background: #f6f4ee;
}
.msg-reply p { margin: 0 0 .3rem; }
.msg-reply-by { font-size: .8rem; color: var(--ink-soft); }
.msg-reply summary { cursor: pointer; }
.msg-quiet { margin: .5rem 0 0; font-size: .84rem; color: var(--ink-soft); }

@media (max-width: 40rem) {
  .msg-when { margin-left: 0; width: 100%; }
  .msg-head { gap: .35rem; }
}

/* Rows of buttons need room. Left to themselves they sit flush against
   each other and read as one long bar rather than as separate choices. */
.tool-grid {
  display: flex; flex-wrap: wrap;
  gap: .75rem;
  margin: .75rem 0 0;
}
.tool-grid .button { margin: 0; }

.exp-tools {
  display: flex; flex-wrap: wrap; gap: .5rem;
  margin: .9rem 0 .2rem;
  padding-top: .8rem;
  border-top: 1px solid var(--rule);
}
.exp-tools .button { margin: 0; font-size: .85rem; }

/* A life, in the order it happened. Undated entries sit at the bottom
   rather than pretending to be earliest — most of this archive has no
   dates at all, and putting a blank at the top of somebody's life is a
   lie about when it happened. */
.life-line { list-style: none; margin: 0; padding: 0; }
.life-item {
  display: flex; gap: .9rem; align-items: baseline;
  padding: .45rem 0; border-bottom: 1px dotted var(--rule);
}
.life-year {
  flex: 0 0 3.4rem; font-variant-numeric: tabular-nums;
  color: var(--ink-soft); font-size: .88rem;
}
.life-what { flex: 1; }
.life-body { display: block; font-size: .86rem; color: var(--ink-soft); margin-top: .15rem; }
.life-item--migration .life-year { color: #A05A1C; }
.life-item--death .life-year, .life-item--birth .life-year { font-weight: 600; }

/* The places row. One, two or three across, like the panels above it.
   The first has a quiet tint because it is not a choice — it is whichever
   place the family is most tied to, counted. */
.front-places {
  display: grid; gap: 1rem;
  max-width: var(--wide); margin: 1.5rem auto 0; padding: 0 1.5rem;
}
.front-places--1 { grid-template-columns: 1fr; }
.front-places--2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.front-places--3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.front-places-more { grid-column: 1 / -1; margin: .2rem 0 0; font-size: .88rem; }

.place-card {
  display: flex; flex-direction: column; gap: .35rem;
  border: 1px solid var(--rule); border-radius: 6px;
  padding: 1rem 1.1rem; background: var(--paper);
  text-decoration: none; color: inherit;
}
.place-card:hover { border-color: var(--ink-soft); }
.place-card--home { background: #f6f4ee; }
.place-card-name { font-family: var(--font-display, inherit); font-size: 1.25rem; font-weight: 600; }
.place-card-where { font-size: .8rem; color: var(--ink-soft); }
.place-card-body {
  font-size: .9rem; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden;
}
.place-card-cta { font-size: .85rem; color: #2F6E63; margin-top: auto; padding-top: .3rem; }

@media (max-width: 52rem) {
  .front-places--2, .front-places--3 { grid-template-columns: 1fr; }
}

/* Small, beside the navigation. It is a standing fact about this
   family, not an announcement, so it does not get hero type. */
/* A hair of space between groups of links, so nineteen of them read as
   three handfuls rather than a wall. Not a line — a line would be a
   border, and this is a pause. */
.nav-sep { display: inline-block; width: .9rem; }
@media (max-width: 52rem) { .nav-sep { display: none; } }

.site-motto {
  font-size: .72rem; color: var(--ink-soft); letter-spacing: .01em;
  white-space: nowrap;
}
@media (max-width: 52rem) { .site-motto { display: none; } }

/* The clock strip: one thin line above everything, because "somebody is
   always awake" is the argument this page makes and it should be made
   before the reader has scrolled. Small enough to be read at a glance
   and ignored the rest of the time. */
/* The strip is its own thing, not a row of cards laid flat.
 *
 * It inherited from .clock, and .clock--night sets pale text on a dark
 * background — which is right for a card and wrong here, where there is
 * no dark background to sit on. A city where it is night was drawn in
 * near-white on the page's own pale grey and could not be read at all.
 *
 * So every colour is stated here. Night is shown by weight and a mark,
 * not by inverting something that is no longer inverted.
 */
.clocks--strip {
  display: flex; flex-wrap: wrap;
  align-items: baseline;
  gap: 0 1.1rem;
  list-style: none; margin: 0; padding: 0 0 .4rem;
  background: none; border: 0; border-radius: 0;
  border-bottom: 1px solid var(--rule);
  font-size: .72rem; line-height: 1.5;
}
.clocks--strip .clock {
  display: inline-flex; align-items: baseline; gap: .3rem;
  padding: 0; margin: 0; background: none; border: 0;
  flex-direction: row; transition: none;
}
.clocks--strip .clock-city {
  font-size: .72rem; letter-spacing: .04em; text-transform: none;
  font-weight: 600; color: var(--ink);
}
.clocks--strip .clock-time {
  font-size: .72rem; font-variant-numeric: tabular-nums; color: var(--ink-soft);
}
.clocks--strip .clock-day { font-size: .68rem; color: var(--ink-faint); }

/* Asleep: dark ground, light type — the way it was on the cards.
 *
 * Dimming it was the wrong answer twice over. Faint grey on pale grey had
 * no contrast at all, and even read properly it says "less important"
 * rather than "it is the middle of the night there", which is the one
 * thing this strip exists to say. Inverted, you can see at a glance who
 * is awake without reading a single number.
 *
 * A pill rather than a card, so the height stays where it is. */
.clocks--strip .clock--night {
  background: var(--ink); border-radius: 3px;
  padding: .1rem .4rem; margin: -.1rem 0;
}
.clocks--strip .clock--night .clock-city { color: var(--paper); font-weight: 600; }
.clocks--strip .clock--night .clock-time { color: #D6DCE0; }
.clocks--strip .clock--night .clock-day  { color: #A9B4BC; }

/* Wide screens have room for all of them on one line. */
.clockbar { max-width: var(--wide); margin: .7rem auto 0; padding: 0 1.5rem; }

@media (max-width: 52rem) {
  .clocks--strip { gap: 0 .8rem; font-size: .68rem; }
  .clocks--strip .clock-day { display: none; }
}
@media (max-width: 34rem) {
  .clocks--strip .clock:nth-child(n+4) { display: none; }
}

/* ------------------------------------------------------------------
   The panels across the top of the front page
   ------------------------------------------------------------------
   One, two or three across. Nothing scrolls: a panel that needs a
   scrollbar should have been an excerpt with a link on it, so the text
   is clamped instead and the whole card is the link.
   ------------------------------------------------------------------ */

.front-top {
  /* The same width as everything else on the page.
  
     It was capped at 62rem while the rest of the site runs to --wide
     (84rem), so the panel rows sat in a narrower column with a band of
     nothing either side — and the panels themselves were smaller than
     they needed to be for no reason anybody chose.
  
     One number, from the variable, so the preview in the editor can be
     derived from it rather than guessed at. */
  display: grid; gap: 1rem;
  max-width: var(--wide); margin: 1.5rem auto 0; padding: 0 1.5rem;
}
/* ------------------------------------------------------------------
   Front page panels: always three by four, upright
   ------------------------------------------------------------------

   The shape never changes. That is the whole point: a panel whose shape
   depends on how many are beside it can never be previewed accurately,
   so somebody setting a picture to fill half the width is guessing, and
   heads get cut off.

   Fixed at 3:4, the preview in the editor is the same shape as the real
   thing and what you set up is what everybody sees.

   How wide each one is depends on how many there are — one panel takes
   less of the row than three do, so a single item does not become a
   billboard. The widths below are chosen so the height comes out at
   roughly 29rem whichever count it is, which means the row does not jump
   in height as the front page changes.

   The percentages are deliberately lower than the obvious ones. Fifty
   per cent for a single panel sounds modest until the arithmetic: 3:4
   upright makes it 54rem tall, which is taller than a laptop screen for
   one item.
   ------------------------------------------------------------------ */

.front-top--1 { grid-template-columns: minmax(0, 29%); justify-content: center; }
.front-top--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: calc(var(--wide) * 0.56); }
.front-top--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: calc(var(--wide) * 0.82); }

/* The shape itself. Nothing may override this — it is what the editor's
   preview measures against. */
/* Three by four, and nothing may stretch it.
 *
 * A grid stretches its items to fill the row by default, and a stretched
 * height overrides aspect-ratio completely — so the panels came out
 * whatever shape the row happened to be, which is exactly the thing this
 * was supposed to stop. The ratio was there and doing nothing.
 *
 * align-items: start on the row lets each panel take its own height from
 * the ratio instead. */
.front-top { align-items: start; }
.front-panel { aspect-ratio: 3 / 4; overflow: hidden; align-self: start; }

/* A panel is a column: the picture at the top, the words below it, and
   the way in pinned to the bottom so every panel in a row lines up. */
.front-panel-text { display: flex; flex-direction: column; gap: .4rem; min-height: 0; flex: 1; }
.front-panel-body { overflow: hidden; }
.front-panel-cta { margin-top: auto; }

.front-panel {
  display: flex; flex-direction: column; gap: .55rem;
  border: 1px solid var(--rule); border-radius: 6px;
  padding: 1rem 1.1rem; background: var(--paper);
  text-decoration: none; color: inherit;
  transition: border-color .15s ease;
}
.front-panel:hover { border-color: var(--ink-soft); }
/* A fixed box, with the whole picture inside it.
 *
 * Two goes at this were wrong in the same way. `object-fit: cover` on a
 * 150px band took a horizontal slice out of the middle and cut Elias's
 * head off. Replacing it with `height: auto; max-height` was no better:
 * with an automatic height there is no box for object-fit to work in, so
 * the picture is sized by its width alone and the height is whatever it
 * turns out to be — which is why the donkey survived and the head did
 * not.
 *
 * A box with a real height, and `contain` inside it. A tall portrait is
 * letterboxed at the sides, a wide poster at the top and bottom, and
 * neither loses anything. Small here on purpose: the panel is an
 * invitation, and the picture is shown properly on the page behind it.
 */
/* The founding panel puts the picture beside the words rather than above
   them.
   
   Stacked, a portrait and a paragraph in a third of the page width give
   the picture nowhere to go — it is either tiny or it pushes everything
   below it off the panel. Side by side, a tall picture is an asset
   rather than a problem: it fills the height the text needs anyway.
   
   Only for this one. A flyer is a wide image and belongs across the top,
   which is where every other panel keeps it. */
.front-panel--founding { flex-direction: row; gap: .9rem; align-items: flex-start; }
.front-panel--founding .front-panel-pic {
  flex: 0 0 7rem; height: 9.5rem; margin: -1rem 0 -1rem -1.1rem;
  border-radius: 6px 0 0 6px;
}
.front-panel--founding .front-panel-text { flex: 1; min-width: 0; }

@media (max-width: 52rem) {
  .front-panel--founding { flex-direction: column; }
  .front-panel--founding .front-panel-pic {
    flex: none; width: auto; height: 11rem;
    margin: -1rem -1.1rem .3rem; border-radius: 6px 6px 0 0;
  }
}

/* A shape, not a height.
 *
 * This was 11rem tall whatever width it happened to be — so narrowing a
 * panel made the picture box proportionally taller, and a photograph set
 * to fill it was cropped to a different shape at every window size. The
 * preview in the editor could not match it because there was nothing
 * fixed to match.
 *
 * An aspect ratio instead. The box is now the same shape at every width
 * on every screen, and the editor's preview is the same shape as well,
 * so what somebody sets up is what everybody sees.
 *
 * 3:2 because it is the shape most photographs are already taken in, so
 * the commonest picture loses the least to the crop.
 */
.front-panel-pic {
  display: block; line-height: 0; margin: -1rem -1.1rem .3rem;
  /* Tall enough for whatever the picture turns out to be, up to a limit.
  
     This was a flat 38% of the panel's height, so a picture told to be
     60% wide was then squeezed to fit that box and came out at about
     half the width asked for — the setting was read, saved, and then
     quietly overruled by the box it landed in.
  
     Now the box follows the picture and only the ceiling is fixed, so a
     width of 60% means 60%. */
  flex: 0 0 auto; height: auto; max-height: 55%;
  background: #ece9e0; border-radius: 6px 6px 0 0; overflow: hidden;
}
/* Only when nothing has been asked for. A size set in the editor arrives
   as an inline style and must win over this. */
.front-panel-pic img { width: 100%; height: auto; max-height: 100%; }
.front-panel-pic img {
  width: 100%; height: 100%;
  object-fit: contain; object-position: center center;
  display: block;
}
.front-panel-text { display: flex; flex-direction: column; gap: .35rem; }
/* The title cannot be squeezed away.
 *
 * A flex item shrinks by default. Giving the body "flex: 1 1 auto" so it
 * could fill the panel meant it took the space from whatever would yield
 * — and the title, with no flex of its own, yielded all of it. The words
 * were in the markup the whole time and simply had no height.
 *
 * The title and the lede hold their size; only the body gives. */
.front-panel-title {
  font-family: var(--font-display, inherit); font-size: 1.2rem; font-weight: 600;
  flex: 0 0 auto;
}
.front-panel-lede { flex: 0 0 auto; }
.front-panel-lede { font-size: .82rem; color: var(--ink-soft); }

/* Clamped rather than scrolled. Three lines is an invitation; ten is
   an article somebody has to escape from. */
/* As much as fits, and never a scrollbar.
 *
 * This clamped to four lines whatever room there was — so a panel with no
 * picture, four times taller than it is wide, showed four lines and then
 * a great deal of nothing. The text was cut long before the space ran
 * out.
 *
 * Filling the space it is given instead: the panel is a fixed 3:4 and
 * hides what overflows, so the words simply run to the bottom of it and
 * stop. A scrollbar inside a panel would be worse than truncation —
 * nobody scrolls a panel, they click it.
 */
.front-panel-body {
  font-size: .9rem; line-height: 1.45;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

/* A soft fade at the bottom, so a sentence that stops mid-word reads as
   "there is more" rather than as a mistake. */
.front-panel-body { -webkit-mask-image: linear-gradient(to bottom, #000 85%, transparent 100%);
                            mask-image: linear-gradient(to bottom, #000 85%, transparent 100%); }

/* Nothing inside a panel in the top two rows may scroll. */
.front-top .front-panel,
.front-top .front-panel * { overflow: hidden; }
.front-top .front-panel { scrollbar-width: none; }
.front-top .front-panel::-webkit-scrollbar { display: none; }
.front-panel-cta { font-size: .85rem; color: #2F6E63; margin-top: auto; padding-top: .2rem; }
.front-panel--founding { background: #f6f4ee; }

@media (max-width: 52rem) {
  .front-top--2, .front-top--3 { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------
   The notice across the top
   ------------------------------------------------------------------
   Big and unmissable while it is on, and completely absent when it is
   off — there is no rule here for a hidden state, because there is no
   hidden state. The markup simply is not written.
   ------------------------------------------------------------------ */

.site-notice {
  background: #8a6d1f;
  color: #fffdf6;
  border-bottom: 3px solid #6d5518;
}
.site-notice-inner {
  max-width: 62rem; margin: 0 auto; padding: .9rem 1.5rem 1rem;
}
.site-notice-title {
  margin: 0 0 .2rem;
  font-family: var(--font-display, inherit);
  font-size: 1.15rem; font-weight: 600;
}
.site-notice-body { margin: 0; font-size: .95rem; line-height: 1.45; max-width: 46rem; }
.site-notice-do { margin: .5rem 0 0; font-size: .9rem; }
.site-notice-do a { color: #fffdf6; text-decoration: underline; }
.site-notice-do a:hover { color: #fff; }

@media (max-width: 40rem) {
  .site-notice-inner { padding: .75rem 1rem .8rem; }
  .site-notice-title { font-size: 1.05rem; }
  .site-notice-body { font-size: .9rem; }
}

/* Never on paper. A sheet taken to an eighty-year-old aunt in Feeki
   should not carry a note about a website being tested. */
@media print { .site-notice { display: none; } }

/* A poster at the top of the thing it advertises. Not a link here —
   you are already on the page it would point at. */
.event-flyer { margin: 1rem 0; }
.event-flyer img {
  width: 100%; max-width: 40rem; height: auto;
  border: 1px solid var(--rule); border-radius: 4px;
}
.flyer-preview img {
  max-width: 20rem; height: auto;
  border: 1px solid var(--rule); border-radius: 4px;
}

/* The calendar. Small, dense, and readable at a glance — a month that
   needs scrolling is not a month you can see. */
.cal { width: 100%; border-collapse: collapse; font-size: .8rem; }
.cal th {
  text-align: left; font-size: .7rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-soft); padding: .2rem .3rem;
  border-bottom: 1px solid var(--rule);
}
.cal td { border: 1px solid var(--rule); vertical-align: top; height: 4.2rem; padding: .2rem .3rem; }
.cal-empty { background: #faf9f5; border-color: #eeece5; }
.cal-n { display: block; font-size: .72rem; color: var(--ink-soft); }
.cal-day--has { background: #f6f4ee; }
.cal-day--today { border-color: #2F6E63; border-width: 2px; }
.cal-ev {
  display: block; font-size: .72rem; line-height: 1.25; margin-top: .12rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-more { font-size: .68rem; color: var(--ink-soft); }
.cal-nav { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }

/* Which of the three phases an event is in. */
.event-phase {
  display: inline-block; padding: .2rem .6rem; border-radius: 999px;
  font-size: .8rem; border: 1px solid var(--rule); color: var(--ink-soft);
}
.event-phase--during { border-color: #2F6E63; color: #2F6E63; font-weight: 600; }
.event-phase--after  { color: #8d8778; }

.agenda { white-space: pre-line; }
.share-link code { font-size: .8rem; word-break: break-all; }
.place-ties li { display: inline-block; margin-right: 1.1rem; }

/* A section still being built, visible only to whoever is building it.
   Marked so nobody mistakes it for finished. */
.band--admin-only {
  border: 1px dashed #c8b98a;
  border-radius: 6px;
  padding: 1rem 1.2rem;
  background: #fdfcf7;
}

/* Branch stories, side by side, the reader's own line first. */
.branch-reel { display: flex; gap: 1rem; overflow-x: auto; padding-bottom: .5rem; }
.branch-card {
  flex: 0 0 min(22rem, 80%);
  border: 1px solid var(--rule); border-radius: 6px;
  padding: .9rem 1rem; background: var(--paper);
}
.branch-card--on { border-color: var(--ink-soft); }
.branch-card h3 { margin-top: 0; }

/* ------------------------------------------------------------------
   The family chat
   ------------------------------------------------------------------
   A column down the right of the front page, and the same conversation
   with room to breathe on a page of its own. The width is the reader's
   and is remembered per device.
   ------------------------------------------------------------------ */

:root { --chat-rail-w: 320px; }

.chat-rail {
  /* Below the header, not beside it — see the note above. The height is
     measured once and written into --chat-rail-top, because the header
     wraps to two lines on a narrow window and guessing at it would put
     the rail over the menu on exactly the screens where that hurts. */
  position: fixed; top: var(--chat-rail-top, 0px); right: 0; bottom: 0;
  width: var(--chat-rail-w);
  display: flex; z-index: 200;
  background: var(--paper);
  border-left: 1px solid var(--rule);
}
.chat-rail-grip {
  width: 6px; flex: 0 0 6px; cursor: col-resize;
  background: transparent; border-right: 1px solid var(--rule);
}
.chat-rail-grip:hover, .chat-rail-grip:focus { background: var(--rule); outline: none; }

.chat-rail-inner { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.chat-rail-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .5rem; padding: .7rem .8rem .4rem; border-bottom: 1px solid var(--rule);
}
.chat-rail-head h2 { font-size: .95rem; margin: 0; }
.chat-rail-open { font-size: .8rem; }
.chat-rail-scroll { flex: 1; overflow-y: auto; padding: .6rem .8rem; }
/* The form sits above the conversation now, so its rule goes underneath
   it rather than on top of it — otherwise there are two lines under the
   heading and none between the box and the messages. */
.chat-rail-form { border-bottom: 1px solid var(--rule); padding: .6rem .8rem; }
.chat-rail-form textarea { width: 100%; }
/* What is waiting to go with the message. Quiet, and only there when
   there is something. */
.chat-attached {
  margin: .3rem 0 .4rem; font-size: .72rem; color: #2F6E63;
}
.chat-attached {
  margin: .35rem 0 .4rem; font-size: .78rem; color: #2F6E63;
}

/* On the front page, leave room for it. */
/* The header keeps still.
 *
 * It used to be padded out of the way of the chat column like everything
 * else, so the navigation shifted sideways the moment you left the front
 * page and the rail went away. A menu that moves when you use it is a
 * menu you stop trusting to be where you left it.
 *
 * So the rail starts below the header instead of beside it, and the
 * header is never padded. Only the content below has to make room. */
/* The room is made once, around everything, not inside each block.
 *
 * Padding each content block was taking the rail's width out of that
 * block's own max-width — so widening the chat made every panel on the
 * front page narrower, and a panel built to look right at one size was
 * quietly redrawn at another. Dragging a sidebar should move content, not
 * reshape it.
 *
 * On <main> instead, so each block still measures itself against its own
 * max-width and simply centres in whatever room is left. Drag the rail
 * and the panels slide across unchanged; only when the remaining space
 * falls below what a block asks for does anything narrow, and by then the
 * layout has changed shape anyway.
 *
 * The header and the footer are left alone deliberately: a menu that
 * moves when you drag something else is a menu you stop trusting to be
 * where you left it.
 */
@media (min-width: 60rem) {
  body.has-chat-rail #main {
    padding-right: calc(var(--chat-rail-w) + 2rem);
  }
  /* The footer sits outside main, so it needs the room made for it
     separately or it runs underneath the rail. Its own width is not
     doing any work, so narrowing it costs nothing. */
  body.has-chat-rail .site-footer {
    padding-right: calc(var(--chat-rail-w) + 2rem);
  }
}

.chat-list { list-style: none; margin: 0; padding: 0; }
.chat-msg { margin: 0 0 .85rem; }

/* Who, when and where, small and grey above the words — so the message
   is what the eye lands on, not the label. */
.chat-meta {
  display: flex; flex-wrap: wrap; gap: .4rem; align-items: baseline;
  margin: 0 0 .15rem; font-size: .72rem; color: var(--ink-soft);
}
.chat-who { font-weight: 600; color: var(--ink-soft); text-decoration: none; }
.chat-who:hover { color: var(--ink); }
.chat-time, .chat-where { font-size: .7rem; }
/* The poster's account, for whoever can act on it. Quiet — it is a tool,
   not part of the conversation. */
.chat-mod {
  font-size: .68rem; color: var(--ink-soft); text-decoration: none;
  border-bottom: 1px dotted var(--rule);
  max-width: 12rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-mod:hover { color: #9C3A2C; border-bottom-color: #9C3A2C; }
.chat-where::before { content: "· "; }

.chat-body { margin: 0; font-size: .9rem; line-height: 1.45; overflow-wrap: anywhere; }

/* Shrunk to the column and clickable — a picture nobody can see
   properly has not been shared. */
.chat-photos { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .4rem; }
.chat-photo { display: block; line-height: 0; }
.chat-photo img {
  max-width: 100%; width: 88px; height: 88px; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 3px;
}
.chat-list--wide .chat-photo img { width: 190px; height: 190px; }
.chat-list--wide .chat-body { font-size: 1rem; }
.chat-older { font-size: .85rem; margin-bottom: .8rem; }
/* At the foot of the list rather than the head of it, since older
   messages are now further down. */
.chat-older--below { margin: .8rem 0 0; }
.chat-form { margin-bottom: 1.2rem; }
/* One line for the photograph control rather than three. The box you
   type in sits above the conversation now, so every line it costs is a
   line of conversation pushed off the screen. */
.chat-photos-row { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: .4rem 0; }
.chat-photos-open { font-size: .82rem; padding: .3rem .7rem; cursor: pointer; }

/* The chat page opens on the conversation, not on a screenful of form.
   Measured: every line above the first message is a line of the chat
   nobody sees without scrolling. */
.chat-page-head { margin-bottom: .2rem; }
.chat-page-head h1 { margin-bottom: .2rem; }
.chat-page-head .lede { margin: 0; font-size: .95rem; }
.chat-page-body { padding-top: .6rem; }
.chat-form textarea { width: 100%; }

.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* A phone has no room for a column beside anything. The rail becomes a
   link in the menu and the conversation lives on its own page. */
@media (max-width: 60rem) {
  .chat-rail { display: none; }
}

/* The key, with the instructions at the bottom left rather than
   floating over the canvas. They answer the same question — what am I
   looking at — and two small boxes in two corners is one box too many. */
/* The instructions and the key, in one thin bar under the canvas.
 *
 * Two problems at once here. The bar was four lines of ordinary body
 * text, which on a small laptop took a fifth of the screen away from the
 * tree — the one screen where every pixel is doing work. And the key
 * carried `display: flex`, which beats the `hidden` attribute in the
 * cascade, so it drew itself whether or not there was anything in it and
 * left an empty strip over the canvas.
 *
 * So: small type, one line where it fits, and the key hidden properly
 * when it is empty.
 */
.exp-foot {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .2rem .9rem;
  padding: .35rem 1rem; margin: 0;
  border-top: 1px solid var(--rule); background: var(--paper-warm);
  font-size: .7rem; line-height: 1.4; color: var(--ink-faint);
}
.exp-foot b { font-weight: 600; color: var(--ink-soft); }

.exp-legend {
  display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .8rem;
  margin: 0; font-size: .7rem; color: var(--ink-soft);
}
/* An attribute and a class have the same weight, so the class wins on
   source order alone. Said explicitly, because "hidden" meaning hidden
   should not depend on which rule was written last. */
.exp-legend[hidden] { display: none; }

.legend-title { font-weight: 600; color: var(--ink); }
.legend-item { display: inline-flex; align-items: center; gap: .28rem; }
.legend-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.legend-ring {
  width: 11px; height: 11px; border-radius: 50%;
  border: 1.6px solid #2F6E63; display: inline-block;
}
.legend-me {
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid var(--ink); display: inline-block;
}



/* The dismiss control on the panel. Present at every width — a panel
   with no way out is wrong on a wide screen too, it just does no visible
   damage there. */
.exp-dismiss {
  position: absolute; top: .5rem; right: .5rem; z-index: 2;
  width: 44px; height: 44px; min-height: 44px;
  border: 1px solid var(--rule); border-radius: 50%;
  background: var(--paper); color: var(--ink-soft);
  font: inherit; font-size: 1.5rem; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.exp-dismiss:hover { border-color: var(--ink-soft); color: var(--ink); }

/* Two versions of the same guidance: one for a mouse, one for a finger. */
.exp-help--touch { display: none; }

/* Full screen, on request. Everything the page normally puts around the
   canvas gets out of the way; the chip turns into Done, and Escape also
   leaves. Plain CSS, so it behaves the same on an iPhone as anywhere
   else — the Fullscreen API does not exist on iOS Safari. */
.exp-wrap--full {
  position: fixed; inset: 0; z-index: 300;
  height: 100vh; max-height: none; margin: 0;
  background: var(--paper);
}
@supports (height: 100dvh) {
  .exp-wrap--full { height: 100dvh; }
}
.exp-wrap--full .exp-foot { display: none; }

@media (max-width: 40rem) {
  /* 100vh on a phone measures the screen, not the part of it you can
     see: the browser's address bar sits on top of the bottom of it. dvh
     is the honest number. The vh rule stays as the fallback. */
  .exp-wrap { height: calc(100vh - 7rem); min-height: 20rem; }
  @supports (height: 100dvh) {
    .exp-wrap { height: calc(100dvh - 7rem); }
  }

  /* The search keeps its own row; the chips get one row that scrolls
     sideways rather than three that wrap. Nothing is hidden — every
     button is still there, it is a scroll and not a menu. */
  .exp-controls { padding: .5rem .75rem; gap: .5rem; }
  .exp-search { flex: 1 1 100%; max-width: none; }
  .exp-buttons {
    flex-wrap: nowrap; overflow-x: auto; width: 100%;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
    padding-bottom: 2px;
  }
  .exp-buttons::-webkit-scrollbar { display: none; }
  .exp-buttons .chip, .exp-buttons .chip--seal { flex: 0 0 auto; white-space: nowrap; }

  /* A sheet along the bottom, and a small one.
  
     It was 58% of the screen, which on a phone leaves the tree with less
     room than the panel describing one person in it — and the tree is
     what somebody came to look at. A third is enough for a name, dates
     and the handful of links; anything longer scrolls inside the sheet
     rather than taking the canvas.
  
     Scrolls inside itself, so a person with a great many children does
     not push the buttons off the bottom of the screen. */
  .exp-panel {
    top: auto; bottom: 0; height: auto; width: 100%;
    max-height: 34vh;
    overflow-y: auto; overscroll-behavior: contain;
    border-left: 0; border-top: 1px solid var(--rule);
    padding: .7rem .9rem .9rem;
    box-shadow: 0 -8px 24px rgba(30,42,51,.12);
  }
  .exp-panel h2 { font-size: 1.1rem; margin-bottom: .25rem; }
  .exp-panel p { margin: 0 0 .4rem; font-size: .85rem; }
  .exp-panel .button { padding: .3rem .6rem; font-size: .82rem; }

  /* A very short screen — a phone held sideways — has almost no height
     to give away, so give away less. */
  @media (max-height: 30rem) {
    .exp-panel { max-height: 46vh; padding: .5rem .8rem .7rem; }
  }

  .exp-help--pointer { display: none; }
  .exp-help--touch { display: block; padding: .45rem .75rem; font-size: .8rem; }

  .exp-status { left: .6rem; bottom: .6rem; font-size: .75rem; }
  .exp-tip { max-width: 15rem; font-size: .8rem; }
}

/* A phone turned on its side: plenty of width, almost no height. So the
   panel goes back to being a column beside the tree rather than a sheet
   across the bottom of it, and the guidance line goes away entirely. */
@media (orientation: landscape) and (max-height: 30rem) {
  .exp-wrap { height: calc(100vh - 4.5rem); min-height: 0; }
  @supports (height: 100dvh) {
    .exp-wrap { height: calc(100dvh - 4.5rem); }
  }
  .exp-controls { padding: .35rem .75rem; gap: .4rem; flex-wrap: nowrap; }
  .exp-search { flex: 0 1 12rem; max-width: 12rem; }
  .exp-search input { min-height: 36px; }
  .exp-buttons { flex-wrap: nowrap; overflow-x: auto; }
  .exp-buttons .chip { flex: 0 0 auto; white-space: nowrap; font-size: .85rem; }

  .exp-panel {
    top: 0; bottom: 0; right: 0; left: auto;
    width: min(18rem, 46vw); max-height: none;
    border-top: 0; border-left: 1px solid var(--rule);
    padding: .9rem 1rem; box-shadow: none;
  }
  .exp-help { display: none; }
}

.evidence { max-width: 16rem; }
.idbox { max-width: 7rem; }

/* ============================================================
   Package 006 — the crossing, and the branches
   ============================================================ */

.crossing { background: var(--paper-warm); border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule); padding: 2.5rem 1.25rem; }
.crossing-inner { max-width: 46rem; margin-inline: auto; }
.crossing h2 { font-size: 1.8rem; }
.crossing-lede { font-size: 1.1rem; color: var(--ink-soft); max-width: none; }
.crossmap { display: block; width: 100%; height: auto; margin: 1.5rem 0 1rem;
  border-radius: 8px; background: #F3F1EB; }
.crossing-note { font-size: .95rem; color: var(--ink-faint); font-style: italic; max-width: none; }

.branchfeature { display: grid; gap: 1.25rem; }
.bf-img { display: block; border-radius: 8px; overflow: hidden; background: var(--ink); }
.bf-img img { display: block; width: 100%; height: auto; }
.bf-body h3 { font-size: 1.5rem; margin-bottom: .2rem; }
.bf-body h3 a { text-decoration: none; color: inherit; }
.bf-body h3 a:hover { color: var(--spring); }
.bf-sub { color: var(--ink-faint); font-size: .95rem; letter-spacing: .04em;
  text-transform: uppercase; margin-bottom: .6rem; }

.branchcards { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; }
.branchcard a { display: block; background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 8px; overflow: hidden; text-decoration: none; color: inherit; }
.branchcard a:hover { border-color: var(--spring); }
.branchcard img { display: block; width: 100%; height: auto; }
.bc-body { display: flex; flex-direction: column; gap: .15rem; padding: 1rem; }
.bc-title { font-family: var(--font-display); font-size: 1.2rem; }
.bc-sub { font-size: .85rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-faint); }
.bc-count { font-size: .9rem; color: var(--spring); font-weight: 600; }

.branch-head { padding: 2.5rem 1.25rem 1rem; }
.branch-sub { font-size: 1.1rem; color: var(--ink-soft); letter-spacing: .04em;
  text-transform: uppercase; }
.branch-hero { margin: 0 0 1.5rem; }
.branch-hero img { display: block; width: 100%; height: auto; }
.branch-hero figcaption { padding: .6rem 1.25rem; font-size: .9rem;
  color: var(--ink-faint); background: var(--paper-warm); }
.branchpage .prose { padding: 0 1.25rem; }

@media (min-width: 40rem) {
  .crossing { padding-left: 2.5rem; padding-right: 2.5rem; }
  .branchfeature { grid-template-columns: 1fr 1fr; align-items: center; }
  .branch-head, .branchpage .prose { padding-left: 2.5rem; padding-right: 2.5rem; }
  .branch-hero figcaption { padding-left: 2.5rem; padding-right: 2.5rem; }
}
@media (min-width: 64rem) {
  .branch-head, .branchpage .prose, .branch-hero { max-width: var(--wide); margin-inline: auto; }
}

/* ---------- help and flyers ---------- */

.helpcards { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; }
.helpcard a { display: flex; flex-direction: column; gap: .3rem; height: 100%;
  padding: 1.25rem; background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 6px; text-decoration: none; color: inherit; }
.helpcard a:hover { border-color: var(--spring); }
.hc-role { font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--spring); }
.hc-title { font-family: var(--font-display); font-size: 1.3rem; }
.hc-blurb { font-size: .95rem; color: var(--ink-soft); }

.helpbody { max-width: 40rem; }
.helpsection { margin-bottom: 2rem; }
.helpsection h2 { font-size: 1.3rem; margin-bottom: .5rem; }
.helpsection p { color: var(--ink-soft); }

.flyerlist { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1.5rem; }
.flyercard { background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 8px; overflow: hidden; }
.flyercard img { display: block; width: 100%; height: auto; }
.fc-body { padding: 1rem 1.25rem 1.25rem; }
.fc-body h2 { font-size: 1.15rem; margin-bottom: .25rem; }
.fc-body p { font-size: .95rem; color: var(--ink-soft); }

.sharebox { width: 100%; max-width: 40rem; font: inherit; font-size: .95rem;
  background: #fff; border: 1px solid var(--rule); border-radius: 6px;
  padding: .8rem; margin-bottom: 1.5rem; resize: vertical; }
.nav-help { color: var(--ink-soft); }

/* ---------- then and now ---------- */

.thennow .tn-lede { font-size: 1.05rem; color: var(--ink-soft); max-width: var(--measure); }
.tn-pair { display: grid; gap: 1.25rem; margin: 1.25rem 0; }
.tn-item { margin: 0; background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 8px; overflow: hidden; }
.tn-item img { display: block; width: 100%; height: auto; }
.tn-item figcaption { padding: .9rem 1rem; font-size: .95rem; color: var(--ink-soft); }
.tn-item figcaption strong { font-family: var(--font-display); color: var(--ink); }

.branchstory .facets { padding-left: 0; padding-right: 0; }

@media (min-width: 46rem) {
  .tn-pair { grid-template-columns: 1fr 1fr; align-items: start; }
}

/* ---------- portraits ---------- */

.focus-head--portrait { display: grid; gap: 1.5rem; }
.focus-portrait { display: block; border-radius: 8px; overflow: hidden;
  background: var(--ink); max-width: 20rem; }
.focus-portrait img { display: block; width: 100%; height: auto; }

.exp-portrait { display: block; width: 100%; max-width: 9rem; height: auto;
  border-radius: 6px; margin-bottom: .75rem; }

.mypic { margin: 0 0 1.25rem; max-width: 20rem; }
.mypic img { display: block; width: 100%; height: auto; border-radius: 8px; }
.mypic figcaption { font-size: .9rem; color: var(--ink-faint); padding-top: .5rem; }

@media (min-width: 46rem) {
  .focus-head--portrait { grid-template-columns: 20rem 1fr; align-items: start; }
}

/* A class selector beats the browser's own [hidden] rule, so anything
   given a display value has to opt back in. Without this, hiding an
   element leaves it on screen and apparently working. */
[hidden] { display: none !important; }

/* ============================================================
   Package 008 — navigation, the picker, invitations, merging
   ============================================================ */

/* ---------- navigation ----------
   Eleven items is too many for a row on a phone, so below 52rem the
   list collapses behind a button and stacks. Above it, everything is
   visible at once, which is what a family site should feel like. */

.navtoggle {
  display: flex; align-items: center; gap: .5rem;
  min-height: 44px; padding: .4rem .8rem;
  background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 4px; font: inherit; font-weight: 600;
  color: var(--ink-soft); cursor: pointer;
}
.navtoggle-bars { position: relative; width: 17px; height: 2px;
  background: var(--ink-soft); border-radius: 2px; }
.navtoggle-bars::before, .navtoggle-bars::after {
  content: ''; position: absolute; left: 0; width: 17px; height: 2px;
  background: var(--ink-soft); border-radius: 2px;
}
.navtoggle-bars::before { top: -5px; }
.navtoggle-bars::after  { top: 5px; }

.nav-sep { display: none; }

@media (max-width: 52rem) {
  .site-header { flex-direction: row; flex-wrap: wrap; align-items: center;
    justify-content: space-between; }
  .site-nav { display: none; width: 100%; flex-direction: column; gap: 0;
    margin-top: .5rem; border-top: 1px solid var(--rule); }
  .site-nav--open { display: flex; }
  .site-nav a { padding: .1rem 0; border-bottom: 1px solid var(--rule); }
  .nav-sep { display: block; height: .5rem; }
}

@media (min-width: 52rem) {
  .navtoggle { display: none; }
  .site-nav { display: flex !important; }
  .nav-sep { display: block; width: 1px; height: 1.1rem;
    background: var(--rule); margin: 0 .2rem; }
}

/* ---------- the person picker ---------- */

.picker { position: relative; margin-top: 1rem; }
.picker > label { display: block; font-weight: 600; margin-bottom: .25rem; }
.pk-input { width: 100%; }
.pk-chosen {
  display: flex; flex-direction: column; gap: .1rem;
  padding: .7rem .9rem; background: var(--spring-dim);
  border: 1px solid var(--spring); border-radius: 4px; margin-bottom: .5rem;
}
.pk-chosen strong { font-size: 1.05rem; }
.pk-chosen span { font-size: .9rem; color: var(--ink-soft); }
.pk-clear { margin-top: 0; min-height: 40px; padding: .35rem .8rem; font-size: .9rem; }

.pk-results {
  position: absolute; z-index: 30; left: 0; right: 0; top: 100%;
  list-style: none; margin: .25rem 0 0; padding: 0;
  background: #fff; border: 1px solid var(--rule); border-radius: 6px;
  box-shadow: 0 10px 28px rgba(30,42,51,.16);
  max-height: 22rem; overflow: auto;
}
.pk-results button {
  display: flex; flex-direction: column; gap: .1rem;
  width: 100%; text-align: left; background: none; border: 0;
  border-bottom: 1px solid var(--rule); padding: .65rem .85rem;
  font: inherit; cursor: pointer; color: var(--ink); min-height: 48px;
}
.pk-results button:hover, .pk-results button:focus { background: var(--spring-dim); }
.pk-name { font-weight: 600; }
.pk-line { font-size: .85rem; color: var(--ink-soft); }
.pk-below { font-size: .8rem; color: var(--spring); font-weight: 600; }
.pk-none { padding: .8rem .85rem; color: var(--ink-faint); font-size: .95rem; }

/* ---------- invitations ---------- */

.invitelist { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.25rem; }
.invitecard { background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 8px; padding: 1rem; }
.ic-head { display: flex; flex-direction: column; gap: .1rem; margin-bottom: .6rem; }
.ic-code {
  font: 700 2rem/1 var(--font-display); letter-spacing: .18em;
  font-variant-numeric: tabular-nums; color: var(--spring);
  background: var(--spring-dim); border-radius: 4px;
  padding: .5rem; text-align: center; margin-bottom: .6rem;
}
.ic-msg { width: 100%; font: inherit; font-size: .85rem; line-height: 1.5;
  background: #fff; border: 1px solid var(--rule); border-radius: 4px;
  padding: .6rem; resize: vertical; }
.ic-actions { margin: .6rem 0 0; }

/* ---------- merging ---------- */

.mergecols { display: grid; gap: 1.5rem; margin-bottom: 1.25rem; }
.mergecol { background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 8px; padding: 1.25rem; }
.mergecol h2 { font-size: 1.15rem; margin-bottom: .25rem; }
.mergecol--keep { border-color: var(--spring); }
.mergefacts { list-style: none; margin: 1rem 0 0; padding: 0;
  font-size: .9rem; color: var(--ink-soft); }
.mergefacts li { padding: .2rem 0; border-bottom: 1px solid var(--rule); }
.claimlink { display: flex; flex-direction: column; gap: .5rem; min-width: 15rem; }

@media (min-width: 46rem) {
  .mergecols { grid-template-columns: 1fr 1fr; }
}

/* ---------- the crossing, with Elias set into it ---------- */

.elias-fig { margin: 0 0 1.25rem; }
.elias-fig img { display: block; width: 100%; height: auto; border-radius: 6px; }
.elias-fig figcaption { font-size: .85rem; color: var(--ink-faint);
  font-style: italic; padding-top: .5rem; line-height: 1.5; }
.crossing-fine { font-size: .85rem; color: var(--ink-faint); font-style: italic;
  max-width: none; margin-top: .6rem; }

@media (min-width: 46rem) {
  .elias-fig { float: right; margin: .25rem 0 1rem 2rem; width: 300px; }
}

.exp-spouse { color: var(--ink-soft); font-size: .95rem; margin-top: -.2rem; }

.portraitbox { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-start;
  padding: 1rem; background: var(--paper-warm); border: 1px solid var(--rule);
  border-radius: 8px; margin-top: 1.25rem; }
.pb-img { width: 90px; height: 90px; object-fit: cover; border-radius: 6px; }
.pb-form { flex: 1 1 14rem; display: flex; flex-direction: column; gap: .4rem; }
.pb-form label { font-weight: 600; }
.pb-form .button { align-self: flex-start; }

/* ---------- marriages ---------- */

.unionbox { margin-top: 1.5rem; padding: 1.25rem; background: var(--paper-warm);
  border: 1px solid var(--rule); border-radius: 8px; }
.unionbox h2 { font-size: 1.15rem; margin-bottom: .5rem; }
.unionlist { list-style: none; margin: 0 0 .75rem; padding: 0; }
.unionrow { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center;
  padding: .7rem 0; border-bottom: 1px solid var(--rule); }
.unionrow:last-child { border-bottom: 0; }
.ur-who { flex: 1 1 12rem; }
.ur-form { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.ur-form select { width: auto; min-height: 40px; }
.ur-date { max-width: 8rem; min-height: 40px; }
.ur-form .button { min-height: 40px; padding: .35rem .8rem; font-size: .9rem; }

/* ---------- signing in ---------- */

.signinbox { margin: 1.25rem 0; padding: 1.25rem; border-radius: 8px; max-width: 32rem;
  background: var(--spring-dim); border: 1px solid var(--spring); }
.signinbox--code { background: var(--paper-warm); border-color: var(--rule); }
.signinbox label { display: block; font-weight: 600; font-size: 1.1rem; }
.signinbox .fineprint { margin-top: .2rem; margin-bottom: .8rem; }
.codeform { margin: 1.5rem 0; padding: 1.25rem; background: var(--spring-dim);
  border: 1px solid var(--spring); border-radius: 8px; max-width: 32rem; }
.codeform label { display: block; font-weight: 600; font-size: 1.1rem; }
.codeform .fineprint { margin-top: .2rem; margin-bottom: .8rem; }
.codeline { display: flex; flex-wrap: wrap; gap: .6rem; align-items: stretch; }
.codeline .codebox { flex: 1 1 11rem; min-width: 9rem; }
.codeline .button { flex: 0 0 auto; }
.ic-link { font-size: .85rem; margin: .5rem 0 0; }

.isthisyou { margin: 1rem 0; padding: 1rem 1.25rem; background: var(--spring-dim);
  border: 1px solid var(--spring); border-radius: 6px; max-width: var(--measure); }
.isthisyou p { margin-bottom: .5rem; }
.claimform { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .75rem; }
.claimform input[type=text] { flex: 1 1 14rem; }
.why { color: var(--spring); font-style: italic; }

/* ---------- the sign-in log ---------- */

.loglist { list-style: none; margin: 0; padding: 0; }
.logrow { display: flex; flex-wrap: wrap; gap: .3rem 1rem; align-items: baseline;
  padding: .5rem 0; border-bottom: 1px solid var(--rule); font-size: .95rem; }
.logrow--bad { background: #FBF0EE; }
.logrow--wait { background: #FBF7EA; }
.lg-when { font-variant-numeric: tabular-nums; color: var(--ink-faint); min-width: 6.5rem; }
.lg-what { font-weight: 600; min-width: 11rem; }
.lg-who { color: var(--ink-soft); }
.lg-why { color: var(--seal); font-size: .9rem; }
.lg-ip { color: var(--ink-faint); font-size: .85rem; margin-left: auto; }

/* ---------- the journey map ---------- */

.crossmap-fig { margin: 1.5rem 0 1rem; }
.crossmap-fig img { display: block; width: 100%; height: auto; border-radius: 8px;
  border: 1px solid var(--rule); }
.crossmap-legend { display: flex; flex-wrap: wrap; gap: .3rem 1.5rem;
  padding-top: .6rem; font-size: .95rem; color: var(--ink-soft); }
.crossmap-legend strong { font-family: var(--font-display); color: var(--ink); }
.cm-border { color: var(--ink-faint); font-size: .9rem; }

/* ---------- explorer search results ---------- */

.exp-results button { display: flex; flex-direction: column; gap: .1rem; }
.sr-name { font-weight: 600; }
.sr-meta { font-size: .85rem; color: var(--ink-soft); }
.sr-line { font-size: .78rem; color: var(--ink-faint); }

/* ---------- until somebody says who they are ---------- */

.whoami {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem;
  padding: .7rem 1.25rem;
  background: var(--spring); color: #fff;
  border-bottom: 1px solid var(--rule); font-size: .95rem;
}
.whoami strong { font-weight: 600; }
.whoami a { color: #fff; font-weight: 600; }
.whoami-go {
  margin-left: auto; background: #fff; color: var(--spring);
  padding: .3rem .8rem; border-radius: 4px; text-decoration: none;
  min-height: 36px; display: inline-flex; align-items: center;
}
.whoami-go:hover { background: var(--spring-dim); color: var(--spring); }
@media (min-width: 40rem) { .whoami { padding-left: 2.5rem; padding-right: 2.5rem; } }

/* ---------- placing somebody in the tree ---------- */

.pickbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem 1rem;
  padding: .7rem 1.25rem; background: var(--seal); color: #fff; font-size: .95rem;
}
.pickbar strong { font-weight: 600; }
.pickbar-out { margin-left: auto; color: #fff; background: rgba(255,255,255,.18);
  padding: .3rem .8rem; border-radius: 4px; text-decoration: none;
  min-height: 34px; display: inline-flex; align-items: center; }
.pickbar-out:hover { background: rgba(255,255,255,.3); color: #fff; }
.exp-wrap--picking { height: calc(100vh - 11rem); }
.exp-pick .button { background: var(--seal); border-color: var(--seal); }
.exp-pick .button:hover { background: #82301F; }

.qactions--stack { flex-direction: column; align-items: stretch; gap: .5rem; min-width: 15rem; }
.linkform { display: flex; flex-direction: column; gap: .4rem; }
.linkform .picker { margin-top: 0; }
.confirmlink h2 { font-size: 1.2rem; margin-bottom: .4rem; }
.confirmlink .band-actions { align-items: center; }

/* ============================================================
   Package 016 — buttons carry the level they need

   White is for anybody, green for family, amber for a contributor,
   red for a curator, blue for an administrator. Each has a solid form
   for the main action and an outline for the rest, and a dashed outline
   where something is destroyed.

   The colours are held apart by hue rather than by weight: white text
   sits between 5.3:1 and 7.6:1 on all four, and each works as text on
   paper at 4.7:1 or better so the outline forms stay readable. They are
   not matched in luminance exactly — forcing that turned the amber to
   mud and washed out the red, because those hues need more saturation
   to read as themselves at this darkness.

   Anything without a role class stays family green, so nothing that
   was not touched changes.
   ============================================================ */

.button {
  --btn: #2F6E63;
  --btn-hover: #275A51;
  --btn-tint: #E9F1F0;
  background: var(--btn);
  border: 1px solid var(--btn);
  color: #fff;
}
.button:hover, .button:focus-visible { background: var(--btn-hover); border-color: var(--btn-hover); color: #fff; }

.button--guest    { --btn: #FFFFFF; --btn-hover: #F3F1EB; --btn-tint: #FFFFFF;
                    color: var(--ink); border-color: var(--rule); }
.button--guest:hover, .button--guest:focus-visible { color: var(--ink); border-color: var(--ink-faint); }

.button--family   { --btn: #2F6E63; --btn-hover: #275A51; --btn-tint: #E9F1F0; }
.button--contrib  { --btn: #A05A1C; --btn-hover: #834A17; --btn-tint: #F4EDE6; }
.button--curator  { --btn: #9C3A2C; --btn-hover: #803024; --btn-tint: #F3E9E8; }
.button--admin    { --btn: #31548A; --btn-hover: #284571; --btn-tint: #E8ECF2; }

/* Destructive, which is not a role.
 *
 * The three colours above say who may press a thing: green for family,
 * terracotta for a curator, blue for an administrator. Delete was
 * wearing the administrator blue, which said the wrong thing twice — it
 * looked like every other blue button, and it implied only an
 * administrator could press it when a curator can.
 *
 * So a fourth treatment that says what the button does rather than who
 * may press it. Quiet until you are near it, then plainly red: a
 * destructive control should be easy to find on purpose and hard to hit
 * by accident. */
.button--danger {
  --btn: #A3372C; --btn-hover: #8A2E24; --btn-tint: #F7E9E7;
  border-color: transparent;
  background: transparent;
  color: #A3372C;
}
.button--danger:hover,
.button--danger:focus-visible {
  background: #F7E9E7;
  border-color: #A3372C;
  color: #8A2E24;
}

/* the outline form: same colour, no fill */
.button--quiet {
  background: transparent;
  color: var(--btn);
  border-color: var(--btn);
}
.button--quiet:hover, .button--quiet:focus-visible {
  background: var(--btn-tint);
  color: var(--btn-hover);
  border-color: var(--btn-hover);
}
.button--guest.button--quiet { color: var(--ink-soft); border-color: var(--rule); }

/* something is destroyed: the role's colour, and a dashed edge to pause on */
.button--danger {
  background: transparent;
  color: var(--btn);
  border: 2px dashed var(--btn);
}
.button--danger:hover, .button--danger:focus-visible {
  background: var(--btn-tint);
  color: var(--btn-hover);
  border-color: var(--btn-hover);
}

/* ---------- the panel on a person's page ---------- */

.actionpanel {
  margin: 1.5rem 0; padding: 1.25rem;
  background: var(--paper-warm);
  border: 1px solid var(--rule); border-left: 4px solid #31548A;
  border-radius: 6px; max-width: var(--measure);
}
.actionpanel h2 { font-size: 1.1rem; margin-bottom: .5rem; }
.ap-who { color: var(--ink-soft); margin-bottom: .9rem; }
.ap-who.muted { color: var(--ink-faint); font-style: italic; margin-bottom: 0; }
.ap-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; }
.ap-form { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.ap-form--col { flex-direction: column; align-items: stretch; }
.ap-form label { font-weight: 600; font-size: .9rem; }
.ap-form select { width: auto; min-height: 42px; }
.ap-form textarea { width: 100%; }
.ap-ban { margin-top: 1rem; border-top: 1px solid var(--rule); padding-top: .8rem; }
.ap-ban summary { cursor: pointer; font-weight: 600; color: var(--ink-soft); min-height: 40px;
  display: flex; align-items: center; }
.ap-ban summary:hover { color: var(--seal); }
.ap-ban form { margin-top: .8rem; }
.ap-more { margin-top: 1rem; font-size: .9rem; }
.banform { display: inline; }

/* ============================================================
   Package 019
   ============================================================ */

/* ---------- where you are signed in ---------- */

.devlist { list-style: none; margin: 0 0 1rem; padding: 0; }
.devrow { display: flex; flex-wrap: wrap; gap: .4rem 1rem; align-items: center;
  padding: .75rem 0; border-bottom: 1px solid var(--rule); }
.devrow--here { background: var(--spring-dim); padding-left: .75rem; padding-right: .75rem;
  border-radius: 4px; }
.dev-what { font-weight: 600; flex: 1 1 12rem; }
.dev-when { color: var(--ink-faint); font-size: .9rem; }
.dev-form { margin-left: auto; }
.dev-form .button { min-height: 40px; padding: .3rem .8rem; font-size: .9rem; }
.nav-signout { color: var(--ink-faint); }

/* ---------- choosing somebody on the canvas ---------- */

.exp-place { position: relative; }
.exp-place > summary { list-style: none; cursor: pointer; }
.exp-place > summary::-webkit-details-marker { display: none; }
.chip--seal { border-color: var(--seal); color: var(--seal); }
.exp-placelist {
  position: absolute; z-index: 30; top: 100%; left: 0; width: 20rem; max-width: 80vw;
  background: #fff; border: 1px solid var(--rule); border-radius: 6px; padding: .8rem;
  box-shadow: 0 10px 28px rgba(30,42,51,.16); max-height: 22rem; overflow: auto;
}
.exp-placelist ul { list-style: none; margin: .5rem 0 0; padding: 0; }
.exp-placelist li a { display: block; padding: .5rem .4rem; border-bottom: 1px solid var(--rule);
  min-height: 42px; }
.exp-pickcount { display: block; font-size: .85rem; color: var(--ink-faint); margin-top: .3rem; }

/* ---------- lists that are just lists ---------- */

.plainlist { list-style: none; margin: .5rem 0 0; padding: 0; font-size: .95rem; }
.plainlist li { padding: .25rem 0; }
.linkbtn { background: none; border: 0; padding: 0 .3rem; font: inherit; font-size: .85rem;
  color: var(--seal); text-decoration: underline; cursor: pointer; }
.inline { display: inline; }

.exp-also { font-size: .9rem; color: var(--ink-soft); background: var(--paper);
  border-left: 3px solid var(--ink-faint); padding: .6rem .7rem; border-radius: 0 4px 4px 0; }
.exp-also strong { color: var(--ink); }

.exp-status--bad { color: var(--seal); max-width: 34rem; white-space: pre-wrap;
  background: rgba(255,255,255,.85); padding: .5rem .7rem; border-radius: 4px; }

.sr-why { font-weight: 400; font-size: .75rem; color: var(--spring); font-style: italic; }

.scopeline { display: block; font-size: .88rem; color: var(--ink-soft); margin-top: .15rem; }
.scopeline--all { color: var(--seal); }
.scopecount { color: var(--ink-faint); }

/* ------------------------------------------------------------------
   Then and now, and the moderation page
   ------------------------------------------------------------------ */

/* Two photographs of the same place, side by side and sized to the
   column rather than to the front page they came off. On a phone they
   stack, because a pair of postage stamps is not a comparison. */
.thennow-pair {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem;
}
.thennow-pair figure { margin: 0; }
.thennow-pair img {
  width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 4px; background: #f1efe7;
}
.thennow-pair figcaption {
  margin-top: .35rem; font-size: .84rem; color: var(--ink-soft); line-height: 1.45;
}
@media (max-width: 40rem) { .thennow-pair { grid-template-columns: 1fr; } }

/* One photograph, as large as the page allows — this is a page for
   looking hard at a face, so the picture gets the room. */
.identify-pic { margin: 0 0 1rem; }
.identify-pic img {
  width: 100%; height: auto; max-height: 70vh; object-fit: contain;
  border: 1px solid var(--rule); border-radius: 4px; background: #f1efe7;
}
.identify-pic figcaption { margin-top: .4rem; font-size: .88rem; color: var(--ink-soft); }

/* The moderation page: small enough to scan a screenful at a time. */
.mod-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: .8rem; }
.mod-photo img {
  width: 100%; height: 9rem; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 4px;
}
.mod-photo--down img { opacity: .35; }
.mod-photo p { font-size: .72rem; margin: .3rem 0; }

.posting-note { font-size: .85rem; }

/* The illustration of Elias, on the page that tells his story. Beside
   the text where there is room, above it on a phone. */
/* And on the page that tells the story, the opposite: as large as the
   column allows. Somebody who followed a small panel wants to see the
   thing properly, and a picture that is small in both places was never
   worth clicking through to. */
.elias-figure { margin: 0 0 1.5rem; }
.elias-figure img {
  display: block; width: 100%; height: auto;
  max-height: 75vh; object-fit: contain;
  border-radius: 4px; border: 1px solid var(--rule); background: #ece9e0;
}
.elias-figure figcaption {
  margin-top: .4rem; font-size: .8rem; color: var(--ink-soft); line-height: 1.45;
}
@media (max-width: 40rem) {
  .elias-figure { float: none; width: 100%; margin: 0 0 1.25rem; }
  .elias-figure img { max-height: 60vh; object-fit: contain; }
}

/* Somewhere to drop a picture. Big enough to aim at — somebody dragging
   an image aims at the general area, not a thin strip. */
.dropzone {
  border: 2px dashed var(--rule); border-radius: 6px;
  padding: 1.1rem 1rem; text-align: center;
  background: var(--paper-warm); transition: border-color .15s ease, background .15s ease;
}
.dropzone--over { border-color: #2F6E63; background: #f1f6f3; }
.dropzone-say { margin: 0; font-size: .88rem; color: var(--ink-soft); }
.dropzone-pick { text-decoration: underline; cursor: pointer; color: #2F6E63; }
.dropzone-got { margin: .5rem 0 0; font-size: .82rem; color: #2F6E63; }

/* An event in the list: its poster beside the words, small. Somebody
   scanning a list recognises a picture before they read a title. */
.ev-row { display: flex; gap: .9rem; align-items: flex-start; }
.ev-row .ev-body { flex: 1; min-width: 0; }
.ev-row .ev-body h3 { margin-top: 0; }
.ev-thumb { flex: 0 0 4.5rem; display: block; line-height: 0; }
.ev-thumb img {
  width: 4.5rem; height: 4.5rem; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 4px; background: #ece9e0;
}

/* And in a calendar cell, where there is room for a stamp and no more. */
.cal-ev { display: flex; align-items: center; gap: .25rem; }
.cal-ev span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-pic {
  flex: 0 0 14px; width: 14px; height: 14px; object-fit: cover;
  border-radius: 2px; border: 1px solid var(--rule);
}

@media (max-width: 40rem) {
  .ev-thumb { flex-basis: 3.25rem; }
  .ev-thumb img { width: 3.25rem; height: 3.25rem; }
  .cal-pic { display: none; }
}

/* A place in the front row, with its picture. The photograph is what
   makes anybody look; the village lost it when the hardcoded section
   went, and with it the only reason to click. */
.place-card { overflow: hidden; padding-top: 0; }
.place-card-pic {
  display: block; margin: 0 -1.1rem .7rem;
  height: 9rem; background: #ece9e0; overflow: hidden;
}
.place-card-pic img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
.place-card--home .place-card-pic { height: 11rem; }

/* A photograph on each place in the list, and a year box small enough to
   sit under a thumbnail. */
.place-shot { margin: .6rem 0; }
.place-shot img {
  display: block; width: 100%; max-width: 22rem; height: 11rem;
  object-fit: cover; border: 1px solid var(--rule); border-radius: 4px; background: #ece9e0;
}
.year-box { width: 4.2rem; font-size: .78rem; }
.latlng { display: flex; gap: .8rem; }
.latlng label { flex: 1; }

/* ------------------------------------------------------------------
   Alternating rows
   ------------------------------------------------------------------

   A faint band on every other row. Long lists of short entries — a
   conversation, a column of events, a directory — run together into a
   wall of type, and the eye loses which line it was on halfway across.
   Faint enough that it is not stripes, strong enough to hold a line.
   ------------------------------------------------------------------ */

.msg-list > .msg:nth-child(even),
.chat-list > .chat-msg:nth-child(even),
.facts > li:nth-child(even),
.dir-list > li:nth-child(even) {
  background: var(--paper-warm);
}

.msg-list > .msg,
.chat-list > .chat-msg,
.facts > li,
.dir-list > li {
  padding-left: .55rem; padding-right: .55rem;
  border-radius: 3px;
}

/* An event in a list is a card already; the band goes behind the whole
   row rather than inside it. */
.ev-row:nth-of-type(even) { background: var(--paper-warm); }

/* The map on the journey page, as wide as the column allows — it is the
   point of the page, not an illustration beside it. */
.journey-map { margin: 0 0 1.5rem; }
.journey-map img {
  display: block; width: 100%; height: auto; max-height: 70vh;
  object-fit: contain; background: #ece9e0;
  border: 1px solid var(--rule); border-radius: 4px;
}
.journey-map figcaption {
  margin-top: .4rem; font-size: .84rem; color: var(--ink-soft); line-height: 1.5;
}

/* Saying how a picture should be shown. Small, and sat under the
   thumbnail it belongs to. */
.fitform { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin: .3rem 0; }
.fitform select { font-size: .78rem; padding: .15rem .3rem; }
.fitlabel { display: inline-flex; align-items: center; gap: .3rem; font-size: .78rem; }
.fitnote { margin: .1rem 0 .4rem; font-size: .7rem; color: var(--ink-faint); }

/* Who can read a story, under its card. Quiet — it is a fact about the
   thing, not part of it. */
.story-audience {
  margin: .25rem 0 0; font-size: .72rem; color: var(--ink-faint);
  padding: 0 .1rem;
}

/* ------------------------------------------------------------------
   The administrator's screen
   ------------------------------------------------------------------

   It had grown to twelve sections stacked in one column down the middle
   of a very wide monitor, and every queue added its whole length to the
   page whether or not anything was in it. Scrolling past four empty
   sections to reach the one with work in it is how a screen stops being
   looked at.

   Two columns where there is room, and any list that can grow is folded
   up until it has something in it.
   ------------------------------------------------------------------ */

.admin-cols { display: grid; gap: 1rem; grid-template-columns: 1fr; }

@media (min-width: 70rem) {
  .admin-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* A queue with nothing in it takes one line, not a screenful. */
.queue-head {
  display: flex; align-items: baseline; gap: .5rem;
  margin: 0 0 .5rem;
}

.panel-group {
  border: 1px solid var(--rule); border-radius: 4px;
  padding: .4rem .6rem; margin: 0 0 .4rem;
}
.panel-group > summary {
  cursor: pointer; font-size: .85rem; font-weight: 600;
  list-style: revert;
}
.panel-group[open] > summary { margin-bottom: .35rem; }
.panel-group .inline-label { display: flex; font-size: .85rem; padding: .1rem 0; }

/* The second row of panels sits closer to the first than a new section
   would — they are one thing seen twice, not two things. */
.front-top--second { margin-top: .6rem; }

.queue { margin: 0 0 1rem; }
.queue-summary {
  cursor: pointer; font-family: var(--font-display, inherit);
  font-size: 1.15rem; font-weight: 600;
  display: flex; align-items: baseline; gap: .5rem;
  padding: .3rem 0;
}
.queue:not([open]) > .queue-summary { color: var(--ink-soft); }
.queue[open] > .queue-summary { margin-bottom: .5rem; }

/* The conversation is a box that scrolls, not a column that grows.
 *
 * On the front page it has to be, because the rail is a fixed height. On
 * the full page it wants a ceiling too — a thread that grows for ever
 * pushes the box you type in off the bottom of the screen, and the box
 * you type in is why anybody came. */
/* The list does NOT scroll. Its wrapper does.
 *
 * In the rail the scrolling container is .chat-rail-scroll, and putting
 * overflow on the list as well made two scrollers inside one another.
 * The inner one had nothing to scroll and `overscroll-behavior: contain`
 * stopped the wheel chaining out to the one that did — so the wheel did
 * nothing over the conversation while the scrollbar beside it worked
 * perfectly. That is a horrible thing to debug and an easy thing to
 * cause.
 *
 * On the full page there is no wrapper, so there the list is the
 * scroller and says so explicitly. */
.chat-list--wide {
  max-height: min(60vh, 40rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: .4rem;
}

/* Saying how big a picture is: which dimension, how much, in what.
   Three controls on one line, because they are one decision. */
.sizebox-row { display: flex; gap: .3rem; align-items: center; margin-top: .15rem; }
.sizebox-row select { font-size: .78rem; padding: .15rem .25rem; }
.sizebox-row .year-box { width: 4.5rem; }

/* ------------------------------------------------------------------
   A picture at the top of its own page
   ------------------------------------------------------------------

   Modest by default, and whatever was asked for when somebody has asked.

   Taking the full width and three quarters of the screen meant a page
   opened on a photograph with its own words somewhere below the fold.
   The only place a picture should take everything available is its own
   page, where taking everything is the entire point.
   ------------------------------------------------------------------ */

.page-lead { margin: 0 0 1.25rem; }
.page-lead img {
  display: block; max-width: 100%; width: auto; height: auto;
  max-height: 24rem;
  border-radius: 4px; border: 1px solid var(--rule); background: #ece9e0;
}
/* A size chosen on purpose is not overruled by the ceiling above. Being
   quietly ignored is worse than having no control at all. */
.page-lead img[style] { max-height: none; }
.page-lead figcaption {
  margin-top: .4rem; font-size: .84rem; color: var(--ink-soft); line-height: 1.5;
}

/* Setting one picture up in the shared editor: the thumbnail beside its
   settings, so you can see what you are describing. */
.pic-row { display: flex; gap: 1rem; align-items: flex-start; }
.pic-shot { flex: 0 0 8rem; }
.pic-shot img {
  width: 8rem; height: 8rem; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 4px; background: #ece9e0;
}
.pic-set { flex: 1; min-width: 0; display: grid; gap: .5rem; }
@media (min-width: 48rem) {
  .pic-set { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pic-set .tool-grid { grid-column: 1 / -1; }
}
@media (max-width: 40rem) {
  .pic-row { flex-direction: column; }
  .pic-shot { flex: none; }
}

/* Adding somebody from the canvas. Folded away, because most of the time
   you are looking rather than filling in. */
.exp-add { margin-top: .6rem; border-top: 1px solid var(--rule); padding-top: .5rem; }
.exp-add > summary { cursor: pointer; font-size: .85rem; font-weight: 600; }
.exp-addform { display: grid; gap: .4rem; margin-top: .5rem; }
.exp-addform label { display: grid; gap: .15rem; font-size: .82rem; }
.exp-addform input { font-size: .9rem; padding: .3rem .4rem; }
.exp-addrow { display: flex; gap: .4rem; margin: .2rem 0 0; }
.exp-addrow .button { font-size: .82rem; padding: .3rem .6rem; }
.exp-addsaid { margin: .3rem 0 0; font-size: .78rem; color: #2F6E63; }

/* Choosing between somebody already in the archive and a new person. */
.exp-pick-why { display: block; margin-bottom: .4rem; color: var(--ink-soft); }
.exp-addsaid .button { display: block; width: 100%; text-align: left; margin: .2rem 0; }
.exp-addsaid .hint { display: block; font-weight: 400; }

/* The record a correction is about, and its kin. */
.corr-about { margin: .3rem 0; }
.corr-kin { display: block; margin-top: .15rem; }
.corr-said {
  margin: .5rem 0; padding: .5rem .7rem;
  background: var(--paper-warm); border-left: 3px solid var(--rule); border-radius: 2px;
}

/* Choosing between somebody new and somebody already in the tree. */
.exp-who { display: flex; gap: .8rem; margin: 0 0 .3rem; font-size: .82rem; }
.exp-who label { display: inline-flex; align-items: center; gap: .25rem; }
.exp-new, .exp-known { display: grid; gap: .4rem; }
.exp-found { margin: .3rem 0 0; display: grid; gap: .2rem; font-size: .78rem; }
.exp-found .button { text-align: left; width: 100%; font-size: .82rem; padding: .25rem .5rem; }
.exp-found .hint { display: block; font-weight: 400; font-size: .72rem; }
.exp-found .is-picked { border-color: #2F6E63; background: #f1f6f3; }

/* Choosing somebody by pointing at them on the tree. */
.exp-foundnote { margin: .3rem 0 0; font-size: .76rem; color: #C97A2B; }
.exp-picked {
  margin: .3rem 0 0; padding: .3rem .5rem; font-size: .8rem;
  background: #f1f6f3; border-left: 3px solid #2F6E63; border-radius: 2px;
}
/* While picking, the canvas says so — the pointer is doing something
   different from usual and should look like it. */
#explorer.is-picking { cursor: crosshair; }

/* ------------------------------------------------------------------
   The editor, on a wide screen
   ------------------------------------------------------------------

   The form on the left, what it will look like on the right. On a very
   wide monitor there is room to show the answer rather than describe it,
   and the whole difficulty of this screen is holding two appearances in
   your head at once — a picture that suits a small panel is often wrong
   at the top of a page.
   ------------------------------------------------------------------ */

.edit-split { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 78rem) {
  .edit-split { grid-template-columns: minmax(0, 1fr) minmax(22rem, 28rem); align-items: start; }
  /* The preview lives in the first row and the pictures span the second,
     so the preview must be told where its row ends. Without this the
     sticky column keeps travelling down the page and settles on top of
     the pictures — which are full width and directly beneath it. */
  .ed-layout > .ed-col { grid-row: 1; }
  .ed-layout > .ed-preview { grid-row: 1; }
  .ed-layout > .ed-full { grid-row: 2; grid-column: 1 / -1; }

  /* The preview must not travel past its own column.
  
     grid-row alone was not enough: a sticky element keeps its position
     while its containing block is in view, and the containing block was
     the whole grid. Giving the preview column its own height, and
     scrolling inside it, means it can never reach the pictures below. */
  /* The preview scrolls with the page. Nothing sticks.
  
     It was sticky, then sticky with a ceiling, then sticky with its own
     scroller — three attempts, three rules in three places, all fighting
     each other. The result moved a little, jammed, and then sat on top of
     the picture controls.
  
     Mike asked twice for it to simply scroll with everything else. That
     is one line, it cannot overlap anything, and it needs no reasoning
     about containing blocks. It should have been the first answer. */
  .ed-layout > .ed-preview {
    position: static;
    align-self: start;
  }

  .edit-previews {
    /* Sticky, and scrolling inside itself.
    
       Stuck to the top with no ceiling, a preview taller than the window
       simply ran off the bottom with no way to reach the rest of it —
       and the taller it is, the more there was to see. */
    position: static;
    
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: .5rem;
  }
}

.edit-previews h3 { margin: 1rem 0 .2rem; font-size: 1rem; }
.preview-row {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .6rem;
}
.front-panel--ghost { opacity: .35; }
.preview-page {
  border: 1px solid var(--rule); border-radius: 6px; padding: .8rem;
  background: var(--paper);
}

/* Below the two-column width the previews sit in the flow, where they had
   no ceiling at all — so a long story made the page enormous rather than
   scrolling. Given one here, and only here, because above that width the
   sticky pane is already the scroller and two inside one another is how
   the wheel stops working. */
@media (max-width: 77.99rem) {
  .preview-page {
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}
.preview-page h4 { margin: 0 0 .5rem; font-size: 1.1rem; }
.preview-page p { font-size: .85rem; line-height: 1.5; }

/* The picture controls: wide enough to read what they say. Three narrow
   columns squeezed every dropdown to the point where the words in it
   could not be seen, which is the one thing a dropdown has to do. */
.pic-set { display: grid; gap: .6rem; }
@media (min-width: 60rem) {
  .pic-set { grid-template-columns: repeat(2, minmax(11rem, 1fr)); }
}
.pic-set select { width: 100%; min-width: 9rem; font-size: .85rem; padding: .25rem .35rem; }
.pic-set label { display: grid; gap: .15rem; font-size: .8rem; }
.valbox { width: 5rem; font-size: .85rem; }
/* Take it off this page.
 *
 * It was a checkbox inside a label the picture grid had turned into a
 * grid of its own — so the box sat on one row and the words on another,
 * a few pixels of target with the sentence beside it doing nothing. It
 * read as a label because it behaved as one.
 *
 * Laid out on a line, with the whole row clickable and enough of it to
 * hit, and marked so it is plainly a thing you do rather than a thing
 * you read. */
.pic-off {
  grid-column: 1 / -1;
  display: flex !important;
  align-items: center; gap: .45rem;
  font-size: .82rem;
  padding: .35rem .5rem; margin-top: .2rem;
  border: 1px solid var(--rule); border-radius: 4px;
  background: var(--paper-warm);
  cursor: pointer;
}
.pic-off:hover { border-color: #9C3A2C; color: #9C3A2C; }
.pic-off input { width: 1rem; height: 1rem; cursor: pointer; flex: 0 0 auto; }
/* Once ticked it should look decided, not merely ticked. */
.pic-off:has(input:checked) {
  border-color: #9C3A2C; background: #fbf1ef; color: #9C3A2C; font-weight: 600;
}
.pic-save { margin-top: 1rem; }

/* Edit and Delete under a story. Two buttons rather than one link,
   because they are not the same kind of act and should not look alike.
   The row clears whatever follows — the section beneath was sitting on
   top of them. */
.story-acts {
  display: flex; gap: .4rem; flex-wrap: wrap;
  margin: .5rem 0 0; position: relative; z-index: 1; clear: both;
}
.story-acts .button { font-size: .8rem; padding: .25rem .6rem; }

/* A story card has to make room for them rather than assuming nothing
   comes after the summary. */
.story-card { overflow: visible; padding-bottom: .6rem; }

/* Finding a person for a story: search rather than a dropdown of two and
   a half thousand. */
.whobox { display: grid; gap: .2rem; }
.whofound { display: grid; gap: .2rem; margin: .2rem 0 0; }
.whofound .button { text-align: left; width: 100%; font-size: .82rem; padding: .25rem .5rem; }
.whofound .hint { display: block; font-weight: 400; font-size: .72rem; }

/* The page preview shows the whole story, so it needs room and its own
   line breaks. The scrollbar belongs to the pane around it — one
   scroller, not two, or the wheel stops chaining out of the inner one. */
.preview-body { white-space: pre-wrap; }
.preview-gallery {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
  gap: .35rem; margin: .6rem 0 .3rem;
}
.preview-gallery img {
  width: 100%; height: 4.5rem; object-fit: cover;
  border: 1px solid var(--rule); border-radius: 3px; background: #ece9e0;
}

/* ------------------------------------------------------------------
   The front-page preview, at the real proportions
   ------------------------------------------------------------------

   A picture told to fill its space is cropped to the shape of that
   space. Draw the preview panel narrower than the real one and it crops
   differently from the page it is previewing — which is the one thing a
   preview must not do.

   So the row is drawn at its true width, 59rem of content inside a
   62rem row, and the whole thing is scaled down to fit this column.
   Every proportion survives; only the size changes.
   ------------------------------------------------------------------ */

.preview-frame {
  overflow: hidden;
  /* A floor, so a panel with no picture — which is most stories — still
     has somewhere to be drawn. Without it the frame can end up the height
     of nothing and the preview reads as broken rather than as empty. */
  min-height: 8rem;
  /* The scaled height, so nothing below it is pushed down by the full
     un-scaled height of what is inside. */
  height: var(--preview-h, 14rem);
}
.preview-frame > .preview-row {
  transform: scale(var(--preview-scale, 0.4));
  transform-origin: top left;
  gap: 1rem;
}

/* A picture placed to one side with the words flowing round it. The
   figure carries the float, so the caption travels with the picture
   rather than being left behind under the text. */
.page-lead { max-width: 100%; }
.preview-body { overflow: hidden; }   /* so a floated figure does not escape it */
.pic-wrap { grid-column: 1 / -1; display: flex !important; align-items: center; gap: .45rem; }
.pic-wrap input { width: 1rem; height: 1rem; flex: 0 0 auto; }

@media (max-width: 40rem) {
  /* On a phone there is no room for words beside a picture — two
     columns of four words each is not text. */
  .page-lead { float: none !important; max-width: 100% !important;
               margin-left: auto !important; margin-right: auto !important; }
}

/* Who is attached to somebody, and taking a link off. Folded away,
   because most of the time you are looking rather than correcting. */
.exp-links { margin-top: .5rem; border-top: 1px solid var(--rule); padding-top: .5rem; }
.exp-links > summary { cursor: pointer; font-size: .85rem; font-weight: 600; }
.exp-link {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .25rem 0; font-size: .82rem;
}
.exp-link .hint { display: block; font-weight: 400; font-size: .72rem; }
.exp-link .button { font-size: .74rem; padding: .2rem .45rem; white-space: nowrap; }
.exp-unlinksaid { margin: .3rem 0 0; font-size: .78rem; color: #9C3A2C; }

/* Taking a person out. Folded, and the button is the only red thing on
   the panel — this is the one control here that cannot be undone. */
.exp-remove { margin-top: .5rem; border-top: 1px solid var(--rule); padding-top: .5rem; }
.exp-remove > summary { cursor: pointer; font-size: .85rem; font-weight: 600; }
.exp-remove .button { margin-top: .35rem; font-size: .82rem; }
.exp-removesaid { margin: .35rem 0 0; font-size: .78rem; color: #9C3A2C; }

/* ------------------------------------------------------------------
   The sentence between the two rows of panels
   ------------------------------------------------------------------

   The one place the site says what it is for, so it is worth the room.
   Centred, because it speaks for the whole family rather than
   introducing whatever comes next; larger, because a page made of cards
   needs somewhere the eye can rest; and set between two thin rules
   rather than in a box, because a box would make it a fourth panel and
   it is not a panel.
   ------------------------------------------------------------------ */

.creed {
  max-width: var(--wide);
  margin: 2.25rem auto;
  padding: 1.75rem 1.5rem;
  text-align: center;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.creed-said {
  margin: 0 auto .5rem;
  max-width: 26ch;
  font-family: var(--font-display, inherit);
  font-size: clamp(1.35rem, 2.6vw, 2rem);
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
  text-wrap: balance;
}

.creed-why {
  margin: 0 auto;
  max-width: 46ch;
  font-size: clamp(.92rem, 1.2vw, 1.05rem);
  line-height: 1.55;
  color: var(--ink-soft);
  text-wrap: balance;
}

@media (max-width: 40rem) {
  .creed { margin: 1.5rem auto; padding: 1.25rem 1rem; }
}

/* A panel that is a poster. The picture fills it and the words are there
   for a screen reader only — the flyer already carries the date, the
   place and the church, and saying it twice in two voices is worse than
   saying it once. */
.front-panel:has(> .front-panel-text.sr-only) { padding: 0; overflow: hidden; }
.front-panel:has(> .front-panel-text.sr-only) .front-panel-pic {
  margin: 0; height: 100%; aspect-ratio: 3 / 2;
}

/* The curator's controls on a place, ruled off from the facts above.
   A row of buttons directly under a list of counts reads as another
   count rather than as something you can press. */
.place-foot {
  margin-top: .85rem;
  padding-top: .7rem;
  border-top: 1px solid var(--rule);
}

/* The stories page: the group of prompts had nothing separating it from
   the cards above, so the first question sat against the last card. */
.prompts { margin-top: .5rem; }
.story-audience {
  margin: 0; font-size: .78rem; color: var(--ink-soft);
}

/* A lead picture set inside the words, so a float has lines to push
   against. Outside the prose it had nothing to wrap and the setting
   silently did nothing. */
.page-lead--inline { margin-top: .2rem; }
.prose { overflow: visible; }
.prose::after { content: ''; display: block; clear: both; }

/* ==================================================================
   Cards for a page of things — places, stories, and their like
   ==================================================================

   A shape borrowed from a design Mike liked and worked through against
   the archive's own colours rather than pasted in with a second palette.
   The tokens below map onto the ones already here: paper, ink, rule and
   the terracotta that runs through the site.

   The parts, top to bottom:

     the media    a picture at a fixed 16:9, with a badge over it
     the body     name, where it is, a few lines of description
     the stats    counted things, ruled off — the archive's own habit of
                  saying what it knows rather than asserting importance
     the actions  a tinted strip at the foot, Edit at one end and Delete
                  at the other, so the destructive one is never where a
                  hand lands by accident

   Auto-fit rather than a fixed two columns: side by side wherever there
   is room for them and stacked when there is not, which is what a
   family reading this on a phone needs without a breakpoint per screen.
   ================================================================== */

.thing-grid {
  list-style: none; margin: 1.5rem 0 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
  gap: 1.5rem;
}

.thing-card {
  display: flex; flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(33,29,23,.06), 0 2px 8px rgba(33,29,23,.05);
  transition: box-shadow .2s ease, transform .2s ease, border-color .2s ease;
}
.thing-card:hover {
  box-shadow: 0 6px 18px rgba(33,29,23,.10), 0 2px 6px rgba(33,29,23,.07);
  transform: translateY(-2px);
  border-color: var(--rule-strong, #d8d0bf);
}

.thing-media {
  position: relative; aspect-ratio: 16 / 9;
  overflow: hidden; background: #e9e4d9; display: block;
}
.thing-media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .4s ease;
}
.thing-card:hover .thing-media img { transform: scale(1.03); }

.thing-badge {
  position: absolute; top: .7rem; left: .7rem;
  background: rgba(33,29,23,.72); color: #fff;
  font-size: .7rem; font-weight: 600;
  padding: .25rem .6rem; border-radius: 999px;
}

.thing-body { padding: 1.15rem 1.3rem .9rem; flex: 1; }
.thing-body h2, .thing-body h3 {
  font-family: var(--font-display, Georgia, serif);
  font-size: 1.4rem; font-weight: 400; margin: 0;
  line-height: 1.2;
}
.thing-body a { text-decoration: none; color: inherit; }
.thing-card:hover .thing-body h2 a,
.thing-card:hover .thing-body h3 a { text-decoration: underline; }

.thing-where {
  display: flex; align-items: center; gap: .35rem;
  margin-top: .3rem;
  font-size: .78rem; font-weight: 600; letter-spacing: .02em;
  color: #6b7048;
}
.thing-where svg { width: 13px; height: 13px; flex-shrink: 0; }

.thing-desc {
  margin-top: .7rem; font-size: .88rem; line-height: 1.55;
  color: var(--ink-soft);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}

.thing-stats {
  display: flex; flex-wrap: wrap; gap: 1.1rem;
  margin-top: 1rem; padding-top: .85rem;
  border-top: 1px solid var(--rule);
  list-style: none;
}
.thing-stats li { display: flex; flex-direction: column; }
.thing-stats b { font-size: 1rem; font-weight: 700; line-height: 1.2; }
.thing-stats span { font-size: .72rem; color: var(--ink-faint, #8a8172); letter-spacing: .02em; }

.thing-actions {
  display: flex; justify-content: space-between; align-items: center;
  gap: .5rem;
  padding: .7rem 1rem;
  background: var(--paper-warm);
  border-top: 1px solid var(--rule);
}
.thing-actions form { display: contents; }
.thing-actions .button { font-size: .8rem; padding: .3rem .75rem; }

/* The heading of such a page: what it is on the left, why it matters in
   a column beside it, and the one thing you might do on the right. */
.thing-head {
  max-width: var(--wide); margin: 0 auto; padding: 0 1.5rem;
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 1.5rem 2.5rem;
}
.thing-head-title { flex: 0 1 16rem; }
.thing-head-title h1 { margin: .2rem 0 0; }
.thing-head-lede { flex: 1 1 22rem; margin: 0; color: var(--ink-soft); }
.thing-head-lede p { margin: 0 0 .6rem; }
.thing-head-act { margin-left: auto; }

/* Names the archive knows and nothing else about. */
.thing-empty {
  max-width: var(--wide); margin: 2rem auto 0; padding: 1.25rem 1.4rem;
  border: 1px dashed var(--rule-strong, #d8d0bf); border-radius: 14px;
}
.thing-empty h2 { font-family: var(--font-display, Georgia, serif); font-weight: 400; margin: 0 0 .3rem; }
.thing-tags { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .9rem; }
.thing-tags .tag {
  display: inline-block; padding: .3rem .75rem;
  border: 1px solid var(--rule-strong, #d8d0bf); border-radius: 999px;
  background: var(--paper); font-size: .82rem; text-decoration: none; color: inherit;
}
.thing-tags .tag:hover { border-color: var(--spring); }

@media (max-width: 46rem) {
  .thing-head { flex-direction: column; }
  .thing-head-act { margin-left: 0; }
}

/* The inside of a card is the front-page panel, so it reads the same in
   both places and nobody has to tune two things. Only the spacing is
   adjusted — a card has more room than a panel does. */
.thing-body .front-panel-text {
  display: flex; flex-direction: column; gap: .35rem; height: 100%;
}
.thing-body .front-panel-title { font-size: 1.4rem; font-weight: 400; }
.thing-body .front-panel-title a,
.thing-body .front-panel-cta a { text-decoration: none; color: inherit; }
.thing-card:hover .front-panel-title a { text-decoration: underline; }
.thing-body .front-panel-cta { padding-top: .5rem; }

/* An event held at the top of the list. A left edge rather than a
   different background, so it stands out without becoming a second kind
   of card. */
.thing-card--pinned { border-left: 3px solid var(--spring); }

/* ==================================================================
   The editor: numbered sections beside a live preview
   ==================================================================

   The shape of a design Mike liked, in this archive's own colours and
   fonts. What it gets right is the reading order — a column of small
   numbered cards, each one thing, with what you are making sitting
   beside it rather than somewhere you have to remember.

   Every control from the old screen is still here. A layout that loses a
   field is not a redesign, it is a regression that looks tidier.
   ================================================================== */

.ed-layout {
  max-width: var(--wide); margin: 0 auto; padding: 0 1.5rem 5rem;
  display: grid; grid-template-columns: minmax(0, 1fr) 25rem;
  gap: 2.5rem; align-items: start;
}
@media (max-width: 72rem) {
  .ed-layout { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
}

.ed-col { display: flex; flex-direction: column; gap: 1.4rem; min-width: 0; }

.ed-card {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(33,29,23,.05);
  overflow: hidden;
}
.ed-card-head {
  display: flex; gap: .85rem; align-items: flex-start;
  padding: 1rem 1.25rem;
  background: var(--paper-warm);
  border-bottom: 1px solid var(--rule);
}
/* The number is a step, not a decoration: it says how far through you
   are, which a long form otherwise never tells you. */
.ed-step {
  flex: 0 0 auto;
  font-size: .68rem; font-weight: 700; letter-spacing: .06em;
  padding: .2rem .45rem; border-radius: 6px;
  background: var(--accent-soft, #F3E9E8); color: #9C3A2C;
}
.ed-card-head h2 {
  font-family: var(--font-display); font-size: 1.1rem; font-weight: 400;
  margin: 0; letter-spacing: var(--track-tight);
}
.ed-card-head p { margin: .15rem 0 0; font-size: .8rem; color: var(--ink-soft); }
.ed-card-body { padding: 1.15rem 1.25rem 1.25rem; display: grid; gap: 1rem; }

.ed-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
.ed-row--3 { grid-template-columns: 1fr 1fr 1.2fr; }
@media (max-width: 40rem) { .ed-row, .ed-row--3 { grid-template-columns: 1fr; } }

.ed-field { display: flex; flex-direction: column; gap: .4rem; min-width: 0; }
.ed-field > label, .ed-label {
  font-size: .7rem; font-weight: 700;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ink-soft);
}
.ed-field .hint { font-size: .75rem; color: var(--ink-faint, #8a8172); text-transform: none;
                  letter-spacing: 0; font-weight: 400; }

/* Which dimension is being set: two halves of one control rather than a
   dropdown, because it is a choice between two things and a dropdown
   hides one of them. */
.ed-wh { display: inline-flex; border: 1px solid var(--rule-strong, #d8d0bf); border-radius: 7px; overflow: hidden; }
.ed-wh label {
  padding: .3rem .6rem; font-size: .78rem; font-weight: 600; cursor: pointer;
  background: var(--paper); color: var(--ink-soft);
}
.ed-wh input { position: absolute; opacity: 0; pointer-events: none; }
.ed-wh input:checked + label { background: #9C3A2C; color: #fff; }

/* The preview stays beside you as you scroll, and scrolls inside itself
   when it is taller than the window. One scroller, not two. */
.ed-preview {
  position: static;
    
  display: flex; flex-direction: column; gap: 1.1rem;
  padding-right: .4rem;
}
.ed-preview h2 { font-family: var(--font-display); font-weight: 400; margin: 0; }
.ed-preview-note { margin: .2rem 0 .4rem; font-size: .78rem; color: var(--ink-soft); }
.ed-preview-cap {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: .68rem; font-weight: 700; letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--ink-soft);
  margin-bottom: .4rem;
}
.ed-preview-cap span { font-weight: 400; letter-spacing: 0; text-transform: none;
                       color: var(--ink-faint, #8a8172); }

/* Save sits at the bottom of what you were reading, and again at the top
   where a long form makes you scroll back. */
.ed-actionbar {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: .9rem 1.25rem;
  background: var(--paper); border: 1px solid var(--rule); border-radius: 14px;
}
.ed-actionbar p { margin: 0; font-size: .8rem; color: var(--ink-soft); }

.ed-top {
  max-width: var(--wide); margin: 0 auto 1.25rem; padding: 0 1.5rem;
  display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; align-items: center;
}
.ed-top h1 { margin: 0; font-size: 1.6rem; letter-spacing: var(--track-tight); }
.ed-crumb { font-size: .78rem; color: var(--ink-soft); flex-basis: 100%; margin: 0 0 .1rem; }
.ed-top-acts { margin-left: auto; display: flex; gap: .5rem; align-items: center; }

/* ------------------------------------------------------------------
   The fields inside the editor
   ------------------------------------------------------------------

   Most of what makes the borrowed design look considered is here rather
   than in the layout: a field is a generously padded box with a soft
   border that warms on focus, and the focus ring is a tinted halo rather
   than the browser's blue outline.

   I built the section cards first and left this undone, which is why the
   screen still read as the old one — the shape had changed and every
   control on it had not.
   ------------------------------------------------------------------ */

.ed-card input[type="text"],
.ed-card input[type="number"],
.ed-card input[type="search"],
.ed-card select,
.ed-card textarea {
  width: 100%;
  font: 15px/1.45 var(--font-body);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid #D9D3C5;
  border-radius: 8px;
  padding: .6rem .8rem;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.ed-card input[type="text"]:focus,
.ed-card input[type="number"]:focus,
.ed-card select:focus,
.ed-card textarea:focus {
  outline: none;
  border-color: #9C3A2C;
  box-shadow: 0 0 0 3px rgba(156, 58, 44, .13);
}

.ed-card textarea { resize: vertical; min-height: 7.5rem; line-height: 1.65; }

/* Numbers read better in a face where a one and a seven cannot be
   confused, and a year box should be the width of a year. */
.ed-card .valbox,
.ed-card input[name$="_val]"],
.ed-card input[name$="[year]"] {
  width: 5.5rem;
  font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Consolas, monospace;
  font-size: .85rem; letter-spacing: .01em;
}
.ed-card input[name="lat"], .ed-card input[name="lng"] {
  font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Consolas, monospace;
  font-size: .88rem;
}

/* The dropzone, dashed and warm, and plainly a place to drop something. */
.ed-card .dropzone {
  display: flex; align-items: center; gap: 1rem;
  padding: 1.1rem 1.25rem;
  border: 1.6px dashed #CFC8B6;
  border-radius: 10px;
  background: var(--paper-warm);
  transition: border-color .15s ease, background .15s ease;
}
.ed-card .dropzone--over { border-color: #9C3A2C; background: #F7F1EC; }
.ed-card .dropzone-say { margin: 0; font-size: .88rem; color: var(--ink-soft); }
.ed-card .dropzone-pick {
  display: inline-block; margin-left: auto;
  border: 1px solid #D9D3C5; border-radius: 8px;
  padding: .4rem .8rem; background: var(--paper);
  font-size: .85rem; cursor: pointer; white-space: nowrap;
}
.ed-card .dropzone-pick:hover { border-color: #9C3A2C; color: #9C3A2C; }

/* One picture and its settings, as a row in the pictures card. */
.ed-pic {
  display: flex; gap: 1rem; align-items: flex-start;
  padding: 1rem 0; border-top: 1px solid var(--rule);
}
.ed-pic:first-of-type { border-top: 0; padding-top: .4rem; }
.ed-pic-shot { position: relative; flex: 0 0 8.5rem; }
.ed-pic-shot img {
  width: 8.5rem; height: 10rem; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--rule); background: #ece9e0;
}
.ed-pic-year {
  position: absolute; left: .4rem; bottom: .4rem;
  background: rgba(33,29,23,.72); color: #fff;
  font-size: .68rem; font-weight: 600; padding: .12rem .4rem; border-radius: 4px;
}
.ed-pic-set { flex: 1; min-width: 0; display: grid; gap: .8rem; }
@media (min-width: 52rem) {
  .ed-pic-set { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ed-pic-set .ed-pic-wide { grid-column: 1 / -1; }
}
@media (max-width: 40rem) { .ed-pic { flex-direction: column; } }

/* Take it off, at the end of the row and quiet until wanted. */
.ed-pic-off { margin-left: auto; }

/* An editor with no preview beside it — events and trips show what they
   are on their own page, and a second copy of a date and a place would
   be a preview of nothing much. */
.ed-layout--one { grid-template-columns: minmax(0, 1fr); max-width: 56rem; }

/* The pictures, under both columns.
 *
 * A picture with its settings needs width: a thumbnail, six dropdowns and
 * two size controls. Squeezed into the form column beside the preview it
 * had about twenty rem, and the words inside the dropdowns were clipped —
 * which is the one thing a dropdown must not do, since reading the
 * options is its entire function. */
.ed-full { grid-column: 1 / -1; margin-top: 1.4rem; }

/* And the settings themselves fit to the room rather than always being
   three across. Below about fifty rem that becomes one column, which is
   wide enough to read even on a phone. */
.ed-pic-set { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
.ed-pic-set select,
.ed-pic-set input { min-width: 0; }
.ed-pic-set select { text-overflow: ellipsis; }

/* A size control is three things on a line and needs to keep them there. */
.ed-pic-set .sizebox-row { display: flex; gap: .35rem; align-items: center; flex-wrap: nowrap; }
.ed-pic-set .sizebox-row .valbox { flex: 1 1 3.5rem; min-width: 3rem; }
.ed-pic-set .sizebox-row select { flex: 0 0 4.2rem; }
.ed-pic-set .ed-wh { flex: 0 0 auto; }

/* Looking at the site as somebody else. Unmissable on purpose: somebody
   who forgets will report the site as broken and be right about what
   they see and wrong about why. */
.pretending {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .6rem 1.25rem;
  background: #3E2E1F; color: #F6EFE4;
  font-size: .88rem;
  position: sticky; top: 0; z-index: 400;
}
.pretending strong { color: #F0C98B; }
.pretending form { margin: 0 0 0 auto; }
.pretending button {
  background: #F0C98B; color: #3E2E1F; border: 0; border-radius: 6px;
  padding: .3rem .8rem; font-size: .82rem; font-weight: 600; cursor: pointer;
}
.pretending button:hover { background: #fff; }

/* A branch manager: a curator for one line.
 *
 * Its own colour, because it is its own role to the family even though
 * the storage is a flag. Amber sits between the family green and the
 * curator's terracotta, which is about where the responsibility sits. */
.button--branch { --btn: #8A6A1F; --btn-hover: #71571A; --btn-tint: #F5EFE0; }

/* Making a story the one its line leads with. Set from the story itself,
   because that is where somebody is when they realise it. */
.mainstory {
  max-width: 44rem; margin: 2rem auto 0; padding: 1rem 1.25rem;
  border: 1px solid var(--rule); border-radius: 12px; background: var(--paper-warm);
}
.mainstory-is { margin: 0 0 .6rem; font-size: .9rem; }
.mainstory-is strong { color: #8A6A1F; }
.mainstory-form { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin: 0; }
.mainstory-pick { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; font-size: .88rem; }
.mainstory .hint { margin: .6rem 0 0; }

/* A correction, showing from what to what. Without this the queue reads
   as a wall of sentences, each needing a second tab to judge. */
.corr-change {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
  margin: .4rem 0; padding: .5rem .7rem;
  background: var(--paper-warm); border-radius: 6px; font-size: .9rem;
}
.corr-field {
  font-size: .7rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-soft);
}
.corr-was { text-decoration: line-through; color: var(--ink-soft); }
.corr-arrow { color: var(--ink-faint, #8a8172); }
.corr-now { font-weight: 600; color: #2F6E63; }
.corr-moved {
  margin: .3rem 0 0; font-size: .8rem; color: #9C3A2C;
  padding: .3rem .55rem; background: #F9F1EF; border-radius: 6px;
  max-width: 32rem;
}
.corr-yes:disabled, .corr-newval:disabled { opacity: .55; cursor: not-allowed; }

/* ------------------------------------------------------------------
   A correction, as a decision
   ------------------------------------------------------------------

   Who says so, then what changes, then why they say it. The person the
   correction is about sits underneath and quiet — it is there to judge
   the change by, not to be read first. The old arrangement led with the
   record and buried the change inside it, which is most of why a queue
   goes unhandled. */

.corr-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .45rem;
  margin: 0 0 .1rem; font-size: .9rem;
}
.corr-by { font-weight: 600; }
.corr-role {
  font-size: .66rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-soft);
  border: 1px solid var(--rule); border-radius: 999px; padding: .05rem .4rem;
}
.corr-role--none { color: #9C3A2C; border-color: #E2C9C3; }
.corr-when { margin-left: auto; font-size: .75rem; color: var(--ink-soft); }

/* The change itself, larger than anything around it. It is the one
   thing being decided. */
.corr-item--apply .corr-change { font-size: 1rem; }
.corr-item--apply .corr-now { font-size: 1.05rem; }
.corr-change--vague { background: transparent; padding-left: 0; }

/* A left edge that says at a glance which kind this is: one that can be
   pressed, one that needs a person, one to be careful with. */
.corr-item { border-left: 3px solid transparent; padding-left: .7rem; }
.corr-item--apply  { border-left-color: #2F6E63; }
.corr-item--byhand { border-left-color: var(--rule); }
.corr-item--moved  { border-left-color: #9C3A2C; }

.corr-byhand {
  margin: .3rem 0 0; font-size: .82rem; color: var(--ink-soft); font-style: italic;
}
.corr-open { font-size: .8rem; margin-left: .2rem; white-space: nowrap; }

/* Putting it right, in the queue.
   ------------------------------------------------------------------

   The three questions a curator has, in the order they have them: who is
   asking, whose record, and what would change. The change is a small
   table rather than a sentence — detail, what it says now, what it would
   say — because two values stacked one above the other can be compared
   at a glance, and two values either side of an arrow have to be read.

   It is the same block whether the sender named a field or wrote a
   paragraph. Where they named one it comes filled in; where they did
   not, the curator fills it. Nothing else on the card competes with it
   for attention. */
/* One form around the whole card, because the change box is part of the
   decision and has to post with it. .qitem is a flex row of body and
   actions; with the form as its only child both stacked and the card
   went ragged — the body at its natural 16rem-ish width and the buttons
   the full width of the page. The form takes the row's job instead, and
   the card is held to a measure a person can read across. */
.corr-item { display: block; }
.corr-form { display: block; width: 100%; }
.corr-item .qbody,
.corr-item .qactions,
.corr-item .corr-form { max-width: 54rem; }
.corr-item .qbody { display: block; }
.corr-item .qactions { display: block; margin-top: .55rem; }
.corr-item .qactions form { display: block; }

.corr-lab {
  font-size: .68rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-soft);
}
.corr-head .corr-lab, .corr-about .corr-lab { margin-right: .1rem; }

.corr-track {
  font-size: .72rem; color: var(--ink-soft);
  border-left: 1px solid var(--rule); padding-left: .5rem;
}

.corr-set {
  margin: .55rem 0 .5rem; padding: .6rem .75rem .55rem;
  background: var(--paper-warm); border: 1px solid var(--rule); border-radius: 10px;
}
.corr-set-head {
  margin: 0 0 .45rem; font-size: .68rem; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft);
}
.corr-grid {
  display: grid; grid-template-columns: 5.5rem minmax(0, 1fr);
  gap: .35rem .7rem; align-items: center;
}
/* Labels sit against the FIRST line of their cell, not centred on
   whatever grew underneath it. A cell can carry a note — where the value
   came from, that somebody has changed it since — and a centred label
   then floats halfway down the row, pointing at nothing. */
.corr-grid > .corr-lab { justify-self: start; align-self: start; padding-top: .65rem; }
.corr-kind {
  font-size: .66rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-soft);
  border: 1px solid var(--rule); border-radius: 999px; padding: .05rem .4rem;
}
.corr-cell { min-width: 0; }
.corr-pick { width: 100%; max-width: 16rem; min-height: 36px; font-size: .9rem; }
.corr-nowval {
  display: inline-block; max-width: 100%; font-size: .95rem; font-weight: 600;
  color: var(--ink-soft); text-decoration: line-through;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}
.corr-nowval--empty { text-decoration: none; font-style: italic; font-weight: 400; opacity: .75; }
.corr-newval {
  width: 100%; max-width: 32rem; min-height: 38px;
  font-size: 1rem; padding: .35rem .55rem;
  border: 1px solid #B9AE9B; border-radius: 6px; background: #fff;
}
.corr-newval:disabled { background: var(--paper-warm); color: var(--ink-faint, #8a8172); }
.corr-hint { margin: .25rem 0 0; font-size: .78rem; color: var(--ink-soft); }
.corr-guessed { font-style: italic; }
.corr-hint--warn { color: #9C3A2C; }
.corr-manual { margin: .4rem 0 0; font-size: .8rem; color: var(--ink-soft); }
/* A suggestion arrives with the detail and the value already named, so
   those two rows are printed where a correction offers a picker and a
   box. Same three rows, same order, so a curator reads both queues the
   same way. */
.corr-detail { font-size: .95rem; font-weight: 600; }
/* A message is words, not a change, so the words get the room a before
   and after would have taken. */
.corr-said--whole { font-size: 1rem; padding: .55rem .8rem; }
.corr-newshown { font-size: 1rem; font-weight: 700; color: #2F6E63; }

/* Yes, no, or neither. Accept and Decline are the decision and sit
   together on their own line; the two that are neither — filing it
   unchanged, and opening the record — are quieter and underneath, so a
   queue of forty is not forty rows of five equal buttons. */
.corr-actions { display: block; }
.corr-decide { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.corr-decide .qnote { flex: 1 1 11rem; width: auto; margin: 0; min-height: 38px; }
.corr-yes { font-weight: 700; }
.corr-lesser {
  display: flex; flex-wrap: wrap; align-items: center; gap: .9rem;
  margin-top: .45rem; font-size: .82rem;
}
.corr-plain {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: var(--ink-soft); text-decoration: underline;
  text-underline-offset: 2px;
}
.corr-plain:hover { color: var(--ink); }
.corr-open { font-size: .82rem; white-space: nowrap; }

/* Their own words are evidence, not the headline: they sit below the
   change they are being offered in support of. */
.corr-said {
  margin: .1rem 0 .1rem; padding: .4rem .7rem;
  border-left: 3px solid var(--rule); background: transparent;
  font-size: .95rem; color: var(--ink);
}

@media (max-width: 560px) {
  .corr-grid { grid-template-columns: 1fr; gap: .15rem; }
  .corr-grid > .corr-lab { margin-top: .35rem; }
  .corr-decide .qnote { flex: 1 1 100%; }
}

/* Somewhere to say why, beside the button that says no. Optional — the
   decision goes through either way — but a decline with a sentence is a
   different thing from a decline in silence. */
.qnote { display: block; width: 100%; margin-bottom: .35rem; font-size: .8rem; padding: .25rem .4rem; }

/* When and where, which is most of what an invitation is. Larger than
   the note under it, because a wrong date costs somebody a drive. */
.hp-facts { margin: .3rem 0 .2rem; font-size: .92rem; }

/* An event nobody else can see yet. Said plainly and early — somebody
   reading a waiting event has no other way of knowing it is invisible. */
.hp-waiting {
  margin: .6rem 0 1rem; padding: .7rem .9rem; border-radius: 8px;
  background: var(--paper-warm); border-left: 3px solid var(--rule);
  font-size: .92rem;
}
.hp-waiting--yours { border-left-color: #2F6E63; background: #F1F6F4; }
.hp-waiting p { margin: 0 0 .5rem; }
.hp-waiting .band-actions { display: inline-block; margin: 0 .3rem 0 0; }

/* Accepting something that has moved on since it was sent is still one
   press, but it should not look like the ordinary one. */
.button--warn { border-color: #9C3A2C; color: #9C3A2C; background: #F9F1EF; }
.button--warn:hover { background: #9C3A2C; color: #fff; }

/* What was just dropped, so adding a picture is not an act of faith. */
.dropzone-got { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.dropzone-thumb {
  width: 3rem; height: 3rem; object-fit: cover;
  border-radius: 4px; border: 1px solid var(--rule);
}

/* ------------------------------------------------------------------
   A page of panels — stories, places
   ------------------------------------------------------------------

   The same panel the front page uses, at the same 3:4. There is no third
   layout: somebody setting up a picture in the editor previews one thing
   and it is what appears everywhere.

   The width per panel matches the front page's three-across, so a panel
   is roughly the same size wherever a reader meets it.
   ------------------------------------------------------------------ */

.panel-grid {
  list-style: none; margin: 1.5rem 0 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 1.5rem;
  align-items: start;
}
.panel-cell { display: flex; flex-direction: column; gap: .5rem; }

/* Beneath the panel, never inside it. Anything inside would eat into the
   3:4 and the editor's preview would stop matching. */
.panel-under {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin: 0; font-size: .78rem;
}
.panel-under .story-acts { margin: 0 0 0 auto; }
.panel-under .button { font-size: .76rem; padding: .2rem .55rem; }

/* A setting that is not doing anything at the moment. Dimmed rather than
   removed, so it is clear the option exists and why it is not in play. */
.ed-field--idle { opacity: .45; }
/* Still usable — the dimming says "not doing anything just now", not
   "you may not touch this". A not-allowed cursor said the second, which
   was never true and is why the placement looked disabled. */
.ed-field--idle select { cursor: pointer; }
.ed-field--idle:focus-within { opacity: 1; }

/* ------------------------------------------------------------------
   A flyer that is the whole panel
   ------------------------------------------------------------------

   Somebody made a flyer with the date, the place and the church on it.
   Drawing our own title and summary over the top says the same things
   twice in two voices, and boxing it inside a padded panel with a
   coloured border says it is an illustration rather than the notice it
   is.

   So: edge to edge, no padding, no border, filling the 3:4 exactly.
   A flyer made at 3:4 lands in it with nothing cropped and nothing
   letterboxed.
   ------------------------------------------------------------------ */

.front-panel--poster {
  padding: 0;
  border: 0;
  background: transparent;
  gap: 0;
}

.front-panel--poster .front-panel-pic {
  margin: 0;
  height: 100%;
  max-height: 100%;
  width: 100%;
  border-radius: 6px;
  overflow: hidden;
}

/* cover, so a flyer that is not exactly 3:4 still fills the panel rather
   than sitting in bars. Made at 3:4, nothing is lost. */
.front-panel--poster .front-panel-pic img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center center;
}

.front-panel--poster:hover { border: 0; }

/* Which way the photographs are sorted. The chosen one is filled in, so
   it is clear which you are looking at rather than which you could pick. */
.gallery-sort { display: flex; align-items: center; gap: .5rem; margin: .6rem 0 0; }
.gallery-sort .button { font-size: .8rem; padding: .25rem .7rem; }
.button--on { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.button--on:hover { background: var(--ink); color: var(--paper); }

/* One position per row in the front page picker. */
.slot-field { margin-bottom: .7rem; }
.slot-field select { width: 100%; }

/* The file input inside a dropzone: visible and usable on its own.
   Dropping and pasting are conveniences on top of it, never the only way
   in — a control that works only when the script works is a control that
   fails silently. */
.dropzone input[type="file"] { font-size: .85rem; max-width: 100%; }
.dropzone { flex-wrap: wrap; }

/* Taking a photograph that is already in the archive. */
.take-photo { margin-top: .8rem; }
.take-photo summary { cursor: pointer; font-size: .9rem; }
.take-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  gap: .6rem; margin-top: .7rem;
}
.take-one { margin: 0; }
.take-one button {
  width: 100%; padding: 0; border: 1px solid var(--rule); border-radius: 8px;
  background: var(--paper); cursor: pointer; overflow: hidden; text-align: left;
}
.take-one button:hover { border-color: #9C3A2C; }
.take-one img { width: 100%; height: 5rem; object-fit: cover; display: block; }
.take-one span {
  display: block; padding: .3rem .4rem; font-size: .72rem; color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Save, beside the pictures rather than only at the foot of the section.
   "1 picture ready" with no button in sight is an instruction with no way
   to follow it. */
.dropzone-save { flex: 0 0 auto; margin-left: auto; }

/* Destroying a photograph. Behind a fold, because it is the one action on
   this site that cannot be undone. */
.destroy-fold { margin-top: 1rem; border-top: 1px solid var(--rule); padding-top: .8rem; }
.destroy-fold summary { cursor: pointer; font-size: .88rem; color: #A3372C; }
.destroy-form { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: .6rem 0 0; }
.destroy-form label { font-size: .82rem; }
.destroy-form code { background: var(--paper-warm); padding: .1rem .3rem; border-radius: 3px; }
.destroy-form input { border: 1px solid var(--strong, #D9D3C5); border-radius: 6px; padding: .35rem .6rem; }

/* Deleting a photograph for good. Folded away, because reaching for it
   should take a decision rather than a glance. */
.destroy-fold { margin-top: 1rem; border-top: 1px solid var(--rule); padding-top: .8rem; }
.destroy-fold summary { cursor: pointer; font-size: .85rem; color: #A3372C; }
.destroy-fold p { font-size: .85rem; }
.destroy-form { margin-top: .6rem; display: grid; gap: .6rem; max-width: 22rem; }

/* Which line a branch manager looks after. Hidden until that role is
   chosen — asking beside every role would be noise on four of the five. */
.branch-pick { display: none; margin-top: .3rem; font-size: .82rem; }
.branch-pick.branch-pick--on { display: inline-block; }

/* Giving somebody a line to look after, from the canvas. */
.exp-branch { margin-top: .6rem; }
.exp-branch summary { cursor: pointer; font-size: .85rem; color: #8A6A1F; }
.exp-branch select { width: 100%; margin: .4rem 0; font-size: .82rem; }
.exp-branchsaid { margin: .4rem 0 0; font-size: .82rem; color: var(--ink-soft); }

/* Who wrote a story, in the editor's title bar. The person approving it
   needs to know whose it is — a first submission reads differently from
   the fourth. */
.ed-wrote { flex-basis: 100%; margin: .2rem 0 0; font-size: .85rem; }
.ed-wrote a { color: var(--ink); }

/* ------------------------------------------------------------------
   Liking something
   ------------------------------------------------------------------

   Prominent on purpose. A like is the smallest thing most of the family
   will ever do here, and a control nobody notices is a control nobody
   uses.
   ------------------------------------------------------------------ */

.likes { display: inline-flex; align-items: center; }
/* .front-panel .likes is styled once, further down — it used to be
   set here as well, 76 lines apart, which is how a rule ends up with
   two answers. */

.likes-go {
  display: inline-flex; align-items: center; gap: .35rem;
  border: 1px solid var(--rule); border-radius: 999px;
  padding: .25rem .7rem; background: var(--paper);
  font: inherit; font-size: .85rem; color: var(--ink-soft);
  cursor: pointer; transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.likes-go:hover { border-color: #9C3A2C; color: #9C3A2C; }
.likes--mine .likes-go { border-color: #9C3A2C; color: #9C3A2C; background: #F9F1EF; }
.likes-heart { font-size: 1rem; line-height: 1; }
.likes-n { font-variant-numeric: tabular-nums; font-weight: 600; }

/* A guest sees the count and no button — a button that refuses is worse
   than none. */
.likes-go--quiet { cursor: default; }
.likes-go--quiet:hover { border-color: var(--rule); color: var(--ink-soft); }

/* The count, beside the button rather than inside it.
   It is the thing the names explain, so the hover hangs off it. */
.likes { gap: .55rem; flex-wrap: wrap; }
/* The count is a button: pressing it names them. It should not look
   like the heart beside it, so it is set as text with a dotted underline
   rather than as a second pill. */
.likes-tally {
  font: inherit; font-size: .85rem; color: var(--ink-soft);
  background: none; border: 0; padding: 0; cursor: pointer;
  border-bottom: 1px dotted var(--rule);
}
.likes-tally:hover:not(:disabled) { color: #9C3A2C; border-bottom-color: #9C3A2C; }
.likes-tally:disabled { cursor: default; border-bottom-color: transparent; }
.likes-tally:focus-visible { outline: 2px solid #9C3A2C; outline-offset: 2px; }

/* Who liked it.
 *
 * position: fixed, set by the script. A front-page panel is a 3:4 box
 * with overflow:hidden and would clip anything positioned inside it. */
.likes-who {
  position: fixed; z-index: 60; max-width: min(18rem, calc(100vw - 1rem));
  background: var(--paper); border: 1px solid var(--rule); border-radius: 8px;
  padding: .5rem .7rem; box-shadow: 0 6px 22px rgba(30, 42, 51, .16);
  font-size: .85rem; color: var(--ink);
  display: flex; flex-direction: column; gap: .1rem;
  text-align: left;
}
.likes-who[hidden] { display: none; }
.likes-who-head {
  font-size: .66rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-soft); margin-bottom: .2rem;
}
.likes-who-list { display: flex; flex-direction: column; gap: .05rem; }
.likes-who-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.likes-who-more { color: var(--ink-soft); font-size: .8rem; margin-top: .15rem; }
.likes-say { white-space: nowrap; }

/* Bigger on a thing's own page, where liking it is a reasonable thing to
   have come for. */
.likes--big .likes-go { font-size: 1rem; padding: .45rem 1.1rem; }
.likes--big .likes-heart { font-size: 1.3rem; }
.likes--big .likes-tally { font-size: .95rem; }

/* On a panel the two sit on one line at the foot, and the panel is
   narrow, so the count gives way before the button does.
 *
 * flex: 0 0 auto is not decoration. A panel is a fixed 3:4 box with
 * overflow hidden, and everything in it is a flex item that shrinks by
 * default. Give a panel a story long enough to fill it and the like
 * control was the thing that yielded: measured at 20px tall against its
 * natural 32px, with the bottom half of the pill sliced off by the
 * panel's own overflow. The words "just the top of it" describe a flex
 * item with no flex-shrink of its own.
 *
 * The body is the thing that should give — it already has flex: 1 1 auto
 * and a mask that fades it out, which is exactly what yielding is
 * supposed to look like here. */
.front-panel .likes { margin-top: auto; align-self: flex-start; gap: .4rem; flex: 0 0 auto; }

/* The like control is a sibling of the words now, not inside them.
   front-panel-text goes sr-only on a poster panel, and anything in it
   goes with it — which is how a heart could be in the markup, pass every
   check that looked for it, and be invisible on exactly the panels made
   of a single picture. */
.front-panel-likes { margin-top: auto; align-self: flex-start; }

/* On a poster the picture is not beside the heart, it is the whole panel:
   .front-panel--poster .front-panel-pic is height:100%, so a heart left in
   the flex column is pushed past the bottom edge and cut off by the
   panel's overflow. Measured: the panel ran 883→1260 and the control sat
   at 1260→1267, six pixels tall, entirely outside.
 
   Worth knowing — Playwright's is_visible() called that visible. It has a
   box and it is not display:none; it is simply outside the clipped area.
   Only a screenshot showed it, which is the second time this panel has
   taught the same lesson.
 
   So on a poster it comes out of the flow and sits on the picture, with
   its own ground to be legible against. */
.front-panel--poster { position: relative; }
.front-panel--poster .front-panel-likes {
  position: absolute; left: .55rem; bottom: .55rem; z-index: 2;
  margin: 0;
  background: rgba(255, 255, 255, .92);
  border-radius: 999px; padding: .2rem .5rem;
  box-shadow: 0 1px 6px rgba(30, 42, 51, .22);
}
.front-panel .likes-go,
.front-panel .likes-tally { flex: 0 0 auto; }
.front-panel .likes-tally { font-size: .78rem; }

/* And nothing inside the control may be clipped either. The panel sets
   overflow:hidden on every descendant to stop anything scrolling; that
   is right for the words and wrong for a button. */
.front-panel .likes,
.front-panel .likes-go,
.front-panel .likes-tally { overflow: visible; }

/* Deciding what happens to each story when a line is deleted. */
.branch-stories { list-style: none; margin: .6rem 0; padding: 0; display: grid; gap: .4rem; }
.branch-stories li {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  padding: .4rem .6rem; background: var(--paper-warm); border-radius: 6px;
}
.branch-story-name { flex: 1 1 12rem; font-size: .88rem; }
.branch-stories select { font-size: .82rem; }


/* A video in a panel
   ------------------------------------------------------------------

   The frame is click-through and the panel's own link lies over it, so
   pressing the video opens it on YouTube — which is what was asked for.
   That is also why the two controls have to sit above the link: a pause
   button underneath it would navigate instead of pausing.

   The still frame sits behind the iframe rather than beside it. iOS
   refuses autoplay in Low Power Mode and Android does the same on a data
   saver, so the video not starting is an ordinary Tuesday, not an error
   — and behind the frame the panel shows the picture instead of a black
   rectangle, with no code deciding which to draw. */
.front-panel-video { display: block; width: 100%; margin: 0 0 .5rem; }
.front-video {
  position: relative; display: block;
  width: var(--video-width, 100%); margin: 0 auto;
  aspect-ratio: 16 / 9;
  background: #1E2A33; border-radius: 8px; overflow: hidden;
}
/* Older browsers without aspect-ratio keep the box from collapsing. */
@supports not (aspect-ratio: 16 / 9) {
  .front-video { height: 0; padding-bottom: 56.25%; }
}
.front-video-still,
.front-video-frame {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.front-video-still { object-fit: cover; }
.front-video-frame { pointer-events: none; }

.front-video-controls {
  position: absolute; right: .4rem; bottom: .4rem; z-index: 3;
  display: flex; gap: .25rem;
  padding: .15rem; border-radius: 999px;
  background: rgba(20, 26, 31, .55);
  backdrop-filter: blur(2px);
}
.front-video-btn {
  appearance: none; border: 0; cursor: pointer;
  width: 26px; height: 26px; padding: 0; line-height: 1;
  border-radius: 999px; background: transparent;
  color: #fff; font-size: .85rem;
}
.front-video-btn:hover,
.front-video-btn:focus-visible { background: rgba(255, 255, 255, .18); }

/* The caption is the panel's own words, under the video, in the same
   type as every other panel. */
.front-panel--video .front-panel-text { padding-top: .1rem; }


/* The month, in one page
   ------------------------------------------------------------------

   A digest is a list of doors. Every line is a link and the type is
   arranged so the eye can run down the titles and stop at one — which
   means the title carries the weight, the date is small and to the side,
   and the summary is quiet underneath rather than competing. */
.digest-walk {
  display: flex; justify-content: space-between; gap: 1rem;
  margin: 0 0 .6rem; font-size: .85rem;
}
.digest-walk--foot { margin: 2rem 0 0; padding-top: .8rem; border-top: 1px solid var(--rule); }
.digest-walk-next { margin-left: auto; }

.digest-part { margin: 1.8rem 0 0; }
.digest-part h2 {
  display: flex; align-items: baseline; gap: .5rem;
  margin: 0 0 .5rem; padding-bottom: .3rem;
  border-bottom: 1px solid var(--rule);
}
.digest-count {
  font-size: .7rem; font-weight: 700; letter-spacing: .06em;
  color: var(--ink-soft); border: 1px solid var(--rule);
  border-radius: 999px; padding: .05rem .45rem;
}

.digest-list { list-style: none; margin: 0; padding: 0; }
.digest-list > li {
  display: block; padding: .5rem 0;
  border-bottom: 1px solid var(--rule-faint, rgba(0,0,0,.06));
}
.digest-list > li:last-child { border-bottom: 0; }
.digest-title { font-size: 1.02rem; font-weight: 600; }
.digest-when { font-size: .78rem; color: var(--ink-soft); margin-left: .45rem; white-space: nowrap; }
.digest-meta { display: block; font-size: .85rem; color: var(--ink-soft); }
.digest-note {
  display: block; font-size: .9rem; margin-top: .15rem;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* New joiners, with faces. A row of names is a list; a row of faces is
   the family arriving. The initial stands in where there is no portrait
   so the column does not go ragged. */
.digest-list--faces > li { display: flex; gap: .7rem; align-items: flex-start; }
.digest-said { flex: 1 1 auto; min-width: 0; }
.digest-face {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
  object-fit: cover; background: var(--paper-warm); display: block;
}
.digest-face--none {
  display: grid; place-items: center;
  font-weight: 600; color: var(--ink-soft);
  border: 1px solid var(--rule);
}

/* The map of what moved in the tree.
   The picture is drawn at its natural size and allowed to scroll inside
   its own frame rather than shrinking the names to nothing on a phone —
   an unreadable map is not a smaller map, it is a different thing. */
.digest-map { margin: 1rem 0 1.4rem; }
.digest-map h3 {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  margin: 0 0 .4rem; font-size: 1.05rem;
}
.digest-map h3 .digest-meta { display: inline; font-size: .8rem; }
.digest-mapframe {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--rule); border-radius: 8px;
  padding: .5rem .2rem;
  /* A shadow at whichever edge still has picture behind it, so a map
     that is wider than a phone looks scrollable instead of looking
     cropped. Four backgrounds: two that scroll with the content and
     cover the shadow when there is nothing more that way, and two that
     stay put and show it when there is. No JavaScript, and nothing to
     go wrong when the map happens to fit. */
  background:
    linear-gradient(to right, var(--paper-warm) 40%, rgba(240,236,227,0)) left center,
    linear-gradient(to left,  var(--paper-warm) 40%, rgba(240,236,227,0)) right center,
    radial-gradient(farthest-side at 0 50%,   rgba(30,42,51,.20), rgba(30,42,51,0)) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(30,42,51,.20), rgba(30,42,51,0)) right center,
    var(--paper-warm);
  background-repeat: no-repeat;
  background-size: 36px 100%, 36px 100%, 18px 100%, 18px 100%, auto;
  background-attachment: local, local, scroll, scroll, scroll;
}
.treemap { display: block; max-width: none; }
.treemap a text { text-decoration: none; }
.treemap a:hover text { text-decoration: underline; }
.digest-list--tight > li { padding: .3rem 0; }
.digest-list--tight .digest-meta { display: inline; margin-left: .4rem; font-size: .8rem; }
.digest-lede { margin: 0 0 .6rem; font-size: .9rem; color: var(--ink-soft); }

/* Photographs are shown, not described. */
.digest-photos {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: .7rem;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}
.digest-photos img {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: 8px; display: block; background: var(--paper-warm);
}
.digest-photo-cap {
  display: block; margin-top: .25rem; font-size: .8rem; line-height: 1.3;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
