/*
 * memorials directives — full text in DIRECTIVES.md
 * 1. SSOT — Extend the one that exists. Never a second copy that can disagree.
 * 2. Caveman comments — Shortest technical line that lands. Why, not what.
 * 3. No post-mortems — Code says what is, never what it used to be. Git keeps the history.
 * end directives
 */
/* Fonts, palette, reset, and the chrome all three surfaces share: the button,
 * the focus ring, the placeholder hatch. Loaded first by site.css, hub.css and
 * checkout.css; none of them works alone. Hand-authored, no build step.
 *
 * The palette is a default: brand_vars() re-declares it per outlet on the
 * public site. The console ignores that and stays in our colors.
 *
 * Fonts are local. No third-party request means no DNS, TLS and redirect in
 * front of the text — that is where font-driven layout shift comes from. The
 * three latin faces are preloaded, so they are usually in hand before paint.
 */

/* ---------- fonts ---------- */
/* All OFL 1.1, cut by tools/fonts.py. wght stays wide enough for a <strong> in
 * a submitted obituary; opsz is whole, because the design sets one face at 11px
 * and at 78px. */
@font-face {
  font-family: Newsreader;
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/static/fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: Newsreader;
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/static/fonts/newsreader-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: Newsreader;
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/newsreader-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: Newsreader;
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/newsreader-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/instrument-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/instrument-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/plex-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/plex-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- tokens ---------- */
/* The working voice — the consoles and the family's workflow pages. Read at a
   glance, not read: Plex Sans matches the Plex Mono already in the labels, and
   the editorial serif stays for the one thing that is writing - the notice. */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/plex-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/static/fonts/plex-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --color-paper: #faf8f4;
  --color-paper-alt: #f2efe8;
  --color-card: #ffffff;

  --color-ink: #2c2a26;
  --color-ink-2: #3a3630;
  --color-ink-3: #4d4841;
  --color-muted: #6f6a61;
  --color-muted-2: #8a8377;

  --color-rule: #eae5dc;
  --color-rule-2: #dcd5c8;
  --color-rule-3: #e5dfd3;

  /* The sage. The one color an outlet is most likely to change; see
     app/services/branding.py, which derives the rest from theirs. */
  --color-accent: #e7ece3;
  --color-accent-ink: #3e4a3a;
  --color-accent-ink-2: #5c6656;
  --color-accent-rule: #c6d0c0;

  /* The palette has no red. This is for form errors only. */
  --color-danger: #8a1c1c;

  /* Chart series. Deeper than the console's washes on purpose: a wash reads as
     gray in a 24px column, and the two have to stay apart for a reader who
     cannot tell green from amber. Checked, not eyeballed. */
  --chart-paid: #3e7d3a;
  --chart-free: #c98a2e;

  /* Placeholder hatch, for a notice with no photograph. --ph-step is the comb
     it is drawn at; a variant sets that one number and nothing else. */
  --color-ph-a: #e8e3d8;
  --color-ph-b: #ddd7c9;
  --ph-step: 10px;

  /* The bar's own height. A number anything sitting under a sticky bar has to
     agree with, so it is declared once. */
  --adminbar-h: 38px;

  --font-serif: Newsreader, Georgia, serif;
  --font-sans: "Instrument Sans", Helvetica, Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;

  --maxw: 1180px;
  --pad: 40px;
  /* The masthead's height. Anything pinning under it measures from this
     rather than guessing. */
  --bar-1: 72px;
  /* Where the masthead's top edge sits: 0, or under the desk's bar when a
     staffer is signed in. Everything that pins or lands under the masthead
     adds this, so staff and readers get the same clearance. */
  --bar-top: 0px;
}
body:has(.adminbar) { --bar-top: var(--adminbar-h); }
/* The focus ring, mixed off the accent, so a newsroom that re-brands glows its
   own color. On body as well as :root because brand_vars() re-points the accent
   there, and a variable is substituted where it is declared, not where used. */
:root, body {
  --focus-rule: color-mix(in srgb, var(--color-accent-rule) 92%, var(--color-ink));
  --focus-halo: color-mix(in srgb, var(--color-accent-rule) 35%, transparent);
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; border: 0 solid; }
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
/* Anchor jumps land clear of the sticky bar, not under it. On the targets, not
   the root: root scroll-padding also covers the bar's own inputs, and every
   caret reveal in one would creep the page up. */
main [id] { scroll-margin-top: calc(var(--bar-top) + var(--bar-1) + 24px); }
body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
blockquote, dl, dd, figure, h1, h2, h3, h4, h5, h6, p, pre { margin: 0; }
h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }
fieldset, legend { margin: 0; padding: 0; }
img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: var(--color-ink); text-decoration: none; transition: opacity .15s ease; }
a:hover { opacity: .7; }
button, input, optgroup, select, textarea {
  font: inherit; letter-spacing: inherit; color: inherit;
  background-color: transparent; margin: 0; padding: 0;
}
button { cursor: pointer; }
summary { cursor: pointer; }
input[type="file"] { cursor: pointer; }
input[type="file"]::file-selector-button {
  cursor: pointer; font: inherit; font-size: 13px; margin-right: 10px;
  padding: 6px 12px; border: 1px solid var(--color-rule-2); border-radius: 7px;
  background: var(--color-paper-alt); color: var(--color-ink-2);
}
/* The browse button is 4px taller with its UA padding. */
::file-selector-button {
  box-sizing: border-box; border: 0 solid; margin: 0; padding: 0;
  font: inherit; color: inherit; background-color: transparent;
}
code, kbd, samp, pre { font-family: var(--font-mono); font-size: 1em; }
/* Chrome pads a date input's internals: 2px taller without this. */
::-webkit-date-and-time-value { min-height: 1lh; text-align: inherit; }
::-webkit-datetime-edit { display: inline-flex; }
::-webkit-datetime-edit-fields-wrapper { padding: 0; }
::-webkit-datetime-edit,
::-webkit-datetime-edit-year-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-minute-field,
::-webkit-datetime-edit-second-field,
::-webkit-datetime-edit-millisecond-field,
::-webkit-datetime-edit-meridiem-field { padding-block: 0; }
[hidden] { display: none !important; }
::selection { background: var(--color-accent); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Hidden to the eye, read aloud. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0;
}

/* ---------- buttons ---------- */
/* One button on all three surfaces. The scale here is the default; a surface
   that runs its own re-declares .btn after this file. Below a:hover, or a link
   drawn as a button would fade to the link's opacity instead of the button's. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: 15px; padding: 13px 24px; border-radius: 8px;
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
}
.btn--block { width: 100%; padding: 16px 28px; font-size: 16px; border-radius: 12px; }
.btn--sm { font-size: 14px; padding: 11px 20px; }
.btn--primary { background: var(--color-ink); color: var(--color-paper); }
.btn--ghost { background-color: transparent; border-color: var(--color-rule-2); color: var(--color-ink); }
.btn:hover { opacity: .85; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
/* Posted, waiting on the server. The spinner sits inside the button's own box
   and the room for it is reserved, so the page never moves. */
.btn.is-busy { position: relative; cursor: progress; padding-left: 38px; }
.btn.is-busy::before {
  content: "";
  position: absolute; left: 16px; top: 50%; margin-top: -7px;
  width: 14px; height: 14px; border-radius: 999px;
  border: 2px solid currentColor; border-right-color: transparent;
  opacity: .55;
  animation: btn-spin .7s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn.is-busy::before { animation: none; } }

/* ---------- the focus ring ---------- */
/* Every field that glows, glowing the same way. The tokens are mixed off the
   outlet's accent, so this follows a re-brand. */
:is(.input, .search):focus,
:is(.editor, .attach):focus-within,
:is(.form, .note-form) :is(input, textarea):focus,
.form-field :is(input, select):focus {
  border-color: var(--focus-rule);
  box-shadow: 0 0 0 3px var(--focus-halo);
}

/* ---------- the placeholder hatch ---------- */
/* One gradient. The three names beside .ph wear the hatch without the class,
   and it is held as a variable as well so a variant that has to out-rank a
   background of its own re-applies the hatch rather than re-writing it. */
.ph, .notice__thumb, .compare__gap, .wiz__readportrait {
  --ph-hatch: repeating-linear-gradient(135deg,
    var(--color-ph-a) 0 var(--ph-step),
    var(--color-ph-b) var(--ph-step) calc(var(--ph-step) * 2));
  background: var(--ph-hatch);
}

/* ---------- the admin bar ---------- */
/* Thin, dark, above everything, on every page a signed-in person can see -
   their own public site included. Borrowed from WordPress because a newsroom
   already knows what it is and where it lives. */
.adminbar {
  position: sticky; top: 0; z-index: 60;
  background: #2c2a26; color: #d9d4cb;
  font-size: 12.5px; line-height: 1;
}
.adminbar__in {
  max-width: none; margin: 0; padding: 0 10px; height: var(--adminbar-h);
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
}
.adminbar__left, .adminbar__right { display: flex; align-items: center; gap: 2px; }
/* The console's nav opens from the bar, so a phone has one bar and not two.
   A desk has the nav as a column already, and a page with no drawer behind it
   has nothing to open. */
.navbutton { display: none; }
/* The hover highlight is a shape, so it needs room to be one: enough padding
   that the label sits inside it, and enough height that it never reaches the
   top and bottom of a 38px bar. */
/* Not the tray's links: those are rows on paper, styled below. */
.adminbar a:not(.bell__tray a, .barmenu__sheet a), .adminbar__out {
  color: #d9d4cb; padding: 7px 12px; border-radius: 8px; line-height: 1.4;
  background: none; border: 0; font: inherit; cursor: pointer; white-space: nowrap;
}
.adminbar a:not(.bell__tray a, .barmenu__sheet a):hover, .adminbar__out:hover { background: #3b3630; color: #fff; }
/* A page with no drawer keeps its links in the bar on a desk, and behind the
   bar's own menu on a phone. Never both. */
.barmenu { display: none; }
.adminbar__home { font-weight: 500; }
.adminbar a.adminbar__who { color: #8a8377; padding: 7px 8px; }
.adminbar__count {
  background: #4a443c; border-radius: 6px; padding: 2px 7px; font-size: 11px;
  margin-left: 2px;
}
/* On a phone the links do not fit. They scroll sideways rather than squashing
   the one thing on the right that always has to be reachable: sign out. */
@media (max-width: 760px) {
  /* A phone gets one bar instead of two, so it can be a bar rather than a
     hairline: 52px is a real touch target for the two things on it. */
  :root { --adminbar-h: 52px; }
  .adminbar { font-size: 14px; }
  .adminbar__in { gap: 8px; padding: 0 8px; }
  .adminbar__right { flex: none; }
  /* Who and sign-out move to the foot of the drawer; the bar keeps the bell. */
  .adminbar__who, body:has(.navtoggle) .adminbar__out { display: none; }
  .adminbar__home { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }

  .adminbar a:not(.bell__tray a, .barmenu__sheet a), .adminbar__out { padding: 10px 12px; }
  /* No drawer: the links and sign-out leave the bar for the menu, so what is
     left is the menu, whose desk this is, and the bell. */
  body:not(:has(.navtoggle)) .adminbar__left a:not(.adminbar__home, .barmenu__sheet a),
  body:not(:has(.navtoggle)) .adminbar__out { display: none; }
  body:not(:has(.navtoggle)) .barmenu { display: block; position: relative; flex: none; }
  .barmenu__button {
    list-style: none; display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; margin-right: 2px; border-radius: 8px;
    font-size: 17px; line-height: 1; color: #d9d4cb; cursor: pointer;
  }
  .barmenu__button::-webkit-details-marker { display: none; }
  .barmenu__button:hover, .barmenu[open] .barmenu__button { background: #3b3630; color: #fff; }
  /* The same sheet as the bell's, from the other side of the bar. */
  .barmenu__sheet {
    position: fixed; left: 0; right: 0; top: var(--adminbar-h); z-index: 70;
    background: #fff; color: #2c2a26; border-bottom: 1px solid #e3ded4;
    box-shadow: 0 12px 32px rgba(20, 18, 14, .18); padding: 6px 0;
  }
  .barmenu__sheet a, .barmenu__out {
    display: flex; align-items: center; justify-content: space-between; width: 100%;
    padding: 14px 16px; font: inherit; font-size: 15px; color: inherit; text-decoration: none;
    background: none; border: 0; text-align: left; cursor: pointer;
  }
  .barmenu__sheet a:hover, .barmenu__out:hover { background: #faf8f4; }
  .barmenu__sheet .adminbar__count { background: #ece8e0; color: #2c2a26; font-size: 12px; }
  .barmenu__who { border-top: 1px solid #ece8e0; margin-top: 6px; color: #6b655b; }
  .barmenu__out { color: #8a8377; }
  body:has(.navtoggle) .navbutton {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 40px; height: 40px; margin-right: 2px; border-radius: 8px;
    font-size: 17px; line-height: 1; color: #d9d4cb; cursor: pointer;
  }
  body:has(.navtoggle) .navbutton:hover { background: #3b3630; }
  /* The checkbox is the control and the label is only its face, so the focus
     ring has to be moved onto the face by hand. */
  .navtoggle:focus-visible ~ .adminbar .navbutton { outline: 2px solid #d9d4cb; outline-offset: -2px; }
  /* The drawer holds these, and holds them the same way on every page. */
  body:has(.navtoggle) .adminbar__left a:not(.adminbar__home) { display: none; }
}

/* ---------- the bell ---------- */
/* A details element in the bar: opens with no script, closes on the next
   click anywhere. The count is what this person has not looked at. Its box
   is always there, so a number arriving later changes a digit, not the bar. */
.bell { position: relative; }
.bell__button {
  list-style: none; display: inline-flex; align-items: center; gap: 4px;
  padding: 7px 10px; border-radius: 8px; cursor: pointer; color: #d9d4cb; line-height: 1;
}
.bell__button::-webkit-details-marker { display: none; }
.bell__button:hover, .bell[open] .bell__button { background: #3b3630; color: #fff; }
.bell__count {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: #c8552e; color: #fff; font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.bell__count.is-empty { display: none; }
.bell__tray {
  position: absolute; right: 0; top: calc(100% + 6px); width: 360px; max-width: calc(100vw - 16px);
  background: #fff; color: #2c2a26; border: 1px solid #e3ded4; border-radius: 12px;
  box-shadow: 0 12px 32px rgba(20, 18, 14, .18); overflow: hidden; z-index: 70;
}
.bell__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid #ece8e0;
}
/* The bar loads on the desk and the public site too, where hub.css is not. */
.bell__head .mono-label {
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: .12em; text-transform: uppercase; color: #8a8377;
}
/* Five rows tall, then it scrolls. A row is two lines and its padding. */
.bell__list { max-height: calc(5 * 62px); overflow-y: auto; overscroll-behavior: contain; }
.bell__item {
  display: grid; grid-template-columns: 1fr auto; grid-template-rows: auto auto; column-gap: 10px;
  padding: 10px 14px 10px 18px; border-bottom: 1px solid #f1ede6; text-decoration: none;
  color: inherit; position: relative; line-height: 1.35; white-space: normal;
}
.bell__item:last-child { border-bottom: 0; }
.bell__item:hover { background: #faf8f4; }
/* A new item wears a dot; a seen one wears nothing, and the tone colors the dot. */
.bell__item::before {
  content: ""; position: absolute; left: 7px; top: 15px; width: 6px; height: 6px;
  border-radius: 50%; background: transparent;
}
.bell__item.is-new::before { background: #8a8377; }
.bell__item--risk.is-new::before { background: #c8552e; }
.bell__item--watch.is-new::before { background: #c9962a; }
.bell__item.is-new .bell__title { font-weight: 600; }
.bell__title { grid-area: 1 / 1; font-size: 13px; }
.bell__at { grid-area: 1 / 2; font-size: 11px; color: #8a8377; white-space: nowrap; }
.bell__body { grid-area: 2 / 1 / 3 / 3; font-size: 12px; color: #6b655b; }
.bell__empty { margin: 0; padding: 18px 14px; font-size: 13px; color: #6b655b; }
@media (max-width: 760px) {
  /* A sheet under the bar, edge to edge: a phone has no room for a card's margins. */
  .bell__tray {
    position: fixed; left: 0; right: 0; top: var(--adminbar-h); width: auto; max-width: none;
    border-radius: 0; border-left: 0; border-right: 0;
  }
  /* Thumb-sized: the bar's type, room around each row, and a list that
     stops short of the bottom of the screen instead of five rows exactly. */
  .bell__head { padding: 12px 16px; }
  .bell__head .mono-label { font-size: 12px; }
  .bell__list { max-height: calc(100dvh - var(--adminbar-h) - 120px); }
  .bell__item { padding: 14px 16px 14px 26px; row-gap: 3px; column-gap: 12px; line-height: 1.4; }
  .bell__item::before { left: 12px; top: 20px; width: 7px; height: 7px; }
  .bell__title { font-size: 15px; }
  .bell__at { font-size: 12px; }
  .bell__body { font-size: 13.5px; }
  .bell__empty { padding: 24px 16px; font-size: 15px; }
}

/* The bar is chrome for one person, not part of the page: it never prints, and
   it never appears in a screenshot of a family's notice. */
@media print { .adminbar { display: none; } }

/* ---------- the body editor ---------- */
/* A textarea a script upgrades in place. Both states are styled the same box,
   so the upgrade changes what you can do and never where anything sits. */
.editor {
  border: 1px solid var(--color-rule-2); border-radius: 8px;
  background: var(--color-card); overflow: hidden;
}
.editor__bar {
  display: flex; align-items: center; gap: 4px; min-height: 40px;
  padding: 0 8px; border-bottom: 1px solid var(--color-rule);
  background: var(--color-paper-alt);
}
/* The no-script line. Same height as the buttons that replace it. */
.editor__hint { font-size: 12px; color: var(--color-muted-2); padding: 0 4px; line-height: 1.4; }
.editor__hint code { font-family: var(--font-mono); font-size: 11px; }
.editor__tools { display: flex; align-items: center; gap: 2px; }
.editor__btn {
  min-width: 28px; height: 28px; padding: 0 9px; border: 0; border-radius: 6px;
  background: transparent; color: var(--color-ink-2); cursor: pointer;
  font-size: 12.5px; line-height: 1; font-family: var(--font-display, var(--font-sans)); white-space: nowrap;
}
/* B and I are the two that stay glyphs, so they get an even box. */
.editor__btn:nth-child(-n+2) { padding: 0; }
/* The break between marking words up and marking blocks up. */
.editor__btn:nth-child(3) { margin-left: 6px; border-left: 1px solid var(--color-rule-2); border-radius: 0 6px 6px 0; padding-left: 12px; }
.editor__btn:hover { background: var(--color-rule); }
.editor__btn:active { background: var(--color-rule-2); }
/* The typing surface. Serif, because the thing being typed is an obituary and
   the desk should show it the way a reader will meet it. */
.editor__box, .editor__source {
  display: block; width: 100%; box-sizing: border-box;
  padding: 18px 20px; border: 0; outline: none; background: transparent;
  font-family: var(--font-serif); font-size: 17px; line-height: 1.7;
  color: var(--color-ink-2); resize: vertical;
}
.editor__source { font-family: var(--font-mono); font-size: 13px; line-height: 1.7; }
.editor__box > * { margin: 0 0 16px; }
.editor__box > :last-child { margin-bottom: 0; }
.editor__box blockquote {
  padding-left: 16px; border-left: 2px solid var(--color-rule-2); color: var(--color-muted);
}
.editor__box ul, .editor__box ol { padding-left: 24px; }
.editor__box li { margin-bottom: 6px; }
.editor__box a { color: var(--sage-ink, var(--color-ink-2)); text-decoration: underline; }
.editor__box:empty::before { content: attr(data-placeholder); color: var(--color-muted-2); }

/* ---------- picking a photo ---------- */
/* The picture is the control: hovering it offers the change, clicking it opens
   the picker. The visible file input under it is the no-script path; the
   script hides it and previews the choice in place. */
/* A popover modal: the top layer centres it and dims the page, and clicking
   the backdrop closes it - all the browser's own machinery. The desk's
   decline dialog and the family's photo picker are the same card. */
.modal__card {
  background: var(--color-card); border: 0; border-radius: 12px; padding: 24px 26px;
  width: min(480px, calc(100vw - 48px));
  box-shadow: 0 18px 50px rgba(30, 28, 24, .25); text-align: left;
}
.modal__card::backdrop { background: rgba(30, 28, 24, .45); }
.modal__card textarea { width: 100%; }

.photopick { display: block; position: relative; border-radius: 8px; overflow: hidden; cursor: pointer; }
.photopick__img { display: block; width: 100%; border-radius: 8px; }
.photopick__empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; min-height: 140px; border: 1.5px dashed var(--color-rule-2); border-radius: 8px;
  color: var(--color-muted-2); font-size: 13px; background: var(--color-paper-alt);
}
.photopick__veil {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px;
  background: rgba(30, 28, 24, .55); color: #fff; font-size: 13px;
  opacity: 0; transition: opacity .15s ease;
}
.photopick:hover .photopick__veil, .photopick:focus-within .photopick__veil { opacity: 1; }
/* No image yet: nothing to veil, so the empty tile is the invitation and the
   hover simply firms it up. */
.photopick--empty .photopick__veil { display: none; }
.photopick--empty:hover .photopick__empty { border-color: var(--color-muted-2); color: var(--color-muted); }
.photopick__icon { width: 26px; height: 26px; }
.photopick__note { font-size: 12px; color: var(--color-muted-2); margin-top: 6px; }


/* Who makes this. It lives in base.css because the desk loads hub.css and the
   sign-in page loads site.css, and a line that says our name twice in two
   stylesheets is two names waiting to disagree.
   currentColor for the same reason it has to work on both: the sidebar is dark
   and the sign-in card is light, and neither should need its own copy. */
.madeby {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: currentColor; opacity: .5;
  margin-top: 14px; padding-top: 14px;
  border-top: 1px solid currentColor; border-image: none;
}
/* The rule above it is a hairline, not a box: a full-strength border under a
   half-strength word reads as a divider somebody forgot to style. */
.madeby { border-top-color: color-mix(in srgb, currentColor 22%, transparent); }
.madeby.centered { text-align: center; }

/* ---------- the refusal page ---------- */
.oops { min-height: 100vh; display: grid; place-items: center; padding: 24px; background: #f6f3ee; color: #2c2a26; }
.oops__card { max-width: 440px; }
.oops__card h1 { font-family: Newsreader; font-size: 28px; margin: 6px 0 10px; }
.oops__card p { margin: 0 0 8px; font-size: 15px; line-height: 1.5; }
.oops__back { display: inline-block; margin-top: 12px; text-decoration: none; color: #2c2a26; font-weight: 600; }

/* ---------- suggestions (places.js) ---------- */
/* Floats under its input; the page beneath never moves. */
.suggest { position: relative; display: block; }
.suggest__list {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 30;
  margin: 0; padding: 6px; list-style: none;
  background: var(--color-card); border: 1px solid var(--color-rule-2); border-radius: 10px;
  box-shadow: 0 10px 30px rgba(44, 42, 38, .12);
  max-height: 320px; overflow-y: auto;
}
.suggest__opt {
  display: flex; flex-direction: column; gap: 2px;
  padding: 9px 11px; border-radius: 7px; cursor: pointer;
  font-family: var(--font-sans); font-size: 15px; line-height: 1.3; color: var(--color-ink);
}
.suggest__opt.is-active { background: var(--color-accent); color: var(--color-accent-ink); }
.suggest__detail { font-size: 13px; color: var(--color-muted); }
.suggest__opt.is-active .suggest__detail { color: var(--color-accent-ink-2); }

/* The residence note (_residence.html). Spans its row and keeps its height:
   a found or missing town changes the words, never the layout. */
.filed { grid-column: 1 / -1; min-height: 1.5em; margin: 0; font-size: 13px; line-height: 1.5; color: var(--color-muted); }
.filed--no { color: var(--color-danger); }
.filed button { background: none; border: 0; padding: 0; font: inherit; color: var(--color-accent-ink); text-decoration: underline; cursor: pointer; }
