/* Karoo Family History — the land view.
   Cadastral, not decorative: the polygons and the documents carry the story.
   Light only, deliberately: the map is a plain canvas and inverting it makes a
   survey diagram look like something it is not. */

:root {
  --paper:      #f6f3ec;
  --paper-2:    #fffdf8;
  --canvas:     #ece7dc;   /* the map background — dry ground, not white */
  --ink:        #23201b;
  --ink-2:      #55503f;
  --ink-3:      #746e5f;   /* was #8a8371, which is 3.40:1 on --paper and below
                              WCAG AA's 4.50 for normal text. This is the same hue
                              darkened until it passes, 4.58:1. It carries every
                              de-emphasised label on the site - source references,
                              record counts, the lane names - so it is the single
                              value that decides whether the quiet text is legible. */
  --rule:       #ddd6c6;
  --rule-2:     #cfc6b2;
  --core:       #a4542a;   /* farms with a written history */
  --core-soft:  rgba(164, 84, 42, 0.16);
  --other:      #6c7b6a;
  --other-soft: rgba(108, 123, 106, 0.12);
  --thin:       #a9a292;
  --accent:     #2f5d74;
  --withheld:   #8a6d3b;
  --tint:       rgba(35, 32, 27, 0.05);   /* the one hover wash: ink at 5%. It was
                              used in three places and DEFINED in none, so every
                              var(--tint) computed to nothing and the hover states
                              it carried simply did not happen (13 Aug 2026). */
  --ok:         #4a6b3f;   /* the "done" green, one badge. In the palette so that
                              nothing on the site wears a colour the palette does
                              not know about. */
  --serif:      "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
                Georgia, serif;
  --sans:       -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
                Arial, sans-serif;
  --mono:       ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  /* THE SCALE HAS NO "-1". The base of each family is the bare name - --paper,
     --ink, --rule - and the variants count up from it: --paper-2 (raised card),
     --ink-2, --ink-3 (each quieter), --rule-2 (stronger rule). Thirteen usages of
     --paper-1/--ink-1/--rule-1 accumulated against tokens that never existed, and
     an undefined custom property does not fall back or warn: the declaration
     computes to `unset`, so the search dialog and the terms gate were drawing with
     NO BACKGROUND and three hover states were dead. css_audit() in build_site.py
     now fails the build on any var() this block does not define. */
}

/* THE BREAKPOINT LADDER, all of it: 420 (small phone), 560 (phone), 720 (the fold
   between phone and the full layout), 900 (narrow laptop), 1100 (wide). Media
   queries cannot read custom properties, so the ladder is enforced by css_audit()
   instead: a query on any other width fails the build. It was 420/560/620/700/
   720/900/1100 in two spellings, each opened where a feature happened to need
   one - a reader crossing 640px met three different pages. */

/* Light only, and say so. Without this a browser in dark mode applies its own automatic
   inversion, which turns a warm paper palette into grey-on-black and makes a scanned
   survey diagram look like a photographic negative. */
:root { color-scheme: light; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

body.map-view {
  display: grid;
  /* THREE rows: masthead, layout, foot. COUNT THE CHILDREN - this line has now been
     wrong twice for the same reason, in both directions.
     It said three rows when the ribbon was added on 3 Aug 2026, and the extra child fell
     into an implicit track and OVERLAPPED the top of the map. It said four rows on
     14 Aug 2026 when the ribbon moved INSIDE the masthead (templates/map.html) and the
     count went back down to three: .layout landed in an `auto` track and the footer took
     the minmax(0,1fr), so the map got whatever height the farm list happened to need and
     the footer got the rest of the window.
     THAT SECOND ONE HID FOR A WHILE, WHICH IS THE PART WORTH REMEMBERING. At level 2 the
     farm list is long, so the `auto` track was tall and the page looked right - I checked
     it at two widths and saw a working map. Frans opened it at LEVEL 1, where the list is
     nearly empty, the auto track collapsed to almost nothing and the whole layout
     vanished with the legend left floating under the title. A grid whose template does
     not match its children does not fail loudly; it fails at whatever size its content
     happens to be, so testing one view proves nothing about the others. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  height: 100vh;
  overflow: hidden;
}
/* Nothing scrolls on the map view, so sticky has no work to do and only creates a
   stacking context that can cover the map controls. Sticky stays on the prose pages. */
body.map-view .ribbon { position: static; }

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

/* ---------------------------------------------------------------- topbar */
.topbar {
  display: flex; flex-wrap: wrap; gap: 1.5rem 2.5rem;
  align-items: flex-end; justify-content: space-between;
  padding: 0.85rem 1.4rem;
  border-bottom: 1px solid var(--rule-2);
  background: var(--paper-2);
}
.brand h1 {
  font-family: var(--serif);
  font-size: 1.4rem; font-weight: 600; margin: 0;
  letter-spacing: 0.01em;
}
.brand p { margin: 0.15rem 0 0; color: var(--ink-2); font-size: 0.86rem; max-width: 46ch; }

.tallies { display: flex; gap: 1.6rem; margin: 0; }
.tallies div { text-align: right; }
.tallies dt {
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--ink-3);
}
.tallies dd {
  margin: 0; font-family: var(--serif); font-size: 1.25rem; font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------- language switch */
/* A flex child of .topbar, so it flows with the header instead of floating over it
   and it wraps with everything else on a narrow screen. Two sites, two words; the
   current one is a <span>, not a link to itself. */
.langswitch {
  display: flex; align-items: center; gap: 0.15rem;
  font-size: 0.72rem; letter-spacing: 0.08em;
  margin-left: auto; align-self: center;
}
.langswitch a,
.langswitch .on {
  padding: 0.2rem 0.42rem; border-radius: 3px;
  text-decoration: none; font-variant-numeric: tabular-nums;
}
.langswitch a { color: var(--accent); }
.langswitch a:hover { background: var(--paper); text-decoration: underline; }
.langswitch .on {
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--rule-2); cursor: default;
}

/* ---------------------------------------------------------------- layout */
.layout {
  display: grid;
  /* THE FARM-LIST TRACK IS A VARIABLE so it can be collapsed from one place. Every rule
     below states three tracks, and hiding the list by zeroing --list-w keeps all of them
     correct without a second copy of each for the collapsed case. */
  --list-w: 19rem;
  grid-template-columns: var(--list-w) minmax(0, 1fr) 0;
  min-height: 0;
  /* No transition on grid-template-columns. Animating grid tracks means the map column
     changes width on every frame, which drives the map's ResizeObserver in a loop and
     leaves the panel column stuck at 0. A data panel should appear at once anyway. */
}
.layout.panel-open { grid-template-columns: var(--list-w) minmax(0, 1fr) 27rem; }

/* EACH CHILD NAMES ITS OWN COLUMN, and that is not decoration.
   `display: none` takes an element OUT of the grid entirely, so the remaining children
   slide up a track: hiding the farm list put the MAP in the list's column and the PANEL
   in the map's, and the map came out at 19rem - narrower than before it was given the
   space. Pinning each child to its column makes hiding any of them safe. */
.farmlist { grid-column: 1; }
.mapwrap  { grid-column: 2; }
.panel    { grid-column: 3; }

/* ------------------------------------------------------------- farm list */
.farmlist {
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 0;
  border-right: 1px solid var(--rule-2);
  background: var(--paper-2);
}
.farmlist-head { padding: 0.6rem 0.8rem 0.5rem; border-bottom: 1px solid var(--rule); }
.modes { display: flex; gap: 0; margin-bottom: 0.45rem; }
.modes .mode {
  flex: 1; padding: 0.28rem 0.4rem;
  border: 1px solid var(--rule-2); background: var(--paper);
  font: inherit; font-size: 0.74rem; color: var(--ink-3); cursor: pointer;
}
.modes .mode:first-child { border-radius: 3px 0 0 3px; border-right: 0; }
.modes .mode:last-child { border-radius: 0 3px 3px 0; }
.modes .mode.on { background: #fff; color: var(--core); font-weight: 600; }
.modes .mode:hover { color: var(--ink); }

/* families */
.familylist h2 {
  margin: 0; padding: 0.45rem 0.85rem 0.3rem;
  font-family: var(--sans); font-size: 0.64rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-3);
}
ul.fams { list-style: none; margin: 0; padding: 0; }
button.fam {
  display: block; width: 100%; text-align: left;
  padding: 0.3rem 0.85rem 0.34rem;
  border: 0; border-left: 3px solid transparent;
  background: none; font: inherit; color: inherit; cursor: pointer;
}
button.fam:hover { background: rgba(47, 93, 116, 0.09); }
button.fam[aria-current="true"] {
  background: rgba(47, 93, 116, 0.14); border-left-color: var(--accent);
}
button.fam .fname { font-weight: 600; }
.familylist-foot {
  margin: 0.5rem 0 0; padding: 0.6rem 0.85rem 0;
  border-top: 1px solid var(--rule);
  font-size: 0.7rem; line-height: 1.4; color: var(--ink-3);
}
.search input {
  width: 100%; padding: 0.4rem 0.6rem;
  border: 1px solid var(--rule-2); border-radius: 3px;
  background: #fff; font: inherit; font-size: 0.85rem; color: var(--ink);
}
.search input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

.farmlist-scroll { overflow-y: auto; padding-bottom: 0.6rem; }
.farmgroup h2 {
  position: sticky; top: 0;
  margin: 0; padding: 0.45rem 0.85rem 0.3rem;
  font-family: var(--sans); font-size: 0.64rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-3);
  background: linear-gradient(var(--paper-2) 70%, rgba(255, 253, 248, 0));
}
.farmgroup ul { list-style: none; margin: 0 0 0.4rem; padding: 0; }
.farmgroup[hidden] { display: none; }

/* Two levels: the original farm, and the ground cut out of it. The indent and the rule
   are doing real work here — they say that a portion's history is not the farm's. */
.farm-item { margin-bottom: 0.15rem; }
.farm-item.has-children { margin-bottom: 0.45rem; }
ul.portions {
  margin: 0 0 0 1.55rem;
  padding: 0;
  border-left: 1px solid var(--rule);
}
button.farm-head .fname { font-weight: 600; }
button.farm-head { padding-top: 0.34rem; }
button.farm-child { padding-left: 0.7rem; }
button.farm-child .fname { font-size: 0.83rem; color: var(--ink-2); }
button.farm-child .fmeta { font-size: 0.68rem; }
button.farm[disabled] { cursor: default; opacity: 0.75; }
button.farm[disabled]:hover { background: none; }

button.farm {
  display: block; width: 100%; text-align: left;
  padding: 0.3rem 0.85rem 0.34rem;
  border: 0; border-left: 3px solid transparent;
  background: none; font: inherit; color: inherit; cursor: pointer;
}
button.farm:hover { background: var(--core-soft); }
button.farm:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
button.farm[aria-current="true"] {
  background: var(--core-soft); border-left-color: var(--core);
}
.fname { display: block; font-size: 0.88rem; }
.fmeta {
  display: block; font-size: 0.71rem; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
button.farm.unmapped .fname { color: var(--ink-2); }
button.farm.unmapped { border-left-color: transparent; }
button.farm.unmapped .fname::after {
  content: "○"; margin-left: 0.35em; color: var(--thin); font-size: 0.8em;
}

.farmlist-foot {
  margin: 0; padding: 0.6rem 0.85rem;
  border-top: 1px solid var(--rule);
  font-size: 0.72rem; line-height: 1.4; color: var(--ink-3);
}

/* ------------------------------------------------------------------- map */
.mapwrap { position: relative; min-height: 0; background: var(--canvas); }
#map { position: absolute; inset: 0; }

.legend {
  position: absolute; left: 0.8rem; bottom: 0.8rem; z-index: 2;
  max-width: 15rem; padding: 0.4rem 0.6rem;
  background: rgba(255, 253, 248, 0.94);
  border: 1px solid var(--rule-2); border-radius: 3px;
  font-size: 0.7rem; color: var(--ink-2);
  box-shadow: 0 1px 6px rgba(35, 32, 27, 0.08);
  /* BOUNDED, so it can never grow up into the control panel above it. Both sit in the
     left margin of the map, and with the family key unfolded on a short window the legend
     grew until it covered the Zoom to buttons. Scroll the key rather than eat the
     controls: the key is reference, the controls are how you change the map. */
  max-height: 44%; overflow-y: auto; overscroll-behavior: contain;
}
.legend-title {
  margin: 0; font-weight: 600; color: var(--ink); cursor: pointer;
  list-style: none; user-select: none;
}
.legend-title::-webkit-details-marker { display: none; }
.legend-title::before { content: "▾ "; color: var(--ink-3); }
.legend:not([open]) .legend-title::before { content: "▸ "; }
.legend:not([open]) { padding: 0.3rem 0.55rem; }
.legend-hint { font-weight: 400; color: var(--ink-3); font-size: 0.66rem; }
.legend[open] .legend-hint { display: inline; }
.legend:not([open]) .legend-hint { display: none; }
/* The family mode lists every surname present. Two narrow columns and a hard cap
   keep it off the map instead of over it. */
.legend ul {
  list-style: none; margin: 0.35rem 0 0; padding: 0;
  max-height: 40vh; overflow-y: auto;
  columns: 2; column-gap: 0.7rem;
}
.legend li {
  display: flex; align-items: center; gap: 0.35rem; margin-bottom: 0.1rem;
  break-inside: avoid; line-height: 1.25;
}
.legend-more { margin-top: 0.4rem; }
.legend-more > summary {
  cursor: pointer; color: var(--ink-3); font-size: 0.68rem; list-style: none;
}
.legend-more > summary::-webkit-details-marker { display: none; }
.legend-more > summary::before { content: "＋ "; }
.legend-more[open] > summary::before { content: "－ "; }
@media (max-width: 720px) {
  .legend { max-width: 11rem; font-size: 0.66rem; }
  .legend ul { columns: 1; max-height: 30vh; }
}
.swatch { width: 14px; height: 10px; border-radius: 1px; display: inline-block; }
.swatch.core  { background: var(--core-soft);  border: 1.5px solid var(--core); }
.swatch.other { background: var(--other-soft); border: 1.5px solid var(--other); }
.swatch.thin  { background: none; border: 1.5px dashed var(--thin); }
.swatch.ctx   { background: none; border: 1px solid #b3a98f; opacity: 0.75; }
.legend-note { margin: 0.45rem 0 0; font-size: 0.69rem; color: var(--ink-3); }

/* map labels — DOM markers, so no glyph fetch and the page works offline */
.mk-town {
  display: flex; align-items: center; gap: 0.3rem;
  transform: translateX(4px);
  font-size: 0.72rem; color: var(--ink-2); white-space: nowrap;
  text-shadow: 0 0 3px var(--canvas), 0 0 3px var(--canvas), 0 0 6px var(--canvas);
  pointer-events: none;
}
.mk-town i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--ink-2); box-shadow: 0 0 0 2px rgba(236, 231, 220, 0.9);
  flex: none;
}
/* BLACK FOR THIS FAMILY, GREY FOR CONTEXT . The test is the Klaarstroom level: a place is level 1 when something this family did is tied to it. Context towns are there so a reader can see where the Koup sits, and they should not compete with the ground the site is about. */
.mk-fam { color: var(--ink); font-weight: 600; }
.mk-fam i { background: var(--ink); }
.mk-ctx { color: var(--ink-3); }
.mk-ctx i { background: var(--ink-3); opacity: .75; }
/* A place typed FARM is a farm this project could not draw - Calvinia is outside the
   Western Cape cadastre, so Biesies and Tygerhoek have a point and no polygon. Square,
   to say it is not a town. */
.mk-plfarm i { border-radius: 1px; }
/* Homestead (werf). A dwelling, not a settlement and not a boundary, so it reads
   differently from both: a solid square in the core-farm ochre rather than the town's
   grey ring, and the name in italic. Only drawn at zoom >= 9.5 (see map.js). */
.mk-werf {
  display: flex; align-items: center; gap: 0.3rem;
  transform: translateX(4px);
  font-size: 0.68rem; font-style: italic; color: var(--core); white-space: nowrap;
  text-shadow: 0 0 3px var(--canvas), 0 0 3px var(--canvas), 0 0 6px var(--canvas);
  pointer-events: none;
}
.mk-werf i {
  width: 5px; height: 5px; border-radius: 1px;
  background: var(--core); box-shadow: 0 0 0 2px rgba(236, 231, 220, 0.9);
  flex: none;
}

.mk-farm {
  padding: 0.05rem 0.2rem;
  font-family: var(--serif); font-size: 0.76rem; font-weight: 600;
  color: var(--core); white-space: nowrap; cursor: pointer;
  text-shadow: 0 0 3px var(--canvas), 0 0 3px var(--canvas), 0 0 7px var(--canvas);
}
.mk-farm:hover { text-decoration: underline; }
/* A farm with no written history yet. Same size and placement - it is the same kind
   of thing on the ground - but in the neutral ink rather than the core ochre, so the
   five that carry a story still read first without the other twenty-seven being
   demoted to background. */
.mk-farm.plain { color: var(--ink-2); font-weight: 500; }

.viewctl { display: flex; overflow: hidden; }
.viewctl button {
  border: 0; border-right: 1px solid var(--rule);
  background: #fff; padding: 0.3rem 0.55rem;
  font: inherit; font-size: 0.72rem; color: var(--ink-2); cursor: pointer;
  width: auto; height: auto;
}
.viewctl button:last-child { border-right: 0; }
.viewctl button:hover { background: var(--core-soft); color: var(--core); }

.maplibregl-popup-content {
  font-family: var(--sans); font-size: 0.78rem; padding: 0.4rem 0.6rem;
  border-radius: 2px; box-shadow: 0 2px 8px rgba(35, 32, 27, 0.18);
}
.maplibregl-popup-content strong { display: block; font-size: 0.85rem; }
.maplibregl-popup-content span { color: var(--ink-3); }

/* ----------------------------------------------------------------- panel */
.panel {
  min-height: 0; overflow-y: auto; overflow-x: hidden;
  border-left: 1px solid var(--rule-2);
  background: var(--paper-2);
}
.layout:not(.panel-open) .panel { border-left: 0; }
.panel-empty, .panel-body { padding: 1.1rem 1.2rem 2.5rem; }
.layout:not(.panel-open) .panel-empty { display: none; }

.panel-empty h2 { font-family: var(--serif); font-size: 1.05rem; margin: 0 0 0.5rem; }
.panel-empty p { font-size: 0.84rem; color: var(--ink-2); }
.panel-empty .hint { font-size: 0.78rem; color: var(--ink-3); }

.panel-close {
  float: right; margin: -0.2rem -0.3rem 0 0;
  border: 0; background: none; cursor: pointer;
  font-size: 1.2rem; line-height: 1; color: var(--ink-3); padding: 0.2rem 0.4rem;
}
.panel-close:hover { color: var(--ink); }

.p-title { font-family: var(--serif); font-size: 1.22rem; margin: 0 0 0.15rem; }
.p-sub {
  margin: 0 0 0.9rem; font-size: 0.78rem; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.p-core {
  display: inline-block; margin-bottom: 0.55rem;
  padding: 0.1rem 0.45rem; border-radius: 2px;
  background: var(--core-soft); color: var(--core);
  font-size: 0.66rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em;
}

.p-facts { margin: 0 0 1rem; font-size: 0.82rem; }
.p-facts div {
  display: grid; grid-template-columns: 7.5rem 1fr; gap: 0.5rem;
  padding: 0.22rem 0; border-bottom: 1px dotted var(--rule);
}
.p-facts dt { color: var(--ink-3); font-size: 0.75rem; }
.p-facts dd { margin: 0; font-variant-numeric: tabular-nums; }
/* A block child of dd, not a div: `.p-facts div` selects rows, and a nested div showed up
   as a phantom row of its own. */
.p-facts .c-sub { display: block; font-size: 0.75rem; color: var(--ink-3); }

.caveat {
  margin: 0.5rem 0 1.1rem; padding: 0.5rem 0.6rem;
  border-left: 2px solid var(--withheld);
  background: rgba(138, 109, 59, 0.07);
  font-size: 0.75rem; line-height: 1.45; color: var(--ink-2);
}

.panel h3 {
  margin: 1.4rem 0 0.5rem;
  font-family: var(--sans); font-size: 0.66rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink-3);
  border-bottom: 1px solid var(--rule); padding-bottom: 0.25rem;
}

/* the history chain */
ol.chain { list-style: none; margin: 0; padding: 0; }
ol.chain li {
  position: relative; padding: 0.35rem 0 0.5rem 4.6rem;
  border-bottom: 1px dotted var(--rule);
}
ol.chain li:last-child { border-bottom: 0; }
.c-date {
  position: absolute; left: 0; top: 0.4rem; width: 4.1rem;
  font-size: 0.72rem; font-variant-numeric: tabular-nums;
  color: var(--ink-2); text-align: right;
}
.c-date.undated { color: var(--thin); font-style: italic; }
.c-kind {
  display: inline-block; margin-right: 0.4rem;
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3);
}
.c-kind.k-ownership { color: var(--core); }
.c-kind.k-residence { color: var(--other); }
.c-kind.k-document  { color: var(--accent); }
.c-what { font-size: 0.85rem; }
.c-detail { font-size: 0.75rem; color: var(--ink-3); margin-top: 0.1rem; }
.c-withheld { color: var(--withheld); font-style: italic; }

/* documents */
.docs { display: grid; grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr)); gap: 0.6rem; }
.doc { font-size: 0.71rem; color: var(--ink-2); }
.doc a { display: block; border: 1px solid var(--rule-2); background: #fff; }
/* `contain`, not `cover`: a survey diagram is thin ink on a lot of white paper, and
   cropping to the centre of one shows a blank square. The whole sheet at 7rem is
   recognisable — you can see which diagram it is. */
.doc img { display: block; width: 100%; height: 7rem; object-fit: contain; background: #fff; }
.doc a:hover { border-color: var(--core); }
.doc .noimg {
  display: flex; align-items: center; justify-content: center;
  height: 7rem; padding: 0.3rem; text-align: center;
  background: repeating-linear-gradient(45deg, var(--paper), var(--paper) 6px, var(--canvas) 6px, var(--canvas) 12px);
  color: var(--ink-3); font-size: 0.66rem; line-height: 1.3;
}
.doc-cap { display: block; margin-top: 0.22rem; line-height: 1.35; }
.doc-cap code { font-family: var(--mono); font-size: 0.92em; color: var(--ink-3); }

/* neighbours */
ul.nb { list-style: none; margin: 0; padding: 0; font-size: 0.8rem; columns: 2; column-gap: 1rem; }
ul.nb li { break-inside: avoid; padding: 0.1rem 0; }
ul.nb .dir {
  display: inline-block; width: 2.1rem;
  font-size: 0.68rem; color: var(--ink-3); font-weight: 700;
}
ul.nb button {
  border: 0; background: none; padding: 0; font: inherit;
  color: var(--accent); cursor: pointer; text-align: left;
  text-decoration: underline; text-decoration-color: var(--rule-2);
}
ul.nb button:hover { text-decoration-color: var(--accent); }

/* stories */
.story { margin-bottom: 0.7rem; font-size: 0.82rem; }
.story-role {
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--core);
}
.story-title { font-family: var(--serif); font-size: 0.95rem; margin: 0.05rem 0 0.15rem; }
.story p { margin: 0; color: var(--ink-2); font-size: 0.78rem; }

/* farm <-> portion links in the panel */
.kin { margin: 0 0 0.3rem; font-size: 0.84rem; }
ul.kinlist { list-style: none; margin: 0 0 0.4rem; padding: 0; font-size: 0.84rem; }
ul.kinlist li { padding: 0.16rem 0; border-bottom: 1px dotted var(--rule); }
ul.kinlist li:last-child { border-bottom: 0; }
.kin button, ul.kinlist button {
  border: 0; background: none; padding: 0; font: inherit;
  color: var(--accent); cursor: pointer; text-align: left;
  text-decoration: underline; text-decoration-color: var(--rule-2);
}
.kin button:hover, ul.kinlist button:hover { text-decoration-color: var(--accent); }

.empty { font-size: 0.78rem; color: var(--ink-3); font-style: italic; margin: 0; }

/* ----------------------------------------------------------------- foot */
.foot {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding: 0.5rem 1.4rem;
  border-top: 1px solid var(--rule-2); background: var(--paper-2);
  font-size: 0.71rem; color: var(--ink-3);
}
.foot p { margin: 0; }

/* --------------------------------------------------------------- narrow */
@media (max-width: 1100px) {
  .layout { --list-w: 15rem; }
  .layout, .layout.panel-open { grid-template-columns: var(--list-w) minmax(0, 1fr) 0; }
  .layout.panel-open .panel {
    position: fixed; inset: auto 0 0 0; height: 62vh; z-index: 5;
    border-top: 1px solid var(--rule-2);
    box-shadow: 0 -4px 18px rgba(35, 32, 27, 0.14);
  }
}
@media (max-width: 720px) {
  body.map-view { height: auto; overflow: auto; }
  /* One column: the list stacks above the map. The explicit grid-column assignments have
     to be undone here, or `.mapwrap { grid-column: 2 }` invents a second track on a phone
     and the map lands beside the list instead of under it. Hiding the list still works -
     with every child in column 1 there is no track for anything to slide into, which is
     the whole reason those assignments exist. */
  .farmlist, .mapwrap, .panel { grid-column: 1; }
  .layout, .layout.panel-open { grid-template-columns: 1fr; }
  .farmlist { border-right: 0; border-bottom: 1px solid var(--rule-2); }
  .farmlist-scroll { max-height: 40vh; }
  .mapwrap { height: 60vh; }
  .tallies { gap: 1rem; }
}

/* ===================================================================== prose
   Story, document and person pages. A reading column, not an interface: these
   pages are meant to be read start to finish, so the measure is tight and the
   entity links are marked without shouting. */
body.doc-view { display: block; height: auto; overflow: auto; background: var(--paper); }

/* The two workhorse utilities. They were used 31 times across nine templates and
   defined NOWHERE - every "muted small" annotation on the index rows rendered at
   full size and full ink, which is why those pages read louder than they were
   written. A class the templates lean on this hard is part of the system, not an
   ad-hoc name; css_audit() now fails the build if a template uses either and this
   file stops defining it.

   EM, NOT REM. "Small" is a promise RELATIVE to the text around it: a first draft
   said 0.85rem and thereby ENLARGED the "(N)" counts inside the 0.68rem panel-h
   headers and the captions inside the 0.71rem doc cards - the two places already
   smaller than 0.85rem - inverting the one thing the class means. */
.muted { color: var(--ink-3); }
.small { font-size: 0.85em; line-height: 1.45; }

.topbar.slim {
  align-items: center; gap: 1rem;
  padding: 0.6rem 1.4rem;
}
.topbar.slim .back {
  color: var(--accent); text-decoration: none; font-size: 0.85rem;
}
.topbar.slim .back:hover { text-decoration: underline; }
.topbar.slim .crumb {
  margin: 0; font-size: 0.72rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--ink-3);
}

.prose {
  max-width: 42rem; margin: 0 auto; padding: 2rem 1.4rem 4rem;
  font-size: 1rem; line-height: 1.62;
}
.prose h1 {
  font-family: var(--serif); font-size: 1.9rem; font-weight: 600;
  line-height: 1.18; margin: 0 0 0.6rem;
}
.prose .lede {
  margin: 0 0 1.8rem; color: var(--ink-2); font-size: 1.02rem;
  padding-bottom: 1rem; border-bottom: 1px solid var(--rule);
}
.prose h2 {
  font-family: var(--serif); font-size: 1.28rem; font-weight: 600;
  margin: 2.2rem 0 0.6rem;
}
.prose h3 {
  font-family: var(--serif); font-size: 1.06rem; font-weight: 600;
  margin: 1.7rem 0 0.4rem; border: 0; padding: 0;
  text-transform: none; letter-spacing: 0; color: var(--ink);
}
.prose h2.panel-h {
  font-family: var(--sans); font-size: 0.68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-3);
  border-bottom: 1px solid var(--rule); padding-bottom: 0.3rem;
  margin: 2.6rem 0 0.8rem;
}
.prose p { margin: 0 0 1rem; }
.prose ul, .prose ol { margin: 0 0 1rem; padding-left: 1.3rem; }
.prose li { margin-bottom: 0.3rem; }
.prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2rem 0; }
.prose blockquote {
  margin: 1.2rem 0; padding: 0.7rem 1rem;
  border-left: 3px solid var(--core); background: var(--core-soft);
  font-size: 0.94rem;
}
.prose blockquote p:last-child { margin-bottom: 0; }
.prose code {
  font-family: var(--mono); font-size: 0.86em;
  background: rgba(35, 32, 27, 0.05); padding: 0.08em 0.3em; border-radius: 2px;
}
.prose .note { font-size: 0.83rem; color: var(--ink-3); margin: -0.3rem 0 1rem; }

.tablewrap { overflow-x: auto; margin: 0 0 1.3rem; }
.prose table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }
.prose th, .prose td {
  text-align: left; vertical-align: top;
  padding: 0.38rem 0.6rem 0.38rem 0; border-bottom: 1px solid var(--rule);
}
.prose th {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3); border-bottom: 1px solid var(--rule-2);
}

/* LINKS: NOTHING FALLS THROUGH TO THE BROWSER'S DEFAULT.
   Added 6 Aug 2026. Frans, reading the live site: "some hyperlinks are text colour, some
   blue. i dont like the blue." Both halves of that were real and they had different causes.
   There was no base `a` rule at all, so every link that carried no class - the farm links
   into the map, the terms link, the language switch - got the user agent's #0000EE. And
   a.e-story was the one entity link that set a COLOUR rather than following the house
   pattern of ink text with a tinted underline, so it read blue even where the rule existed.
   The palette here is warm - ochre, olive, ink - and a browser blue belongs to none of it. */
a, a:visited { color: var(--core); }
a:hover { color: var(--ink); }
/* The underline is scoped to running prose ONLY. A global one would put a line under the
   nav, the farm cards, the A-Z bar and the story list, none of which have ever had one. */
.prose a:not(.e), .research a:not(.e) { text-decoration: underline; text-underline-offset: 2px; }

/* entity references */
.prose a.e { text-decoration: none; border-bottom: 1px solid; }
a.e-person { color: var(--ink); border-bottom-color: var(--core); }
a.e-person:hover { background: var(--core-soft); }
a.e-farm { color: var(--ink); border-bottom-color: var(--other); }
a.e-farm:hover { background: var(--other-soft); }
a.e-doc { color: var(--ink); border-bottom-color: var(--accent); }
a.e-doc:hover { background: rgba(47, 93, 116, 0.1); }
a.e-story { color: var(--ink); border-bottom-color: var(--ink-3); }
a.e-story:hover { background: rgba(116, 110, 95, 0.12); }
.e-event {
  border-bottom: 1px dotted var(--ink-3); cursor: help;
}
/* A reference the publishing rule withheld, or one that does not resolve. Deliberately
   unremarkable: it must not look like a broken link, because nothing is broken. */
.e-plain { color: inherit; }

.tag-verify, .tag {
  font-family: var(--mono); font-size: 0.82em;
  background: rgba(138, 109, 59, 0.13); color: var(--withheld);
  padding: 0.05em 0.3em; border-radius: 2px; white-space: nowrap;
}
.tag { background: rgba(35, 32, 27, 0.06); color: var(--ink-3); }
.brk { color: var(--core); font-weight: 700; }

.ecard {
  position: absolute; z-index: 40; max-width: 20rem;
  padding: 0.5rem 0.7rem; background: var(--paper-2);
  border: 1px solid var(--rule-2); border-radius: 3px;
  box-shadow: 0 3px 12px rgba(35, 32, 27, 0.16);
  font-size: 0.8rem; line-height: 1.4; pointer-events: none;
}
.ecard strong { display: block; }
.ecard span { display: block; color: var(--ink-3); font-size: 0.75rem; }
.ecard .go { color: var(--accent); margin-top: 0.2rem; }

/* sources, subjects, facts */
ol.sources { padding-left: 1.2rem; font-size: 0.88rem; }
ol.sources li { margin-bottom: 0.6rem; }
ol.sources .ref, ol.sources .repo, .ref {
  display: block; font-size: 0.78rem; color: var(--ink-3);
}
ol.sources .incomplete {
  display: block; font-size: 0.78rem; color: var(--withheld);
  border-left: 2px solid var(--withheld); padding-left: 0.5rem; margin-top: 0.2rem;
}
ul.subjects { list-style: none; padding: 0; font-size: 0.9rem; }
ul.subjects li { padding: 0.2rem 0; border-bottom: 1px dotted var(--rule); }
ul.subjects .role {
  display: inline-block; min-width: 5.5rem;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3);
}
dl.facts { margin: 0 0 1.2rem; font-size: 0.9rem; }
dl.facts div {
  display: grid; grid-template-columns: 11rem 1fr; gap: 0.6rem;
  padding: 0.3rem 0; border-bottom: 1px dotted var(--rule);
}
dl.facts dt { color: var(--ink-3); font-size: 0.8rem; }
dl.facts dd { margin: 0; }
ul.kinlist.plain li { border-bottom: 1px dotted var(--rule); padding: 0.25rem 0; }
ol.chain.plain li { padding-left: 5.5rem; }

figure.scan { margin: 0 0 1.5rem; }
figure.scan img {
  display: block; width: 100%; max-height: 30rem; object-fit: contain;
  background: #fff; border: 1px solid var(--rule-2);
}
figure.scan figcaption {
  font-size: 0.76rem; color: var(--ink-3); margin-top: 0.35rem;
}

/* person page: descent, withheld slots, gaps */
.descent {
  margin: 0 0 1.6rem; padding: 0.8rem 1rem;
  background: var(--core-soft); border-left: 3px solid var(--core); border-radius: 2px;
}
.descent-head {
  margin: 0 0 0.4rem; font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.07em; color: var(--core);
}
ol.chainline { list-style: none; margin: 0; padding: 0; font-size: 0.88rem; }
ol.chainline li { display: inline; }
ol.chainline li:not(:first-child)::before {
  content: "←"; margin: 0 0.4rem; color: var(--ink-3);
}
.e-plain.withheld { color: var(--withheld); font-style: italic; }
.ref-inline { font-size: 0.78rem; color: var(--ink-3); }
ul.unknown {
  list-style: none; padding: 0; margin: 0 0 0.6rem;
  font-size: 0.88rem;
}
ul.unknown li {
  padding: 0.28rem 0 0.28rem 1.1rem; border-bottom: 1px dotted var(--rule);
  position: relative; color: var(--ink-2);
}
ul.unknown li::before {
  content: "?"; position: absolute; left: 0; top: 0.28rem;
  color: var(--withheld); font-weight: 700;
}
.c-kind.k-born, .c-kind.k-died { color: var(--ink); }
.c-kind.k-land { color: var(--core); }
.c-kind.k-witness, .c-kind.k-occasion { color: var(--other); }
.c-kind.k-married { color: var(--accent); }
ul.kinlist.plain .role {
  display: inline-block; min-width: 7.5rem;
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-3);
}

/* ------------------------------------------------ research notes + questions
   Only rendered on the private local build (config.PUBLISH_RESEARCH). Set apart
   from the sourced record above them on purpose: this is the working journal,
   with corrections and hedges in it, and it must not read like settled fact. */
.research { border-left: 2px solid var(--rule-2); padding-left: 0.9rem; margin: 0.4rem 0 0.8rem; }
.research p { margin: 0 0 0.6rem; font-size: 0.85rem; line-height: 1.55; color: var(--ink-2); }
.research p:last-child { margin-bottom: 0; }

ul.leads { list-style: none; margin: 0.3rem 0 0.9rem; padding: 0; }
ul.leads > li {
  border-left: 2px solid var(--withheld);
  padding: 0.1rem 0 0.35rem 0.9rem; margin-bottom: 0.8rem;
}
ul.leads strong { font-size: 0.9rem; }
ul.leads p { margin: 0.25rem 0 0; font-size: 0.83rem; line-height: 1.5; color: var(--ink-2); }
ul.leads .settle .k {
  display: inline-block; font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--ink-3); margin-right: 0.4em;
}

/* Colour-mode switch. Sits under the view control; the pressed state has to be
   obvious because the legend changes with it and a reader must know which
   colouring they are looking at. */
.colourctl button[aria-pressed="true"] {
  background: var(--core-soft); color: var(--core); font-weight: 600;
}
.legend .swatch { border: 1px solid var(--rule-2); }

/* ------------------------------------------------------ cross-links in prose
   Two kinds, and the difference is the point. `.xl-id` is a RESOLVED reference:
   the prose named the id, we linked it, there is nothing to be unsure about.
   `.xl` is an INFERRED one: we recognised a name and are stating how confident
   that reading is. H corroborated, M name-only, L ambiguous and NOT linked. */
.xl-id { font-family: var(--mono); font-size: 0.86em; color: var(--accent); }
/* A FamilySearch id leaves the site, so it is marked as an outward link rather
   than styled like our own ids - the reader should know before they click. */
.xl-fs { font-family: var(--mono); font-size: 0.86em; color: var(--withheld); }
.xl-fs::after { content: "\2197"; font-size: 0.85em; margin-left: 0.1em; opacity: 0.7; }

.xl {
  display: inline-block; margin-left: 0.2em;
  min-width: 1.05em; padding: 0 0.18em;
  border-radius: 2px; vertical-align: super;
  font-family: var(--sans); font-size: 0.6em; font-weight: 700;
  line-height: 1.5; text-align: center; text-decoration: none;
}
.xl-h { background: rgba(47, 93, 116, 0.16); color: var(--accent); }
.xl-m { background: rgba(138, 109, 59, 0.18); color: var(--withheld); }
.xl-l { background: rgba(138, 131, 113, 0.16); color: var(--ink-3); cursor: help; }
a.xl:hover { text-decoration: none; filter: brightness(0.9); }

/* Breaks — the questions the graph asks of itself.
   Styled DELIBERATELY UNLIKE `ul.leads`. A hand-written lead has a researcher's
   prediction behind it and gets the solid rule; a break was found by a machine
   reading the model and nobody has looked at it yet, so it gets a dotted one. The
   severity band on the left is the only colour: red-brown for a missing deed,
   ochre for a hole with a known place to look, grey for what we have not started. */
ul.breaks { list-style: none; margin: 0.3rem 0 1rem; padding: 0; }
ul.breaks > li {
  border-left: 3px dotted var(--thin);
  padding: 0.15rem 0 0.4rem 0.9rem; margin-bottom: 1rem;
}
ul.breaks > li.sev-1 { border-left-color: var(--core); }
ul.breaks > li.sev-2 { border-left-color: var(--withheld); }
ul.breaks strong { font-size: 0.9rem; }
ul.breaks p { margin: 0.25rem 0 0; font-size: 0.83rem; line-height: 1.5; color: var(--ink-2); }
ul.breaks .settle .k,
ul.breaks .blinks { color: var(--ink-3); }
ul.breaks .settle .k {
  display: inline-block; font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.07em; margin-right: 0.4em;
}
ul.breaks .blinks { font-size: 0.78rem; margin-top: 0.35rem; }

.prose.wide { max-width: 54rem; }
.sev-note {
  font-size: 0.83rem; color: var(--ink-3); margin: -0.3rem 0 0.9rem;
  max-width: 40rem;
}
table.checks {
  border-collapse: collapse; width: 100%; margin: 1.2rem 0 1.6rem;
  font-size: 0.84rem;
}
table.checks th, table.checks td {
  text-align: left; padding: 0.34rem 0.6rem 0.34rem 0;
  border-bottom: 1px solid var(--rule);
}
table.checks th {
  font-family: var(--sans); font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--ink-3); font-weight: 600;
}
table.checks td { color: var(--ink-2); }
table.checks .n { text-align: right; font-variant-numeric: tabular-nums; padding-right: 0; }

/* The index's own pointer at the questions page. */
.asks {
  margin: 0.6rem 0 0; padding: 0.55rem 0.7rem;
  border: 1px dotted var(--rule-2); border-radius: 3px;
  font-size: 0.78rem; line-height: 1.5; color: var(--ink-2);
}
.asks strong { font-weight: 600; }

/* A source PDF, shown inline. Taller than a raster scan's 30rem cap on purpose:
   these are estate inventories running to several sheets, and the whole point of
   embedding one is that a reader can work through it without leaving the page. */
details.scan.pdf { margin: 0 0 1.4rem; }
details.scan.pdf > summary {
  cursor: pointer; display: inline-block;
  font-family: var(--sans); font-size: 0.72rem; letter-spacing: 0.04em;
  color: var(--accent); padding: 0.1rem 0;
}
details.scan.pdf object {
  display: block; width: 100%; height: 44rem; margin-top: 0.5rem;
  background: #fff; border: 1px solid var(--rule-2);
}
details.scan.pdf object p { padding: 1.5rem; font-size: 0.85rem; }

/* The always-visible line above it: the scan is one click away whether or not
   anyone opens the inline viewer. */
.scanbar {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.6rem;
  margin: 0 0 0.3rem; padding: 0.5rem 0.7rem;
  background: var(--paper-2); border: 1px solid var(--rule); border-radius: 3px;
}
.scan-open { font-weight: 600; font-size: 0.86rem; }
.scan-meta { font-size: 0.74rem; color: var(--ink-3); }

/* An estate, item by item, room by room. Set narrow and dense - it is a list a
   clerk wrote standing up, and it reads best looking like one. */
.estate-of { font-family: var(--serif); font-size: 1.05rem; margin-bottom: 0.9rem; }
table.inv {
  border-collapse: collapse; width: 100%; max-width: 30rem;
  margin: 0.5rem 0 1.4rem; font-size: 0.85rem;
}
table.inv caption {
  caption-side: top; text-align: left;
  font-family: var(--sans); font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--ink-3); font-weight: 600;
  padding-bottom: 0.3rem; border-bottom: 1px solid var(--rule-2); margin-bottom: 0.2rem;
}
table.inv td { padding: 0.2rem 0.5rem 0.2rem 0; vertical-align: baseline; }
table.inv tr + tr td { border-top: 1px solid var(--rule); }
table.inv .q {
  width: 2.2rem; text-align: right; padding-right: 0.7rem;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}
table.inv .v {
  text-align: right; white-space: nowrap; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
table.inv .gloss {
  display: block; font-size: 0.78rem; color: var(--ink-3); margin-top: 0.05rem;
}

/* A document transcribed in full. Set to read like the sheet it came off: the
   clerk's own blockquoted Dutch a shade smaller, the summary table tight, and no
   attempt to make 1802 orthography look like modern prose. */
.transcription { font-size: 0.92rem; margin-bottom: 1.6rem; }
.transcription h2 { font-size: 1.05rem; margin: 1.6rem 0 0.5rem; }
.transcription h3 { font-size: 0.92rem; margin: 1.2rem 0 0.4rem; }
.transcription table {
  border-collapse: collapse; width: 100%; margin: 0.6rem 0 1.2rem; font-size: 0.83rem;
}
.transcription th, .transcription td {
  text-align: left; padding: 0.24rem 0.6rem 0.24rem 0;
  border-bottom: 1px solid var(--rule); vertical-align: baseline;
}
.transcription th { color: var(--ink-3); font-weight: 600; white-space: nowrap; }
.transcription blockquote {
  margin: 0.7rem 0; padding: 0.1rem 0 0.1rem 0.9rem;
  border-left: 2px solid var(--rule-2);
  font-size: 0.86rem; color: var(--ink-2);
}
.transcription blockquote em { font-style: italic; }

/* "Why this farm is here" — the section that ties a parcel back to the anchor rule.
   Given the core tint because it is the answer to the question the site exists to
   ask, not another fact about the farm. */
.why-head {
  margin: 0.3rem 0 0.8rem; padding: 0.6rem 0.8rem;
  background: var(--core-soft); border-left: 3px solid var(--core); border-radius: 2px;
  font-size: 0.9rem; line-height: 1.55;
}
.why-foot { font-size: 0.78rem; color: var(--ink-3); margin: 0.4rem 0 1rem; }

/* The kinship path, one step per line. Numbered because the ORDER is the argument -
   this person, then their spouse, then that spouse's father, and so on to a name the
   project is anchored on. */
ol.trace {
  list-style: none; counter-reset: t; margin: 0 0 0.3rem; padding: 0;
  font-size: 0.86rem;
}
ol.trace li {
  position: relative; padding: 0.16rem 0 0.16rem 1.5rem; margin: 0;
  border-left: 1px solid var(--rule-2); margin-left: 0.35rem;
}
ol.trace li:first-child { border-left-color: var(--core); }
ol.trace li::before {
  content: ""; position: absolute; left: -3.5px; top: 0.62rem;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--rule-2);
}
ol.trace li:first-child::before { background: var(--core); }
ol.trace li:last-child::before { background: var(--accent); }
ol.trace .how {
  display: inline-block; min-width: 5.4rem;
  font-family: var(--sans); font-size: 0.66rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-3);
}

ul.ties { list-style: none; margin: 0.2rem 0 1.2rem; padding: 0; font-size: 0.83rem; }
ul.ties li {
  padding: 0.22rem 0; border-bottom: 1px solid var(--rule); color: var(--ink-2);
}
ul.ties li:last-child { border-bottom: 0; }
.tie-kind {
  display: inline-block; min-width: 4.6rem;
  font-family: var(--sans); font-size: 0.62rem; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--ink-3);
}
.tie-links { color: var(--ink-3); }
.tie-more { color: var(--ink-3); font-style: italic; }

/* Siblings and children, in birth order with the year in its own column. The year is
   not decoration: birth order is how the vernoemingspatroon is read, and how the
   reused-name trap shows itself where a child who died young has their name given
   again to a later sibling. An undated person shows an em dash and sorts last, so a
   missing date never masquerades as an early birth. */
ul.kinyears { list-style: none; margin: 0; padding: 0; }
ul.kinyears li {
  display: flex; gap: 0.6rem; align-items: baseline;
  padding: 0.12rem 0; line-height: 1.45;
}
ul.kinyears .ky {
  flex: none; min-width: 3.1rem; text-align: right;
  font-variant-numeric: tabular-nums; font-size: 0.78rem; color: var(--ink-3);
}
/* What a witness is to us besides a signature — the ground they hold, how often they
   signed. Set quieter than the occasion line above it: the occasion is transcribed
   from a record, this is derived from the model, and they should not read alike. */
ul.kinlist .also {
  margin-top: 0.1rem; font-size: 0.76rem; color: var(--ink-3);
}

/* The stories index. Set as a reading list rather than a table: each entry has to say
   enough that somebody can choose, which a title alone does not. */
ul.storylist { list-style: none; margin: 0.3rem 0 1.6rem; padding: 0; }
ul.storylist > li {
  border-left: 2px solid var(--core); padding: 0.15rem 0 0.5rem 0.9rem;
  margin-bottom: 1.1rem;
}
ul.storylist .s-title { font-family: var(--serif); font-size: 1.02rem; font-weight: 600; }
ul.storylist .s-meta {
  display: block; font-size: 0.72rem; color: var(--ink-3); margin-top: 0.1rem;
  font-variant-numeric: tabular-nums;
}
ul.storylist p { margin: 0.3rem 0 0; font-size: 0.84rem; line-height: 1.5; color: var(--ink-2); }
ul.storylist .s-about { font-size: 0.76rem; color: var(--ink-3); }

/* ---- the lanes view -------------------------------------------------------
   A row per family, a bar per span of ground, a vertical connector per marriage.
   Generated as SVG at build time: no library, no network, and it prints. */
svg.lanes { width: 100%; height: auto; margin: 0.6rem 0 0.4rem; display: block; }
svg.lanes .grid      { stroke: var(--rule); stroke-width: 1; }
svg.lanes .gyr       { fill: var(--ink-3); font: 10px var(--sans); text-anchor: middle; }
svg.lanes .lane-name { fill: var(--ink); font: 600 11.5px var(--sans); text-anchor: end; }
svg.lanes .lane-rule { stroke: var(--rule); stroke-width: 1; }
svg.lanes .bar       { opacity: 0.92; }
/* An INFERRED end is faded and uncapped: the eye must not read it as a recorded date. */
svg.lanes .bar.inferred { opacity: 0.4; }
svg.lanes .bar.stub     { opacity: 0.85; }
svg.lanes .tie      { stroke: var(--ink-2); stroke-width: 1.4; stroke-dasharray: 3 2; }
svg.lanes .tie-dot  { fill: var(--ink); }
svg.lanes .tie-yr   { fill: var(--ink-2); font: 600 9.5px var(--sans); text-anchor: middle; }

/* WITNESS TIES. A different KIND of link, so a different colour and a finer dash
   rather than a heavier version of the marriage line: rust against the marriage's
   ink, thinner, and a wider dash so the two never read as the same thing at a
   glance. Hidden until the toggle is ticked. */
svg.lanes .wit-ties        { display: none; }
svg.lanes.show-wit .wit-ties { display: inline; }
svg.lanes .wtie     { stroke: var(--core); stroke-width: 1.1; stroke-dasharray: 1.5 3;
                      stroke-opacity: .85; }
svg.lanes .wtie-dot { fill: var(--core); fill-opacity: .9; }
.lanes-toggle label { cursor: pointer; user-select: none; }
.lanes-toggle input { margin-right: .4em; vertical-align: baseline; }
.lanes-toggle .ref  { display: block; margin-top: .25em; }

/* NO DARK THEME, ON PURPOSE. This is a paper-coloured document site and every
   colour in :root is chosen against --paper. A witness-tie override added on
   1 Aug 2026 was the ONLY prefers-color-scheme rule in the whole stylesheet, which
   meant a dark-preferring device got one recoloured line on an otherwise light
   page. Removed rather than half-built: a real dark theme is a design decision
   about the whole palette, not a patch. */
.lanes-note, .density-note { max-width: 46rem; }

/* the decade density strip */
.density { display: flex; align-items: flex-end; gap: 2px; height: 66px; margin: 0.8rem 0 0.3rem; }
.density .dbar {
  flex: 1; display: flex; flex-direction: column; justify-content: flex-end;
  align-items: center; height: 100%; text-decoration: none;
}
.density .dbar i { display: block; width: 100%; background: var(--core-soft); border-top: 2px solid var(--core); }
.density .dbar:hover i { background: var(--core); }
.density .dbar span { font-size: 0.58rem; color: var(--ink-3); margin-top: 2px; font-variant-numeric: tabular-nums; }

/* the chronology, in four scale columns — read ACROSS a decade */
.tl-filters { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 1rem 0 0.6rem; }
.tl-filters button {
  border: 1px solid var(--rule-2); background: var(--paper-2); border-radius: 3px;
  padding: 0.22rem 0.55rem; font: 0.74rem var(--sans); color: var(--ink-2); cursor: pointer;
}
.tl-filters button.on { background: var(--core); border-color: var(--core); color: #fff; }
.tl-filters button span { opacity: 0.65; margin-left: 0.25em; }

.tl-head, .tl-cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.9rem; }
.tl-head {
  position: sticky; top: 0; z-index: 2; background: var(--paper);
  padding: 0.3rem 0; border-bottom: 1px solid var(--rule-2); margin-bottom: 0.2rem;
}
.tl-head span {
  font-family: var(--sans); font-size: 0.62rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--ink-3); font-weight: 600;
}
/* One colour per scale, used on the heading, the rule and the entry marker, so a column
   is identifiable after scrolling past its header. */
.tl-head .th-world    { color: var(--withheld); }
.tl-head .th-country  { color: var(--accent); }
.tl-head .th-district { color: var(--other); }
.tl-head .th-family   { color: var(--core); }

.tl-decade { margin: 0 0 1.1rem; }
.dec-h {
  font-family: var(--sans); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em;
  color: var(--ink); margin: 1rem 0 0.35rem; padding-bottom: 0.15rem;
  border-bottom: 1px solid var(--rule);
}
.dec-h .dec-count { color: var(--ink-3); font-weight: 400; margin-left: 0.4em; }

ol.tl-col { list-style: none; margin: 0; padding: 0 0 0 0.55rem; border-left: 2px solid var(--rule); }
ol.tl-col[hidden] { display: none; }
ol.tl-col.tl-world    { border-left-color: var(--withheld); }
ol.tl-col.tl-country  { border-left-color: var(--accent); }
ol.tl-col.tl-district { border-left-color: var(--other); }
ol.tl-col.tl-family   { border-left-color: var(--core); }
ol.tl-col > li { margin: 0 0 0.5rem; font-size: 0.79rem; line-height: 1.45; }
ol.tl-col .tl-date {
  display: block; font-size: 0.68rem; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
ol.tl-col .tl-what { color: var(--ink-2); }
ol.tl-col .tl-links { display: block; font-size: 0.72rem; color: var(--ink-3); margin-top: 0.1rem; }

@media (max-width: 900px) {
  .tl-head { display: none; }
  .tl-cols { grid-template-columns: 1fr; }
  ol.tl-col::before {
    content: attr(data-band); display: block; font-family: var(--sans);
    font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--ink-3); margin-bottom: 0.2rem;
  }
}
.tl-filters { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 1.2rem 0 0.8rem; }
.tl-filters button {
  border: 1px solid var(--rule-2); background: var(--paper-2); border-radius: 3px;
  padding: 0.22rem 0.55rem; font: 0.74rem var(--sans); color: var(--ink-2); cursor: pointer;
}
.tl-filters button.on { background: var(--core); border-color: var(--core); color: #fff; }
.tl-filters button span { opacity: 0.65; margin-left: 0.25em; }
.tl-decade .dec-count { color: var(--ink-3); font-weight: 400; font-size: 0.72rem; margin-left: 0.4em; }


/* ---- foldable farm list -------------------------------------------------- */
/* The sidebar carries 127 parcels. With every portion always visible it ran to
   several screens and the farms at the bottom were unreachable in practice. The
   farm stays one click; the chevron is a second, separate control so selecting a
   farm on the map and opening its portions never compete for the same click. */
.farm-row { display: flex; align-items: stretch; gap: 0; }
.farm-row > .parcel, .farm-row > button.parcel { flex: 1 1 auto; min-width: 0; }
.fold {
  flex: 0 0 auto; width: 2.1rem; border: 0; background: none; cursor: pointer;
  color: var(--ink-3); font: inherit; line-height: 1; position: relative;
  border-left: 1px solid var(--rule);
}
.fold:hover { background: var(--tint); color: var(--ink); }
.fold::before {
  content: "›"; display: block; font-size: 1.15rem; transition: transform .14s ease;
  transform: rotate(90deg);
}
.fold[aria-expanded="true"]::before { transform: rotate(270deg); }
.fold .fold-n {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: .18rem;
  font-size: .62rem; letter-spacing: .02em; opacity: .75;
}
.farm-item .portions[hidden] { display: none; }


/* ============================================================ iPhone and small
   Checked on a real 375px viewport, not assumed. The page was overflowing by 15px
   on EVERY element, which reads as a dozen broken rules and was one: `body.map-view`
   is a grid whose single column is auto-sized, so the widest child sets the page
   width. That child was the topbar, whose min-content is 390px because `.tallies`
   is a five-item flex row that would not wrap (348px) plus 42px of header padding.
   Everything else was just inheriting it.

   Fixed at the source AND guarded: the column is pinned to minmax(0, 1fr) so no
   future wide child can push the whole page sideways again, which is the failure
   that is invisible on a laptop and obvious on a phone. */
body.map-view { grid-template-columns: minmax(0, 1fr); }
body.map-view > * { min-width: 0; }

@media (max-width: 720px) {
  .topbar { padding: 0.8rem 1rem; }
  .tallies { flex-wrap: wrap; gap: 0.5rem 1.1rem; }
  .tallies div { text-align: left; }
  .tallies dd { font-size: 1.05rem; }
  .brand h1 { font-size: 1.5rem; }
  .brand .tagline { font-size: 0.85rem; }
}

@media (max-width: 420px) {
  /* Touch targets. The fold chevron was 2.1rem wide, which is fine with a mouse
     and under the 44px Apple asks for with a thumb. */
  .fold { width: 2.75rem; }
  .farm-row > button.parcel, .farm-row > .parcel { padding-right: 0.35rem; }
  .prose { padding: 1.25rem 1rem 3rem; }
  .tl-filters { flex-wrap: wrap; }
}

/* Wide content must scroll INSIDE itself rather than widening the page. Tables and
   the lanes SVG are the two that can exceed a phone. */
@media (max-width: 720px) {
  .prose table { display: block; overflow-x: auto; max-width: 100%; }
  svg.lanes { max-width: 100%; height: auto; }
  .density { overflow-x: auto; }
}


/* ---- sources index ------------------------------------------------------- */
/* One source per row: what it is, where it lives, and a way in. The reference is
   the part a reader needs to ORDER an archival document, so it is given room
   rather than tucked into a tooltip. */
.srclist { list-style: none; margin: 0.6rem 0 1.6rem; padding: 0; }
.srclist li {
  padding: 0.55rem 0; border-bottom: 1px solid var(--rule);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.7rem;
}
.srclist li:last-child { border-bottom: 0; }
/* EVERY CHILD MUST BE ABLE TO SHRINK. A flex item will not go below its
   min-content width unless told to, and `flex: 0 0 auto` on the detail line - which
   carries a full repository string like 'FamilySearch, "South Africa, Dutch Reformed
   Church Registers (Cape Town Archives), 1660-1994"' - pushed the page 554px wider
   than the viewport. Same failure as the topbar on a phone, one page later. */
.srclist li > * { min-width: 0; overflow-wrap: anywhere; }
.s-title { flex: 1 1 20rem; font-weight: 600; text-decoration: none; color: var(--ink); }
.s-title:hover { text-decoration: underline; }
.srclist .c-detail { flex: 1 1 14rem; color: var(--ink-3); font-size: 0.78rem; }
.s-ref {
  flex: 1 1 100%; color: var(--ink-2); font-size: 0.8rem; font-family: var(--mono);
  line-height: 1.45; overflow-wrap: anywhere;
}
.s-out { flex: 0 1 auto; font-size: 0.78rem; color: var(--accent); }
.s-used { flex: 0 1 auto; font-size: 0.74rem; color: var(--ink-3); font-style: italic; }
@media (max-width: 720px) {
  .srclist li { gap: 0.2rem 0.5rem; }
  .s-title { flex: 1 1 100%; }
}


/* ------------------------------------------------- the about page's model tables */
/* Two tables of counts, so a reader can see the shape of the model without reading the
   repository. Scrolls inside itself on a narrow screen rather than pushing the page wide. */
.modeltab { width: 100%; border-collapse: collapse; margin: 0.9rem 0 1.6rem; font-size: 0.86rem; }
.modeltab th, .modeltab td { text-align: left; padding: 0.42rem 0.7rem 0.42rem 0;
  border-bottom: 1px solid var(--rule-2); vertical-align: baseline; }
.modeltab thead th { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--ink-3); font-weight: 600; border-bottom-width: 2px; }
.modeltab .n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
  font-family: var(--serif); padding-right: 1.1rem; }
.modeltab td:last-child { color: var(--ink-2); }
@media (max-width: 720px) {
  .modeltab { display: block; overflow-x: auto; }
}


/* ------------------------------------------------- the parent-farm crumb */
/* A portion's first line: what farm it is a piece of, and a way to get there. Above the
   title on a farm page, directly under it in the map panel - either way it is the first
   thing after the name, because on a portion it is the most useful link on the page. */
.p-parent {
  margin: 0 0 0.35rem; font-size: 0.78rem; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-3);
}
.p-parent a,
.p-parent button {
  font: inherit; text-transform: none; letter-spacing: normal;
  font-size: 0.92rem; color: var(--accent);
  background: none; border: 0; padding: 0; cursor: pointer;
  text-decoration: none; border-bottom: 1px solid currentColor;
}
.p-parent a:hover, .p-parent button:hover { color: var(--ink); }
.panel .p-parent { margin: -0.15rem 0 0.6rem; }


/* ------------------------------------------------ the language note */
/* Shown on the Afrikaans build only, wherever research notes appear, because the notes themselves are deliberately kept in English . Quieter than a normal caveat: it is an explanation, not a warning about the record. */
.caveat.lang-note {
  border-left-color: var(--rule-2);
  background: transparent;
  color: var(--ink-3);
  font-size: 0.82rem;
}


/* --------------------------------------------- superseded research notes */
/* A research journal appends, so a question ends up ABOVE its own answer and reads as though it is still open . Nothing is deleted or reordered - the reasoning is the point of these notes - but an entry whose lead was later closed is dimmed and labelled, so the eye goes to the current position. */
.research .rnote.superseded { color: var(--ink-3); }
.rnote-flag {
  display: inline-block; margin-right: 0.4rem; padding: 0.05rem 0.35rem;
  font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.07em;
  border: 1px solid var(--rule-2); border-radius: 2px; color: var(--ink-3);
  vertical-align: 0.1em;
}


/* ---- THE RIBBON: the site's navigation, added 3 Aug 2026 ------------------ [FAMILY] The pane on the left is cluttered. maybe the timeline, stories, what it rests on, what is missing - those items go to a ribbon on top? They did, and it turned out to be the navigation this site never had - every other page's entire nav was a back link. Sticky, because on a long prose page the way out should not scroll away. */
.ribbon { display:flex; align-items:center; gap:1.7rem; height:2.6rem; padding:0 1.4rem;
          border-top:1px solid var(--rule-2); border-bottom:1px solid var(--rule-2);
          background:var(--paper-2); position:sticky; top:0; z-index:6; }
.ribbon .rib { font-size:.84rem; color:var(--ink-2); text-decoration:none;
               padding:.55rem 0; border-bottom:2px solid transparent; white-space:nowrap; }
.ribbon .rib:hover { color:var(--core); }
.ribbon .rib.on { color:var(--core); border-bottom-color:var(--core); }
.ribbon .rib-last { margin-left:auto; }
.ribbon .rib-n { font-size:.66rem; color:var(--ink-3); margin-left:.28em;
                 vertical-align:super; line-height:0; }
.ribbon .rib:focus-visible { outline:2px solid var(--core); outline-offset:3px; }

/* ---- THE MAP PAGE PUTS ITS NAVIGATION IN THE MASTHEAD ------------------ [FAMILY] Quite a bit of wasted space. The brand block is about 450px wide, the masthead is the width of the window, and the ribbon sat on a row of its own below it - so a desktop showed two thirds of a bar holding nothing while the map lost 2.6rem of height. The ribbon moves INTO .topbar on this page only (templates/map.html). Here it stops being a bar of its own: no borders, no background, no sticky, no fixed height - the masthead already provides all four. It is right-aligned so both ends of the header are anchored and the space between them reads as breathing room rather than a hole. THE MAP PAGE IS THE ONLY ONE THAT GETS THIS, because it is the only page whose height is scarce; everything else scrolls, and there a stacked masthead is right. The menus are unaffected: .rib-menu is position:relative, so every popup is placed against its own summary and not against the ribbon. */
.map-view .topbar { flex-wrap:nowrap; align-items:center; gap:1.1rem; }
.map-view .brand { flex:0 1 auto; min-width:0; }
.map-view .ribbon { position:static; border:0; background:none; height:auto;
                    padding:0; flex:1 1 auto; min-width:0;
                    justify-content:flex-end; gap:1.25rem; }
/* rib-last earns its auto margin in a full-width bar; here it would tear About away
   from the group and re-open the gap this change closed. */
.map-view .ribbon .rib-last { margin-left:0; }

/* Below this width the two cannot share a line without the ribbon crushing the
   tagline, so it drops to its own row again - inside the header, which already has
   the rule beneath it. 1100px is a rung of the existing ladder, not a new one: the
   stylesheet audit rejects an invented breakpoint, and it rejected 1180 here. */
@media (max-width: 1100px) {
  /* justify-content:space-between is inherited from .topbar and is right when the bar
     holds mark, brand and switch. Once the ribbon wraps to a second line the first line
     holds three items again and space-between pushed the TITLE into the middle of the
     window - measured at 1024px, x=629 of 1024. So the row is packed left and only the
     switch is pushed away. */
  .map-view .topbar { flex-wrap:wrap; align-items:flex-end; gap:.9rem 1.5rem;
                      justify-content:flex-start; }
  /* The ribbon takes a whole line, so in DOM order the switch lands on a THIRD line
     (measured: y=150, below a ribbon at y=99). Ordering it ahead of the ribbon puts it
     back on the title's line where it belongs, without reordering the markup - which
     must stay mark, brand, nav, switch for a reader without CSS. */
  .map-view .topbar .langswitch { margin-left:auto; order:1; }
  .map-view .ribbon { flex:1 0 100%; justify-content:flex-start; order:2; }
  .map-view .ribbon .rib-last { margin-left:auto; }
}

/* The mark sits beside the title and is the home link on every page. */
.topbar .mark { display:flex; align-items:center; color:var(--ink);
                margin-right:.85rem; flex:none; }
.topbar .mark:focus-visible { outline:2px solid var(--core); outline-offset:3px; }

/* ---- THE FARM LIST GETS ITS HEIGHT BACK ---------------------------------
   The grid was written for three rows and had seven children, so the only track that
   could shrink - the list - was the only one that did. Measured before this change: 176px
   at 1440x900, 36px at 1280x760, and NINE PIXELS at 1024x700, which is no rows at all and
   a filter that matched farms it could not show. overflow-y here also fixes a real clip:
   the pane was overflowing its container by 156px with overflow:visible, inside a
   body{overflow:hidden}, so the last line was unreachable by any means. */
.farmlist { overflow-y:auto; }
.farmlist-scroll { min-height:12rem; }

/* The caveat is a fact about the list, not a destination, so it stayed - as one line. */
details.farmlist-foot { padding:.45rem .8rem; border-top:1px solid var(--rule-2);
                        font-size:.72rem; color:var(--ink-3); }
details.farmlist-foot summary { cursor:pointer; }
details.farmlist-foot p { margin:.4rem 0 0; }

@media (max-width: 720px) {
  /* Six items do not earn a hamburger, and hiding the only navigation behind a tap is
     how the original problem started. Scroll it instead, with a fade that tells a thumb
     there is more to the right. */
  .ribbon { overflow-x:auto; flex-wrap:nowrap; gap:1.1rem; height:44px;
            scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch;
            -webkit-mask-image:linear-gradient(to right,#000 88%,transparent);
            mask-image:linear-gradient(to right,#000 88%,transparent); }
  .ribbon .rib { min-height:44px; display:flex; align-items:center; scroll-snap-align:start; }
  .ribbon .rib-last { margin-left:0; }
  .topbar .mark svg { width:28px; height:28px; }
}

/* PERSELE and OPGEMEET came off the masthead strip and onto the list itself, because
   they are facts ABOUT THE LIST. A headline number that describes what you are looking
   at belongs on it; the other three tallies went to the ribbon counts and the About page. */
.farmlist-count { margin:.35rem 0 0; font-size:.7rem; color:var(--ink-3); }

@media (max-width: 720px) {
  /* THE MAP WAS 1,102px DOWN THE PAGE ON A PHONE - 1.3 screens of scrolling before the
     thing the site is for. Removing the four prose blocks took it to 742. Collapsing the
     masthead to one row takes it to roughly 330: identity is the title and the mark, and
     the two subtitle lines are already the meta description and the opening of the About
     page, so nothing is lost that a reader cannot reach. */
  .topbar .brand .tagline,
  .topbar .brand > p:not(.tagline) { display:none; }
  .topbar .brand h1 { font-size:1.15rem; margin:0; }
  .topbar { align-items:center; padding-top:.5rem; padding-bottom:.5rem; }
}
@media (max-width: 720px) {
  /* One row: mark, title, AF/EN. The language switch was dropping to a line of its own. */
  .topbar { display:flex; flex-wrap:nowrap; gap:.6rem; }
  .topbar .brand { flex:1 1 auto; min-width:0; }
  .topbar .langswitch { flex:none; align-self:center; }
}

/* ---- THE TERMS GATE, added 3 Aug 2026 ------------------------------------
   Covers the page on a first visit and takes focus. The page underneath still renders,
   deliberately: terms nobody can read are not terms, and a family history that hides from
   the relative it was written for has failed at its only job. */
html.gated, html.gated body { overflow: hidden; }
.termsgate { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center;
             padding: 1.2rem; background: rgba(28,25,22,.55);
             -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.termsgate[hidden] { display: none; }
.termsgate-box { background: var(--paper-2); border: 1px solid var(--rule);
                 border-radius: 3px; max-width: 34rem; width: 100%;
                 padding: 1.5rem 1.6rem 1.3rem; box-shadow: 0 8px 40px rgba(0,0,0,.28);
                 max-height: 88vh; overflow-y: auto; }
.termsgate-box h2 { margin: 0 0 .7rem; font-size: 1.15rem; }
.termsgate-box p { margin: 0 0 .7rem; font-size: .87rem; line-height: 1.55; color: var(--ink-2); }
.termsgate-links { margin: 0 0 1rem !important; }
.termsgate-links a { color: var(--core); }
.termsgate-ok { display: block; width: 100%; min-height: 44px; cursor: pointer;
                background: var(--core); color: #fff; border: 0; border-radius: 3px;
                font: inherit; font-size: .92rem; padding: .6rem 1rem; }
.termsgate-ok:hover { filter: brightness(1.06); }
.termsgate-ok:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.foot a { color: var(--core); }

/* THE MAP CONTROL PANEL: one box, three labelled sections. * * [FAMILY] The hierarchy of view is not clear. Three unlabelled groups held * ten buttons doing three different kinds of thing - recolour, toggle a layer, move the * camera - with nothing on screen saying which was which. The headings ARE the fix, so * they have to read as headings and not as more buttons: small, spaced, unclickable. */
/* STACKED, not in a row. `.viewctl` is `display: flex`, which is right for a strip of
   two buttons and wrong for this: the three sections lined up side by side and the panel
   grew to a third of the map's width, so grouping the buttons had made the thing WIDER
   than the three loose groups it replaced. Column, and a width the labels fit inside. */
.mapctl { display: block; padding: .1rem 0 .25rem; max-width: 12rem;
  max-height: 48vh; overflow-y: auto; overscroll-behavior: contain; }
.mapctl button { border-right: 0; }
.mapctl .ctl-sec { padding: .35rem .5rem .1rem; }
.mapctl .ctl-sec + .ctl-sec { border-top: 1px solid var(--rule-2); margin-top: .2rem; }
.mapctl .ctl-lab {
  margin: 0 0 .25rem; font-size: .62rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .09em; color: var(--ink-3); user-select: none;
}
.mapctl .ctl-row { display: flex; flex-wrap: wrap; gap: .15rem; max-width: 13rem; }
.mapctl .ctl-row button {
  font-size: .74rem; padding: .25rem .5rem; min-height: 28px; cursor: pointer;
  background: var(--paper-2); border: 1px solid var(--rule-2); border-radius: 2px;
  color: var(--ink-2);
}
.mapctl .ctl-row button:hover { border-color: var(--ink-3); }

/* The layer switches. On = the modern cadastre; off = the farms as granted. */
.mapctl .sub { display: block; width: 100%; text-align: left;
               font-size: .74rem; padding: .3rem .4rem; min-height: 28px; cursor: pointer;
               background: none; border: 0; color: var(--ink-3); }
.mapctl .sub::before { content: "☐ "; }
.mapctl .sub.on { color: var(--core); }
.mapctl .sub.on::before { content: "☑ "; }
.mapctl .sub:focus-visible { outline: 2px solid var(--core); outline-offset: -2px; }

/* The grave scopes, indented under the switch that gives them meaning. Before this they
   sat as peers of Subdivisions and did nothing at all until Burial grounds had been found
   and pressed - two of the ten buttons were dead controls, which is the clearest single
   case of the hierarchy Frans could not read. Now they are visibly subordinate, and
   disabled rather than merely inert while the layer is off. */
.mapctl .ctl-sub { margin: 0 0 0 1.1rem; border-left: 1px solid var(--rule-2);
                   padding-left: .3rem; }
.mapctl .ctl-sub .sub { font-size: .7rem; min-height: 24px; padding: .2rem .4rem; }
/* The age-cut chips: INLINE, unlike every other .mapctl button. As block rows the
   ten cuts stacked a screen tall and ate the panel the compaction pass had just
   reclaimed. Chips wrap; each label owns its row. */
.mapctl .cuts { display: flex; flex-wrap: wrap; gap: .1rem .2rem; align-items: baseline; }
.mapctl .cuts .ctl-lab { flex-basis: 100%; margin: .25rem 0 .05rem; padding: 0; }
.mapctl .cuts .sub { display: inline-block; width: auto; min-height: 0;
  padding: 0 .4rem; border: 1px solid var(--rule-2); border-radius: 999px;
  white-space: nowrap; }
.mapctl .cuts .sub::before { content: none; }
.mapctl .cuts .sub.on { background: var(--core); border-color: var(--core); color: #fff; }
/* display:inline against the GLOBAL `.ref { display:block }` at the sources
   list - a bare selector in that list reaches every .ref on the site, and it was
   stacking each chip's count under its number. The global stays: cemetery tables
   and lead rows lean on the block behaviour. */
.mapctl .cuts .sub .ref { display: inline; opacity: .65; font-size: .92em; color: inherit; }
.mapctl .ctl-sub.off { opacity: .45; }
.mapctl .ctl-sub .sub[disabled] { cursor: default; }
.mapctl .ctl-sub .sub::before { content: "○ "; }
.mapctl .ctl-sub .sub.on::before { content: "● "; }

/* The count beside each family in the key, and the line under it saying what the key
   is ordered by. Both are subordinate to the swatch and must not compete with it. */
.legend .lg-n { color: var(--ink-3); font-size: .85em; }
/* THE HALF-CENTURY JUMP BAR on the timeline. Sticky: the page is 405 entries long and a
   jump bar you have to scroll back to the top to reach is not navigation. */
.tl-halves {
  position: sticky; top: 0; z-index: 3;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem;
  margin: 1.2rem 0 .6rem; padding: .45rem .2rem;
  background: var(--paper); border-bottom: 1px solid var(--rule-2);
}
.tl-halves-lab {
  font-size: .66rem; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-3); margin-right: .2rem;
}
.tl-halves a {
  display: inline-block; padding: .12rem .45rem; border-radius: .2rem;
  border: 1px solid var(--rule-2); text-decoration: none; font-size: .8rem;
  white-space: nowrap;
}
.tl-halves a:hover { border-color: currentColor; }
.tl-halves-n { color: var(--ink-3); font-size: .85em; }
/* The decade a jump lands on must not hide under the sticky bar above it. */
.tl-decade { scroll-margin-top: 3.2rem; }

.legend .lg-more { list-style: none; }
/* `.legend ul li` sets a display, which outranks the browser's own [hidden] rule - so the
   folded rows rendered exactly as if there were no fold at all. */
.legend ul li[hidden] { display: none; }
.legend .lg-fold {
  font: inherit; font-size: .92em; cursor: pointer; padding: 0;
  background: none; border: 0; color: var(--core);
  text-decoration: underline; text-underline-offset: 2px;
}
.legend .lg-fold::before { content: "+ "; }
.legend .lg-fold[aria-expanded="true"]::before { content: "\2212 "; }
.legend .lg-foot { column-span: all; margin-top: .4rem; padding-top: .35rem;
                   border-top: 1px solid var(--rule-2); color: var(--ink-3);
                   font-size: .78em; line-height: 1.4; list-style: none; }

/* The surname jump bar on people.html. It shipped with no rule at all, so a hundred links ran together into one unreadable block - n gemors...". Spacing and a separator are the whole fix; the grouping bug behind it is fixed in render.py. */
.azbar { margin: 1rem 0 1.5rem; line-height: 2; }
.azbar a { display: inline-block; margin: 0 .15rem; padding: .05rem .4rem;
           border: 1px solid rgba(0,0,0,.12); border-radius: .2rem;
           text-decoration: none; white-space: nowrap; }
.azbar a:hover { border-color: currentColor; }
ul.plain.cols { columns: 3 16rem; column-gap: 2rem; }
ul.plain.cols li { break-inside: avoid; margin: 0 0 .15rem; }

/* THE OPENING TEXT, WHICH COULD NEVER BE SEEN.
 *
 * Found 4 Aug 2026 by a cold-eye review of the live site. #panel-empty holds the only
 * paragraph that tells a stranger what this site is - the Klaarstroom basin, the families,
 * which farms have a written history. It was hidden in BOTH states: the rule above hides it
 * while the panel is closed, and map.js hides it when the panel opens. Dead either way, and
 * still in English on the Afrikaans site, which is presumably why nobody noticed.
 *
 * map.js was always right - it unhides the block and drops `panel-open` when the panel
 * closes. What was missing is a column for it to sit in. On a wide screen the closed layout
 * now keeps a panel track, so the reader lands on an orientation column that becomes the
 * farm panel on the first click. Narrow screens are untouched: there the panel is a bottom
 * sheet and an intro would cover the map. */
@media (min-width: 1100px) {
  .layout { grid-template-columns: var(--list-w) minmax(0, 1fr) 24rem; }
  .layout:not(.panel-open) .panel { border-left: 1px solid var(--rule); }
  .layout:not(.panel-open) .panel-empty { display: block; }
  /* DISMISSED, AND THE COLUMN GOES BACK TO THE MAP. Hiding the text alone would have left a 24rem strip of empty paper, which is the same waste without the words. Only while nothing is selected: once a farm is open the panel is the point of the page. */
  .layout.intro-off:not(.panel-open) { grid-template-columns: var(--list-w) minmax(0, 1fr) 0; }
  .layout.intro-off:not(.panel-open) .panel { display: none; }
}

/* THE FARM LIST, COLLAPSED. [FAMILY] Hide/unhide button for farm scroll panel on left. Applies at every width - on a phone the list and the intro between them were most of the screen, which is where the complaint started. */
.layout.list-off { --list-w: 0; }
.layout.list-off .farmlist { display: none; }

/* Hiding the text itself is NOT inside the media query. The column-reclaim above only
   applies from 1100px up, but a narrow screen shows the panel as a bottom sheet and the
   intro is just as unwanted there - without this rule, dismissing it did nothing at all
   on a phone. */
.layout.intro-off:not(.panel-open) .panel-empty { display: none; }

/* The close control on the intro, and the way back to it. */
.panel-empty { position: relative; }
.intro-x {
  position: absolute; top: .1rem; right: 0; line-height: 1;
  background: none; border: 0; cursor: pointer;
  font-size: 1.5rem; padding: .1rem .35rem; color: var(--ink-3);
}
.intro-x:hover { color: var(--ink); }
.intro-x:focus-visible { outline: 2px solid var(--core); outline-offset: 2px; }
.intro-back {
  font: inherit; font-size: .82em; cursor: pointer; margin-left: .4rem;
  background: none; border: 0; padding: 0; color: var(--core);
  text-decoration: underline; text-underline-offset: 2px;
}
.intro-back:hover { text-decoration-thickness: 2px; }

/* The surname selector in the cemetery view's legend. Chips rather than a list: the question is "show me the Botes" and a chip answers it in one click. Added - "a block where surnames can be selected". */
.legend li.surnames { display: block; margin-top: .5rem; line-height: 1.9;
  /* The legend list is `columns: 2`; chips inside it stacked one per narrow column. */
  column-span: all; }
.legend button.sn {
  font: inherit; font-size: .74em; cursor: pointer; white-space: nowrap;
  background: var(--paper); border: 1px solid var(--rule-2); border-radius: 999px;
  padding: 0 .4rem; margin: 0 .08rem .1rem 0; color: var(--ink);
}
.legend button.sn:hover { border-color: var(--ink-3); }
.legend button.sn.on { background: var(--core); border-color: var(--core); color: #fff; }
.legend button.sn.on .ref { color: var(--paper); }
.legend button.sn.clear { background: transparent; border-style: dashed; }
.legend button.sn .ref { display: inline; font-size: .9em; opacity: .65; color: inherit; }

/* The five kin-line kinds, drawn AS THE MAP DRAWS THEM - these hexes are data
   colours mirroring KIN_LAYERS in map.js, not palette drift: a key that does not
   match the canvas is worse than no key. Solid = a line of descent (and its
   siblings), dashed brown = blood off the line, dashed blue = joined through a
   marriage (affinal must never read as blood), faint dotted = families only. */
.legend .swatch.kin-direct   { background: none; border: 0; border-top: 3px solid #3d3428; height: 0; }
.legend .swatch.kin-linesib  { background: none; border: 0; border-top: 2px solid #55503f; height: 0; }
.legend .swatch.kin-blood    { background: none; border: 0; border-top: 3px dashed #4a3f2f; height: 0; }
.legend .swatch.kin-marriage { background: none; border: 0; border-top: 3px dashed #2f5d74; height: 0; }
.legend .swatch.kin-fam      { background: none; border: 0; border-top: 2px dotted #6b5f4a; height: 0; }

/* The leads pages, split off questions.html on 7 Aug 2026. Collapsed by default: the
   title and the question are what you scan, the rest is what you read once you've
   chosen one. */
.leadlist { list-style: none; padding: 0; margin: 1rem 0; }
.leadlist li.lead { border-top: 1px solid var(--rule); padding: .35rem 0; }
.leadlist summary { cursor: pointer; list-style: none; display: flex; gap: .5rem;
  align-items: baseline; padding: .2rem 0; }
.leadlist summary::-webkit-details-marker { display: none; }
.leadlist summary::before { content: "▸"; color: var(--ink-3); flex: 0 0 auto; }
.leadlist details[open] > summary::before { content: "▾"; }
.leadlist summary strong { font-weight: 600; }
.leadlist details > *:not(summary) { margin: .35rem 0 .35rem 1.4rem; }
.leadlist .pri { flex: 0 0 auto; font-size: .78em; min-width: 1.35em; text-align: center;
  border-radius: 3px; padding: 0 .25em; background: var(--canvas); color: var(--ink-2); }
.leadlist .pri.p1 { background: var(--core); color: #fff; }
.leadlist .pri.done { background: var(--ok); color: #fff; }
.leadlist .lead-q { font-size: .95em; }
.leadfilter { display: flex; gap: .8rem; align-items: center; flex-wrap: wrap;
  margin: .8rem 0; font-size: .9em; }
.leadfilter input[type=search] { flex: 1 1 16rem; padding: .25rem .4rem;
  border: 1px solid var(--rule); border-radius: 3px; font: inherit; }

/* ------------------------------------------------------ story filters A rail, not a toolbar: three short lists read faster stacked than wrapped, and the page's own headings stay the structure. */
.storylayout { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 2rem;
               align-items: start; }
.facets { position: sticky; top: 1rem; font-size: .82rem; }
.facet-head { display: flex; align-items: baseline; justify-content: space-between;
              gap: .5rem; border-bottom: 1px solid var(--rule-2); padding-bottom: .3rem; }
.facet-head h2 { margin: 0; font-size: .68rem; text-transform: uppercase;
                 letter-spacing: .09em; color: var(--ink-3); font-family: var(--sans); }
#facet-clear { font: inherit; font-size: .78rem; cursor: pointer; padding: 0;
               background: none; border: 0; color: var(--core);
               text-decoration: underline; text-underline-offset: 2px; }
.facet-count { margin: .35rem 0 0; color: var(--ink-3); font-size: .78rem; }
.facet { margin: .9rem 0 0; }
.facet h3 { margin: 0 0 .25rem; font-size: .72rem; font-family: var(--sans);
            color: var(--ink-2); font-weight: 600; }
.facet ul { list-style: none; margin: 0; padding: 0; }
.facet li { margin: 0 0 .1rem; }
.facet label { display: flex; gap: .35rem; align-items: baseline; cursor: pointer;
               line-height: 1.35; }
.facet label:hover { color: var(--core); }
.facet input { margin: 0; flex: none; position: relative; top: .15rem; }
.facet-n { color: var(--ink-3); font-size: .85em; margin-left: auto; }
.facet-none { color: var(--ink-3); font-style: italic; }
.storygroup[hidden] { display: none; }
.storylist > li[hidden] { display: none; }

@media (max-width: 720px) {
  /* The rail goes above the list rather than beside it; sticky would pin a filter block
     over the results it is filtering. */
  .storylayout { grid-template-columns: 1fr; gap: 1.2rem; }
  .facets { position: static; }
  .facet ul { display: flex; flex-wrap: wrap; gap: .1rem .9rem; }
}
.facet-more { list-style: none; }
.facet-fold { font: inherit; font-size: .78rem; cursor: pointer; padding: 0;
              background: none; border: 0; color: var(--core);
              text-decoration: underline; text-underline-offset: 2px; }
.facet-fold::before { content: "+ "; }
.facet-fold[aria-expanded="true"]::before { content: "\2212 "; }
.facet li[hidden] { display: none; }

/* ------------------------------------------------- alive-at filter (people) [FAMILY] A option in the people tab to filter people alive at a particular date, and their ages shown. */
.aliveat { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
           margin: 1.2rem 0 .4rem; }
.aliveat label { font-size: .8rem; color: var(--ink-2); }
.aliveat input { font: inherit; font-size: .9rem; width: 6rem; padding: .25rem .4rem;
                 border: 1px solid var(--rule-2); border-radius: 2px;
                 background: var(--paper-2); color: var(--ink); }
.aliveat input:focus-visible { outline: 2px solid var(--core); outline-offset: 1px; }
#alive-clear { font: inherit; font-size: .8rem; cursor: pointer; padding: 0;
               background: none; border: 0; color: var(--core);
               text-decoration: underline; text-underline-offset: 2px; }
.aliveat-n { color: var(--ink-3); font-size: .82rem; }
.aliveat-note { max-width: 44rem; margin: 0 0 1rem; }
.age { color: var(--core); font-size: .82em; white-space: nowrap; }
ul.plain.cols > li[hidden] { display: none; }

/* ------------------------------------------------------- kinship diagram */
.netbar { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
          margin: 1.2rem 0 .5rem; }
.netbar label { font-size: .8rem; color: var(--ink-2); }
.netbar input[type=range] { flex: 1 1 16rem; max-width: 30rem; accent-color: var(--core); }
#net-year-out { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 5.5rem; }
#net-all { font: inherit; font-size: .8rem; cursor: pointer; padding: .2rem .5rem;
           background: var(--paper-2); border: 1px solid var(--rule-2); border-radius: 2px;
           color: var(--ink-2); }
#net-all:hover { border-color: var(--ink-3); }
.netbar-n { color: var(--ink-3); font-size: .82rem; }

.netwrap { position: relative; border: 1px solid var(--rule-2); border-radius: 3px;
           background: var(--paper-2); overflow: hidden; }
/* A square-ish stage. Tall enough to be worth panning around, capped against the viewport
   so the controls above it stay on screen while the slider is being dragged. */
/* `touch-action: pan-y` rather than `none`. Blocking every touch gesture would have made
   the page unscrollable on a phone the moment a finger landed on the diagram - and Frans
   had just told me the map was "pain on phone version too", so shipping that again would
   be answering the complaint by repeating it. Vertical drags scroll the page; the diagram
   keeps horizontal drags for panning. */
#net { display: block; width: 100%; height: min(70vh, 42rem); cursor: grab;
       touch-action: pan-y; }
.netkey { position: absolute; left: .6rem; bottom: .6rem; display: flex; flex-wrap: wrap;
          gap: .2rem .9rem; align-items: center;
          background: rgba(255,253,248,.92); border: 1px solid var(--rule-2);
          border-radius: 3px; padding: .3rem .5rem; font-size: .72rem; color: var(--ink-2); }
.netkey .ek { display: inline-block; width: 1.1rem; height: 0; vertical-align: middle;
              margin-right: .3rem; }
.netkey .ek-p { border-top: 2px solid rgba(90,70,45,.7); }
.netkey .ek-u { border-top: 2px solid rgba(150,60,30,.85); }
.nethint { color: var(--ink-3); }
.nettip { position: absolute; pointer-events: none; z-index: 4;
          background: var(--ink); color: #fff; font-size: .74rem; padding: .2rem .45rem;
          border-radius: 2px; white-space: nowrap; }
.nettip[hidden] { display: none; }

/* A family's first appearance in the record. Marked, because it is a different kind of fact from the transfers around it - the moment a name enters this history rather than another movement inside it. */
.tl-first { border-left: 2px solid var(--core); padding-left: .45rem; }
.tl-first .tl-what { font-weight: 600; }

/* ------------------------------------------------- the ribbon on a phone
   Measured on the live site at 375px: the ribbon is 831px wide, so FIVE of its ten items
   sat off-screen - Places, Sources, Gaps in the record, Questions and About - with nothing
   on screen to say they were there. Half the site was unreachable on a phone unless you
   happened to drag the strip sideways.

   WRAPPING BEATS SCROLLING here. A horizontal scroller hides its own overflow by design;
   two short rows show everything at once and cost about 2rem of height. The site's own
   navigation should never be the thing a reader cannot find. */
@media (max-width: 900px) {
  .ribbon { flex-wrap: wrap; height: auto; gap: .25rem .9rem;
            padding: .35rem .9rem; row-gap: 0; }
  /* `margin-left:auto` pushes About to the far right, which on a wrapped ribbon strands it
     alone on the second row with a band of empty paper beside it. */
  .ribbon .rib-last { margin-left: 0; }
  .ribbon .rib { padding: .35rem 0; }
}
/* Gaps and Questions share a slot. Two pages, one place on the ribbon: they answer the
   same kind of question from two directions, and side by side they were spending two of
   the ten slots on a ribbon that did not fit a phone. */
.ribbon .rib-group { display: inline-flex; align-items: center; gap: .3rem; }
.ribbon .rib-sep { color: var(--rule); font-size: .8rem; user-select: none; }

/* The ribbon's wrapped rows sit tight together. Ten items over three rows is worth about
   2.5rem of a phone screen, not 9rem. */
@media (max-width: 900px) {
  .ribbon { gap: 0 .85rem; line-height: 1.15; }
  .ribbon .rib { padding: .28rem 0; font-size: .8rem; }
}

/* THE KEY STARTS CLOSED ON A PHONE. Open, it and the control panel between them covered
   most of a 375px map - the legend hiding the thing it describes, which is the fault its
   own comment in map.html was written about, arriving from the other direction. It is one
   tap to open and the summary says so. */
@media (max-width: 720px) {
  .legend { max-width: calc(100% - 1.6rem); max-height: 42%; }
  .legend:not([open]) > ul, .legend:not([open]) .legend-more { display: none; }
}

/* ------------------------------------------- how complete the record is
   A bar per row, because a ratio is the point: 82 of 1,445 stones identified reads very
   differently as a number and as a length. Kept to the accent colour and a hairline track
   so the table stays a table rather than becoming a dashboard. */
table.comp { width: 100%; border-collapse: collapse; margin: 1.4rem 0; }
table.comp th { text-align: left; font-size: .7rem; text-transform: uppercase;
                letter-spacing: .08em; color: var(--ink-3); font-weight: 600;
                border-bottom: 1px solid var(--rule-2); padding: .3rem .4rem; }
table.comp th.num, table.comp td.num { text-align: right; white-space: nowrap; }
table.comp td { border-bottom: 1px solid var(--rule-2); padding: .55rem .4rem;
                vertical-align: top; }
table.comp td p { margin: .2rem 0 0; max-width: 34rem; }
.comp-n { font-size: 1.15rem; font-variant-numeric: tabular-nums; color: var(--ink); }
.comp-bar { display: block; width: 7rem; height: 4px; margin: .35rem 0 0 auto;
            background: var(--rule-2); border-radius: 2px; overflow: hidden; }
.comp-bar i { display: block; height: 100%; background: var(--core); }
@media (max-width: 560px) {
  table.comp td p { font-size: .78rem; }
  .comp-bar { width: 4.5rem; }
}

/* ------------------------------------------------------------- site search
   An overlay rather than a box in the ribbon: the ribbon is already two rows on a phone,
   and a field wide enough to be useful would have made it three. */
.rib-search { font: inherit; font-size: .84rem; cursor: pointer; background: none;
              border: 0; border-bottom: 2px solid transparent; color: var(--ink-2);
              padding: .55rem 0; white-space: nowrap; margin-left: auto; }
.rib-search:hover { color: var(--core); }
.rib-search kbd { font: inherit; font-size: .72em; color: var(--ink-3);
                  border: 1px solid var(--rule-2); border-radius: 3px;
                  padding: 0 .25em; margin-left: .15em; }
@media (max-width: 900px) { .rib-search { margin-left: 0; } .rib-search kbd { display: none; } }

.ss { position: fixed; inset: 0; z-index: 40; background: rgba(35,32,27,.34);
      display: flex; align-items: flex-start; justify-content: center;
      padding: 4rem 1rem 1rem; }
.ss[hidden] { display: none; }
body.ss-open { overflow: hidden; }
.ss-box { width: min(38rem, 100%); max-height: min(70vh, 34rem); display: flex;
          flex-direction: column; background: var(--paper-2);
          border: 1px solid var(--rule-2); border-radius: 4px;
          box-shadow: 0 12px 40px rgba(35,32,27,.28); overflow: hidden; }
.ss-head { display: flex; align-items: center; gap: .3rem; padding: .5rem .6rem;
           border-bottom: 1px solid var(--rule-2); }
.ss-head input { flex: 1; font: inherit; font-size: 1rem; padding: .4rem .5rem;
                 border: 0; background: none; color: var(--ink); min-width: 0; }
.ss-head input:focus { outline: none; }
.ss-close { font-size: 1.5rem; line-height: 1; background: none; border: 0; cursor: pointer;
            color: var(--ink-3); padding: .1rem .4rem; }
.ss-close:hover { color: var(--ink); }
.ss-status { margin: 0; padding: .35rem .9rem; font-size: .76rem; color: var(--ink-3);
             border-bottom: 1px solid var(--rule-2); }
.ss-status:empty { display: none; }
.ss-results { list-style: none; margin: 0; padding: .2rem 0; overflow-y: auto; flex: 1; }
.ss-results a { display: flex; align-items: baseline; gap: .5rem; padding: .35rem .9rem;
                text-decoration: none; color: var(--ink); }
.ss-results a:hover, .ss-results a.on { background: var(--core-soft); }
/* The kind is a fixed column so the eye can run down it and skip whole categories. */
.ss-kind { flex: none; width: 6.6rem; font-size: .68rem; text-transform: uppercase;
           letter-spacing: .05em; color: var(--ink-3); }
.ss-l { flex: 1; min-width: 0; }
.ss-s { flex: none; color: var(--ink-3); font-size: .8rem; font-variant-numeric: tabular-nums; }
@media (max-width: 560px) {
  .ss { padding: 1rem .5rem; }
  .ss-box { max-height: 86vh; }
  .ss-kind { width: 5rem; font-size: .62rem; }
}

/* ------------------------------------------------------------------ descent
   An indented outline. Every rule here exists to keep 500 rows readable: a hairline down
   each generation so the eye can run back up to a parent, and the counts kept quiet so the
   names stay the thing you read. */
.dt-lines { display: flex; flex-wrap: wrap; gap: .35rem; margin: 1.1rem 0 .6rem; }
.dt-lines button { font: inherit; font-size: .8rem; cursor: pointer; text-align: left;
                   background: var(--paper-2); border: 1px solid var(--rule-2);
                   border-radius: 3px; padding: .3rem .55rem; color: var(--ink-2);
                   display: flex; align-items: baseline; gap: .4rem; }
.dt-lines button:hover { border-color: var(--ink-3); }
.dt-lines button[aria-pressed="true"] { background: var(--core-soft); border-color: var(--core);
                                        color: var(--core); }
.dt-lines .dt-from { color: var(--ink-3); font-size: .92em; }
.dt-lines .dt-n { color: var(--ink-3); font-variant-numeric: tabular-nums; }

.dt-bar { display: flex; flex-wrap: wrap; align-items: baseline; gap: .7rem;
          margin: .2rem 0 .8rem; padding-bottom: .4rem;
          border-bottom: 1px solid var(--rule-2); }
.dt-status { color: var(--ink-3); font-size: .82rem; margin-right: auto; }
.dt-bar button { font: inherit; font-size: .8rem; cursor: pointer; padding: 0;
                 background: none; border: 0; color: var(--core);
                 text-decoration: underline; text-underline-offset: 2px; }

ul.dt { list-style: none; margin: 0; padding: 0; }
ul.dt ul.dt { margin-left: .55rem; padding-left: .75rem;
              border-left: 1px solid var(--rule-2); }
.dt-i > ul { display: none; }
.dt-i.open > ul { display: block; }
.dt-r { display: flex; align-items: baseline; gap: .4rem; padding: .12rem 0;
        line-height: 1.35; }
.dt-r a { text-decoration: none; }
.dt-r a:hover { text-decoration: underline; }
/* The toggle carries its own child count, so the number of children is known before the
   fold is opened and a childless person is visibly a leaf rather than an empty fold. */
.dt-t { flex: none; font: inherit; font-size: .68rem; cursor: pointer; min-width: 1.5rem;
        background: var(--paper-2); border: 1px solid var(--rule-2); border-radius: 3px;
        color: var(--ink-3); padding: 0 .2rem; }
.dt-t:hover { border-color: var(--core); color: var(--core); }
.dt-t::before { content: "+"; margin-right: .12em; }
.dt-i.open > .dt-r > .dt-t::before { content: "\2212"; }
.dt-leaf { flex: none; width: 1.5rem; height: 1px; }
.dt-l { color: var(--ink-3); font-size: .8rem; font-variant-numeric: tabular-nums;
        white-space: nowrap; }
.dt-c { color: var(--ink-3); font-size: .74rem; }
@media (max-width: 560px) {
  .dt-c { display: none; }
  ul.dt ul.dt { margin-left: .2rem; padding-left: .5rem; }
}
ol.scoperule { margin: 1rem 0; padding-left: 1.3rem; }
ol.scoperule li { margin: 0 0 .7rem; max-width: 44rem; }
ol.scoperule em { font-style: normal; color: var(--ink); }
/* The Connections selection bar. . */
.netsel { margin: .1rem 0 .5rem; padding: .35rem .55rem; font-size: .84rem;
          background: var(--core-soft); border-radius: 3px;
          display: flex; flex-wrap: wrap; align-items: baseline; gap: .55rem; }
.netsel[hidden] { display: none; }
.net-selN { color: var(--ink-3); font-size: .88em; margin-right: auto; }
.netsel a { color: var(--core); }
#net-clear { font: inherit; font-size: .82em; cursor: pointer; padding: 0; background: none;
             border: 0; color: var(--ink-3); text-decoration: underline;
             text-underline-offset: 2px; }
#net-clear:hover { color: var(--ink); }
/* Founding lines - arrival before 1850 - lead the picker and carry the arrival year.
   Emphasis and not a filter: the later arrivals are the graves work and both of Frans's
   grandparents' sides, and cutting them would empty half the project. */
.dt-lines .dt-arr { font-variant-numeric: tabular-nums; color: var(--ink-3);
                    font-size: .9em; flex: none; }
.dt-lines .dt-founding { border-left: 3px solid var(--core); }
.dt-lines .dt-founding .dt-arr { color: var(--core); font-weight: 600; }

/* The research tail of a story, lifted out of the narrative (render.STORY_LEADS_HEADINGS).
   Quieter than the prose it follows: the story has ended, and this is the working note. */
.leadspanel {
  margin: 2.5rem 0 0; padding: 1rem 1.2rem 0.4rem;
  background: rgba(35, 32, 27, 0.035);
  border-left: 2px solid var(--rule-2);
  border-radius: 2px;
  font-size: 0.94em; color: var(--ink-2);
}
.leadspanel h2 { font-size: 1.02rem; margin-top: 1.1rem; color: var(--ink-2); }
.leadspanel h2:first-child { margin-top: 0; }


/* The two doors in the intro panel. Deliberately quiet: they sit inside a panel that is
   already dismissible, and the map is the third door. */
.doors { list-style: none; margin: 0.7rem 0; padding: 0; }
.doors li { margin: 0 0 0.35rem; }
.doors a { text-decoration: none; font-size: 0.9rem; color: var(--ink-2); }
.doors b { color: var(--core); }

/* THE RESEARCH END OF THE RIBBON. Not a menu and not hidden - the open questions and the
   archive references are why this site can be trusted. It is set apart and labelled so a
   first-time reader can see it is a different kind of thing and skip past it, while a
   researcher still sees all four at once. */
.rib-research { display: inline-flex; align-items: baseline; gap: 0.15rem;
  margin-left: auto; padding-left: 0.9rem; border-left: 1px solid var(--rule-2); }
.rib-label { font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3); margin-right: 0.15rem; }
@media (max-width: 900px) {
  /* LET IT WRAP. As an unbreakable inline-flex the group forced its own ribbon row and still
     clipped "How complete" off the right edge at 375px - and an extra nav row is exactly the
     vertical space the masthead rules were written to save (the map was 1,102px down this
     page before that work). Wrapping packs it back among the others. */
  .rib-research { margin-left: 0; border-left: 0; padding-left: 0; flex-wrap: wrap; }
  /* AND THE LABEL GOES. It is what separates discovery from evidence on a wide screen; on a phone the ribbon already wraps into rows and the label bought nothing but an extra one. */
  .rib-label { display: none; }
}

/* ------------------------------------------------- stories on the chronology
   A story's own row - the account, not another dated fact. Serif and italic, the
   voice the story pages themselves are set in, so the doorway looks like where it
   leads. */
.tl-story .tl-what a { font-family: var(--serif); font-style: italic; }
.tl-story .tl-what a::before { content: "› "; color: var(--core); font-style: normal; }

/* ------------------------------------------------- the burial-ground drill-down
   Chips, the same shape as the surname chips in the map legend, because they answer
   the same kind of question in one click: "show me the oldest grounds". */
.cemfilter { display: flex; flex-wrap: wrap; gap: 0.3rem 0.55rem; align-items: baseline;
  margin: 0.8rem 0 0.2rem; }
.cemfilter button { font: inherit; font-size: 0.82em; cursor: pointer;
  background: var(--paper); border: 1px solid var(--rule-2); border-radius: 999px;
  padding: 0.05rem 0.55rem; color: var(--ink); }
.cemfilter button:hover { border-color: var(--ink-3); }
.cemfilter button[aria-pressed="true"] { background: var(--core); border-color: var(--core);
  color: #fff; }
.cemfilter button span { opacity: 0.65; font-size: 0.9em; }

/* ------------------------------------------------- the index lists
   The idiom of places.html, documents.html, events.html, cemeteries.html and
   roads.html. It rode on bare <ul> defaults until its fifth use tripped
   css_audit() - the author's own gate, on the author. A name this common is part
   of the system, and the browser bullets it inherited were noise on rows that
   already open with a link. */
.placelist { list-style: none; padding: 0; margin: 0.6rem 0 1.2rem; }
.placelist > li { margin: 0 0 0.5rem; }

/* ------------------------------------------------- the roads in
   One polyline per family line over bare paper - the basemap extract covers the
   Koup and these roads start in the winelands and the Hantam, so no basemap at
   all beats one that ends mid-journey. */
#roadmap { height: 26rem; margin: 1.2rem 0 1.6rem; border: 1px solid var(--rule-2);
  border-radius: 3px; background: var(--canvas); }
.road-swatch { display: inline-block; width: 1.1em; height: 0.35em; border-radius: 2px;
  vertical-align: middle; margin-right: 0.3em; }
.road-dot { display: block; width: 9px; height: 9px; border-radius: 50%;
  border: 1.5px solid var(--paper-2); box-shadow: 0 0 0 1px rgba(35, 32, 27, 0.25); }
.road-lab { font: 0.62rem/1.2 var(--sans); color: var(--ink-2);
  background: rgba(255, 253, 248, 0.85); padding: 0 0.25em; border-radius: 2px;
  white-space: nowrap; pointer-events: none; }
.roadline { margin-top: 1.4rem; }

/* ================================================================== print
   A history gets printed - a person page for an album, a farm page to take to an
   archive - and until 13 Aug 2026 printing sent the whole interface with it. The
   chrome goes; the record stays. Backgrounds are left to the browser (they strip
   them by default, which suits paper), and the reading measure opens up because a
   printed page has its own margins. Deliberately small: the content was designed
   quiet enough that print needs no second design. */
@media print {
  .ribbon, .langswitch, .topbar .back, .termsgate, .ss, .foot,
  .rib-search, .leadfilter, .azbar { display: none !important; }
  body.doc-view { background: #fff; }
  .prose { max-width: none; padding: 0; }
  .prose a { color: inherit; }
}

/* ---------------------------------------------------------------- levels
   The Klaarstroom levels (LEVELS.md). Four cumulative settings; the root carries
   data-level and rows carry data-lv. A row with NO data-lv is always shown - the
   build could not level it, and hiding it would be a claim we cannot support.

   THIS IS A VIEW AND NEVER A GATE: it hides list rows, nothing more. Every page is
   reachable by its URL at every setting. */
[data-level="1"] [data-lv="2"],
[data-level="1"] [data-lv="3"],
[data-level="1"] [data-lv="4"],
[data-level="2"] [data-lv="3"],
[data-level="2"] [data-lv="4"],
[data-level="3"] [data-lv="4"] { display: none; }

/* Ribbon menus. <details> so they work without script; this only styles them. */
.rib-menu { position: relative; }
.rib-menu > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}
.rib-menu > summary::-webkit-details-marker { display: none; }
.rib-menu > summary::after {
  content: "\25be";
  font-size: .8em;
  padding-left: .3em;
  opacity: .55;
}
.rib-menu[open] > summary::after { opacity: 1; }
.rib-pop {
  position: absolute;
  z-index: 40;
  top: 100%;
  left: 0;
  min-width: 12em;
  max-width: min(24em, 92vw);
  display: flex;
  flex-direction: column;
  gap: .1em;
  padding: .35em;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 3px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .13);
}
/* The last menus open leftward so they cannot push the page sideways. */
.rib-last .rib-pop, .rib-levels .rib-pop { left: auto; right: 0; }
.rib-pop > .rib { display: block; text-align: left; white-space: normal; }
.rib-pop-wide { min-width: 21em; }
.rib-pop-note {
  margin: .1em .3em .45em;
  font-size: .82em;
  color: var(--ink-3);
  max-width: 30em;
}
.rib-lv-now {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.rib-lv {
  display: grid;
  grid-template-columns: 1.5em 1fr;
  gap: 0 .5em;
  text-align: left;
  padding: .3em .35em;
  cursor: pointer;
}
.rib-lv-n {
  grid-row: span 2;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  opacity: .6;
}
.rib-lv-t { grid-column: 2; font-weight: 600; }
.rib-lv-d {
  grid-column: 2;
  font-size: .82em;
  color: var(--ink-3);
  line-height: 1.35;
}
.rib-lv.on { background: var(--tint); }
.rib-lv.on .rib-lv-n { opacity: 1; }
.lv-empty::after {
  content: attr(data-lv-empty);
  display: block;
  color: var(--ink-3);
  font-size: .9em;
  padding: .4em 0;
}
.lv-note {
  background: var(--tint);
  border-left: 3px solid var(--ink-3);
  padding: .5em .7em;
  margin: .6em 0;
  font-size: .9em;
}

/* ------------------------------------------------- what-is-here popup Right-click or long-press on the map. The COORDINATES are the content and the Google link is furniture over them . The value sits in a readonly input because selecting text inside a popup on a phone is otherwise a fight - it selects itself on focus and on tap. */
.coordpop {
  display: flex;
  flex-direction: column;
  gap: .35em;
  font-family: var(--sans);
}
.coordpop-h {
  font-size: .78em;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.coordpop-v {
  font-family: var(--mono);
  font-size: .9em;
  width: 100%;
  padding: .3em .4em;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: var(--paper-2);
  color: var(--ink);
}
.coordpop-a { font-size: .9em; }

/* ------------------------------------------------- the grant day, 1840/1841
   Two years, two kinds of mark. 1840 is points because no farm here had a
   surveyed boundary before the grants; 1841 is ground because after the survey
   it did. The three dot colours are the three states a reader must be able to
   tell apart: a named holder, on record but holder unknown, and no evidence. */
#grantmap {
  height: min(62vh, 560px);
  border: 1px solid var(--rule);
  border-radius: 3px;
  margin: .6em 0;
}
.grantbar {
  display: flex;
  align-items: center;
  gap: .4em;
  margin: 1em 0 .2em;
}
.gyr {
  font-family: var(--sans);
  font-size: 1em;
  font-variant-numeric: tabular-nums;
  padding: .25em .8em;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: var(--paper-2);
  color: var(--ink-2);
  cursor: pointer;
}
.gyr.on {
  background: var(--tint);
  color: var(--ink);
  font-weight: 600;
  border-color: var(--ink-3);
}
.grantbar-n {
  font-size: .85em;
  color: var(--ink-3);
  padding-left: .4em;
}
.glegend {
  list-style: none;
  padding: 0;
  margin: .3em 0 1.4em;
  display: flex;
  flex-wrap: wrap;
  gap: .2em 1.2em;
  font-size: .85em;
  color: var(--ink-2);
}
.glegend li { display: flex; align-items: center; gap: .4em; }
.gk {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex: none;
  display: inline-block;
}
.gk-held { background: var(--core); }
.gk-unknown { background: var(--other); }
.gk-absent { background: var(--rule-2); }
.gdot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 0 0 2px rgba(236, 231, 220, .9);
}
.gdot-held { background: var(--core); }
.gdot-unknown { background: var(--other); }
.gp-t { font-size: .85em; color: var(--ink-3); }
.gtab { width: 100%; border-collapse: collapse; font-size: .92em; }
.gtab th, .gtab td {
  text-align: left;
  padding: .3em .5em .3em 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
/* Towns on the grant-day map. Solid once the record can date them; hollow with the
   year when they are still to come. */
.gtown {
  display: flex; align-items: center; gap: .3rem;
  transform: translateX(4px);
  font-size: .72rem; color: var(--ink); white-space: nowrap;
  text-shadow: 0 0 3px var(--canvas), 0 0 3px var(--canvas), 0 0 6px var(--canvas);
  pointer-events: none;
}
.gtown i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ink); flex: none;
  box-shadow: 0 0 0 2px rgba(236, 231, 220, .9);
}
.gtown-later { color: var(--ink-3); font-style: italic; }
.gtown-later i { background: transparent; border: 1.5px solid var(--ink-3); }
.gtown em { font-style: normal; opacity: .8; }
