/* Member Roster Check stylesheet. The Lantern cool design direction, applied
   2026-09-14 from membership-platform/local/design-directions/direction-3-cool.html,
   which is the reference for every token below. It supersedes the Manuscript
   direction applied earlier the same day: the owner preferred direction 3 on a
   cool base, with a subtle aqua carried over from planomasjid.org.

   One cool near-white ground, deep green as the single primary, a gold used
   twice as a mark of honour, and an aqua confined to the focus ring and the
   found tick. The hero's arch motif and eyebrow chip were removed 2026-09-25
   by owner decision.

   A dark palette was added to the shared token sheet on 2026-09-20 (WP18b)
   for the admin desk, and it is carried here so the two sheets hold one
   palette between them rather than two. It changes nothing on this page. The
   dark declarations are guarded by [data-theme="system"] and
   [data-theme="dark"] on <html>; this page sets neither attribute and ships
   no control that could, so the roster stays light even on a machine set to
   dark. That is the owner's decision of 2026-09-20: the public page keeps one
   palette. Said plainly, because it is the cost of carrying the block: the
   dark values below have never been rendered by this page and nothing here
   can render them.

   Three token names differ from the platform sheet, and that predates this
   change: --success-bg, --warning-bg and --error-bg where the platform says
   --success-soft and its pair, --rule-strong-hover where it says
   --rule-hover, and no legacy alias block at all. So this is the same palette
   under this sheet's own names, not the same bytes.

   One deliberate departure from the mockup, recorded in design.md 5.1: the
   hero carried no monogram pill even before the eyebrow row itself was
   removed; the mockup's pill was dropped 2026-09-15 as it read as out of
   place. */

/* Self-hosted, so the CSP stays at default-src 'self' with no font host added.
   One variable file carries the whole 200 to 800 weight axis in one request,
   and font-display: swap paints the fallback first rather than blocking text.
   unicode-range is left unset, so the browser does not have to decide which
   subset a string belongs to before it paints. */
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("fonts/plus-jakarta-sans-latin-wght-normal.woff2") format("woff2-variations"),
       url("fonts/plus-jakarta-sans-latin-wght-normal.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;

  /* One family for everything: headings 700 to 800, body 400, labels 600. */
  --font: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --size-eyebrow: .75rem;
  --size-meta: .875rem;
  --size-label: .875rem;
  --size-body: 1rem;
  --size-lead: 1.0625rem;
  --size-name: 1.5rem;
  --size-h2: 1.5rem;
  --size-h1: 2.75rem;

  --ground: #f2f5fa;
  --ground-2: #e6ecf4;
  --surface: #ffffff;
  --line: #d3dbe6;
  --rule-strong: #6f7b8a;
  --rule-strong-hover: #5b6879;
  --ink: #101a2e;
  --muted: #4b5a70;

  --green: #12503c;
  --green-deep: #0c3b2c;
  --green-soft: #e6f3ec;
  --green-mint: #f0f7f3;

  --gold: #b98328;
  --gold-soft: #fbf3e3;

  --aqua: #58b2c0;
  --aqua-ink: #377079;
  --aqua-tint: #e3f2f5;

  --button-text: #ffffff;
  --success: #1f6f4a;
  --success-bg: #e6f3ec;
  --warning: #8a5a00;
  --warning-bg: #fff4dc;
  --error: #9b2c2c;
  --error-bg: #fdeaea;

  --radius-card: 20px;
  --radius-tile: 14px;
  --radius-control: 12px;
  --radius-pill: 999px;

  /* Two steps, both on the ink. The softer one carries every resting card, the
     deeper one carries the dialog and a hovered card. */
  --shadow-1: 0 1px 2px rgba(16, 26, 46, .04), 0 6px 16px -8px rgba(16, 26, 46, .10);
  --shadow-2: 0 2px 4px rgba(16, 26, 46, .04), 0 18px 40px -18px rgba(18, 80, 60, .22);
  --focus: 0 0 0 2px rgba(88, 178, 192, .9);

  --ease: cubic-bezier(.2, .8, .2, 1);
  --dur-fast: 120ms;
  --dur: 200ms;
  --dur-slow: 320ms;

  --gutter: 24px;
  --page-top: 56px;
}

/* ---------- the dark palette, inert on this page ----------

   Read the header. Nothing here renders until something sets data-theme on
   <html>, and this page never does. The declarations are written out twice
   because CSS cannot give one declaration list two selectors when one of them
   sits inside a media query; the two bodies are identical character for
   character and must stay that way. That is why the second block's closing
   brace is indented two spaces: not a typo, just what makes the two bodies
   the same bytes as well as the same declarations.

   Ground and surface invert in relation and not in value: the dark surface
   sits lighter than the dark ground, the way the light card sits lighter than
   the light ground, and --ground-2 sits between them. Every text on
   background pair clears 4.5:1; the lowest is --success on --success-bg at
   6.85:1. The gold and the aqua are never text in either palette. */
@media (prefers-color-scheme: dark) {
  :root[data-theme="system"] {
    color-scheme: dark;

    --ground: #101826;
    --ground-2: #18212f;
    --surface: #1c2634;
    --line: #2e3a4c;
    --rule-strong: #75839a;
    --rule-strong-hover: #919eb1;
    --ink: #e3eaf4;
    --muted: #a6b4c7;

    --green: #5ecb9e;
    --green-deep: #86dcba;
    --green-soft: #162d22;
    --green-mint: #13221b;

    --gold: #d8a441;
    --gold-soft: #2d2314;

    --aqua: #6ec8d6;
    --aqua-ink: #7fd3e0;
    --aqua-tint: #13292f;

    /* The primary is a light green in dark, so a white label on it would fail
       AA. The label goes dark instead. */
    --button-text: #06140f;
    --success: #52c68c;
    --success-bg: #162d22;
    --warning: #e2ab46;
    --warning-bg: #2f2515;
    --error: #ff8f8f;
    --error-bg: #31191c;

    /* A shadow on a dark ground reads as noise rather than as height, so both
       steps shrink and darken. */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, .34);
    --shadow-2: 0 2px 4px rgba(0, 0, 0, .34), 0 14px 28px -18px rgba(0, 0, 0, .55);
    --focus: 0 0 0 2px rgba(110, 200, 214, .95);
  }
}

:root[data-theme="dark"] {
    color-scheme: dark;

    --ground: #101826;
    --ground-2: #18212f;
    --surface: #1c2634;
    --line: #2e3a4c;
    --rule-strong: #75839a;
    --rule-strong-hover: #919eb1;
    --ink: #e3eaf4;
    --muted: #a6b4c7;

    --green: #5ecb9e;
    --green-deep: #86dcba;
    --green-soft: #162d22;
    --green-mint: #13221b;

    --gold: #d8a441;
    --gold-soft: #2d2314;

    --aqua: #6ec8d6;
    --aqua-ink: #7fd3e0;
    --aqua-tint: #13292f;

    /* The primary is a light green in dark, so a white label on it would fail
       AA. The label goes dark instead. */
    --button-text: #06140f;
    --success: #52c68c;
    --success-bg: #162d22;
    --warning: #e2ab46;
    --warning-bg: #2f2515;
    --error: #ff8f8f;
    --error-bg: #31191c;

    /* A shadow on a dark ground reads as noise rather than as height, so both
       steps shrink and darken. */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, .34);
    --shadow-2: 0 2px 4px rgba(0, 0, 0, .34), 0 14px 28px -18px rgba(0, 0, 0, .55);
    --focus: 0 0 0 2px rgba(110, 200, 214, .95);
  }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 var(--size-body)/1.55 var(--font);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.page {
  max-width: 32rem;
  margin: 0 auto;
  padding: var(--page-top) var(--gutter) 48px;
  /* backwards, not both: the transform is gone once the entrance has run, so
     no ancestor transform is left standing over the modal dialog. */
  animation: page-in var(--dur-slow) var(--ease) backwards;
}

@keyframes page-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* ---------- masthead ---------- */

/* The hero band. It carried the arch motif and the association eyebrow chip
   until both were removed 2026-09-25 by owner decision; the heading and
   subtitle now sit directly at the top of the page, with only a bottom
   margin to space them from the disclosure panel below. */
.hero-wrap {
  margin: 0 0 24px;
}

h1 {
  font-family: var(--font);
  font-size: var(--size-h1);
  font-weight: 800;
  letter-spacing: -.035em;
  line-height: 1.06;
  color: var(--ink);
  margin: 0 0 14px;
  overflow-wrap: break-word;
}

.intro {
  font-size: var(--size-lead);
  color: var(--muted);
  line-height: 1.55;
  margin: 0;
  max-width: 28rem;
}

/* ---------- the disclosure ---------- */

/* Fixed margins, so opening the panel adds only the panel's own height and the
   form card below it does not shift its distance from the summary. */
.guidance { margin: 0 0 24px; }

/* The summary is the mockup's green-soft pill. */
.guidance > summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  font-size: var(--size-label);
  font-weight: 700;
  color: var(--green);
  background: var(--green-soft);
  cursor: pointer;
  list-style: none;
  border-radius: var(--radius-pill);
  padding: 9px 15px;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.guidance > summary::-webkit-details-marker { display: none; }

/* The chevron is a rotated corner, and the open attribute alone turns it. No
   script touches the disclosure. */
.guidance > summary::after {
  content: "";
  width: 6px;
  height: 6px;
  border-right: 1.8px solid currentColor;
  border-bottom: 1.8px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur) var(--ease);
}

.guidance[open] > summary::after { transform: translateY(1px) rotate(-135deg); }

.guidance > summary:hover { color: var(--green-deep); background: var(--green-mint); }

.guidance > summary:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

/* A tile in the page plane, not a card: the ground colour inside the surface,
   rounded to the tile radius, no shadow and no border. */
.guidance-panel {
  margin-top: 14px;
  padding: 10px 18px;
  background: var(--ground-2);
  border-radius: var(--radius-tile);
}

.guidance[open] .guidance-panel {
  animation: panel-in var(--dur) var(--ease) both;
}

@keyframes panel-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

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

.guidance-rows li {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  font-size: var(--size-label);
  line-height: 1.5;
  color: var(--ink);
  padding: 7px 0;
}

/* The two note rows are asides, not details to enter: they are set in the muted
   colour, and the first of them is given a hairline above, so the pair reads as
   a note under the three rows rather than as more of them. */
.guidance-rows li.note {
  color: var(--muted);
}

.guidance-rows li.note:first-of-type {
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.g-icon {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  margin-top: 1px;
  color: var(--green);
}

/* ---------- form card ---------- */

form {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-1);
  padding: 26px 24px 24px;
}

.field { margin: 0 0 18px; }
form > .field:last-of-type { margin-bottom: 18px; }

/* The house-number column is the wider of the two on purpose: it takes up to
   six digits and the longer label, the phone field takes four.

   The (i) beside each label narrows the room the longer label has, so at card
   width "House or apartment number" wraps to two lines while the phone label
   stays on one. The two .field wrappers therefore dissolve (display: contents)
   and their label, input and error are placed on three shared grid rows, one
   column each: the two inputs sit level whatever the labels do, and an error
   under one field makes room under both instead of pushing one input out of
   line. Plain grid placement, not subgrid, because WebKit sized the inputs in
   a subgrid differently on first layout and after the first interaction. DOM
   order, and so tab and reading order, is still label, (i), input, error for
   one field and then the other. Row gaps stay zero: the spacing between a
   label, its input and its error is each element's own margin. */
.field-row {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  column-gap: 16px;
  row-gap: 0;
  margin-bottom: 18px;
}

.field-row .field { display: contents; }
.field-row .field:nth-child(1) > * { grid-column: 1; }
.field-row .field:nth-child(2) > * { grid-column: 2; }
.field-row .label-row { grid-row: 1; }
.field-row input { grid-row: 2; }
.field-row .error { grid-row: 3; }

label {
  display: block;
  font-size: var(--size-meta);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 7px;
}

/* Boxed, carried over from the Manuscript direction and unchanged in substance:
   the mockup's own field is a box, here at the 12 px control radius. */
input[type="text"],
select,
textarea {
  width: 100%;
  min-height: 2.75rem;
  padding: .75rem .875rem;
  font: 400 var(--size-body)/1.4 var(--font);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-control);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

input[type="text"]::placeholder,
select::placeholder,
textarea::placeholder { color: var(--muted); }

input[type="text"]:hover,
select:hover,
textarea:hover { border-color: var(--rule-strong-hover); }

input[type="text"]:focus,
input[type="text"]:focus-visible,
select:focus,
select:focus-visible,
textarea:focus,
textarea:focus-visible {
  outline: none;
  border-color: var(--green);
  box-shadow: var(--focus);
}

input[aria-invalid="true"] { border-color: var(--error); }

input[aria-invalid="true"]:focus,
input[aria-invalid="true"]:focus-visible {
  border-color: var(--error);
  box-shadow: 0 0 0 2px rgba(155, 44, 44, .55);
}

.error {
  font-size: var(--size-meta);
  line-height: 1.4;
  margin: 6px 0 0;
}

.error { color: var(--error); }
.error:empty { display: none; }

/* The (i) hints beside the house-number and phone labels (owner-approved
   design 2026-09-30). They replace the helper lines that sat under the
   inputs. The tip is absolutely positioned inside .label-row, so it floats
   over the form and takes no height: opening it moves nothing, and the
   embedded frame height is unchanged. app.js clamps its left edge inside the
   form and points the arrow at the button through --tip-arrow. The tip has no
   display rule of its own so the hidden attribute closes it. */
.label-row {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 6px;
  /* The label is meta size; the wrapper must be too, or its own 16px line
     strut makes every label row 3px taller than a plain label. */
  font-size: var(--size-meta);
  margin: 0 0 7px;
}

/* Flex, with the (i) pinned to the first line: an unwrapped label has the (i)
   right beside it, and when the longer label wraps in a narrow column the (i)
   stays at the column's edge on the first line rather than dropping alone onto
   a line of its own, which inline flow did in Chromium however it was joined. */
.label-row label {
  display: block;
  margin: 0;
}

button.info {
  position: relative;
  flex: none;
  display: inline-grid;
  margin: 1px 0 0;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  min-height: 0;
  padding: 0;
  font: 700 .6875rem/1 var(--font);
  letter-spacing: 0;
  color: var(--muted);
  background: transparent;
  border: 1.5px solid var(--muted);
  border-radius: 50%;
  box-shadow: none;
  transform: none;
}

/* The visible circle stays 20px; this widens the touch target to 36px. */
button.info::after {
  content: "";
  position: absolute;
  inset: -8px;
}

button.info:hover:enabled,
button.info[aria-expanded="true"] {
  color: var(--green);
  background: transparent;
  border-color: var(--green);
}

button.info:active:enabled { transform: none; }
button.info:focus-visible { box-shadow: var(--focus); }

.tip {
  position: absolute;
  z-index: 5;
  top: calc(100% + 8px);
  left: 0;
  width: min(20rem, calc(100vw - 4rem));
  padding: 10px 12px;
  font-size: .8125rem;
  font-weight: 500;
  line-height: 1.45;
  color: #fff;
  background: #101a2e;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(16, 26, 46, .18);
}

.tip b { font-weight: 700; }

.tip::before {
  content: "";
  position: absolute;
  top: -5px;
  left: var(--tip-arrow, 24px);
  width: 10px;
  height: 10px;
  background: #101a2e;
  transform: rotate(45deg);
}

.turnstile { margin: 0 0 16px; min-height: 1px; }

button {
  display: block;
  width: 100%;
  min-height: 2.75rem;
  padding: .75rem 1.25rem;
  font: 700 var(--size-body)/1.2 var(--font);
  letter-spacing: -.005em;
  color: var(--button-text);
  background: var(--green);
  border: 1px solid var(--green);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}

button:hover:enabled {
  background: var(--green-deep);
  border-color: var(--green-deep);
}

button:active:enabled { transform: translateY(1px); }

button:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

button[disabled] {
  color: var(--muted);
  background: var(--ground-2);
  border-color: var(--line);
  box-shadow: none;
  cursor: default;
}

/* A button greyed because the record carries no email address keeps its place
   in the tab order, so it cannot use the disabled attribute and the :enabled
   rules above still match it. These three put it back where disabled leaves a
   button: on the tint ground, in the muted colour, and inert under hover and
   press. */
button[aria-disabled="true"] { color: var(--muted); background: var(--ground-2); border-color: var(--line); box-shadow: none; cursor: default; }

button[aria-disabled="true"]:hover:enabled,
button[aria-disabled="true"]:active:enabled {
  color: var(--muted);
  background: var(--ground-2);
  border-color: var(--line);
  transform: none;
}

/* ---------- the inline notice ---------- */

/* RATE_LIMITED and ERROR are not outcomes of a lookup, so they stay in the
   form: one line directly under the submit button, in the same plane, with the
   typed values still on screen and the button still under the thumb. */
.notice {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: var(--size-label);
  line-height: 1.45;
  color: var(--ink);
  margin: 16px 0 0;
  padding: 12px 14px;
  border-radius: var(--radius-tile);
  border: 1px solid var(--line);
}

.notice[hidden] { display: none; }

.notice-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  font-size: 17px;
  line-height: 1;
  font-weight: 700;
}

.notice[data-tone="warn"] { background: var(--warning-bg); border-color: #f0dcae; }
.notice[data-tone="warn"] .notice-icon { color: var(--warning); background: #ffe9bd; }
.notice[data-tone="error"] { background: var(--error-bg); border-color: #f2cfcf; }
.notice[data-tone="error"] .notice-icon { color: var(--error); background: #f9dcdc; }

/* ---------- the searching card ---------- */

.result { margin-top: 24px; }

.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-1);
  padding: 24px;
}

.card p { margin: 0; color: var(--muted); }

.card:focus-visible {
  outline: none;
  box-shadow: var(--focus);
}

/* ---------- the result dialog ---------- */

/* Desktop: a 30 rem card centred over a dimmed page, the same width as the
   column beneath it, so the result lands where the reader is already looking.
   The 3 px green-to-gold top edge is the record's one flourish. */
dialog.sheet {
  position: relative;
  width: 30rem;
  max-width: calc(100vw - 32px);
  max-height: 85vh;
  padding: 0;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}

/* Drawn on the element rather than as a border, so it can carry the gradient
   and still sit inside the 20 px corner. */
dialog.sheet::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--green) 0%, var(--gold) 100%);
}

dialog.sheet[open] {
  display: flex;
  flex-direction: column;
  animation: dialog-in var(--dur) var(--ease) both;
}

@keyframes dialog-in {
  from { opacity: 0; transform: scale(.98); }
  to { opacity: 1; transform: none; }
}

dialog.sheet::backdrop {
  background: rgba(16, 26, 46, .35);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

/* The fallback for a browser with no showModal: the same element in the page
   flow below the form, with no backdrop and nothing to dim. */
dialog.sheet.inline {
  position: relative;
  width: auto;
  max-width: 100%;
  max-height: none;
  margin: 24px 0 0;
}

dialog.sheet[hidden] { display: none; }

.dialog-head {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 8px 8px 0;
}

/* A 44 px target for a 24 px glyph, which is the floor every other control on
   the page already clears. */
.dialog-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  min-height: 0;
  padding: 0;
  font: 400 24px/1 var(--font);
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: var(--radius-pill);
  box-shadow: none;
  cursor: pointer;
}

.dialog-close:hover:enabled {
  color: var(--ink);
  background: var(--ground-2);
  transform: none;
}

.dialog-close:focus-visible { outline: none; box-shadow: var(--focus); }

.dialog-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0 28px 28px;
  -webkit-overflow-scrolling: touch;
}

/* Shown only in the bottom-sheet layout, where it is the affordance that says
   the surface came up from the bottom edge. */
.grab { display: none; }

/* ---------- the result body ---------- */

.res-pane[hidden] { display: none; }

.res-head {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: var(--size-h2);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.028em;
  color: var(--ink);
  margin: 0 0 18px;
}

/* The heading takes focus when the dialog opens, so its ring is drawn often.
   It is offset off the glyphs, so the ring never traps a hairline inside
   itself. */
.res-head:focus-visible {
  outline: 2px solid var(--aqua);
  outline-offset: 4px;
  box-shadow: none;
}

#result-content { padding-top: 2px; }

.res-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1;
  font-weight: 700;
}

dialog[data-tone="found"] .res-icon { color: var(--green); background: var(--aqua-tint); }
dialog[data-tone="warn"] .res-icon { color: var(--warning); background: var(--warning-bg); }

.record-name {
  font-size: var(--size-name);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.03em;
  color: var(--ink);
  margin: 0 0 10px;
}

/* The one muted line under the name, since 2026-09-14. The three masked rows
   and their 8 rem definition list went with the same change: the found card
   now carries the name, the on-file badge and this note, and the contact
   details travel to the inbox on file instead. */
.record-note {
  font-size: var(--size-meta);
  line-height: 1.55;
  color: var(--muted);
  margin: 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--line);
}

/* The election cutoff note, 2026-09-16. Same muted step and line height as
   the on-file note it sits under. Its link takes the page accent from the
   base a rule. A hairline of its own was added 2026-09-25 by owner decision,
   the same weight and colour as the on-file note's rule above it and no
   heavier, so the eligibility line reads as its own paragraph rather than a
   continuation of the on-file sentence; the note simply is not in the flow
   for the members it does not apply to, so the rule never doubles up with
   .record-note's own. */
.record-cutoff {
  font-size: var(--size-meta);
  line-height: 1.55;
  color: var(--muted);
  /* Space above the hairline as well as below it, so the divider separates
     the note from "Your membership is on file." instead of underlining it. */
  margin: 14px 0 0;
  padding: 14px 0 0;
  border-top: 1px solid var(--line);
}

/* The met note's three sentences, split onto their own lines 2026-09-25/26 by
   owner decision, keeping the exact words. A small gap between them, no new
   colour: the paragraphs inherit .record-cutoff's own size, line height and
   colour, and the last one carries no bottom margin so it does not add space
   under the note that .record-cutoff did not already have. Since 2026-09-27
   the gap sits above each following paragraph rather than below each one, so
   a hidden third paragraph (the ZIP variants) leaves no trailing gap either;
   the three-paragraph layout is unchanged. */
.record-cutoff p {
  margin: 0;
}

.record-cutoff p + p {
  margin-top: 7px;
}

.res-hint,
.res-body {
  font-size: var(--size-body);
  color: var(--muted);
  margin: 0;
}

/* The dialog covers the form on a phone, so the four typed values are echoed
   here. Meta size and the muted colour: it is a receipt, not a message. */
.entered {
  font-size: var(--size-meta);
  line-height: 1.45;
  color: var(--muted);
  margin: 10px 0 0;
  overflow-wrap: anywhere;
}

/* The only action in the result: one sentence, body colour. */
.email-line {
  font-size: var(--size-label);
  line-height: 1.55;
  color: var(--ink);
  margin: 20px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

/* Second paragraph of the found card paper-form note (owner 2026-09-29):
   same type and colour as .email-line, no second rule, a tight gap. */
.email-line-follow {
  font-size: var(--size-label);
  line-height: 1.55;
  color: var(--ink);
  margin: 10px 0 0;
  overflow-wrap: anywhere;
}

/* Email me my details, added 2026-09-14. It sits under the on-file note and
   above the correction sentence. Since the copy button was removed later the
   same day it is the card's only action besides Close, so it takes the primary
   button style rather than the smaller secondary one it shipped with. */
.send-details {
  margin: 18px 0 0;
}

.send-help {
  font-size: var(--size-meta);
  line-height: 1.55;
  color: var(--muted);
  margin: 10px 0 0;
}

.turnstile-send:empty { display: none; }
.turnstile-send { margin: 10px 0 0; }

.res-actions { margin-top: 20px; }

/* PO-1 (owner decision 2026-10-02). The word "optional" beside the phone
   label, lighter than the label itself. */
.opt {
  font-weight: 500;
  color: var(--muted);
}

/* The "Almost there" step's own small form, inside the dialog. Every form on
   the page otherwise takes the card style above; this one sits flat in the
   dialog body, which is already the card. */
form.needs-phone {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  margin: 16px 0 0;
}

form.needs-phone input { margin: 0; }
form.needs-phone button { margin-top: 14px; }

.button-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: .75rem 1.25rem;
  font-size: var(--size-body);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.005em;
  color: var(--green);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  box-shadow: none;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}

.button-secondary:hover:enabled {
  background: var(--green-soft);
  color: var(--green-deep);
  border-color: var(--green);
}

.button-secondary:active:enabled { transform: translateY(1px); }

.button-secondary:focus-visible { outline: none; box-shadow: var(--focus); }

.button-secondary.block { display: flex; width: 100%; }

a { color: var(--green); text-underline-offset: 3px; }
a:hover { color: var(--green-deep); }
a:focus-visible { outline: none; box-shadow: var(--focus); border-radius: 6px; }

.status-line { display: flex; align-items: center; gap: 12px; margin: 0; }

.spinner {
  width: 16px;
  height: 16px;
  border: 2px solid var(--ground-2);
  border-top-color: var(--green);
  border-radius: 50%;
  display: inline-block;
  animation: spin .8s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- the bottom sheet ---------- */

@media (max-width: 40rem) {
  :root { --size-h1: 2.125rem; }

  /* The result arrives from the thumb end of the phone, anchored to the bottom
     edge, and leaves the top of the form visible above it. */
  dialog.sheet {
    inset: auto 0 0;
    position: fixed;
    width: 100%;
    max-width: 100%;
    max-height: 85vh;
    margin: 0;
    border-radius: var(--radius-card) var(--radius-card) 0 0;
  }

  dialog.sheet[open] { animation: sheet-up var(--dur) var(--ease) both; }

  dialog.sheet.inline { position: relative; border-radius: var(--radius-card); }

  .grab {
    display: block;
    position: absolute;
    top: 10px;
    left: 50%;
    width: 36px;
    height: 4px;
    margin-left: -18px;
    border-radius: var(--radius-pill);
    background: var(--line);
  }

  .dialog-head { padding-top: 16px; }
  /* Clear the home indicator on a phone with a gesture bar. */
  .dialog-body { padding: 0 20px calc(24px + env(safe-area-inset-bottom, 0px)); }
}

@keyframes sheet-up {
  from { transform: translateY(100%); }
  to { transform: none; }
}

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

/* The footer sits in the same column as the form and adds height below it
   only, so the submit button holds its position in both disclosure states. A
   hairline rule separates it from the form card. The links take the page link
   colour and the page underline, declared once on the a rule above. */
.site-footer {
  margin-top: 28px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: var(--size-meta);
  line-height: 1.7;
  color: var(--muted);
  text-align: left;
}

.site-footer p { margin: 0; }

.site-footer a { font-weight: 600; overflow-wrap: anywhere; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  button:hover:enabled { transform: none; }
  .page { opacity: 1; transform: none; }
  .spinner { animation: none !important; border-top-color: var(--line); }
  .guidance[open] .guidance-panel { opacity: 1; transform: none; }
  dialog.sheet[open] { animation: none !important; }
}

@media (max-width: 26rem) {
  :root { --page-top: 24px; }
  .page { padding-bottom: 32px; }
  form, .card { padding: 20px; }
  .record-name { margin-bottom: 8px; }
  /* One column: the wrappers are boxes again and stack, each carrying its own
     bottom margin, so the pair needs none. */
  .field-row { grid-template-columns: 1fr; margin-bottom: 0; }
  .field-row .field { display: block; margin-bottom: 18px; }
}

/* ---------- embedded in the website ----------

   Every selector in this block starts with html.is-embedded, the class
   src/embed.js puts on the root element only when this document is framed.
   A member who opens the page directly on Azure matches none of them, so the
   centred modal on a desktop and the bottom sheet on a phone above are
   untouched, down to the animation and the focus behaviour. The frozen
   contract is docs/wordpress-embed/COMPATIBILITY.md sections 4 and 6.

   Inside a frame that the parent resizes to fit this document, the viewport
   is the frame. 85vh is then a fraction of a box that was just made tall
   enough to hold the page, and a fixed sheet is pinned to a viewport that can
   be three screens high with the member looking at the middle of it. Both
   assumptions go. The surface is still opened with showModal(), so it lives
   in the browser's top layer, where CSS computes any position other than
   fixed or absolute to absolute. It is therefore anchored, centred, 24px from
   the top of this document, over the form and its backdrop, and the reporter
   asks the parent to scroll that position into view. Measured in a real
   browser at 1440, 770 and 400 with the outer page scrolled to the submit
   button first: the parent scrolls back and the whole card is on screen.
   With no helper installed the frame keeps a fixed height and scrolls
   internally, and the member still reaches the whole surface. */
html.is-embedded dialog.sheet,
html.is-embedded dialog.sheet[open] {
  /* Absolute, stated rather than left to the top layer to impose: the top
     layer computes relative or static to absolute anyway. Left and right 0
     with auto side margins centre the card; it also stays the containing
     block for the 3px gradient edge drawn by its ::before. */
  position: absolute;
  inset: 0 0 auto 0;
  width: 30rem;
  max-width: 100%;
  max-height: none;
  margin: 24px auto 0;
  border-radius: var(--radius-card);
  /* No slide up from the bottom edge, which is a sheet's entrance and this is
     not a sheet. No scale in either: the reporter reads the offset it sends
     from layout, and an animation that moves the surface would make the two
     disagree for the length of the animation. */
  animation: none;
}

/* The frame is the scroll container, so the body of the surface has nothing
   left to scroll inside itself and a second scrollbar would be a dead end. */
html.is-embedded .dialog-body {
  overflow-y: visible;
  -webkit-overflow-scrolling: auto;
}

/* The grab handle says a surface slid up from the bottom edge of a phone.
   Here it did not, so it is not shown. */
html.is-embedded .grab { display: none; }
