/* Famhisto — "The Open Archive" design system. See docs/design/design-system.md */

/* ---------- Fonts (self-hosted variable WOFF2) ---------- */
@font-face {
  font-family: 'Fraunces';
  src: url('/fonts/fraunces-var.woff2') format('woff2-variations');
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Source Serif 4';
  src: url('/fonts/source-serif-var.woff2') format('woff2-variations');
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
}
/* Wedding-poster typography (OFL — free to self-host). Cormorant Garamond is a
   variable font; one file covers the 400–600 weights the renderer uses. */
@font-face {
  font-family: 'Cormorant Garamond';
  src: url('/fonts/cormorant-garamond.woff2') format('woff2-variations');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Pinyon Script';
  src: url('/fonts/pinyon-script.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  --parchment: #f6f1e7;
  --parchment-deep: #efe7d6;
  --card: #fffdf7;
  --ink: #21251f;
  --ink-soft: #4d5246;
  --ink-faint: #8a8674;
  --library-green: #1e3d2f;
  --library-green-2: #2c5743;
  --brass: #a6802e;
  --brass-bright: #c9a14b;
  /* brass at AA on every ground (5.1:1 parchment, 4.7:1 parchment-deep, 5.6:1 card).
     Use for any brass-coloured TEXT; keep --brass for borders, rules and glyphs. */
  --brass-text: #7f6220;
  --error: #8c3a2b;
  --burgundy: #8b3a3a;       /* emotional primary-CTA accent — own token, distinct intent from --error */
  --burgundy-deep: #6f2e2e;  /* primary-CTA hover */
  --sage: #6b8e7b;           /* supporting / secondary accent (borders & non-text only — fails 4.5:1 as text) */
  --dark-ground: #16241d;
  --dark-text: #ece5d3;
  --font-display: 'Fraunces', 'Iowan Old Style', Georgia, serif;
  --font-body: 'Source Serif 4', Georgia, 'Times New Roman', serif;
  --radius: 2px;          /* DESIGN.md rounded.sm — the default sharp corner */
  --radius-md: 6px;       /* DESIGN.md rounded.md — form controls, small cards */
  --radius-lg: 10px;      /* DESIGN.md rounded.lg — interactive panels, tree viewers */
  --line: rgba(138, 134, 116, 0.45); /* standard hairline (matches card borders) */
  --container: 1180px;
  --shadow-card: 0 1px 2px rgba(33, 37, 31, 0.06), 0 4px 16px rgba(33, 37, 31, 0.05);
  --shadow-card-hover: 0 2px 4px rgba(33, 37, 31, 0.08), 0 10px 28px rgba(33, 37, 31, 0.09);
}

/* ---------- Reset & base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
  background-color: var(--parchment);
  font-variant-numeric: oldstyle-nums;
  /* paper grain: inline SVG noise at 2% */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.02'/%3E%3C/svg%3E");
  -webkit-font-smoothing: antialiased;
}
img, svg { max-width: 100%; height: auto; }
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 580;
  line-height: 1.12;
  color: var(--ink);
  margin: 0 0 0.5em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.5rem, 5.5vw, 4.25rem); letter-spacing: -0.015em; }
h2 { font-size: clamp(1.75rem, 3.2vw, 2.375rem); }
h3, .h3 { font-size: clamp(1.3rem, 2.2vw, 1.75rem); }
h4, .h4 { font-size: 1.2rem; }
.h2 { font-size: clamp(1.75rem, 3.2vw, 2.375rem); font-family: var(--font-display); }
p { margin: 0 0 1em; }
a { color: var(--library-green); }
ul, ol { padding-left: 1.2em; }
:focus-visible { outline: 2px solid var(--library-green-2); outline-offset: 3px; border-radius: 1px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--library-green); color: var(--parchment);
  padding: 0.6em 1.2em; border-radius: var(--radius);
}
.skip-link:focus { left: 8px; top: 8px; }

.container { max-width: var(--container); margin: 0 auto; padding: 0 24px; }
.container.narrow, .container .narrow, .narrow { max-width: 880px; margin-inline: auto; }
.portal-narrow { max-width: 560px; }
.centre { text-align: center; }
.section { padding: 88px 0; }
.section-photo { padding: 10px 0 0; }
.section-alt { background: var(--parchment-deep); }
.section-gap { margin-top: 56px; }
.section-heading { text-align: center; margin-bottom: 48px; }

/* ---------- Type details ---------- */
.kicker {
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--brass-text);
  font-weight: 600;
  margin-bottom: 0.9em;
}
.kicker-centre { text-align: center; }
.text-link {
  color: var(--library-green);
  text-decoration-color: var(--brass-bright);
  text-underline-offset: 3px;
  font-weight: 560;
}
.text-link:hover { text-decoration-thickness: 2px; }
.prose { max-width: 68ch; }
.prose h2 { margin-top: 1.6em; }
.article-meta { color: var(--ink-soft); font-size: 0.875rem; }

/* ---------- Buttons ---------- */
.button {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 0.9375rem;
  font-weight: 620;
  letter-spacing: 0.02em;
  padding: 0.78em 1.5em;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  min-height: 44px;
  transition: background-color 160ms ease, color 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
  font-variant-numeric: lining-nums;
}
.button-lg { padding: 0.95em 1.9em; font-size: 1rem; }
.button-primary { background: var(--burgundy); color: var(--parchment); }
.button-primary:hover { background: var(--burgundy-deep); box-shadow: inset 0 0 0 1px var(--brass-bright); }
.button-secondary { background: transparent; color: var(--ink); border-color: var(--sage); }
.button-secondary:hover { border-color: var(--library-green); color: var(--library-green); background: rgba(30, 61, 47, 0.04); }
.button-light { background: var(--parchment); color: var(--library-green); }
.button-light:hover { background: #fff; }
.button-ghost-light { background: transparent; color: var(--dark-text); border-color: rgba(236, 229, 211, 0.5); }
.button-ghost-light:hover { border-color: var(--dark-text); }
.button:disabled { opacity: 0.55; cursor: wait; }
.button-row { display: flex; flex-wrap: wrap; gap: 14px; margin: 1.2em 0 0.4em; }
.button-row-centre { justify-content: center; }

/* ---------- Promo banner ---------- */
.promo-banner { background: var(--burgundy); color: var(--dark-text); }
.promo-banner-inner {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  position: relative; padding: 10px 44px; font-size: 0.9375rem; text-align: center;
}
.promo-banner p { margin: 0; }
.promo-banner-short { display: none; }
.promo-banner strong { color: #fff; }
.promo-banner-dismiss {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  background: none; border: none; color: var(--dark-text); font-size: 1.3rem;
  line-height: 1; cursor: pointer; padding: 4px 8px;
}
.promo-banner-dismiss:hover { color: #fff; }
.promo-banner.is-dismissed { display: none; }

.hero-promo-badge {
  display: inline-block;
  background: var(--burgundy); color: var(--dark-text);
  font-size: 0.875rem; font-weight: 600;
  padding: 7px 16px; border-radius: var(--radius);
  margin: 0 0 1em;
}
.hero-promo-badge strong { color: #fff; }
/* Variant for stating the offer immediately beside a price and a Buy-now button.
   The default badge is burgundy-on-burgundy — byte-identical to .button-primary
   (line 157) — so directly above a primary button it reads as a second button and
   invites a click that does nothing. This reads as a note instead: accent rule and
   tinted ground, no button silhouette. Text is --burgundy-deep for contrast on the
   pale tint. Use where the badge sits adjacent to buttons; keep the solid badge
   where it stands alone. */
.hero-promo-badge--note {
  display: block;
  background: rgba(139, 58, 58, 0.07);
  color: var(--burgundy-deep);
  border-left: 3px solid var(--burgundy);
  border-radius: 0;
  padding: 10px 14px;
}
.hero-promo-badge--note strong { color: var(--burgundy-deep); }

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(246, 241, 231, 0.94);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid rgba(138, 134, 116, 0.35);
}
.header-inner { display: flex; align-items: center; justify-content: space-between; min-height: 68px; gap: 16px; }
.wordmark {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-display); font-size: 1.45rem; font-weight: 620;
  color: var(--library-green); text-decoration: none; letter-spacing: 0.01em;
}
.wordmark-glyph { color: var(--brass); }
/* "House Biography, part of Famhisto." under a checkout heading on the house host. */
.brand-part-of { color: var(--ink-faint); font-size: 0.9rem; margin: 4px 0 0; }
.nav-menu { display: flex; align-items: center; gap: 26px; list-style: none; margin: 0; padding: 0; }
.nav-item > a {
  color: var(--ink-soft); text-decoration: none; font-size: 0.9375rem; font-weight: 560;
  padding: 6px 2px;
  /* Two-word labels ("Family History", "House History", "Our Work") must not break
     onto two lines; at 770 to 910px they did, and the header kept its 69px height so
     nothing measured it. Nowrap plus the gap band below holds one line down to the
     burger breakpoint. */
  white-space: nowrap;
}
.nav-item > a:hover, .nav-item > a[aria-current] { color: var(--library-green); box-shadow: 0 2px 0 var(--brass-bright); }
/* CTA in nav: out-specific .nav-item > a's colour, which would grey the button text */
.nav-item.nav-cta > a { color: var(--parchment); box-shadow: none !important; }
.nav-item.nav-cta > a:hover { color: var(--parchment); }
.nav-toggle { display: none; }
/* Gap-tightening band, back since 2026-10-12: six top-level items plus the CTA. The
   2026-09-06 note said to re-measure before adding a fifth item; measured at 770, 800,
   900 and 920 with the burger at 760. */
@media (max-width: 1000px) { .nav-menu { gap: 14px; } }
.has-children { position: relative; }
.nav-dropdown {
  visibility: hidden; opacity: 0; transform: translateY(6px);
  transition: opacity 140ms ease, transform 140ms ease, visibility 140ms;
  position: absolute; top: calc(100% + 10px); left: -14px; min-width: 230px;
  background: var(--card); border: 1px solid rgba(138, 134, 116, 0.4); border-radius: var(--radius);
  box-shadow: var(--shadow-card-hover); list-style: none; margin: 0; padding: 8px 0; z-index: 60;
}
.has-children:hover .nav-dropdown,
.has-children:focus-within .nav-dropdown { visibility: visible; opacity: 1; transform: translateY(0); }
.nav-dropdown a { display: block; padding: 9px 18px; color: var(--ink-soft); text-decoration: none; font-size: 0.9375rem; }
.nav-dropdown a:hover { background: var(--parchment-deep); color: var(--library-green); }
/* Divides the six research services from the two products beneath them. A border on
   the <li> rather than an <hr>: the list stays a flat list for a screen reader, which
   is what it is — the rule is decoration, not structure. */
.nav-dropdown-sep { margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(138, 134, 116, 0.4); }

/* ---------- Hero ---------- */
.hero { padding: 92px 0 72px; position: relative; }
/* align-items:start, not center. With centring, the height of the art card decides
   where the hero copy sits — so swapping the 16:10 photo for the 4:5 cutaway pushed
   "Book a free 15-minute call" from y=739 to y=827 and below the fold on a 1280x800
   laptop. Top-aligning decouples the CTA from the artwork's height, which also means
   the art can change again later without moving the primary conversion path. */
.hero-grid { display: grid; grid-template-columns: 7fr 5fr; gap: 56px; align-items: start; }
.hero-sub { font-size: 1.2rem; color: var(--ink-soft); max-width: 56ch; }
/* persuasion copy beside the primary CTA — ink-soft (~7:1 AA), not ink-faint (~3.3:1, fails AA) */
.hero-reassure { color: var(--ink-soft); font-size: 0.9rem; margin-top: 1em; }
.hero-price { display: none; }
/* Phone-only furniture for the selling pages' heroes (see .page-hero--sell below). */
.phone-only, .phone-only-row { display: none; }
.hero-card { padding: 28px; }
.hero-card-caption { color: var(--ink-soft); font-size: 0.875rem; text-align: center; margin: 0.8em 0 0; }
/* The commission band: the page's second and final ask, placed after the proof.
   Centred and deliberately sparse, because by this point the visitor has read three
   case studies and three reviews and needs one action, not a menu. */
.commission-band { text-align: center; }
.commission-price { font-family: var(--font-display); font-size: 1.75rem; margin: 0 0 18px; }
.commission-actions { margin: 0 0 12px; }
.commission-reassure { color: var(--ink-soft); font-size: 0.9375rem; margin: 0 0 18px; }
.other-services { margin: 0 0 8px; }

/* The buy path on the hero card. Full-width primary so the action is unmistakable on a
   phone, with a quiet secondary link for people who want to read before committing. */
.hero-card-actions { display: grid; gap: 10px; justify-items: center; margin-top: 16px; }
.hero-card-actions .button-primary { width: 100%; text-align: center; min-height: 48px; line-height: 32px; }

/* The front door (2026-10-12): one centred headline, then two equal doors. The doors are
   the page's only action above the trust strip, so neither carries a checkout link and
   neither is visually heavier than the other. */
.hero--doors { padding: 24px 0 48px; }
/* One size down from the display ramp, in a 760px box (not 60ch: ch is measured in the
   kicker's small font here and came out at 525px), so the five-word headline holds one
   line at 1280px. Measured 2026-10-12: the display size in the narrow box wrapped it to
   two lines and pushed both door buttons below an 800px laptop fold. */
/* 2026-10-19: the eight-word headline wraps to two lines in the 760px box at any size that
   is still a headline, so it is set a step smaller (two lines of 44px, not 52px) and the
   section's top padding closes up, which keeps the door button at 783 on a 1280x800
   screen (813 with the old size). */
.hero--doors h1 { font-size: clamp(2.25rem, 3.6vw, 2.75rem); }
.hero-copy--centre { text-align: center; max-width: 760px; margin: 0 auto 28px; }
/* The door's price is the payable figure with no struck-through "usual price", so the
   "10% off until ..." note under it is the card's only sign of the offer. It used to be
   hidden here because the strike said it; do not hide it again. It is set in the body
   weight, not the price's. */
.door-tile-copy .price-offer-note { font-weight: 400; }
.hero-copy--centre .hero-sub { margin-left: auto; margin-right: auto; max-width: 72ch; }
.hero--doors .hero-copy--centre { margin-bottom: 20px; }
.door-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; align-items: stretch; }
/* .door-grid .door-card, not .door-card: .record-card is declared later in this file
   with the same specificity, so a bare .door-card { padding: 0 } loses to its 26px.
   History: 12 Oct, a portrait cutaway beside the text read as a thin tall strip; that
   evening a 16:9 picture above the text put the buttons at y=1036 on a 1280x800 screen,
   so it became a 200px band, then 150px (15 Oct) with the button at 836, still below
   the fold. 19 Oct (Ian): the band was a letterbox no artwork could sell in, so the
   picture is now the card: a 3:2 tile with the kicker, title, price and button laid
   over its foot, and the description beneath. The button lands at about 717 at 1280x800. */
.door-grid .door-card { display: flex; flex-direction: column; gap: 0; padding: 0; overflow: hidden; }
.door-tile { position: relative; }
.door-tile .door-thumb { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; object-position: 50% 35%; display: block; border-radius: 0; }
.door-thumb-link { display: block; }
/* The copy sits on the lower part of the picture over a gradient, so the artwork is
   composed with its subject in the upper two-thirds (scripts/gen-door-scene.js). */
.door-tile-copy { position: absolute; left: 0; right: 0; bottom: 0; padding: 72px 24px 20px; color: #fff;
  background: linear-gradient(to top, rgba(28, 16, 12, 0.9) 0%, rgba(28, 16, 12, 0.72) 45%, rgba(28, 16, 12, 0) 100%); }
.door-tile-copy .record-no { color: #e9d6c3; margin: 0 0 2px; border-bottom: none; padding-bottom: 0; }
/* .record-card h2 a (declared later, same specificity as .door-tile-copy h2 a) sets the
   ink colour, so the card class is named here to outrank it. */
.door-tile-copy h2 { margin: 0 0 8px; color: #fff; }
.door-card .door-tile-copy h2 a, .door-card .door-tile-copy h2 a:hover { color: #fff; }
/* The note's tail ("applied at checkout") wrapped the price column to two lines on
   the tile; the banner above says it. */
.door-tile-copy .price-offer-tail { display: none; }
.door-title-link { color: inherit; text-decoration: none; }
.door-title-link:hover, .door-title-link:focus-visible { text-decoration: underline; }
.door-buy { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; }
.door-tile-copy .door-buy .package-price { margin: 0; flex: 1 1 0; min-width: 11rem; color: #fff; }
.door-tile-copy .door-buy .package-price .price-now { color: #fff; }
.door-tile-copy .price-offer-note { color: #e9d6c3; }
.door-tile-copy .door-buy .button-primary { margin-top: 0; flex-shrink: 0; }
.door-meta { display: flex; flex-wrap: wrap; gap: 0 6px; }
.door-meta .door-line::before { content: "· "; }
/* The description, the sample link and the dates. The offer note is in the tile. */
.door-body { padding: 16px 24px 20px; display: flex; flex-direction: column; gap: 6px; }
.door-body .door-sample { font-size: 0.9rem; }
.door-body p { margin: 0; }
.door-body .package-meta { padding-top: 0; border-top: none; }

.page-hero { padding: 76px 0 22px; }
.page-hero .hero-sub { font-size: 1.15rem; }

/* staggered load reveal */
.reveal-group > * { opacity: 0; transform: translateY(12px); animation: rise 480ms ease-out forwards; }
.reveal-group > *:nth-child(1) { animation-delay: 0ms; }
.reveal-group > *:nth-child(2) { animation-delay: 60ms; }
.reveal-group > *:nth-child(3) { animation-delay: 120ms; }
.reveal-group > *:nth-child(4) { animation-delay: 180ms; }
.reveal-group > *:nth-child(5) { animation-delay: 240ms; }
@keyframes rise { to { opacity: 1; transform: none; } }

/* ---------- Trust strip ---------- */
.trust-strip {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px 38px;
  padding: 18px 24px;
  border-top: 1px solid rgba(138, 134, 116, 0.35);
  border-bottom: 1px solid rgba(138, 134, 116, 0.35);
  font-size: 0.9rem; color: var(--ink-soft);
}
.trust-strip span::before { content: '◆'; color: var(--brass-bright); font-size: 0.6em; vertical-align: 2px; margin-right: 9px; }

/* ---------- Cards ---------- */
.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }

/* ---------- "What you receive" deliverables gallery ---------- */
/* Four across rather than the usual three: these are object photographs read at a
   glance, not cards to be read through, so they take a tighter gutter and a smaller
   share of the container. */
.deliverable-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; align-items: stretch; }
.deliverable-grid--1 { grid-template-columns: minmax(0, 420px); justify-content: center; }
.deliverable-grid--2 { grid-template-columns: repeat(2, 1fr); }
.deliverable-grid--3 { grid-template-columns: repeat(3, 1fr); }
.deliverable-card { margin: 0; padding: 18px 18px 16px; display: flex; flex-direction: column; }
.deliverable-card figcaption { display: flex; flex-direction: column; flex: 1 1 auto; }
.deliverable-photo { aspect-ratio: 4 / 3; object-fit: cover; margin-bottom: 15px; }
.deliverable-name { margin: 0 0 8px; }
/* :not() excludes the tier line below. Without it this selector (0,1,2) outscores
   .deliverable-included (0,1,0) whatever the source order, and silently overrides both
   its margin-top:auto and its font-size — which is what left the rules ragged. */
.deliverable-card figcaption p:not(.deliverable-included) { margin: 0; font-size: 0.94rem; }
/* margin-top:auto pins the tier line to the card's baseline whatever the blurb length.
   Sentence-case italic rather than the uppercase tracking used elsewhere: this is a
   footnote qualifying the item, and uppercase made it both louder than the section
   heading and wide enough to wrap to four lines on mobile.
   min-height reserves two lines so the rules align across the row — the cards are
   already equal height, but rules landing at four different heights read as sloppy. */
.deliverable-included {
  margin-top: auto; padding-top: 12px; border-top: 1px solid var(--line);
  color: var(--ink-faint); font-size: 0.85rem; font-style: italic;
  line-height: 1.45; min-height: calc(12px + 2.9em);
}
.deliverable-note { text-align: center; margin: 30px auto 0; max-width: 60ch; color: var(--ink-soft); font-size: 0.88rem; }
/* Gift-voucher aside on service pages. Sits above the booking CTA banner and is
   deliberately subordinate to it: the heading carries .h3 for the documented
   Title step, a secondary button, no accent fill — the page's primary action
   stays "Book a Free Call". */
.voucher-aside { text-align: center; }
.voucher-aside h2 { margin: 6px 0 12px; }
.voucher-aside p { margin: 0 auto 20px; max-width: 54ch; }
.record-card {
  background: var(--card);
  border: 1px solid rgba(138, 134, 116, 0.45);
  border-radius: var(--radius);
  padding: 26px 26px 24px;
  box-shadow: var(--shadow-card);
  transition: box-shadow 160ms ease, transform 160ms ease;
  position: relative;
}
a .record-card:hover, .record-card:has(a:hover) { box-shadow: var(--shadow-card-hover); transform: translateY(-2px); }
.record-no {
  font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--ink-soft); border-bottom: 1px solid rgba(138, 134, 116, 0.35);
  padding-bottom: 10px; margin: 0 0 16px;
}
.service-card h3 a, .service-card .h3 a, .record-card h2 a, .record-card h3 a { color: var(--ink); text-decoration: none; }
.service-card h3 a:hover, .record-card h2 a:hover, .record-card h3 a:hover { color: var(--library-green); }
.card-price { color: var(--library-green); font-weight: 620; font-variant-numeric: lining-nums; }
.card-link { color: var(--brass-text); font-weight: 600; font-size: 0.9375rem; margin-bottom: 0; }
/* whole-card click target: stretch the single heading link over the card,
   so the "Explore →" affordance (a plain <p>) actually navigates */
.card-clickable h2 a::after, .card-clickable h3 a::after { content: ''; position: absolute; inset: 0; }
.case-era { color: var(--ink-soft); font-size: 0.9rem; }
.case-feature { max-width: 760px; margin: 0 auto; }
.quote-card {
  background: var(--card); border: 1px solid rgba(138, 134, 116, 0.45); border-radius: var(--radius);
  margin: 0; padding: 30px 28px 24px; position: relative; box-shadow: var(--shadow-card);
}
.quote-card::before {
  content: '“'; position: absolute; top: -6px; left: 14px;
  font-family: var(--font-display); font-size: 5.5rem; color: var(--brass); opacity: 0.18; line-height: 1;
}
.quote-card cite { font-style: normal; color: var(--ink-soft); font-size: 0.875rem; }
.guarantee-card { border-left: 3px solid var(--brass); }

/* check list */
.check-list { list-style: none; padding: 0; }
.check-list li { padding-left: 26px; position: relative; margin-bottom: 0.65em; }
.check-list li::before { content: '◆'; position: absolute; left: 2px; top: 1px; color: var(--brass-bright); font-size: 0.65em; }
.check-list-left { text-align: left; }

/* ---------- Archive photography (sepia duotone, mounted-print frame) ---------- */
.archive-photo {
  display: block;
  width: 100%;
  filter: sepia(0.32) contrast(1.04) saturate(0.92);
  border: 1px solid rgba(138, 134, 116, 0.5);
  background: var(--card);
  padding: 8px;
  box-shadow: var(--shadow-card);
}
.archive-photo--bare { padding: 0; border: none; box-shadow: none; }
.archive-photo--colour { filter: none; }
.portrait-photo { display: block; width: 100%; height: auto; border-radius: 8px; }
.case-thumb { aspect-ratio: 16 / 10; object-fit: cover; margin-bottom: 16px; }
/* Homepage hero cutaway. The village-cutaway artwork is 4:5 portrait, where the photo
   it replaced was 16:10, so unconstrained it makes the hero card roughly twice as tall
   and pushes the CTA below the fold. Capped by height rather than width so the card
   keeps the column width the rest of the hero grid is built on. */
.hero-cutaway { max-height: 460px; width: auto; margin: 0 auto 16px; }
.case-feature-grid { display: grid; grid-template-columns: 5fr 7fr; gap: 28px; align-items: center; }
.case-hero-img { aspect-ratio: 16 / 9; object-fit: cover; margin: 18px 0 26px; }
.aside-photo { margin-top: 28px; }
.aside-photo figcaption, .photo-caption { color: var(--ink-soft); font-size: 0.85rem; margin-top: 8px; }
.lead-magnet-photo { aspect-ratio: 4 / 3; object-fit: cover; }
/* hero gallery — record card beside an aligned strip of archival prints */
.hero-art { display: grid; grid-template-columns: 1fr 138px; gap: 16px; align-items: stretch; }
.hero-strip { display: flex; flex-direction: column; gap: 16px; min-height: 0; }
.hero-strip .archive-photo { flex: 1 1 0; min-height: 0; min-width: 0; width: 100%; object-fit: cover; }
.hero-strip .archive-photo:nth-child(2) { object-position: 50% 42%; }
.hero-strip .archive-photo:nth-child(3) { object-position: 50% 22%; }
@media (max-width: 1100px) {
  .hero-art { grid-template-columns: 1fr; }
  .hero-strip { flex-direction: row; }
  .hero-strip .archive-photo { aspect-ratio: 1; }
}
@media (max-width: 980px) {
  .case-feature-grid { grid-template-columns: 1fr; }
}

/* ---------- Timeline (ancestry line) ---------- */
.timeline { list-style: none; counter-reset: step; padding: 0; margin: 0; position: relative; }
.timeline::before {
  content: ''; position: absolute; left: 17px; top: 8px; bottom: 8px;
  width: 1px; background: var(--brass-bright);
}
.timeline li { position: relative; padding: 0 0 30px 58px; counter-increment: step; }
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute; left: 0; top: 0;
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--parchment); border: 1px solid var(--brass);
  color: var(--brass-text); font-size: 0.8rem; font-weight: 620;
  display: flex; align-items: center; justify-content: center;
  font-variant-numeric: lining-nums;
}
.section-alt .timeline li::before { background: var(--parchment-deep); }
.timeline h3, .timeline .h3, .timeline h2 { margin-bottom: 0.25em; }
.timeline-portal li::before { content: ''; width: 14px; height: 14px; left: 11px; top: 6px; }
.timeline-portal li { padding-left: 50px; }
.milestone-done::before { background: var(--brass) !important; }
.milestone-current::before { background: var(--parchment); border-width: 2px !important; animation: pulse 2s ease-in-out infinite; }
@keyframes pulse { 50% { box-shadow: 0 0 0 6px rgba(201, 161, 75, 0.25); } }
.milestone-upcoming { opacity: 0.6; }

/* ---------- Packages ---------- */
.package-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; align-items: start; }
.package-featured { transform: translateY(-10px) scale(1.015); border-color: var(--brass); box-shadow: var(--shadow-card-hover); }
.stamp-badge {
  position: absolute; top: -13px; right: 16px;
  background: var(--card); border: 1px solid var(--brass); color: var(--brass-text);
  font-size: 0.75rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.1em;
  padding: 4px 11px; border-radius: var(--radius); transform: rotate(-2deg);
  margin: 0;
}
.package-name { margin-bottom: 0.1em; }
.package-price { font-family: var(--font-display); font-size: 2.1rem; font-weight: 600; color: var(--library-green); margin: 0 0 0.35em; font-variant-numeric: lining-nums; }
.package-tagline { color: var(--ink-soft); }
.package-meta { color: var(--ink-soft); font-size: 0.875rem; border-top: 1px solid rgba(138,134,116,0.3); padding-top: 12px; }
.package-card .button-row { flex-direction: column; }
.package-card .button { width: 100%; }
/* a single package shown on its own (e.g. House History) — centred at card width */
.package-solo { max-width: 380px; margin-inline: auto; }
/* wedding ladder: four tiers across on desktop; the shared responsive rules below
   (≤980px → 2-up, ≤720px → 1-up) override this automatically. */
.package-grid--4 { grid-template-columns: repeat(4, 1fr); gap: 18px; }
/* Two cards side by side, centred: the wedding page sells the print and the Heritage
   commission and nothing else with a button. Collapses with .package-grid at 640px. */
.package-grid--2 { grid-template-columns: repeat(2, 1fr); max-width: 860px; margin-inline: auto; }

/* compare table */
.compare-table { width: 100%; border-collapse: collapse; margin-top: 56px; background: var(--card); border: 1px solid rgba(138,134,116,0.45); font-size: 0.9375rem; }
.compare-table caption { text-align: left; font-family: var(--font-display); font-size: 1.2rem; padding: 0 0 14px; }
.compare-table th, .compare-table td { padding: 13px 16px; border: 1px solid rgba(138,134,116,0.3); text-align: left; vertical-align: top; }
.compare-table thead th { background: var(--parchment-deep); font-family: var(--font-display); font-size: 1.02rem; }
.compare-table tbody th { font-weight: 600; color: var(--ink-soft); width: 22%; }

/* price list */
.price-list { list-style: none; padding: 0; }
.price-list li {
  display: flex; justify-content: space-between; gap: 18px;
  padding: 13px 2px; border-bottom: 1px solid rgba(138,134,116,0.3);
}
.price-list li span:last-child { white-space: nowrap; font-weight: 620; color: var(--library-green); font-variant-numeric: lining-nums; }

/* vouchers */
/* four voucher products across on desktop; the shared responsive rules below
   (≤980px → 2-up, ≤720px → 1-up) override this automatically. */
/* The voucher page renders two groups of differing size (four research packages,
   three consultations). A fixed four-column track left an empty cell under the
   three-card group. auto-fit collapses the unused track and 1fr redistributes its
   space, so each group fills its own row. Tracks are deliberately NOT capped to a
   fixed max: a cap only a few pixels wider than the available width silently drops
   the four-card group to three columns and orphans the fourth card onto its own
   row, which is worse than the empty cell it was meant to fix. */
.voucher-grid { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.voucher-card { text-align: center; }
.voucher-card .record-no { text-align: left; }
.voucher-amount { font-family: var(--font-display); font-size: 2.3rem; color: var(--library-green); margin: 0.1em 0; font-variant-numeric: lining-nums; }
.voucher-note { color: var(--ink-soft); min-height: 2.6em; }

/* ---------- Sections: split layouts ---------- */
.split-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; }
.split-form { display: grid; grid-template-columns: 5fr 7fr; gap: 56px; align-items: start; }
.split-form-aside { padding-top: 10px; }

/* ---------- Forms ---------- */
.form-card { padding: 32px; }
.field { margin-bottom: 18px; }
.field label, .field legend { display: block; font-weight: 620; font-size: 0.9375rem; margin-bottom: 7px; }
.label-optional { color: var(--ink-soft); font-weight: 400; }
.field input[type="text"], .field input[type="email"], .field input[type="tel"],
.field input[type="password"], .field input[type="number"], .field textarea,
.newsletter-row input[type="email"] {
  width: 100%; padding: 11px 13px;
  background: var(--parchment-deep);
  border: 1px solid rgba(138, 134, 116, 0.55);
  border-radius: var(--radius);
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
}
.field input:focus, .field textarea:focus, .newsletter-row input:focus {
  outline: 2px solid var(--library-green-2); outline-offset: 0; border-color: var(--library-green-2);
}
.field textarea { resize: vertical; }
/* Match native selects to the text inputs above — same fill/border/radius/height,
   custom chevron (data: URI is permitted by the img-src CSP). */
.field select {
  width: 100%; padding: 11px 38px 11px 13px;
  background-color: var(--parchment-deep);
  border: 1px solid rgba(138, 134, 116, 0.55);
  border-radius: var(--radius);
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  min-height: 44px; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%234d5246' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center; background-size: 12px 8px;
}
.field select:focus { outline: 2px solid var(--library-green-2); outline-offset: 0; border-color: var(--library-green-2); }
fieldset.field { border: none; padding: 0; margin-left: 0; margin-right: 0; }
.choice-row { display: flex; flex-wrap: wrap; gap: 10px 22px; }
.choice-row label { font-weight: 400; display: inline-flex; gap: 8px; align-items: center; margin: 0; }
.form-small { font-size: 0.85rem; color: var(--ink-soft); margin-top: 0.9em; } /* ink-soft (~7:1), not ink-faint (~3.2:1, fails AA) */
.form-status { font-weight: 600; margin-top: 0.8em; min-height: 1.4em; }
.form-status.is-success { color: var(--library-green-2); }
.form-status.is-error { color: var(--error); }
.form-error { color: var(--error); font-weight: 600; }
/* honeypot: off-screen so fill-everything bots still trip it, but visibility:hidden
   so browser autofill / password managers skip it — otherwise they populate _website
   and isBot() locks out real users (notably the native-POST logins). The timing-nonce,
   Cyrillic/link and Turnstile layers cover bots that skip hidden fields. */
.hp-field { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; opacity: 0; visibility: hidden; }

/* ---------- Lead magnet / CTA banner ---------- */
.lead-magnet { background: var(--parchment-deep); border-top: 1px solid rgba(138,134,116,0.35); border-bottom: 1px solid rgba(138,134,116,0.35); padding: 64px 0; }
.lead-magnet-inner { display: grid; grid-template-columns: 6fr 6fr; gap: 48px; align-items: center; }
.newsletter-row { display: flex; gap: 10px; }
.newsletter-row input { flex: 1; }
.cta-banner { background: var(--dark-ground); color: var(--dark-text); padding: 76px 0; }
.cta-banner h2 { color: var(--dark-text); }
.cta-banner p { color: rgba(236, 229, 211, 0.8); max-width: 52ch; }
.cta-banner-inner { text-align: center; }
.cta-banner .cta-banner-inner p { margin-inline: auto; }
.cta-banner .button-row { justify-content: center; }

/* ---------- FAQ ---------- */
.faq-item {
  background: var(--card); border: 1px solid rgba(138,134,116,0.45); border-radius: var(--radius);
  margin-bottom: 12px; padding: 0 22px;
}
.faq-item summary {
  cursor: pointer; list-style: none; font-weight: 620; padding: 17px 30px 17px 0; position: relative;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '+'; position: absolute; right: 2px; top: 50%; transform: translateY(-50%);
  color: var(--brass); font-size: 1.4rem; transition: transform 160ms ease;
}
.faq-item[open] summary::after { transform: translateY(-50%) rotate(45deg); }
.faq-item p { padding-bottom: 18px; color: var(--ink-soft); max-width: 65ch; }
.faq-group { margin: 1.8em 0 0.8em; }

/* Short answers: the questions people ask before they will buy, answered in one sentence.
   A <dl>, not <details>: these must be readable without a click, by a person skimming and
   by anything reading the page on their behalf. Styled as plain text rather than as cards
   so it reads as prose the eye runs down, not as another row of things to compare. */
.key-facts { margin: 0; }
.key-facts dt {
  font-weight: 620; color: var(--ink); margin-top: 1.4em;
  padding-top: 1.4em; border-top: 1px solid rgba(138,134,116,0.3);
}
.key-facts dt:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.key-facts dd { margin: 0.5em 0 0; color: var(--ink-soft); max-width: 65ch; }

/* ---------- Articles ---------- */
.article-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }
.article-list { list-style: none; padding: 0; }
.article-list li { padding: 12px 2px; border-bottom: 1px solid rgba(138,134,116,0.3); }
.article-body h2 { margin-top: 1.5em; }
.article-body blockquote { border-left: 3px solid var(--brass-bright); margin-left: 0; padding-left: 1.2em; color: var(--ink-soft); }
.article-author { border-top: 1px solid rgba(138,134,116,0.35); margin-top: 2.5em; padding-top: 1.2em; color: var(--ink-soft); }

/* ---------- Breadcrumbs ---------- */
.breadcrumbs { padding: 18px 24px 0; max-width: var(--container); margin: 0 auto; }
.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; padding: 0; margin: 0; font-size: 0.85rem; color: var(--ink-soft); }
.breadcrumbs li + li::before { content: '›'; margin-right: 6px; color: var(--brass-bright); }
.breadcrumbs a { color: var(--ink-soft); }

/* ---------- Portal ---------- */
.portal-section { min-height: 50vh; }
.portal-head { display: flex; justify-content: space-between; align-items: start; gap: 20px; margin-bottom: 40px; }
.contact-list { list-style: none; padding: 0; }
.contact-list li { margin-bottom: 0.7em; }
.contact-list strong { display: inline-block; min-width: 84px; }
.portrait-slot { max-width: 300px; float: right; margin: 0 0 20px 28px; text-align: center; }
.portrait-placeholder { color: var(--ink-soft); font-size: 0.875rem; padding: 36px 10px; }

/* About — recovered-logo brand sign-off */
.brand-lockup { clear: both; text-align: center; margin: 52px 0 4px; }
.brand-lockup-mark { color: var(--library-green); display: inline-block; }
.brand-lockup-name { font-family: var(--font-display); font-size: 1.5rem; color: var(--ink); margin: 8px 0 3px; letter-spacing: 0.01em; }
.brand-lockup-tag { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.22em; color: var(--brass-text); margin: 0; }

/* ---------- Footer ---------- */
.site-footer { background: var(--dark-ground); color: var(--dark-text); margin-top: 0; }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1.6fr; gap: 40px; padding: 64px 24px 40px; }
/* House Biography (lib/brand.js) has three columns, not four: no family Services list.
   Before the breakpoint rules below, which still collapse it to two and then one. */
.footer-grid-house { grid-template-columns: 2fr 1.4fr 1.6fr; }
.footer-part-of { color: rgba(236,229,211,0.75); font-size: 0.9rem; }
.footer-part-of a { color: var(--brass-bright); }
.footer-mark { color: var(--brass-bright); display: block; margin: 0 0 12px; }
.footer-wordmark { font-family: var(--font-display); font-size: 1.5rem; margin: 0 0 8px; }
.footer-tag { color: rgba(236,229,211,0.75); }
.footer-meta { color: rgba(236,229,211,0.5); font-size: 0.85rem; }
.footer-heading { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--brass-bright); margin: 0 0 14px; font-family: var(--font-body); font-weight: 650; }
.footer-col ul { list-style: none; padding: 0; margin: 0; }
.footer-col li { margin-bottom: 9px; }
.footer-col a { color: rgba(236,229,211,0.85); text-decoration: none; font-size: 0.9375rem; }
.footer-col a:hover { color: #fff; text-decoration: underline; text-decoration-color: var(--brass-bright); }
.footer-newsletter { margin-top: 22px; }
.footer-newsletter label { display: block; margin-bottom: 10px; }
.footer-newsletter input { background: rgba(236,229,211,0.08); border: 1px solid rgba(236,229,211,0.25); color: var(--dark-text); }
.footer-newsletter input::placeholder { color: rgba(236,229,211,0.4); }
.footer-legal { border-top: 1px solid rgba(236,229,211,0.15); padding-top: 22px; padding-bottom: 26px; font-size: 0.85rem; color: rgba(236,229,211,0.55); }
.footer-legal a { color: rgba(236,229,211,0.7); }

/* ---------- Responsive ---------- */
@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; gap: 36px; }
  .hero-art { max-width: 420px; }
  .card-grid, .package-grid, .voucher-grid, .deliverable-grid { grid-template-columns: 1fr 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .split-2, .split-form, .lead-magnet-inner { grid-template-columns: 1fr; gap: 32px; }
  .package-featured { transform: none; }
  .compare-table { display: block; overflow-x: auto; }
}
@media (max-width: 720px) {
  .section { padding: 56px 0; }
  .hero { padding: 56px 0 44px; }
  /* MOBILE HERO ORDER (2026-10-17, Ian). The copy leads again. The card-first order of
     12 Oct was measured against an 844px fold, but Safari on an iPhone shows about 659px,
     and the cookie bar covered the foot of that: a ChatGPT ad click (text ads, no picture
     to match) saw the offer bar, the logo and half an illustration, with the headline 1.6
     screens down. Now the headline, the promise, a price and the Commission button come
     first and the card follows. The kicker goes (as on the front door) and the hero is
     tightened so the button clears the cookie bar. */
  .hero:not(.hero--doors) { padding-top: 20px; }
  .hero:not(.hero--doors) .kicker { display: none; }
  .hero:not(.hero--doors) h1 { font-size: 2.1rem; }
  .hero:not(.hero--doors) .hero-sub { font-size: 1rem; line-height: 1.5; }
  .hero-price { display: block; margin: 0.6em 0 0; font-size: 1.25rem; color: var(--library-green); font-weight: 620; font-variant-numeric: lining-nums; }
  .hero-price .price-offer-tail { display: none; }
  .hero-price + .button-row { margin-top: 0.6em; }
  /* The selling pages' heroes (gifts, family history, pricing, print, vouchers), same
     reasoning: headline, a price and a way to buy above the cookie bar on a phone. A
     page marks what only a phone shows (.phone-only, .phone-only-row) and what steps
     aside there (.phone-hide, usually the call button the sticky bar already carries). */
  .page-hero--sell { padding-top: 20px; }
  .page-hero--sell .kicker, .page-hero--sell .phone-hide { display: none; }
  .page-hero--sell h1 { font-size: 2.1rem; }
  .page-hero--sell .hero-sub { font-size: 1rem; line-height: 1.5; }
  .page-hero--sell .phone-only { display: inline-block; }
  .page-hero--sell .phone-only-row { display: flex; margin-top: 0.6em; }
  /* The print's price line already names three figures; the offer note split it in two. */
  .page-hero--sell .card-price .price-offer-note { display: none; }
  /* The front door is the exception: its headline is the proposition and leads. At
     phone width the door image becomes a short banner above the text, the door's
     description and the offer note (the promo bar already states it) are dropped, and
     the sub-line steps down a size, so the headline and the first door's button both
     fit in the first screen. Measured 2026-10-12 at 390x844 with the promo bar showing:
     h1 bottom 264px, first door button 651 to 701px, sticky call bar from 773px. The
     image is never hidden. */
  .hero--doors { padding: 20px 0 24px; }
  .hero--doors .hero-copy { order: 0; margin-bottom: 10px; }
  .hero--doors .kicker { display: none; }
  /* The viability promise is the differentiator and the audit (2026-10-12) found it hidden
     at phone width, where the headline is. It stays, set small, and the door banner gives
     back the height it costs. */
  .hero--doors .hero-reassure { font-size: 0.8rem; margin-top: 0.4em; line-height: 1.35; }
  .hero--doors .hero-sub { font-size: 0.9rem; line-height: 1.5; }
  /* 2026-10-19: the eight-word headline runs to three lines on a phone, so it drops a step
     to 2rem, the sub-line's second sentence (for yourself or as a present) goes and the
     reassurance line closes up, which keeps the first door's button above the cookie bar:
     its bottom measured 503 at 393x659, against the bar at 530. */
  .hero--doors .phone-hide { display: none; }
  .hero--doors h1 { font-size: 2rem; }
  .hero--doors .hero-reassure { margin-top: 0.2em; }
  /* The price is the card's largest type; at phone width "From £245 £220.50" wrapped
     into two 50px lines, which is what pushed the button under the sticky call bar. */
  .door-tile-copy .package-price { font-size: 1.125rem; }
  /* One line on a phone: the tail ("applied at checkout") is in the banner and at checkout. */
  .door-tile-copy .price-offer-tail { display: none; }
  .door-grid { grid-template-columns: 1fr; gap: 14px; }
  /* 2026-10-17 (Ian): Safari shows about 659px and the cookie bar covers the foot of that
     (from 530px), so the first door's price and button must sit above it. 19 Oct: the
     picture sits beside the title, price and button (a book and a house, where the 56px
     band showed a strip of desk); the kicker goes (the title says the same), the
     description and timeline go (the hub says them), the offer note stays
     (tests/two-door.test.js) and the Christmas line follows under the tile.
     Later on 19 Oct (Ian: "too narrow and heavily cropped"): the picture widens from
     112px to 166px and the copy column narrows to make room: title at 1.2rem, price at
     1.125rem, the button wraps to two lines at 0.8125rem and the offer note drops to
     0.75rem so it holds one line in the 157px left. The row is then 150px tall, so the
     picture is 166x150 (three quarters of the 3:2 frame) instead of 112x163 (a slice).
     Measured at 393x659: first button bottom 498, second 697. */
  .door-tile { display: grid; grid-template-columns: 166px 1fr; }
  .door-thumb-link { display: block; height: 100%; }
  .door-tile .door-thumb { height: 100%; min-height: 150px; aspect-ratio: auto; object-position: 50% 50%; }
  .door-tile-copy { position: static; padding: 10px 10px 10px; color: inherit; background: none; }
  .door-tile-copy .record-no { display: none; }
  .door-tile-copy h2, .door-card .door-tile-copy h2 a { color: var(--ink); margin-bottom: 2px; }
  .door-tile-copy h2 { font-size: 1.2rem; }
  .door-tile-copy .door-buy .package-price, .door-tile-copy .door-buy .package-price .price-now { color: var(--library-green); }
  .door-tile-copy .price-offer-note { color: inherit; font-size: 0.75rem; line-height: 1.3; }
  .door-body { padding: 0 14px 10px; gap: 4px; }
  .door-body p:not(.door-line) { display: none; }
  /* The timeline is hidden here, so the Christmas line has no dot to follow. */
  .door-buy { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
  /* display: contents lets the price, its button and the offer note share one flex
     row-and-a-line: price and button side by side, the note full width beneath. */
  .door-tile-copy .door-buy .package-price { display: contents; }
  .door-tile-copy .door-buy .price-offer { order: 1; flex: 0 0 auto; }
  .door-tile-copy .door-buy .button-primary { order: 2; flex: 1 1 auto; width: auto; white-space: normal; text-align: center; line-height: 1.2; padding: 10px 8px; font-size: 0.8125rem; }
  .door-tile-copy .door-buy .price-offer-note { order: 3; flex: 0 0 100%; margin-top: -2px; }
  .door-meta .door-line::before { content: none; }
  /* Three decorative thumbnails (alt="") that did no persuasive work and pushed the
     product down by their own height. */
  .hero-strip { display: none; }
  /* The sticky promo bar is already on screen saying the same thing. Two statements of
     one offer inside 250px read as two offers. */
  .hero-promo-badge { display: none; }
  .promo-banner-long { display: none; }
  .promo-banner-short { display: block; }
  .promo-banner-inner { padding: 8px 36px; font-size: 0.8125rem; }
  .card-grid, .package-grid, .voucher-grid, .article-grid { grid-template-columns: 1fr; }
  /* Deliverables stay two-up on mobile: they are photographs, and one enormous
     product shot per screen makes the section scroll far longer than it is worth. */
  .deliverable-grid { grid-template-columns: 1fr 1fr; gap: 14px; }
  .deliverable-card { padding: 12px 12px 11px; }
  /* :not() for the same reason as the desktop rule — it would otherwise outscore
     .deliverable-included and resize the tier line too. */
  .deliverable-card figcaption p:not(.deliverable-included) { font-size: 0.88rem; }
  .footer-grid { grid-template-columns: 1fr; }
  .newsletter-row { flex-direction: column; }
  .portal-head { flex-direction: column; }
  .portrait-slot { float: none; margin: 0 0 24px; }
}

/* ---------- Primary nav: collapse to the burger at 880px ----------
   2026-10-12: raised from 760px for the two-door nav (six top-level items plus the
   CTA, labels nowrap). Measured with the query disabled: the bar overflows the viewport
   below ~800px (menu right edge 789px) and is clean from 800px up; 880px is that floor
   plus breathing room, so iPad portrait (768 to 834) gets the burger. Earlier history:
   History: this was 720px, was raised to 900px on 2026-08-30 because eight top-level
   items overflowed the viewport (`.nav-menu` is `flex-wrap: nowrap`) and wrapped the
   CTA text, inflating the header from 69px to 114px all the way up to 899px. On
   2026-09-06 the bar was cut to four items plus the CTA, so the horizontal nav fits
   again well below that. Measured 2026-09-06 with this query disabled: the header holds
   a single 69px row with no overflow all the way down to ~660px, where the wordmark and
   the menu finally meet. 760px is that floor plus 100px of deliberate breathing room,
   so the two never crowd. Re-measure before lowering it, and raise it again if a fifth
   top-level item is ever added. These rules live in their own query rather than
   the 720px block they came from, because that block also sizes the deliverables grid,
   the footer and the portal head — none of which should change at tablet width. */
@media (max-width: 880px) {
  .nav-toggle {
    display: inline-flex; align-items: center; gap: 9px;
    background: none; border: 1px solid var(--ink-soft); border-radius: var(--radius);
    padding: 8px 14px; font-family: var(--font-body); font-size: 0.9rem; color: var(--ink);
    cursor: pointer;
  }
  .nav-toggle-bar, .nav-toggle-bar::before, .nav-toggle-bar::after {
    display: block; width: 16px; height: 2px; background: var(--ink); position: relative; content: '';
  }
  .nav-toggle-bar::before { position: absolute; top: -5px; }
  .nav-toggle-bar::after { position: absolute; top: 5px; }
  .nav-menu {
    display: none;
    position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--parchment); border-bottom: 1px solid rgba(138,134,116,0.4);
    padding: 8px 24px 20px; box-shadow: var(--shadow-card-hover);
  }
  .nav-menu.is-open { display: flex; }
  .nav-item > a { display: block; padding: 13px 2px; font-size: 1.05rem; }
  .nav-item > a:hover, .nav-item > a[aria-current] { box-shadow: none; }
  .nav-dropdown {
    visibility: visible; opacity: 1; transform: none; position: static;
    border: none; box-shadow: none; background: transparent; padding: 0 0 4px 18px;
  }
  .nav-cta { margin-top: 12px; }
  .nav-cta a { display: block; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  html { scroll-behavior: auto; }
  .reveal-group > * { opacity: 1; transform: none; }
}

/* ---------- Blank family tree chart (lead magnet) ---------- */
.ftc-controls { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 1.5rem; }
.ftc-controls .is-active { background: var(--library-green); color: var(--parchment); border-color: var(--library-green); }
.ftc-chart { display: flex; gap: 1rem; align-items: stretch; }
.ftc-chart[hidden] { display: none; }
.ftc-gen { flex: 1 1 0; display: flex; flex-direction: column; justify-content: space-around; gap: 0.5rem; min-width: 0; }
.ftc-gen-head { font-family: var(--font-display); font-size: 0.8rem; color: var(--brass-text); text-transform: uppercase; letter-spacing: 0.08em; text-align: center; margin: 0 0 0.4rem; }
.ftc-box { border: 1.5px solid var(--ink-soft); border-radius: var(--radius); background: var(--card); padding: 0.5rem 0.55rem; display: flex; flex-direction: column; gap: 0.35rem; }
.ftc-box--compact { padding: 0.4rem; gap: 0.25rem; }
.ftc-field { border-bottom: 1px solid var(--ink-faint); min-height: 1.3rem; display: flex; align-items: flex-end; }
.ftc-field--sm { min-height: 1.05rem; }
.ftc-box--compact .ftc-field { min-height: 1rem; }
.ftc-lab { font-size: 0.72rem; color: var(--ink-soft); font-style: italic; line-height: 1; padding-bottom: 1px; }
.ftc-caption { text-align: center; color: var(--ink-soft); font-size: 0.82rem; margin-top: 1.4rem; }
@media (max-width: 760px) {
  .ftc-chart { gap: 0.4rem; }
  .ftc-gen-head { font-size: 0.6rem; letter-spacing: 0.04em; }
  .ftc-lab { font-size: 0.58rem; }
  .ftc-box { padding: 0.35rem; }
}

/* ---------- Print (voucher & pages read well on paper) ---------- */
@media print {
  .site-header, .site-footer, .cta-banner, .lead-magnet, .nav-toggle, .no-print, .skip-link { display: none !important; }
  body { background: #fff; }
  .ftc-chart[hidden] { display: none !important; }
  .ftc-chart .ftc-box { background: #fff; }
  .section { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; }
  .container { max-width: none; width: 100%; }
  @page { margin: 10mm; }
}

/* Checkout review consent tick */
.consent-field label {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-weight: 400;
}
.consent-field input[type="checkbox"] {
  width: auto;
  margin-top: 0.2rem;
  flex: 0 0 auto;
}

/* ----- Portal: family-details intake ----- */
.family-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin: 1.5rem 0; }
@media (max-width: 640px) { .family-cols { grid-template-columns: 1fr; } }
.family-person { border: 1px solid var(--line); border-radius: var(--radius-lg); margin-bottom: 0.85rem; position: relative; }
.family-person-body { display: grid; gap: 0.5rem; padding: 0.85rem; }
.family-summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 0.5rem; padding: 0.7rem 0.85rem; }
.family-summary::-webkit-details-marker { display: none; }
.family-summary::marker { display: none; }
.family-summary::before { content: '\25B8'; color: var(--ink-soft); font-size: 0.8rem; line-height: 1; }
.family-person[open] .family-summary::before { content: '\25BE'; }
.family-summary-name { flex: 1 1 auto; min-width: 0; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.family-summary-name.is-empty { font-weight: 400; font-style: italic; color: var(--ink-soft); } /* placeholder still passes AA (was #9a958a ≈ 2.6:1) */
.family-summary-rel { flex: 0 0 auto; font-size: 0.8rem; color: var(--ink-soft); }
.family-summary-rel::before { content: '— '; color: var(--ink-faint); }
.family-anchor > .family-summary { background: var(--parchment-deep); border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.family-person[open] > .family-summary { border-bottom: 1px solid var(--line); }
.family-field { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.85rem; }
.family-field input, .family-field select { padding: 0.45rem 0.6rem; border: 1px solid var(--line); border-radius: var(--radius-md); font: inherit; }
.family-remove { align-self: start; margin-top: 0.4rem; padding: 0.25rem 0.6rem; font-size: 0.8rem; border: 1px solid rgba(140, 58, 43, 0.4); border-radius: var(--radius-md); background: var(--card); color: var(--error); cursor: pointer; }
.family-remove:hover { background: rgba(140, 58, 43, 0.08); }
.family-error { display: block; min-height: 1em; font-size: 0.75rem; color: var(--error); }
.family-field input.is-invalid { border-color: var(--error); }
.family-upload { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
.family-field-label { font-size: 0.85rem; }
.family-status { display: flex; gap: 1rem; padding-top: 0.2rem; }
.family-status-opt { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.9rem; }
.family-status input[type="radio"] { width: auto; padding: 0; border: 0; margin: 0; }
.family-anchor { background: var(--parchment-deep); border-color: rgba(166, 128, 46, 0.4); }
.family-anchor .family-anchor-rel { font-weight: 600; color: var(--ink-soft); padding: 0.1rem 0; }
.family-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.tree-preview { margin-top: 1rem; }
.tree-viewer { border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; background: var(--parchment); }
.tree-toolbar { display: flex; gap: 0.4rem; padding: 0.5rem; background: var(--parchment-deep); border-bottom: 1px solid var(--line); }
.tree-zoom { padding: 0.2rem 0.6rem; font-size: 0.85rem; min-width: 2.4rem; }
.tree-viewport { overflow: auto; max-height: 60vh; -webkit-overflow-scrolling: touch; cursor: grab; }
.tree-viewport.is-grabbing { cursor: grabbing; }
.tree-viewport svg { display: block; max-width: none; }
.tree-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: 2vh; background: rgba(40, 36, 28, 0.6); }
.tree-modal-dialog { display: flex; flex-direction: column; gap: 0.5rem; width: min(96vw, 1100px); max-height: 96vh; padding: 0.75rem; background: var(--parchment); border-radius: var(--radius-lg); }
.tree-modal-dialog .tree-viewport { max-height: 82vh; }
.tree-modal-close { align-self: flex-end; }

/* Rendered GEDCOM tree on the portal project page. A 4-generation tree is wide,
   so the block scrolls horizontally and the SVG holds a legible minimum width
   rather than shrinking its text to fit the narrow portal column. */
.portal-tree-block { margin-top: 2.75rem; }
.portal-tree { margin-top: 1rem; overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--parchment); }
.portal-tree svg { display: block; width: 100%; min-width: 1100px; }

/* ----- Customer account & admin areas ----- */
.account-block { margin-top: 40px; padding-top: 28px; border-top: 1px solid var(--parchment-deep); }
.account-block:first-of-type { border-top: 0; padding-top: 0; }
.account-list { list-style: none; padding: 0; margin: 16px 0 0; }
.account-list li { padding: 10px 0; border-bottom: 1px solid var(--parchment-deep); }
.account-list li:last-child { border-bottom: 0; }

.upload-form { display: flex; gap: 14px; align-items: end; flex-wrap: wrap; margin-top: 16px; }
.upload-form .field { margin: 0; flex: 1 1 260px; }
.upload-item { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.upload-item .form-small { color: var(--ink-soft); }
.inline-form { display: inline; margin-left: auto; }

.button-link { background: none; border: 0; padding: 0; font: inherit; color: var(--library-green); text-decoration: underline; cursor: pointer; }
.button-link:hover { color: var(--library-green-2); }
.button-link.danger { color: var(--error); }

.form-success { color: var(--library-green); font-weight: 600; }
.form-error { color: var(--error); font-weight: 600; }
.ok { color: var(--library-green); }
.bad { color: var(--error); }

.admin-table { width: 100%; border-collapse: collapse; margin-top: 16px; font-size: 0.92rem; }
.admin-table th, .admin-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--parchment-deep); }
.admin-table th { color: var(--ink-soft); font-weight: 600; }

/* ----- Admin projects board -----
   Five stage columns on a desktop, three or two on a tablet, stacked on a phone: the board never
   scrolls sideways. A flagged card carries a brass edge so a late one shows from across
   the room. */
.board-wrap { margin-top: 20px; }
.project-board { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; align-items: start; }
.board-column { background: var(--parchment-deep); border-radius: var(--radius-lg); padding: 12px; min-width: 0; }
.board-column-head { font-family: var(--font-body); font-size: 0.95rem; font-weight: 600; color: var(--ink); margin: 2px 4px 10px;
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.board-count { font-size: 0.8rem; color: var(--ink-soft); font-weight: 600; }
.board-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.board-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 12px 12px 10px;
  box-shadow: var(--shadow-card); min-width: 0; overflow-wrap: anywhere; }
.board-card-flagged { border-left: 3px solid var(--brass); padding-left: 10px; }
.board-card-title { font-weight: 600; font-size: 1rem; }
.board-card-meta { font-size: 0.82rem; color: var(--ink-soft); margin: 2px 0 0; }
.board-ref { font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
.board-tag { display: inline-block; padding: 0 6px; border-radius: var(--radius); background: rgba(166,128,46,0.14); color: var(--brass-text);
  font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.board-card-due { font-size: 0.88rem; font-weight: 600; margin: 8px 0 0; font-variant-numeric: tabular-nums; }
.board-card-note { font-size: 0.84rem; color: var(--ink-soft); margin: 2px 0 0; }
.board-progress { display: block; width: 100%; height: 6px; margin-top: 4px; accent-color: var(--library-green); }
.board-flag { font-size: 0.82rem; color: var(--brass-text); margin: 6px 0 0; padding-left: 10px; position: relative; }
.board-flag::before { content: ''; position: absolute; left: 0; top: 0.5em; width: 5px; height: 5px; border-radius: 50%; background: var(--brass); }
.board-move { display: flex; gap: 6px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--parchment-deep); }
.board-move select { flex: 1 1 auto; min-width: 0; min-height: 34px; height: 34px; font-size: 0.82rem; padding: 2px 6px; }
.board-move .button-sm { flex: 0 0 auto; min-height: 34px; padding: 0.3em 0.8em; font-size: 0.8rem; white-space: nowrap; }
.board-next { margin: 10px 0 0; font-size: 0.85rem; }
.board-next .button-sm { width: 100%; min-height: 34px; padding: 0.3em 0.8em; font-size: 0.8rem; }
.board-card-pinned { font-size: 0.75rem; color: var(--ink-faint); margin: 4px 0 0; }
.board-empty { font-size: 0.84rem; color: var(--ink-faint); margin: 0 4px 4px; }
.board-done { margin-top: 28px; }
/* ----- Admin people (CRM) -----
   The list is a row per person that folds to a stacked card on a phone; the person page
   is history on the left, the next step and notes on the right (stacked under 900px). */
.people-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 16px; }
.people-filters a { padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px; font-size: 0.85rem; color: var(--ink-soft); text-decoration: none; }
.people-filters a[aria-current="page"] { background: var(--library-green); border-color: var(--library-green); color: var(--parchment); }
.people-filters a[aria-current="page"] .board-count { color: var(--parchment); }
.people-list { list-style: none; margin: 12px 0 0; padding: 0; border-top: 1px solid var(--parchment-deep); }
.person-row { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.2fr) minmax(0, 1fr); gap: 6px 18px; padding: 12px 4px 12px 10px;
  border-bottom: 1px solid var(--parchment-deep); border-left: 3px solid transparent; }
.person-row-due { border-left-color: var(--brass); background: rgba(166,128,46,0.05); }
.person-row p, .person-side p { margin: 0; }
.person-main, .person-what, .person-when { min-width: 0; overflow-wrap: anywhere; }
.person-name { font-weight: 600; margin-right: 6px; }
.person-sub { font-size: 0.82rem; color: var(--ink-soft); }
.person-what { font-size: 0.9rem; }
.person-status { display: inline-block; padding: 0 7px; border-radius: var(--radius); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.03em;
  text-transform: uppercase; background: var(--parchment-deep); color: var(--ink-soft); vertical-align: 2px; }
.person-status-active { background: rgba(30,61,47,0.1); color: var(--library-green); }
.person-status-lead { background: rgba(166,128,46,0.14); color: var(--brass-text); }
.person-next { font-size: 0.88rem; font-weight: 600; color: var(--ink); }
.person-next-due { color: var(--brass-text); }
@media (max-width: 767px) { .person-row { grid-template-columns: 1fr; } }

.person-layout { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); gap: 28px; align-items: start; margin-top: 24px; }
@media (max-width: 899px) { .person-layout { grid-template-columns: 1fr; } .person-side { order: -1; } }
.person-timeline { list-style: none; margin: 12px 0 0; padding: 0 0 0 16px; border-left: 2px solid var(--parchment-deep); }
.tl-item { position: relative; padding: 0 0 14px 4px; }
.tl-item::before { content: ''; position: absolute; left: -22px; top: 6px; width: 10px; height: 10px; border-radius: 50%; background: var(--parchment-deep); border: 2px solid var(--parchment); }
.tl-email::before, .tl-contact::before { background: var(--sage); }
.tl-call::before { background: var(--library-green-2); }
.tl-order::before, .tl-project::before { background: var(--brass); }
.tl-review::before { background: var(--library-green); }
.tl-note::before { background: var(--burgundy); }
.tl-enquiry::before, .tl-handled::before { background: var(--ink-faint); }
.tl-item p { margin: 0; }
.tl-meta { font-size: 0.78rem; color: var(--ink-soft); }
.tl-kind { font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.72rem; margin-right: 4px; }
.tl-text { font-size: 0.92rem; overflow-wrap: anywhere; }
.person-side { display: grid; gap: 16px; min-width: 0; }
.person-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 14px 16px; box-shadow: var(--shadow-card); }
.person-card .h4 { margin: 0 0 6px; }
.person-card .person-gap { margin-top: 18px; }
.person-card label { display: block; font-size: 0.85rem; margin: 10px 0 4px; }
.person-inline { display: flex; gap: 6px; }
.person-inline input { flex: 1 1 auto; min-width: 0; }
.person-card textarea { width: 100%; margin-bottom: 6px; }
.person-note { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--parchment-deep); font-size: 0.9rem; overflow-wrap: anywhere; }
.person-projects { list-style: none; margin: 0; padding: 0; }
.person-projects li { padding: 6px 0; border-top: 1px solid var(--parchment-deep); display: grid; }
.person-projects li:first-child { border-top: 0; }
.person-account { margin-top: 40px; padding-top: 4px; border-top: 1px solid var(--parchment-deep); }

/* ----- Admin Today (the front page) -----
   What is waiting on the left, projects and deadlines on the right; one column under
   900px with the waiting list first, because it is the part that loses customers. */
/* The research dashboard and chat admin links beside the heading; they drop under it on a
   narrow screen. */
.today-head { flex-wrap: wrap; }
.today-links { display: flex; flex-wrap: wrap; gap: 10px; }
.today-dashboard-link { flex: 0 0 auto; white-space: nowrap; }
.today-grid { display: grid; grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr); gap: 28px; align-items: start; margin-top: 20px; }
@media (max-width: 899px) { .today-grid { grid-template-columns: 1fr; } }
.today-main, .today-side { display: grid; gap: 24px; min-width: 0; }
.today-block .h3 { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
.today-list { list-style: none; margin: 10px 0 0; padding: 0; border-top: 1px solid var(--parchment-deep); }
.today-item { padding: 10px 4px 10px 10px; border-bottom: 1px solid var(--parchment-deep); border-left: 3px solid transparent; overflow-wrap: anywhere; }
.today-item-stale { border-left-color: var(--brass); background: rgba(166,128,46,0.05); }
.today-item p, .today-side p { margin: 0; }
.today-what { font-size: 0.95rem; }
.today-tag { display: inline-block; margin-right: 6px; padding: 0 7px; border-radius: var(--radius); background: var(--parchment-deep); color: var(--ink-soft);
  font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; vertical-align: 1px; }
.today-done { display: flex; gap: 6px; margin-top: 8px; }
.today-done input { flex: 1 1 auto; min-width: 0; min-height: 34px; font-size: 0.85rem; }
.today-done .button-sm { min-height: 34px; padding: 0.3em 0.9em; }
.today-side .today-side-row { padding: 6px 0; border-top: 1px solid var(--parchment-deep); font-size: 0.9rem; overflow-wrap: anywhere; }
.today-side .today-side-row .board-flag { display: block; margin: 2px 0 0; }
.today-block .today-more, .today-side .today-more { margin-top: 10px; }
.today-records summary { cursor: pointer; font-weight: 600; }
.today-records { margin-top: 20px; padding-top: 16px; }

.dispatch-panel { margin-top: 16px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--card); }
.dispatch-panel p { margin: 0; }
.dispatch-panel .dispatch-intro { margin-top: 10px; }
.dispatch-head { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; justify-content: space-between; }
.dispatch-list { list-style: none; padding: 0; margin: 10px 0 0; }
.dispatch-row { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; padding: 8px 0; border-top: 1px solid var(--parchment-deep); font-size: 0.9rem; }
.dispatch-what { flex: 1 1 220px; }
.dispatch-tie { display: flex; gap: 6px; flex: 1 1 260px; min-width: 0; max-width: 100%; }
.dispatch-tie select { flex: 1 1 auto; min-width: 0; min-height: 34px; font-size: 0.85rem; }
.board-done summary { cursor: pointer; font-weight: 600; }
.board-done-list { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); margin-top: 12px; }
@media (max-width: 1099px) { .project-board { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 767px) { .project-board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 639px) { .project-board { grid-template-columns: 1fr; } }

/* ---------- Admin call list ----------
   Phone-first: this page is used standing up, one-handed, often on a bad signal. The
   card is a single column at every width and, on a phone, the dial button is a
   full-width 48px target rather than a text link, because a mis-tap here rings the wrong
   customer. Redesigned 2026-10-14 to be shorter: badges on one line, one row of
   actions, closed leads as one-line rows and only when chosen. */
.section.calls-page { padding: 32px 0 72px; }
.calls-head { margin-bottom: 16px; }
.calls-head .h2 { margin: 0; }
.calls-group { margin-top: 32px; }
.calls-group-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; }
.call-group-head { display: flex; align-items: baseline; gap: 10px; margin: 0; }
.call-count {
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 600;
  color: var(--parchment); background: var(--library-green);
  border-radius: 999px; padding: 2px 10px;
}
.call-group-note { color: var(--ink-soft); font-size: 0.875rem; margin: 0; }
.call-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 12px; }
.call-card {
  border: 1px solid var(--parchment-deep); border-radius: var(--radius);
  padding: 14px 16px; background: var(--card);
}
.call-card-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; }
.call-who { font-family: var(--font-display); font-size: 1.15rem; margin: 0; }
.call-meta { color: var(--ink-soft); font-size: 0.85rem; margin: 0; }
.call-detail { margin: 8px 0 0; font-size: 0.95rem; }
/* Why the card is here, as pills on one line. The row takes no space when empty. */
.call-badges { display: flex; flex-wrap: wrap; gap: 6px; }
.call-badges:has(.call-badge) { margin-top: 8px; }
.call-badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 0.8125rem; font-weight: 600; line-height: 1.5;
  background: var(--parchment-deep); color: var(--ink-soft);
}
.call-badge--hot { background: var(--burgundy); color: #fff; }
.call-badge--time { background: rgba(30, 61, 47, 0.1); color: var(--library-green); }
.call-badge--warn { background: rgba(139, 58, 58, 0.1); color: var(--burgundy-deep); }
.call-badge--good { background: var(--library-green); color: var(--card); }
/* Our notes and what we know about reaching them: always shown when there are any. */
.call-notes { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--parchment-deep); display: grid; gap: 6px; }
.call-notes .call-contact { margin: 0; }
.call-folded > summary { cursor: pointer; font-size: 0.85rem; color: var(--ink-soft); padding: 4px 0; }
.call-folded .call-history { margin-top: 6px; }
/* Closed leads: reference, not work, so one quiet row each. */
.call-closed-list { list-style: none; margin: 12px 0 0; padding: 0; border-top: 1px solid var(--parchment-deep); }
.call-closed-row { padding: 10px 2px; border-bottom: 1px solid var(--parchment-deep); }
.call-closed-who { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; }
.call-closed-row .call-who { font-size: 1rem; }
.call-closed-row .call-detail { margin-top: 4px; font-size: 0.875rem; color: var(--ink-soft); }
.call-closed-row .call-history { margin-top: 6px; }
.calls-closed-hidden { margin: 28px 0 0; color: var(--ink-soft); font-size: 0.95rem; }
/* Payment links: used one-handed on a phone while talking, so targets are large and the
   URL field is tall enough to long-press accurately. */
.paylink-result { margin: 16px 0 24px; padding: 14px; border-radius: var(--radius); background: var(--parchment); border-left: 3px solid var(--library-green); }
.paylink-result-head { margin: 0 0 10px; font-weight: 620; }
.paylink-url-label { display: block; font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: 4px; }
.paylink-url { width: 100%; min-height: 48px; padding: 10px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9rem; border: 1px solid var(--parchment-deep); border-radius: var(--radius); background: var(--card); color: var(--ink); }
.paylink-note, .paylink-hint { margin: 8px 0 0; font-size: 0.85rem; color: var(--ink-soft); }
.paylink-form { display: grid; gap: 14px; max-width: 460px; margin-bottom: 32px; }
.paylink-field { display: grid; gap: 4px; }
.paylink-field label { font-weight: 560; font-size: 0.9rem; }
.paylink-field input { min-height: 48px; padding: 10px; border: 1px solid var(--parchment-deep); border-radius: var(--radius); background: var(--card); color: var(--ink); font-size: 1rem; }
.paylink-recent-head { margin: 32px 0 12px; }
.paylink-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.paylink-item { border: 1px solid var(--parchment-deep); border-radius: var(--radius); padding: 14px; background: var(--card); }
/* Paid links recede: they are a record, not work. Same treatment as a closed call card. */
.paylink-item.is-paid { opacity: 0.62; }
.paylink-item-head { margin: 0; font-weight: 560; }
.paylink-item-state { margin: 4px 0 8px; font-size: 0.85rem; color: var(--ink-soft); }
.call-research { margin: 8px 0 0; padding: 8px 10px; font-size: 0.9rem; background: var(--parchment); border-left: 3px solid var(--library-green); }
.call-research-label { display: block; font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); }
.call-script { margin: 8px 0 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
.call-script summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; padding: 0 12px; font-weight: 600; color: var(--burgundy); }
.call-script-body { padding: 0 12px 10px; font-size: 0.9375rem; }
.call-script-body p { margin: 0 0 8px; }
.call-script-body ul { margin: 0 0 8px; padding-left: 18px; }
.call-script-body li { margin: 0 0 4px; }
.call-script-head { margin: 12px 0 4px !important; font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); font-weight: 600; }
.call-flag { margin: 8px 0 0; font-size: 0.9rem; color: var(--burgundy); font-weight: 560; }
.call-flag--time { color: var(--library-green); }
/* Why this card sits where it does in "To call" (urgencyOf in lib/calls.js). */
.call-urgency { display: inline-block; margin: 6px 0 0; padding: 2px 8px; font-size: 0.8125rem; font-weight: 600; border-radius: 999px; background: var(--parchment); color: var(--ink-soft); }
.call-urgency--hot { background: var(--burgundy); color: #fff; }
/* What we already know about reaching this lead. Quieter than .call-flag: a flag is a
   reason to act, this is context you read before acting. --none is the "we chose not to
   email them" case, which is a decision rather than a gap. */
.call-contact { margin: 8px 0 0; font-size: 0.9rem; color: var(--ink-soft); }
.call-search { display: flex; gap: 8px; align-items: center; margin: 0 0 12px; flex-wrap: wrap; }
/* Styled to match .field inputs rather than inheriting the UA default: this page is
   used on a phone, and an unstyled input[type=search] renders as a native rounded iOS
   field. font-size must stay at 1rem, because iOS Safari zooms the whole page when a
   focused field is under 16px, and -webkit-appearance:none kills the native shape. */
.call-search input {
  flex: 1 1 220px; min-height: 44px; padding: 11px 13px;
  -webkit-appearance: none; appearance: none;
  background: var(--parchment-deep);
  border: 1px solid rgba(138, 134, 116, 0.55);
  border-radius: var(--radius);
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
}
.call-search input:focus {
  outline: 2px solid var(--library-green-2); outline-offset: 0; border-color: var(--library-green-2);
}
.call-search-clear { flex: 0 0 auto; }
.call-filters { display: grid; gap: 6px; margin: 0; }
.call-filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.call-filter-head { flex: 0 0 64px; font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); }
.call-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 4px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--card);
  color: var(--ink); font-size: 0.88rem; text-decoration: none;
}
.call-chip:hover { border-color: var(--library-green); }
.call-chip.is-on { background: var(--library-green); border-color: var(--library-green); color: var(--card); }
.call-chip-count { font-size: 0.78rem; opacity: 0.75; }
/* On a phone the row labels cost a 64px column and pushed the chips onto seven lines.
   The chips say what they are ("All sources", "Any time"), so the labels go. */
@media (max-width: 599px) {
  .call-filter-head { display: none; }
  .call-filters { gap: 6px; }
  .call-chip { min-height: 34px; padding: 3px 10px; }
}
.call-filter-summary { margin: 10px 0 0; font-size: 0.95rem; }
.call-stage { margin: 6px 0 0; font-size: 0.9rem; color: var(--ink-soft); }
.call-value { font-weight: 640; color: var(--library-green); margin-left: 6px; }
/* Louder than the rest: somebody wrote to you and is still waiting. */
.call-flag--reply { color: var(--burgundy-deep); font-weight: 620; }
.call-contact--none { font-style: italic; }
/* One row: Call, the two follow-up texts, Log a call, Close lead. An opened <details>
   takes the whole width, so its form sits under the row instead of beside the buttons. */
.call-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; margin-top: 12px; }
.call-actions > form { margin: 0; }
.call-actions .button { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; padding: 0 16px; }
.call-actions > details { flex: 0 0 auto; }
.call-actions > details[open] { flex-basis: 100%; }
.call-actions > details > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 14px; border: 1px solid var(--line); border-radius: var(--radius);
  font-size: 0.9375rem; font-weight: 600; color: var(--ink-soft); background: var(--card);
}
.call-actions > details > summary::-webkit-details-marker { display: none; }
.call-actions > details > summary:hover { border-color: var(--library-green); color: var(--library-green); }
.call-actions > details[open] > summary { color: var(--library-green); border-color: var(--library-green); }
.call-no-phone { margin: 0; align-self: center; flex-basis: 100%; }
.call-dial { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 20px; }
@media (max-width: 599px) {
  .call-dial { flex-basis: 100%; min-height: 48px; }
}
.call-history { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.call-history li { display: grid; gap: 1px; font-size: 0.9rem; }
.call-history-when { color: var(--ink-soft); font-size: 0.8rem; }
.call-history-outcome { font-weight: 620; color: var(--ink); }
.call-history-note { white-space: pre-wrap; }
.call-form { display: grid; gap: 14px; margin-top: 10px; }
.call-outcomes { display: grid; gap: 8px; }
/* A stack of full-width rows, not a row of chips: chips put five 40px targets side by
   side on a 390px screen, which is how you tap "Not interested" meaning "No answer". */
.call-outcome {
  display: flex; align-items: center; gap: 10px; min-height: 44px;
  padding: 0 12px; border: 1px solid var(--parchment-deep); border-radius: var(--radius);
}
.call-outcome input { margin: 0; }
/* `.field label` (0,1,1) outranked .call-outcome and turned every row back into a block,
   so the radios sat top-left in a tall box. Scoped to the form so it wins. */
.call-form .field { margin: 0; }
.call-form .call-outcome { display: flex; margin: 0; font-weight: 560; cursor: pointer; }
.call-form .call-outcome:has(input:checked) { border-color: var(--library-green); background: rgba(30, 61, 47, 0.06); }
.call-form label.field > span { display: block; margin-bottom: 6px; }
.call-form input[type="date"] {
  min-height: 44px; padding: 8px 12px; background: var(--parchment-deep);
  border: 1px solid rgba(138, 134, 116, 0.55); border-radius: var(--radius);
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
}
.call-form > .button { justify-self: start; }
/* Columns from tablet width up. A phone keeps one full-width row per outcome (above). */
@media (min-width: 600px) {
  .call-outcomes { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .call-outcomes legend { grid-column: 1 / -1; }
}

.audit-list { font-size: 0.82rem; }
.audit-list code { color: var(--ink-soft); }

/* ----- Account research intake + project cards ----- */
/* The research intake now uses the shared .family-person collapsible rows
   (public/js/family-rows.js), same as the wedding intake — no bespoke grid here. */
.project-card { padding: 22px 0; border-bottom: 1px solid var(--parchment-deep); }
.project-card:last-child { border-bottom: 0; }
.project-card .portal-tree-block { margin-top: 18px; }

/* ----- Admin project editor ----- */
.milestone-row { display: grid; grid-template-columns: 120px 1.3fr 1.3fr 0.8fr; gap: 10px 14px; align-items: end; padding: 12px 0; border-bottom: 1px solid var(--parchment-deep); }
.milestone-row .field { margin: 0; }
.stack-form .field { margin-bottom: 14px; }
@media (max-width: 720px) { .milestone-row { grid-template-columns: 1fr 1fr; } }

/* ----- Wedding family-tree example (pre-rendered showcase, border baked in) ----- */
.tree-example { margin: 0 auto 40px; max-width: 800px; }
.tree-example-img { display: block; width: 100%; height: auto; border-radius: 3px;
  box-shadow: 0 2px 4px rgba(33,37,31,.08), 0 20px 46px -18px rgba(33,37,31,.4); }
.tree-example-cap { margin-top: 14px; text-align: center; font-size: .9rem; font-style: italic; color: var(--ink-soft); }

/* ----- Admin wedding-tree poster preview ----- */
.poster-preview { margin-top: 18px; max-width: 840px; }
.poster-preview svg { display: block; width: 100%; height: auto; border-radius: 3px;
  box-shadow: 0 2px 4px rgba(33,37,31,.08), 0 16px 38px -16px rgba(33,37,31,.32); }

/* ----- Poster generate form: empty-state warning, background thumbnail, orientation toggle ----- */
.poster-warn { margin: 4px 0 18px; padding: 11px 14px; border: 1px solid rgba(166,128,46,0.45);
  background: rgba(166,128,46,0.08); border-radius: var(--radius); color: var(--ink-soft); font-size: 0.9rem; }
.poster-bg-row { display: flex; gap: 14px; align-items: flex-start; }
.poster-bg-row select { flex: 1 1 auto; min-width: 0; }
.poster-bg-thumb { flex: 0 0 auto; width: 64px; height: 44px; object-fit: cover; border-radius: var(--radius);
  border: 1px solid rgba(138,134,116,0.4); background: var(--parchment-deep); }
.poster-orient { margin: 0; }
.seg { display: inline-flex; position: relative; border: 1px solid rgba(138,134,116,0.55); border-radius: var(--radius); overflow: hidden; }
.seg input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.seg label { display: inline-flex; align-items: center; gap: 8px; margin: 0; padding: 10px 16px; min-height: 44px;
  font-weight: 400; cursor: pointer; background: var(--parchment-deep); color: var(--ink); }
.seg input + label { border-left: 1px solid rgba(138,134,116,0.55); }
.seg > input:first-child + label { border-left: 0; }
.seg input:checked + label { background: var(--library-green); color: var(--parchment); }
.seg input:focus-visible + label { outline: 2px solid var(--library-green-2); outline-offset: -2px; }
.seg-glyph { font-size: 1.1em; line-height: 1; }

/* --- Manual-order admin forms + checkbox rows (PR: manual orders & onboarding) --- */
.admin-order-form { flex-direction: column; align-items: stretch; }
.admin-order-form .field { flex: 1 1 auto; }
.check { display: flex; align-items: center; gap: 8px; margin: 6px 0; font-size: 0.9375rem; font-weight: 500; }
.check input[type="checkbox"] { width: auto; margin: 0; flex: 0 0 auto; }
.admin-add-order { margin-top: 18px; }
.admin-add-order summary { cursor: pointer; color: var(--library-green); font-weight: 600; }
.admin-add-order .upload-form { margin-top: 14px; flex-direction: column; align-items: stretch; }

/* --- Admin Money tab (/admin/money) --- */
.money-periods { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin: 20px 0 8px; }
.money-period { border: 1px solid var(--line); border-radius: var(--radius-md); padding: 16px 18px; background: var(--card); }
.money-period .h3 { margin: 0 0 10px; }
.money-figures { display: grid; grid-template-columns: auto 1fr; gap: 4px 16px; margin: 0; }
.money-figures dt { color: var(--ink-soft); }
.money-figures dd { margin: 0; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; font-weight: 600; }
.money-figures-other { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); font-size: 0.9rem; }
.money-figures-other dd { font-weight: 400; }
.money-form .field { flex: 1 1 200px; }
/* The shared .field input list has no date type; scoped here so customer-page date inputs are untouched. */
.money-form .field input[type="date"] { width: 100%; padding: 10px 13px; min-height: 44px; background: var(--parchment-deep); border: 1px solid rgba(138, 134, 116, 0.55); border-radius: var(--radius); font-family: var(--font-body); font-size: 1rem; color: var(--ink); }
.money-table-wrap { overflow-x: auto; max-width: 100%; }
.money-mail-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.money-mail-head .h3 { margin: 0; }
.money-found { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.money-found-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--card); }
.money-found-row.is-unsure { border-style: dashed; }
.money-found-what { flex: 1 1 260px; min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.money-found-what .form-small { margin-top: 0; }
.money-found-subject { flex-basis: 100%; overflow-wrap: anywhere; }
.money-found-kind { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--line); }
.money-found-sale { color: var(--library-green); border-color: var(--library-green); }
.money-found-add { display: flex; gap: 8px; align-items: center; }
.money-found-add { flex-wrap: wrap; }
.money-found-add select { min-height: 40px; padding: 6px 10px; background: var(--parchment-deep); border: 1px solid rgba(138, 134, 116, 0.55); border-radius: var(--radius); font-family: var(--font-body); font-size: 0.95rem; color: var(--ink); }
.money-found-add input { width: 7.5em; min-height: 40px; padding: 6px 10px; background: var(--parchment-deep); border: 1px solid rgba(138, 134, 116, 0.55); border-radius: var(--radius); font-family: var(--font-body); font-size: 1rem; color: var(--ink); font-variant-numeric: tabular-nums; }
.money-not-counted td { color: var(--ink-soft); }

/* --- Account getting-started welcome card --- */
.welcome-block { background: var(--parchment-deep); border: 1px solid #e0d6c0; border-radius: 10px; padding: 24px 26px; margin-bottom: 8px; }
.welcome-block .kicker { color: var(--brass-text); }
.welcome-block .h3 { margin-top: 2px; }
.getting-started { margin: 14px 0 0; padding-left: 1.3em; }
.getting-started li { margin-bottom: 0.75em; line-height: 1.55; }

/* --- Mobile sticky CTA: keeps the primary action visible after the hero on phones.
   Tucks away once the footer scrolls into view — the footer carries its own contact +
   booking, so at the bottom the bar is redundant and its reserved band reads as dead
   space. The .cta-docked flag on <body> is toggled by an IntersectionObserver in
   site.js; with JS off it stays put, so behaviour degrades to always-visible. --- */
.mobile-cta { display: none; }
@media (max-width: 720px) {
  .mobile-cta {
    display: block;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
    background: rgba(246, 241, 231, 0.96);
    backdrop-filter: blur(6px);
    border-top: 1px solid rgba(138, 134, 116, 0.4);
    box-shadow: 0 -2px 14px rgba(33, 37, 31, 0.08);
    transition: transform 0.28s ease, opacity 0.28s ease;
  }
  .mobile-cta .button { width: 100%; }
  /* Reserve room for the bar ONLY on pages that render it, so every other page
     doesn't carry an empty band at the foot. */
  body:has(.mobile-cta) { padding-bottom: 76px; transition: padding-bottom 0.28s ease; }
  /* Footer in view → slide the bar out and reclaim its reserved space. */
  body.cta-docked .mobile-cta { transform: translateY(110%); opacity: 0; pointer-events: none; }
  body.cta-docked:has(.mobile-cta) { padding-bottom: 0; }
}
@media (max-width: 720px) and (prefers-reduced-motion: reduce) {
  .mobile-cta { backdrop-filter: none; transition: none; }
  body:has(.mobile-cta) { transition: none; }
}
@media print { .mobile-cta { display: none !important; } }

/* ---------- Authenticated app shell (admin + portal) ---------- */
/* Replaces the marketing header/footer on /admin, /account, /portal so the tool
   isn't wearing the shopfront's sales nav, CTA or newsletter. Three header
   variants (admin / customer / auth) share one sticky bar; one slim legal footer. */
.app-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--parchment);
  border-bottom: 1px solid rgba(138, 134, 116, 0.35);
}
.app-header-inner { padding-top: 12px; padding-bottom: 12px; }
.app-header-row { display: flex; align-items: center; justify-content: space-between; gap: 14px 20px; min-height: 44px; }
.app-header-top { display: flex; align-items: center; justify-content: space-between; gap: 14px 20px; }

.app-wordmark {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-display); font-weight: 580; font-size: 1.15rem;
  color: var(--ink); text-decoration: none; line-height: 1;
}
.app-wordmark:hover { color: var(--library-green); }
.app-wordmark-glyph { color: var(--library-green); flex: 0 0 auto; }
.app-wordmark-tag {
  font-family: var(--font-body); font-size: 0.68rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.13em; color: var(--brass-text);
  border: 1px solid rgba(166, 128, 46, 0.5); border-radius: 2px; padding: 2px 6px;
}

.app-nav { display: flex; flex-wrap: wrap; gap: 2px 4px; margin-top: 8px; }
.app-nav a {
  display: inline-flex; align-items: center; min-height: 40px;
  padding: 6px 12px; border-radius: var(--radius);
  font-size: 0.9rem; font-weight: 600; color: var(--ink-soft); text-decoration: none;
}
.app-nav a:hover { color: var(--library-green); background: rgba(30, 61, 47, 0.05); }
.app-nav a[aria-current="page"] {
  color: var(--library-green); background: rgba(30, 61, 47, 0.07);
  box-shadow: inset 0 -2px 0 var(--brass);
}
/* anchored dashboard sections (Customers/Projects) clear the sticky admin bar */
.account-block[id] { scroll-margin-top: 108px; }
/* in-page section jump-nav for the long admin-project console */
.admin-section-nav { display: flex; flex-wrap: wrap; gap: 4px 6px; margin: 18px 0 4px; }
.admin-section-nav a { font-size: 0.85rem; font-weight: 600; color: var(--ink-soft); text-decoration: none; padding: 5px 11px; border: 1px solid var(--line); border-radius: var(--radius-md); }
.admin-section-nav a:hover { color: var(--library-green); border-color: var(--sage); background: rgba(30, 61, 47, 0.04); }
/* JS pins it below the sticky app bar (top set inline from the header height) */
.admin-section-nav.is-sticky { position: sticky; z-index: 49; background: var(--parchment); padding: 10px 0; margin-bottom: 8px; box-shadow: 0 1px 0 rgba(138, 134, 116, 0.3); }

/* Admin list filter — appears on long Customers/Projects lists */
.list-filter { width: 100%; max-width: 320px; margin: 14px 0 6px; padding: 8px 12px; background: var(--parchment-deep); border: 1px solid var(--line); border-radius: var(--radius-md); font-family: var(--font-body); font-size: 0.9rem; color: var(--ink); }
.list-filter:focus { outline: 2px solid var(--library-green-2); outline-offset: 0; border-color: var(--library-green-2); }
.list-filter-empty { color: var(--ink-soft); }
.account-list li[hidden] { display: none; }

.app-header-end { display: flex; align-items: center; gap: 14px; min-width: 0; }
.app-account-label {
  font-size: 0.85rem; color: var(--ink-soft);
  max-width: 38ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-signout { margin: 0; flex: 0 0 auto; }
.button-sm { padding: 0.45em 1.05em; min-height: 40px; font-size: 0.85rem; }

/* Slim legal-only footer */
.app-footer { border-top: 1px solid rgba(138, 134, 116, 0.35); margin-top: 72px; padding: 26px 0; }
.app-footer-inner { font-size: 0.82rem; color: var(--ink-soft); }
.app-footer-inner p { margin: 0 0 0.4em; }
.app-footer-inner p:last-child { margin-bottom: 0; }
.app-footer-office { color: var(--ink-faint); } /* large/secondary metadata — faint allowed here, not body copy */
.app-footer-links a { color: var(--library-green); }

/* Auth (login / "check your email") screens: centre the card, kill the stranded empty band */
.auth-main { display: flex; flex-direction: column; min-height: calc(100dvh - 69px); }
.auth-main > .portal-section { flex: 1 1 auto; display: flex; align-items: center; min-height: 0; padding: 48px 0; }
.auth-main > .portal-section > .portal-narrow { width: 100%; }
/* "wrong door?" cross-link between the two sign-in surfaces (account ⇄ project portal) */
.portal-crosslink { margin-top: 0.6em; padding-top: 0.85em; border-top: 1px solid var(--parchment-deep); }

@media (max-width: 560px) {
  .app-account-label { max-width: 16ch; }
}

/* Research workflow tracker (admin project page) */
.research-tracker .research-phase { border: 1px solid var(--line, #e6ddce); border-radius: 8px; padding: .75rem 1rem; margin: 0 0 1rem; }
.research-tracker legend { font-weight: 600; padding: 0 .4rem; }
.research-step { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem 1rem; padding: .35rem 0; border-top: 1px solid var(--line, #f0e9db); }
.research-step:first-of-type { border-top: 0; }
.research-check { display: flex; align-items: baseline; gap: .5rem; flex: 1 1 60%; cursor: pointer; }
.research-links { display: inline-flex; flex-wrap: wrap; gap: .75rem; font-size: .85em; }
.research-links a { white-space: nowrap; }

/* ---------- Portal Library + book reader ---------- */

.library {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 26px;
  margin-bottom: 44px;
  box-shadow: var(--shadow-card);
}
.library #library-heading { margin-bottom: 0.7em; }
.library-lead { display: flex; gap: 26px; flex-wrap: wrap; align-items: flex-start; }
.library-cover {
  width: 140px; height: auto; flex: 0 0 auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}
.library-lead-body { flex: 1 1 18rem; min-width: 0; }
.library-title { font-family: var(--font-display); font-size: 1.35rem; font-weight: 580; margin: 0 0 0.2em; }
.library-subtitle { color: var(--ink-soft); margin: 0 0 0.6em; }
.library-chapter-count { margin-top: 0; }
.library-cta { margin: 1.1em 0 0; }
.library-notes {
  list-style: none; margin: 24px 0 0; padding: 20px 0 0;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 10px 32px;
}
.library-note { display: flex; align-items: baseline; gap: 0.6em; }
.library-note-link { font-weight: 560; }
.library-note-count { margin-top: 0; }
.library-progress-heading { color: var(--ink-soft); font-family: var(--font-body); font-weight: 600; }

/* ---------- Book reader ---------- */

.reader-section { padding-top: 56px; }
/* Fixed-width columns, and the wrapper itself is sized to its content
   (width: max-content — the two fixed tracks + gap, never full-container-width)
   then centred with margin-inline: auto. Centring the box itself, rather than
   leaving it full-width and centring the tracks inside with justify-content,
   means .reader's own bounding rect IS the visible contents-rail + text-column
   group, so it can't silently drift left inside a wider container. */
.reader {
  display: grid; grid-template-columns: 15rem 34rem;
  gap: 56px; align-items: start;
  width: max-content; max-width: 100%; margin-inline: auto;
}

.reader-contents-desktop {
  position: sticky; top: 24px;
  border-right: 1px solid var(--line);
  padding-right: 24px;
}
.reader-contents-mobile { display: none; }
.reader-book-title { font-family: var(--font-display); font-weight: 580; margin: 0 0 0.85em; }
.reader-chapter-list { list-style: none; margin: 0; padding: 0; }
.reader-chapter-list li { margin-bottom: 0.55em; }
.reader-chapter-list a { font-size: 0.9375rem; text-decoration: none; color: var(--ink-soft); }
.reader-chapter-list a:hover { color: var(--library-green); text-decoration: underline; text-decoration-color: var(--brass-bright); }
.reader-chapter-list a[aria-current="page"] { color: var(--library-green); font-weight: 620; }
.reader-back { margin: 1.4em 0 0; font-size: 0.875rem; }

.reader-body { max-width: 34rem; }

/* Contents/title view with a cover: cover + title text side by side within
   the same 34rem column the chapters read at. */
.reader-title-block { display: flex; gap: 32px; align-items: flex-start; }
.reader-title-cover {
  width: 160px; height: auto; flex: 0 0 auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
}
.reader-title-text { flex: 1 1 auto; min-width: 0; }
.reader-position { color: var(--ink-faint); font-size: 0.875rem; margin: 0 0 0.5em; }
.reader-subtitle { font-family: var(--font-display); color: var(--ink-soft); font-size: 1.125rem; font-weight: 500; }
.reader-intro { color: var(--ink-soft); }

.reader-prose { font-size: 1.125rem; line-height: 1.75; font-variant-numeric: oldstyle-nums; }
.reader-prose p { margin: 0 0 1.15em; }
.reader-prose h3 { font-family: var(--font-display); margin: 2rem 0 0.6rem; }
.reader-prose blockquote {
  margin: 1.75rem 0; padding-left: 1.25rem;
  border-left: 3px solid var(--brass); color: var(--ink-soft); font-style: italic;
}
.reader-prose figure { margin: 2.25rem 0; width: 100%; }
.reader-prose img { width: 100%; height: auto; border-radius: var(--radius); display: block; }
.reader-prose figcaption { font-size: 0.875rem; color: var(--ink-faint); margin-top: 0.6em; }
.reader-prose table { width: 100%; border-collapse: collapse; font-size: 0.9375rem; margin: 1.75rem 0; }
.reader-prose th, .reader-prose td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--line); }

.reader-pager {
  display: flex; justify-content: space-between; gap: 24px;
  margin-top: 3.5rem; padding-top: 24px; border-top: 1px solid var(--line);
}
.reader-pager-link {
  display: flex; flex-direction: column; gap: 0.3em;
  text-decoration: none; min-height: 44px; justify-content: center;
  padding: 0.6em 0;
}
.reader-pager-label { font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--brass-text); font-family: var(--font-body); font-weight: 600; }
.reader-pager-title { color: var(--ink); font-family: var(--font-display); font-size: 1.05rem; }
.reader-next { margin-left: auto; text-align: right; align-items: flex-end; }

/* ---------- The family's photographs (portal) ---------- */

.photos-section { padding-top: 56px; }
.photos-intro, .photos-reply { color: var(--ink-soft); max-width: 42rem; }
.photos-admin-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; margin: 0 0 28px; padding: 14px 18px;
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--card);
}
.photos-admin-status { margin: 0; flex: 1 1 18rem; }
.photos-admin-bar form { margin: 0; }
.photos-admin-bar .form-small { margin: 0; }
.photo-group { margin-top: 44px; }
.photo-grid {
  display: grid; gap: 22px; margin-top: 16px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  align-items: start;
}
/* A <figure> keeps the UA margin (1em 40px) unless it is reset here. */
.photo-card {
  margin: 0; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-card); overflow: hidden;
  scroll-margin-top: 96px; /* the reply redirect lands on #photo-<id>, under the sticky header */
}
.photo-card-img { display: block; width: 100%; height: auto; background: var(--line); }
.photo-card-body { padding: 16px 18px 18px; }
.photo-caption { margin: 0; }
.photo-from { margin-top: 8px; color: var(--ink-faint); }
.photo-questions { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.photo-questions-heading { margin: 0; font-weight: 560; }
.photo-questions-list { margin: 8px 0 0; padding: 0; list-style: none; }
.photo-questions-list li + li { margin-top: 8px; }
.photo-q-num {
  display: inline-block; min-width: 2.4em; margin-right: 0.35em; padding: 0.05em 0.45em;
  border: 1px solid var(--sage); border-radius: var(--radius-md);
  font-size: 0.875rem; font-weight: 600; text-align: center; color: var(--library-green);
}
.photo-replies { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.photo-replies-list { margin: 6px 0 0; padding: 0; list-style: none; }
.photo-replies-list li + li { margin-top: 8px; }
.photo-reply-meta { display: block; margin: 0; color: var(--ink-faint); }
.photo-reply-text { white-space: pre-line; }
.photo-reply-form { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.photo-reply-label { display: block; margin-top: 10px; font-size: 0.9375rem; font-weight: 560; }
.photo-reply-box {
  display: block; width: 100%; margin-top: 4px; padding: 9px 11px; resize: vertical;
  background: var(--parchment-deep); border: 1px solid rgba(138, 134, 116, 0.55); border-radius: var(--radius);
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
}
.photo-reply-box:focus { outline: 2px solid var(--library-green-2); outline-offset: 0; border-color: var(--library-green-2); }
.photo-comment { margin-top: 12px; }
.photo-comment summary { cursor: pointer; color: var(--library-green); font-size: 0.9375rem; }
.photo-reply-send { margin-top: 12px; }
.photo-notice {
  margin: 12px 0 0; padding: 0.5em 0.8em; border-radius: var(--radius-md);
  border: 1px solid var(--sage); background: rgba(30, 61, 47, 0.05); font-size: 0.9375rem;
}
.photo-notice-empty { border-color: var(--line); background: transparent; color: var(--ink-soft); }
.photo-preview-note { margin-top: 12px; color: var(--ink-faint); }

/* ---------- Research notes tables ---------- */

.notes-section { padding-top: 56px; }
.notes-intro { color: var(--ink-soft); max-width: 42rem; }
.notes-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius-md); margin-top: 12px; }
.notes-table { width: 100%; min-width: 640px; border-collapse: collapse; font-size: 0.9375rem; }
.notes-table th, .notes-table td {
  text-align: left; padding: 0.85em 1em;
  border-bottom: 1px solid var(--line); vertical-align: top;
}
.notes-table thead th {
  position: sticky; top: 0;
  background: var(--parchment-deep);
  font-family: var(--font-body); font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; font-size: 0.75rem;
  color: var(--ink-soft);
}
.notes-table tbody tr:hover { background: rgba(30, 61, 47, 0.035); }
.notes-years, .notes-source { color: var(--ink-soft); white-space: nowrap; }

/* Sources table: 3 columns (Record / What it told us / Citation), sized so the
   long-form citation text has room to WRAP rather than overflow or clip, and
   "What it told us" — the column that matters most — gets the most width. */
.notes-table-sources { table-layout: fixed; }
.notes-table-sources th:nth-child(1), .notes-table-sources td:nth-child(1) { width: 18%; }
.notes-table-sources th:nth-child(2), .notes-table-sources td:nth-child(2) { width: 58%; }
.notes-table-sources th:nth-child(3), .notes-table-sources td:nth-child(3) { width: 24%; }
.notes-table-sources .notes-source { white-space: normal; word-break: break-word; }
.notes-record { word-break: break-word; }
.notes-record a { font-weight: 560; }
.notes-record-ref { display: block; margin-top: 0.3em; font-size: 0.8125rem; color: var(--ink-faint); }

/* .reader needs 15rem + 34rem + 56px gap = 840px, and .container adds 24px
   padding on each side (48px total) around it — so two columns need a
   viewport of at least 888px. The old 860px breakpoint left an ~845-887px
   gap where the two-column layout overflowed the container horizontally. */
@media (max-width: 890px) {
  .reader { grid-template-columns: 1fr; gap: 28px; }
  .reader-contents-desktop { display: none; }
  .reader-contents-mobile {
    display: block;
    border: 1px solid var(--line); border-radius: var(--radius-md);
    padding: 14px 18px; background: var(--card);
  }
  .reader-contents-mobile summary {
    font-family: var(--font-display); font-weight: 580; cursor: pointer;
    padding: 0.3em 0;
  }
  .reader-contents-mobile .reader-chapter-list { margin-top: 0.8em; }
  .reader-contents-mobile .reader-back { border-top: 1px solid var(--line); padding-top: 0.9em; }
  .reader-body { max-width: none; }
  .reader-body-title { max-width: none; }
  .reader-title-block { flex-direction: column; gap: 20px; }
  .reader-title-cover { width: 128px; }
  .library-cover { width: 104px; }
  .reader-pager { flex-direction: column; }
  .reader-next { margin-left: 0; text-align: left; align-items: flex-start; }
}

/* ==========================================================================
   Customer reviews
   Appended at the end of the file on purpose. site.css is one flat sheet, so
   equal-specificity rules are decided by source order: .review-card (0,1,0)
   sitting after .record-card (0,1,0) is what lets it adjust the shared card
   without a heavier selector. Selectors here stay FLAT (single class) rather
   than descendant chains — a `.review-card figcaption p` (0,1,2) would quietly
   outrank a later single-class rule and is exactly the trap documented in
   CLAUDE.md.
   ========================================================================== */

/* ----- shared utilities these components need ----- */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.section-tight { padding: 22px 0 0; }
.centre-row { justify-content: center; }
.rule { border: 0; border-top: 1px solid var(--line); margin: 22px 0; }

/* ----- star rating (display) -----
   Brass, small and set on the baseline of the card. Deliberately NOT the large
   yellow star of a generic review widget: at this size it reads as a mark of
   record, which is the register-and-archive language the rest of the site uses. */
.stars { display: inline-flex; gap: 2px; line-height: 1; }
.star { color: var(--line); font-size: 0.95rem; }
.star.is-on { color: var(--brass); }
.review-stars { margin-bottom: 12px; }

.reviews-sub {
  max-width: 56ch; margin: 0 auto 8px; text-align: center; color: var(--ink-soft);
}
.reviews-aggregate {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  margin: 0 0 30px; color: var(--ink-soft); font-size: 0.94rem;
}
.reviews-aggregate--hero { margin-top: 18px; margin-bottom: 0; justify-content: flex-start; }

/* ----- the review card -----
   No `align-items: start` here on purpose: .card-grid's default stretch is what
   makes every card in a row the same height, which is in turn what makes the
   margin-top:auto on .review-attribution do anything. Measured at 1280px — with
   align-items:start the three cards came back 540/344/260px tall and the
   attribution lines did not align across the row. */
.review-card { display: flex; flex-direction: column; }
.review-quote { margin: 0 0 14px; }
.review-quote p { margin: 0; }
.review-photo {
  width: 100%; height: auto; margin: 4px 0 14px; border-radius: var(--radius);
}
/* Pushes the attribution to the bottom so names line up across a row of cards of
   differing quote lengths. */
.review-attribution {
  margin-top: auto; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 0.875rem; color: var(--ink-soft);
}
.review-name { font-weight: 600; color: var(--ink); }
.review-meta { color: var(--ink-faint); }
.review-service {
  display: block; margin-top: 3px; font-size: 0.78rem; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--brass-text);
}
.review-date { display: block; margin-top: 2px; font-size: 0.78rem; }

/* Column count follows the review count while the collection is small, so one
   review fills the row instead of leaving two empty columns beside it. The quote
   keeps a readable measure inside the wider card: a 1,200px line of prose is
   harder to read, not more impressive. Both modifiers stop applying at three,
   where .card-grid's own three columns are already right. */
.review-grid--1 { grid-template-columns: minmax(0, 1fr); }
/* A review card is a <figure>, and .record-card never resets the UA default
   margin: 1em 40px. Across three columns that reads as a gutter; in a single
   full-width row it just loses 80px, most of it noticeable on a phone. Reset
   only here, where the card is the row. */
.review-grid--1 .review-card { margin-inline: 0; }
.review-grid--1 .review-quote { max-width: 62ch; margin-inline: auto; }
/* .review-stars is an inline-block, so it needs to be a block before text-align
   can centre it over the quote. Left-aligned stars beside a centred quote read as
   a mistake at this width. */
.review-grid--1 .review-stars { display: block; text-align: center; }
.review-grid--1 .review-attribution { text-align: center; }
.review-grid--2 { grid-template-columns: repeat(2, 1fr); }

/* Single-quote layout, used in the checkout column where a grid would be noise. */
.reviews-section--single { padding: 0; }
.review-single { max-width: 100%; }

/* ----- filters ----- */
.review-filters {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 0 0 6px;
}
.review-filter {
  display: inline-block; padding: 7px 15px; border: 1px solid var(--line);
  border-radius: var(--radius-md); font-size: 0.875rem; color: var(--ink-soft);
  text-decoration: none; background: var(--card);
  transition: border-color 140ms ease, color 140ms ease, background 140ms ease;
}
.review-filter:hover { border-color: var(--brass); color: var(--ink); }
.review-filter.is-active {
  background: var(--library-green); border-color: var(--library-green); color: var(--parchment);
}

/* ----- star rating (input) -----
   The radios are rendered 5→1 in the markup so this sibling selector can light the
   checked star and every star AFTER it in source order (which is every LOWER
   number, i.e. the stars to its left once row-reverse flips them). That gives a
   working rating control with no JavaScript at all. */
.rating-fieldset { border: 0; padding: 0; margin: 0; }
.star-input { display: flex; flex-direction: row-reverse; justify-content: flex-end; gap: 4px; }
.star-input input { position: absolute; opacity: 0; width: 0; height: 0; }
.star-input label {
  cursor: pointer; font-size: 2rem; line-height: 1; color: var(--line);
  transition: color 120ms ease;
}
.star-input input:checked ~ label,
.star-input label:hover,
.star-input label:hover ~ label { color: var(--brass); }
/* Keyboard focus must be visible even though the input itself is hidden. */
.star-input input:focus-visible + label { outline: 2px solid var(--brass); outline-offset: 3px; }

/* ----- admin moderation ----- */
.admin-review-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 10px; }
.admin-review-body {
  margin: 0 0 14px; padding-left: 14px; border-left: 2px solid var(--brass);
  white-space: pre-wrap;
}
.admin-review-meta {
  display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px;
  margin: 0 0 14px; font-size: 0.85rem;
}
.admin-review-meta dt { color: var(--ink-faint); }
.admin-review-meta dd { margin: 0; color: var(--ink-soft); }
.admin-review-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.admin-review-actions form { margin: 0; }
.admin-review-photo { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 14px; }
.admin-review-photo img { max-width: 300px; height: auto; border-radius: var(--radius); }
.admin-review-edit { margin-top: 6px; }
.admin-review-filters { justify-content: flex-start; margin-bottom: 20px; }

.review-status {
  display: inline-block; padding: 2px 9px; border-radius: var(--radius-md);
  font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase;
  border: 1px solid;
}
.review-status--pending { color: var(--brass-text); border-color: var(--brass); }
.review-status--approved { color: var(--library-green); border-color: var(--sage); }
.review-status--rejected { color: var(--error); border-color: var(--error); }
.review-status--featured { color: var(--burgundy); border-color: var(--burgundy); }
.review-status--hidden { color: var(--ink-faint); border-color: var(--line); }

@media (max-width: 720px) {
  .review-grid { grid-template-columns: 1fr; }
  .reviews-aggregate { flex-wrap: wrap; }
  .admin-review-meta { grid-template-columns: 1fr; gap: 2px; }
  .admin-review-meta dt { margin-top: 6px; }
  .admin-review-photo { flex-direction: column; }
}

/* ---------- Cookie consent bar ---------- */
/* Fixed strip rather than a blocking modal: the Meta Pixel is non-essential, so
   barring the page until someone answers would be disproportionate — and this is a
   sales site. Dark ground matches the footer it sits over. */
.consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  background: var(--dark-ground);
  color: var(--dark-text);
  border-top: 1px solid var(--brass);
  box-shadow: 0 -8px 24px rgba(22, 36, 29, 0.28);
}
.consent-banner[hidden] { display: none; }
.consent-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px 28px;
  padding-top: 16px;
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
}
.consent-text { flex: 1 1 380px; }
/* Colour stated explicitly: the global `h1,h2,h3,h4 { color: var(--ink) }` rule is
   element-level and beats the dark ground colour inherited from .consent-banner,
   which rendered this heading at 1.03:1, i.e. genuinely invisible. */
.consent-title { font-family: var(--font-display); font-size: 1rem; margin: 0 0 4px; color: var(--dark-text); }
.consent-text p { margin: 0; font-size: 0.9rem; line-height: 1.55; }
.consent-text a { color: var(--brass-bright); }
.consent-actions { display: flex; gap: 12px; flex: 0 0 auto; }
.consent-actions .button { min-width: 132px; }

/* The bar covers the foot of the page while it is open, so reserve its measured
   height (set by consent.js) underneath the content. */
body.consent-open { padding-bottom: var(--consent-height, 0px); }

/* The website chat launcher (chat.exmoorlabs.com, customer-comms) reads these two custom
   properties from :root. Unlifted it covered the cookie bar's Accept button and the right
   edge of the sticky call bar (audit, 2026-10-12), so it rides above whatever is fixed to
   the foot of the screen: the consent bar (--consent-height, set by consent.js) and, on
   phones, the sticky call bar unless it has docked away. */
:root { --ccw-bottom: calc(16px + var(--consent-height, 0px)); }
@media (max-width: 720px) {
  :root:has(.mobile-cta) { --ccw-bottom: calc(88px + var(--consent-height, 0px)); }
  :root:has(body.cta-docked) { --ccw-bottom: calc(16px + var(--consent-height, 0px)); }
}

@media (max-width: 720px) {
  .consent-actions { width: 100%; }
  .consent-actions .button { flex: 1 1 0; min-width: 0; }
  /* 2026-10-15 (Ian): a first visit on a 390x844 phone, which is every Facebook ad click,
     had the bar (251px) with the sticky call bar lifted above it (71px) covering the foot
     of the screen from y=522, burying the first door's title, price and button. The bar
     is compacted here, same wording and buttons (44px tap targets), and the call bar
     steps aside until a choice is made, then comes back. The heading goes visually only:
     the region keeps its "Cookie consent" label.
     These selectors must out-specify the `body:has(.mobile-cta)` padding rules above
     (0,1,1): `body.consent-open:has(.mobile-cta)` is (0,2,1). */
  .consent-title { display: none; }
  .consent-inner { gap: 10px; padding-top: 10px; padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
  .consent-text p { font-size: 0.8rem; line-height: 1.4; }
  .consent-actions .button { min-height: 44px; padding-top: 8px; padding-bottom: 8px; }
  body.consent-open .mobile-cta { display: none; }
  /* The chat launcher steps aside too (2026-10-17). It rides just above the bar, which on
     House History is exactly where the copy-first hero puts the Commission button, so it
     covered the button on every first visit. It comes back once a choice is made. The
     widget mounts a light-DOM host (<div data-customer-comms>) around its shadow root. */
  body.consent-open > [data-customer-comms] { display: none; }
  body.consent-open:has(.mobile-cta) { padding-bottom: var(--consent-height, 0px); }
  :root:has(body.consent-open) { --ccw-bottom: calc(16px + var(--consent-height, 0px)); }
}

/* ---------- Offer pricing (was/now) ----------
   Rendered by views/partials/price-tag.ejs while catalogue.promo.active is true.
   Only the payable figure is shown (no struck-through "usual price" since 2026-10-12).
   Burgundy is NOT used here: it is the primary-CTA colour and nothing else (the
   Burgundy Reservation in DESIGN.md), and a price is not an action. Library green
   carries prices everywhere else on the site, so the offer price stays green. */
.price-offer { display: block; }
.price-offer-inline { display: inline; }
.price-now { color: inherit; }
/* The offer note under a card's price. Not a badge: DESIGN.md rules out decorative
   side-stripes and stamp treatments here, and the banner already carries the code. */
.price-offer-note {
  display: block;
  margin-top: 2px;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
}

/* ---------- Campaign landing page ---------- */
/* The band that meets someone arriving from print. Its whole job is continuity: the
   leaflet is burgundy where it states the offer, so this is too, and the visitor sees the
   same colour saying the same thing. It renders only when an offer actually applies to
   this visitor (see house-history-offer.ejs), so it can never announce a discount the
   checkout would refuse. */
.campaign-welcome {
  background: var(--burgundy);
  color: #fdf8f2;
  padding: 26px 0;
}
.campaign-welcome-kicker {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(253, 248, 242, 0.75);
  margin: 0 0 4px;
}
/* Fraunces at the section-title size, not a page heading: the h1 below it is still the
   page's subject, and this band must not out-shout it. */
.campaign-welcome-heading {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 2.2vw, 1.75rem);
  font-weight: 580;
  line-height: 1.12;
  color: #fdf8f2;
  margin: 0;
}
.campaign-welcome-note {
  margin: 6px 0 0;
  color: rgba(253, 248, 242, 0.85);
  max-width: 56ch;
}
/* The page-hero above already pads the top, so the band sits flush under the header. */
.campaign-welcome + .page-hero { padding-top: 44px; }

.campaign-price-line { margin-top: 14px; }

/* Price card: copy left, figure right, stacking on narrow screens. .record-card is a
   <figure> and never resets the UA default margin, which reads as a stray gutter here. */
.campaign-price-card {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 28px;
  align-items: center;
}
.campaign-price-figure {
  text-align: right;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3.2vw, 2.375rem);
  line-height: 1.12;
  color: var(--burgundy);
}
/* The offer note is set at a fixed small size, so it must not inherit the display size. */
.campaign-price-figure .price-offer-note {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  color: var(--ink-soft);
  text-align: right;
}
@media (max-width: 640px) {
  .campaign-price-card { grid-template-columns: 1fr; gap: 18px; }
  .campaign-price-figure, .campaign-price-figure .price-offer-note { text-align: left; }
}

/* The cutaway is wide and detailed, so it runs to the container rather than the narrow
   measure used for prose. */
.campaign-cutaway { width: 100%; height: auto; display: block; }

/* ---------- Portal: research progress panel ---------- */
/* Figures only (see views/partials/research-progress.ejs). Matches .library's
   card treatment so the portal reads as one surface. No inline styles anywhere:
   server.js sets styleSrc 'self' with no 'unsafe-inline', so a style attribute
   is dropped silently and the panel would render unstyled in production only. */
.progress-panel {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 26px;
  margin-bottom: 44px;
  box-shadow: var(--shadow-card);
}
/* Hooked on the class, not #progress-heading: the id exists for aria-labelledby,
   and an id selector here would sit at (1,1,0) and silently outrank any later
   single-class rule on the same heading (see the specificity note in CLAUDE.md). */
.progress-panel .h3 { margin-bottom: 0.7em; }
/* The plain status line that replaced "Stage 5 of 7 · <internal phase name>". */
.progress-status {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 580;
  margin: 0 0 1em;
}
/* A grid, not flex-wrap: with five tiles the wrapped one lands on the same
   column as the first rather than floating at an arbitrary offset. */
.progress-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 22px 32px;
  margin: 0;
}
.progress-tile { min-width: 0; }
.progress-label {
  color: var(--ink-soft);
  font-size: 0.92rem;
  margin: 0;
}
.progress-figure {
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 580;
  line-height: 1.1;
  margin: 0.1em 0 0;
  color: var(--library-green);
}
.progress-updated {
  margin: 22px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

/* Two columns on a phone. The base rule is auto-fit with a 160px minimum, and at 390px the
   container leaves about 342px of content: 160 + 32 gap + 160 = 352, just over, so it fell
   to ONE column and a family story ran to fifteen rows and roughly 1,250px of scroll.
   repeat(2, …) rather than auto-fit so the count is guaranteed, and minmax(0, 1fr) so a long
   label wraps inside its column instead of widening the grid. The figure and label step down
   slightly because a half-width column is not the place for 2rem numerals.

   ⚠️ Placed AFTER the .progress-* rules it overrides, and with single-class selectors, so it
   wins on SOURCE ORDER. An earlier version sat above them and used `.progress-tiles
   .progress-figure` (0,2,0), which beat the base rule (0,1,0) on specificity instead: it
   worked, but it would then have silently beaten any later `.progress-figure` rule added in
   another media query, at every width below 560px, with no error anywhere. */
@media (max-width: 560px) {
  .progress-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 18px; }
  .progress-figure { font-size: 1.6rem; }
  .progress-label { font-size: 0.85rem; }
}

/* ---------- Admin: disclosure for a rarely-wanted action ---------- */
/* Used where an action is legitimate but almost never the right one (a second
   project for a customer who already has one). Folded away rather than removed,
   and deliberately quiet: no button styling, so it does not read as the next
   step on the page. <details> is native, so there is no script here. */
.account-disclosure { margin-top: 18px; }
.account-disclosure > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--ink-soft);
  font-size: 0.95rem;
}
.account-disclosure > summary::-webkit-details-marker { display: none; }
.account-disclosure > summary::marker { content: ''; }
.account-disclosure > summary::before { content: '\25B8'; font-size: 0.8rem; line-height: 1; }
.account-disclosure[open] > summary::before { content: '\25BE'; }
.account-disclosure[open] > summary { margin-bottom: 0.6em; }

/* ---------- Admin: customer preview banner ---------- */
/* Says plainly that the page below is someone else's view, so the admin never
   mistakes a read-only preview for their own tools. */
.preview-banner {
  background: var(--parchment-deep);
  border-left: 3px solid var(--brass);
  border-radius: var(--radius);
  padding: 0.7rem 0.9rem;
  margin: 0 0 24px;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* A research session in the timeline: real, but quieter than a milestone Ian
   wrote himself, so his words stay the foreground. */
.milestone-session .h4 {
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--ink-soft);
}

/* --- research tree: fan chart + the list that re-roots it ------------------ */
/* The fan is square-ish and scales down, so unlike .portal-tree it needs no
   min-width and never scrolls sideways. That is the whole reason it was chosen
   over the banded chart: at 50 people the box tree is unreadable on a phone. */
.research-tree-block { margin-top: 2.75rem; scroll-margin-top: 1.25rem; }
.research-fan {
  margin-top: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--parchment);
  overflow: hidden;
}
.research-fan svg { display: block; width: 100%; height: auto; }
.research-tree-empty { margin-top: 0.75rem; }
/* Names whoever the chart is drawn around. Without it, re-rooting on a person with
   a short line redraws into a mostly blank ring and reads as nothing having changed. */
.research-centred-on {
  margin: 1rem 0 0; font-size: 0.9rem; color: var(--ink-soft);
}

.research-legend { list-style: none; margin: 1.25rem 0 0; padding: 0; display: grid; gap: 0.4rem; }
.research-legend-item { display: flex; align-items: baseline; gap: 0.55rem; font-size: 0.9rem; }
.research-legend-blurb { color: var(--ink-soft); }

/* A swatch is decorative: every row also carries its level as visually-hidden
   text, because colour alone is not an accessible way to state a finding. */
.research-swatch {
  width: 0.7rem; height: 0.7rem; border-radius: 50%; flex: none;
  border: 1px solid rgba(33, 37, 31, 0.12);
  transform: translateY(0.05rem);
}
.research-swatch-confirmed { background: #2f6f3e; }
.research-swatch-probable { background: #eab53a; }
.research-swatch-unfound { background: #e0735a; }
.research-swatch-supplied { background: #a9bccf; }

.research-tree-list-heading { margin-top: 1.75rem; margin-bottom: 0.25rem; }
.research-people {
  list-style: none; margin: 0.75rem 0 0; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--card); overflow: hidden;
}
.research-person {
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
  padding: 0.5rem 0.75rem;
  border-top: 1px solid rgba(138, 134, 116, 0.22);
}
.research-person:first-child { border-top: 0; }
.research-person-current { background: var(--parchment); }
.research-person-depth-1 { padding-left: 1.6rem; }
.research-person-depth-2 { padding-left: 2.45rem; }
.research-person-depth-3 { padding-left: 3.3rem; }
.research-person-depth-4 { padding-left: 4.15rem; }
.research-person-depth-5 { padding-left: 5rem; }
/* Nobody the walk could reach from the root. Parked at the end rather than
   dropped: a tree that silently omits a person is worse than one that parks them. */
.research-person-depth-x { padding-left: 0.75rem; opacity: 0.85; }
.research-person-name { font-size: 0.98rem; }
.research-person-years { font-size: 0.82rem; color: var(--ink-soft); font-variant-numeric: lining-nums tabular-nums; }
.research-group-heading {
  margin: 1.5rem 0 0; font-size: 0.78rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-soft);
}

@media (max-width: 560px) {
  .research-person-depth-3 { padding-left: 2.6rem; }
  .research-person-depth-4 { padding-left: 3.1rem; }
  .research-person-depth-5 { padding-left: 3.6rem; }
}

/* --- research tree: the pedigree view, the view switch and the sign-off ----- */
/* Two plain links styled as a segmented switch. No script: each is a real URL. */
.research-views {
  display: inline-flex; margin-top: 1rem;
  border: 1px solid var(--line); border-radius: 999px; overflow: hidden;
}
.research-view {
  padding: 0.4rem 0.95rem; font-size: 0.9rem; text-decoration: none; color: var(--ink);
}
.research-view + .research-view { border-left: 1px solid var(--line); }
.research-view-current { background: var(--library-green); color: var(--parchment); }
.research-view:not(.research-view-current):hover { background: var(--parchment); }

/* The pedigree SVG carries its natural width, so a short tree draws at real size
   instead of being blown up; a wide one scales down to the column. */
.research-pedigree {
  margin-top: 1rem; padding: 1rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--parchment);
}
.research-pedigree svg { display: block; max-width: 100%; height: auto; margin: 0 auto; }
.research-pedigree svg a:hover rect:first-of-type,
.research-pedigree svg a:focus rect:first-of-type { stroke: #a6802e; }

/* The phone version: the same people as a nested list. Hidden on wide screens. */
.research-pedigree-list { list-style: none; margin: 0; padding: 0; }
.research-tree-block > .research-pedigree-list {
  display: none; margin-top: 1rem; padding: 0.5rem 0.75rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--card);
}
.research-pedigree-list .research-pedigree-list {
  margin-left: 0.4rem; padding-left: 0.85rem; border-left: 1px solid rgba(166, 128, 46, 0.45);
}
.research-pedigree-item { padding: 0.3rem 0; }
.research-pedigree-item > .research-swatch { display: inline-block; margin-right: 0.35rem; }
.research-person-place { font-size: 0.82rem; font-style: italic; color: var(--ink-soft); }
.research-pedigree-item > .research-person-years,
.research-pedigree-item > .research-person-place { margin-left: 0.35rem; }

@media (max-width: 700px) {
  .research-pedigree { display: none; }
  .research-tree-block > .research-pedigree-list { display: block; }
}

/* Close family of whoever is at the foot: where siblings and children appear. */
.research-family {
  margin-top: 1.25rem; padding: 0.9rem 1rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--card);
}
.research-family-heading { margin: 0 0 0.5rem; }
.research-family-groups { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 1rem; }
.research-family-groups dt { font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-soft); padding-top: 0.2rem; }
.research-family-groups dd { margin: 0; }
.research-family-people { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.2rem; }
.research-family-people li { display: flex; align-items: baseline; gap: 0.45rem; flex-wrap: wrap; }
@media (max-width: 560px) {
  .research-family-groups { grid-template-columns: 1fr; }
  .research-family-groups dt { padding-top: 0.5rem; }
}

/* "Is this right?" */
.research-signoff {
  margin-top: 1.75rem; padding: 1rem 1.1rem;
  border: 1px solid var(--brass-bright, #c9a14b); border-radius: var(--radius-lg);
  background: var(--card); scroll-margin-top: 1.25rem;
}
.research-signoff .h4 { margin-top: 0; }
.research-signoff-notice {
  margin: 0 0 0.75rem; padding: 0.55rem 0.75rem; border-radius: var(--radius-lg);
  background: var(--parchment); font-size: 0.95rem;
}
.research-signoff-changed { font-weight: 600; }
.research-signoff-notes-heading { margin: 0.75rem 0 0.25rem; font-size: 0.9rem; color: var(--ink-soft); }
.research-signoff-notes { margin: 0; padding-left: 1.1rem; }
.research-signoff-notes li { margin: 0.2rem 0; white-space: pre-line; }
.research-signoff-approve { margin-top: 0.9rem; }
.research-signoff-correct { margin-top: 1.1rem; display: grid; gap: 0.4rem; justify-items: start; }
.research-signoff-correct .photo-reply-box { width: 100%; }
.research-signoff-prompt { margin-top: 1rem; font-size: 0.95rem; }
/* A year range must never break at its dash ("1888–" / "1951" on a phone). */
.research-person-years, .research-person-place { white-space: nowrap; }

/* --- research tree: Everyone (the whole tree, for sign-off) ------------------ */
/* The chart keeps its size and the panel scrolls sideways: a whole tree shrunk to
   the column would print every name at a couple of pixels. */
.research-everyone {
  margin-top: 1rem; padding: 0.75rem;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--parchment); overflow-x: auto;
}
.research-everyone svg { display: block; max-width: none; height: auto; }
.research-everyone svg a:hover rect:first-of-type,
.research-everyone svg a:focus rect:first-of-type { stroke: #a6802e; }
.research-families { display: none; margin-top: 1rem; }
.research-family-list {
  list-style: none; margin: 0.5rem 0 0; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--card);
}
.research-family-group { padding: 0.6rem 0.8rem; border-top: 1px solid rgba(138, 134, 116, 0.22); }
.research-family-group:first-child { border-top: 0; }
.research-family-couple { margin: 0; }
.research-family-children { margin: 0.25rem 0 0; padding-left: 0.9rem; font-size: 0.92rem; color: var(--ink-soft); }
@media (max-width: 700px) {
  .research-everyone { display: none; }
  .research-families { display: block; }
}

/* ---------- Portal: "How far the research has got" (readiness) ---------- */
/* views/partials/research-readiness.ejs. Sits inside .progress-panel for the card
   treatment. Single-class selectors throughout, all prefixed readiness-, so nothing
   here outranks or is outranked by the flat file's other rules (CLAUDE.md). Colours:
   the same green, amber and coral as the research-tree legend. */
.readiness-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem 1rem; }
.readiness-pill {
  font-size: 0.85rem; color: var(--ink-soft); background: var(--parchment);
  border: 1px solid var(--line); border-radius: 999px; padding: 0.15rem 0.7rem;
}
.readiness-intro { color: var(--ink-soft); margin: 0 0 1.4rem; max-width: 62ch; }
.readiness-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 28px 40px; }
.readiness-sub { font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-soft); margin: 0 0 0.8rem; font-family: var(--font-body); font-weight: 600; }
.readiness-scores { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.8rem; }
.readiness-score { display: grid; grid-template-columns: minmax(0, 1fr) auto 2.4rem; gap: 0.15rem 0.7rem; align-items: center; }
.readiness-score.is-bonus { opacity: 0.8; }
.readiness-score-label { font-size: 0.95rem; }
.readiness-score-num { font-size: 0.85rem; color: var(--ink-soft); text-align: right; font-variant-numeric: tabular-nums; }
.readiness-score-detail { grid-column: 1 / -1; font-size: 0.82rem; color: var(--ink-soft); }
.readiness-bar { display: inline-flex; gap: 3px; }
.readiness-bar span { width: 16px; height: 9px; border-radius: 2px; background: var(--parchment-deep); }
.readiness-bar.good .on { background: #2f6f3e; }
.readiness-bar.warn .on { background: #e0a526; }
.readiness-bar.bad .on { background: #d4583e; }
.readiness-gens { display: grid; gap: 1rem; }
.readiness-gen-label { font-size: 0.85rem; color: var(--ink-soft); margin: 0 0 0.35rem; }
.readiness-people { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 0.55rem 1rem; }
.readiness-person { display: flex; align-items: flex-start; gap: 0.45rem; min-width: 0; }
.readiness-person-text { display: flex; flex-direction: column; min-width: 0; }
.readiness-name { display: block; font-size: 0.9rem; line-height: 1.25; overflow-wrap: anywhere; }
.readiness-years { display: block; font-size: 0.78rem; color: var(--ink-soft); font-variant-numeric: lining-nums tabular-nums; }
.readiness-dot { flex: none; width: 12px; height: 12px; border-radius: 50%; border: 2px solid transparent; margin-top: 0.2rem; box-sizing: border-box; }
.readiness-dot.complete { background: #2f6f3e; }
.readiness-dot.good { background: #bfdcc6; border-color: #2f6f3e; }
.readiness-dot.partial { background: #eab53a; }
.readiness-dot.blank { background: var(--card); border-color: #d4583e; border-style: dashed; }
.readiness-legend { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin: 1.3rem 0 0; font-size: 0.82rem; color: var(--ink-soft); }
.readiness-legend-item { display: inline-flex; align-items: center; gap: 0.35rem; }
.readiness-legend-item .readiness-dot { margin-top: 0; }
.readiness-details { margin-top: 1.4rem; border-top: 1px solid var(--line); padding-top: 1rem; }
.readiness-details > summary { cursor: pointer; color: var(--ink-soft); }
/* The table scrolls inside its own box. Without the min-width: 0 chain the panel
   grew to the table's width and the whole page scrolled sideways on a phone. */
.readiness-panel { min-width: 0; max-width: 100%; }
.readiness-details { min-width: 0; max-width: 100%; }
.readiness-table-wrap { overflow-x: auto; margin-top: 0.8rem; max-width: 100%; position: relative; } /* holds the cells' visually-hidden text, which is absolutely positioned */
.readiness-table { border-collapse: separate; border-spacing: 3px; font-size: 0.82rem; }
.readiness-table th { font-weight: 500; text-align: left; color: var(--ink-soft); padding: 0 0.3rem; white-space: nowrap; }
.readiness-table tbody th { color: var(--ink); min-width: 11rem; white-space: normal; padding: 0.2rem 0.5rem 0.2rem 0; }
.readiness-cell { min-width: 2.4rem; height: 1.7rem; border-radius: 2px; text-align: center; font-variant-numeric: tabular-nums; }
/* Four states, four hues AND four lightnesses (Ian, 2026-09-30: the old pale tints
   read as one colour). Dark green, pale hatched blue, bright amber, coral: they stay
   apart in greyscale and for red-green colour blindness, not just in full colour. */
.readiness-cell.done { background: #2f6f3e; color: #ffffff; }
.readiness-cell.none { background: #c9d9e8 repeating-linear-gradient(135deg, transparent 0 4px, rgba(62, 92, 122, 0.22) 4px 5px); color: #2c4a66; }
.readiness-cell.thin { background: #eab53a; color: #3d2c05; }
.readiness-cell.todo { background: #e0735a; color: #3f1208; }
.readiness-cell.na { background: transparent; border: 1px dashed var(--line); }
.readiness-swatch { display: inline-block; min-width: 18px; height: 12px; }
@media (max-width: 760px) {
  .readiness-grid { grid-template-columns: minmax(0, 1fr); }
}

/* --- /h/<code>: the house checker texted to Facebook leads --------------------------- */
.house-check .h3 { margin-top: 0; }
.house-verdict { margin: 24px 0; padding: 16px 18px; background: var(--parchment); border-left: 4px solid var(--sage); border-radius: var(--radius); }
.house-verdict--green { border-left-color: var(--library-green); }
.house-verdict--red { border-left-color: var(--error); }
.house-verdict--amber { border-left-color: var(--brass); }
.house-verdict-for { font-size: 0.875rem; color: var(--ink-soft); margin-top: -4px; }
.house-verdict-price { font-weight: 600; }
.house-check-done { padding: 10px 12px; background: var(--parchment); border-left: 4px solid var(--library-green); }
.house-check-form { margin: 28px 0; }
.house-check-form .button { min-height: 44px; }
.house-check-choices { border-top: 1px solid var(--line); padding-top: 24px; display: grid; gap: 16px; }
.house-check-choices form { margin: 0; }
.house-option { padding: 18px 18px 20px; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-md); }
.house-option .h3 { margin: 0 0 10px; }
.house-option p { margin: 0 0 14px; }
.house-option--order { background: var(--parchment); border-left: 4px solid var(--burgundy); }
.house-option--quiet .h3 { font-size: 1.05rem; }
.house-check-times { border: 0; padding: 0; margin: 0 0 14px; display: flex; flex-wrap: wrap; column-gap: 20px; }
.house-check-when { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-weight: 400; }
.house-check-choice { width: 100%; min-height: 44px; white-space: normal; }
.house-check-art { display: block; width: 100%; height: auto; margin: 8px 0 20px; }
.house-check-choice { text-align: center; }
/* Wide screens: intro beside the checker, the three choices in a row. Phones keep one column. */
.house-check-wide { max-width: 1100px; }
.house-check-grid { display: grid; gap: 8px 40px; }
.house-check-tool .house-check-form { margin-top: 0; }
@media (min-width: 900px) {
  .house-check-grid { grid-template-columns: 1fr 1fr; grid-template-areas: "intro tool" "intro about" "choices choices"; align-items: start; }
  .house-check-intro { grid-area: intro; }
  .house-check-tool { grid-area: tool; }
  .house-check-about { grid-area: about; }
  .house-check-grid > .house-check-choices { grid-area: choices; }
  .house-check-tool { padding-top: 4px; }
  .house-check-tool .house-verdict { margin-top: 0; }
  .house-check-choices { grid-template-columns: repeat(3, 1fr); align-items: stretch; }
  .house-option { display: flex; flex-direction: column; }
  .house-option form, .house-option > .button { margin-top: auto; }
}
.house-check-book { display: flex; align-items: center; gap: 18px; margin: 0 0 20px; }
.house-check-book img { width: 160px; height: auto; flex: none; object-fit: contain; }
.house-check-book figcaption { font-size: 0.9375rem; color: var(--ink-soft); }

/* Once checked, the result leads and "Check another address" follows it. */
.house-check-tool { display: flex; flex-direction: column; }
.house-check-tool .house-verdict { order: -1; margin-top: 0; }
.house-verdict { scroll-margin-top: 88px; }
#fh-delivered { scroll-margin-top: 72px; } /* the homepage family door links here; clears the sticky header */
.house-check-tool .house-verdict + .house-check-form, .house-check-tool .house-check-form:has(~ .house-verdict) { margin-top: 24px; }
.house-verdict-sub { font-size: 1rem; margin: 18px 0 6px; }
.house-verdict .check-list { margin: 0 0 6px; }
.house-verdict-when { font-size: 0.9375rem; color: var(--ink-soft); }
.house-verdict-call { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }
.house-verdict-call-title { font-size: 1.05rem; margin: 0 0 6px; }
.house-verdict-call legend { font-size: 0.9375rem; color: var(--ink-soft); padding: 0; margin-bottom: 2px; }
.house-verdict-call form { margin: 0; }
/* Phones: the checker first, so the form is on the first screen a text message opens. */
@media (max-width: 899px) {
  .house-check-tool { order: -1; }
  .house-check-tool .house-check-form { margin-bottom: 8px; }
}
.house-check-about { padding-top: 8px; }
.house-check-about-sub { font-size: 1rem; margin: 14px 0 6px; }
@media (max-width: 599px) {
  .house-check-book img { width: 110px; }
  .house-check-book { gap: 14px; }
  .house-check-book figcaption { font-size: 0.875rem; }
}
.call-close-actions { display: flex; flex-wrap: wrap; gap: 8px; padding: 6px 0 4px; }
.call-close-actions form { margin: 0; }
.call-close-actions .button { min-height: 44px; }

/* ---------- Admin: house-history research framework ---------- */
/* views/partials/admin-house-readiness.ejs. Reuses the .readiness-score rows from the
   portal panel; only the stage ladder and blocker list are new. */
.framework-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.25rem 1rem; }
.framework-stages { list-style: none; margin: 0.4rem 0 1.2rem; padding: 0; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 4px; counter-reset: framework; }
.framework-stage { counter-increment: framework; padding: 0.5rem 0.6rem; font-size: 0.85rem; line-height: 1.25; border-radius: 4px; background: var(--parchment-deep); color: var(--ink-soft); }
.framework-stage::before { content: counter(framework) ". "; }
.framework-stage.is-done { background: #bfdcc6; color: var(--ink); }
.framework-stage.is-current { background: #2f6f3e; color: #fff; font-weight: 600; }
.framework-scores { margin-bottom: 1.2rem; }
.framework-blockers { margin: 0; padding-left: 1.2rem; font-size: 0.88rem; display: grid; gap: 0.35rem; overflow-wrap: anywhere; }
@media (max-width: 760px) {
  .framework-stages { grid-template-columns: minmax(0, 1fr); }
}
