/* ==========================================================================
   Parable design system
   Single stylesheet for every member and admin screen. Load it with
   <link rel="stylesheet" href="/static/parable.css">.

   Written for adults 55+, people with low technical confidence, and family
   caregivers. That drives most of the decisions below: 18px body minimum,
   48px touch targets, high-contrast text, no meaning carried by colour alone,
   and zoom left switched on.

   CONTRAST. Every text colour here was measured against the surface it sits
   on, not assumed. The brand orange #ea580c is 3.56:1 on white, which FAILS
   AA for normal text and only scrapes large text. So it is never used for
   body copy or small text: --brand-ink (#c2410c, 5.18:1) is the readable
   orange for text and solid buttons, and #ea580c survives as borders,
   focus accents, and large display only.
   ========================================================================== */

/* --- tokens --------------------------------------------------------------- */
:root {
  /* brand */
  --navy: #020617;          /* 19.9:1 on white */
  --ink: #111827;           /* 16.9:1 — body text */
  --brand: #ea580c;         /* 3.56:1 — accents/borders ONLY, never text */
  --brand-ink: #c2410c;     /* 5.18:1 — orange that is safe as text */
  --brand-ink-hover: #9a3412;

  /* surfaces */
  --bg: #f4f6fb;
  --surface: #ffffff;
  --surface-sunken: #f8fafc;
  --surface-warm: #fff7ed;   /* brand-tinted callout */
  --line: #d8dee9;           /* 1.6:1 on white — visible divider */
  --line-strong: #94a3b8;

  /* text */
  --muted: #475569;          /* 7.58:1 — the ONLY muted grey. Never #64748b */
  --on-dark: #ffffff;

  /* status — all measured on --surface */
  --ok: #166534;             /* 7.4:1  */
  --ok-bg: #f0fdf4;
  --ok-line: #86efac;
  --warn: #b45309;           /* 5.02:1 */
  --warn-bg: #fffbeb;
  --warn-line: #fcd34d;
  --danger: #b91c1c;         /* 6.3:1  */
  --danger-bg: #fef2f2;
  --danger-line: #fca5a5;
  --info: #1e40af;           /* 8.6:1  */
  --info-bg: #eff6ff;
  --info-line: #93c5fd;
  --neutral: #475569;
  --neutral-bg: #f1f5f9;
  --neutral-line: #cbd5e1;

  /* type scale */
  --t-xs: 0.9375rem;   /* 15px — admin metadata only, never member copy */
  --t-sm: 1rem;        /* 16px */
  --t-base: 1.125rem;  /* 18px — member body minimum */
  --t-md: 1.1875rem;   /* 19px */
  --t-lg: 1.375rem;    /* 22px */
  --t-xl: 1.625rem;    /* 26px */
  --t-2xl: 2rem;       /* 32px */
  --t-3xl: 2.5rem;     /* 40px */

  /* spacing — 4px base */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;  --s-4: 1rem;
  --s-5: 1.25rem;  --s-6: 1.5rem;   --s-8: 2rem;     --s-10: 2.5rem;
  --s-12: 3rem;    --s-16: 4rem;

  /* widths */
  --w-narrow: 40rem;   /* forms, single-column reading */
  --w-page: 70rem;     /* member dashboard */
  --w-wide: 84rem;     /* admin tables */

  --radius: 14px;
  --radius-lg: 20px;
  --shadow: 0 2px 4px rgba(2, 6, 23, 0.04), 0 8px 24px rgba(2, 6, 23, 0.06);
  --shadow-lift: 0 4px 8px rgba(2, 6, 23, 0.06), 0 16px 32px rgba(2, 6, 23, 0.09);

  /* Structural padding, deliberately NOT rem. At 200% text a rem gutter
     doubles too, so the frame squeezed the content instead of the content
     getting bigger. These grow with the viewport, not with the type. */
  --pad-page: clamp(12px, 4vw, 24px);
  --pad-card: clamp(16px, 5vw, 24px);

  --tap: 48px;         /* minimum touch target */
  --focus: 3px;
}

/* The portal is deliberately single-theme: a light, calm, paper-like surface.
   Members are told to distrust screens that suddenly look different, and a
   security product that changes appearance between devices erodes exactly the
   trust it needs. So every colour is painted explicitly and nothing is
   inherited from the host. Dark mode is a future decision, not an omission. */

* { box-sizing: border-box; }

html {
  /* Never set a px font-size here — it breaks the browser's own text-size
     setting, which is the first accommodation an older member finds. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--t-base);
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
}

/* --- landmarks & skip link ------------------------------------------------ */
.skip-link {
  position: absolute;
  left: var(--s-4);
  top: calc(-1 * var(--s-16));
  z-index: 100;
  padding: var(--s-3) var(--s-5);
  background: var(--navy);
  color: var(--on-dark);
  border-radius: var(--radius);
  font-weight: 700;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus { top: var(--s-4); }

.page {
  width: 100%;
  padding: var(--pad-page) var(--pad-page)
           calc(var(--s-10) + env(safe-area-inset-bottom, 0px));
  padding-left: max(var(--pad-page), env(safe-area-inset-left, 0px));
  padding-right: max(var(--pad-page), env(safe-area-inset-right, 0px));
  display: flex;
  justify-content: center;
}
.wrap { width: 100%; max-width: var(--w-page); }
.wrap--narrow { max-width: var(--w-narrow); }
.wrap--wide { max-width: var(--w-wide); }

.stack > * + * { margin-top: var(--s-5); }
.stack-tight > * + * { margin-top: var(--s-3); }
.stack-loose > * + * { margin-top: var(--s-8); }

/* --- masthead ------------------------------------------------------------- */
.masthead {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  padding-top: env(safe-area-inset-top, 0px);
  margin-bottom: var(--s-6);
}
.masthead__brand {
  /* A link, so it needs a real target. The audit caught this at 80x35 —
     comfortably tappable for a 30-year-old, a miss for anyone with a tremor. */
  display: inline-flex;
  align-items: center;
  /* flex-wrap and max-width matter here: the admin brand is two flex items
     ("Parable" + "operations") and without them the pair stayed on one line,
     overflowing a 360px phone once text was scaled to 200%. */
  flex-wrap: wrap;
  column-gap: var(--s-2);
  max-width: 100%;
  min-height: var(--tap);
  font-size: var(--t-lg);
  font-weight: 800;
  color: var(--navy);
  letter-spacing: -0.01em;
  text-decoration: none;
}
.masthead__brand:hover { color: var(--brand-ink); }
.masthead__nav {
  margin-left: auto;
  display: flex;
  gap: var(--s-3);
  flex-wrap: wrap;
  align-items: center;
}

/* --- typography ----------------------------------------------------------- */
h1, h2, h3, h4 {
  color: var(--navy);
  line-height: 1.2;
  margin: 0;
  text-wrap: balance;
  font-weight: 800;
  /* A single long word at heading size is wider than a phone once text is
     scaled to 200% — "Reconciliation" at 64px is 355px. Breaking it is far
     better than a page that scrolls sideways. */
  overflow-wrap: break-word;
}
h1 { font-size: var(--t-3xl); letter-spacing: -0.02em; }
h2 { font-size: var(--t-2xl); letter-spacing: -0.01em; }
h3 { font-size: var(--t-xl); }
h4 { font-size: var(--t-lg); }
p  { margin: 0; }
.lead { font-size: var(--t-lg); line-height: 1.55; color: var(--muted); }
.muted { color: var(--muted); }
.small { font-size: var(--t-sm); }
.strong { font-weight: 700; color: var(--navy); }
.prose { max-width: 65ch; }
.prose > * + * { margin-top: var(--s-4); }

a { color: var(--brand-ink); text-decoration-thickness: 2px; text-underline-offset: 3px; }
a:hover { color: var(--brand-ink-hover); }

.eyebrow {
  font-size: var(--t-sm);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- focus ---------------------------------------------------------------- */
/* One rule, applied everywhere. Never removed anywhere: a focus ring that
   disappears makes the whole portal unusable by keyboard. The double ring
   (navy on white) stays visible on white, warm, and orange surfaces alike. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--focus) solid var(--navy);
  outline-offset: 2px;
  border-radius: 6px;
  box-shadow: 0 0 0 calc(var(--focus) + 2px) var(--surface);
}
.card :where(a, button, input, select, textarea):focus-visible {
  box-shadow: 0 0 0 calc(var(--focus) + 2px) var(--surface);
}

/* --- buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap);
  min-width: var(--tap);
  padding: var(--s-3) var(--s-5);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  border-radius: var(--radius);
  border: 2px solid transparent;
  max-width: 100%;
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn--primary { background: var(--brand-ink); color: var(--on-dark); border-color: var(--brand-ink); }
.btn--primary:hover:not(:disabled) { background: var(--brand-ink-hover); border-color: var(--brand-ink-hover); color: var(--on-dark); }
.btn--secondary { background: var(--surface); color: var(--navy); border-color: var(--line-strong); }
.btn--secondary:hover:not(:disabled) { background: var(--surface-sunken); border-color: var(--navy); color: var(--navy); }
.btn--quiet { background: transparent; color: var(--brand-ink); border-color: transparent; padding-inline: var(--s-3); }
.btn--quiet:hover:not(:disabled) { background: var(--surface-warm); color: var(--brand-ink-hover); }
.btn--danger { background: var(--danger); color: var(--on-dark); border-color: var(--danger); }
.btn--danger:hover:not(:disabled) { background: #991b1b; border-color: #991b1b; color: var(--on-dark); }
.btn--block { width: 100%; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }

/* Busy state. aria-busy carries it to assistive tech; the spinner is purely
   decorative and hidden from it. */
.btn[aria-busy="true"] { cursor: progress; }
.btn[aria-busy="true"]::before {
  content: "";
  width: 1em; height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  flex: none;
}
@keyframes spin { to { transform: rotate(360deg); } }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* --- cards ---------------------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--pad-card);
  box-shadow: var(--shadow);
}
.card--flush { padding: 0; overflow: hidden; }
.card__header {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
}
/* min-width:0 is what actually lets the heading wrap. As a flex item it
   defaults to min-width:auto and is sized to its max-content, so a long word
   widened the row past the viewport no matter what overflow-wrap said. */
.card__header > * { min-width: 0; }
.card__header > h2, .card__header > h3 { margin-right: auto; }

/* --- page title with the "protection is active" tick ----------------------
   The tick is the compact form of the active banner. Its label appears on
   hover AND on focus: the tick is focusable, so a tap on a phone shows it
   too — hover alone does not exist on a touchscreen. The label is anchored to
   the title row, not to the tick, so it can never run off a narrow screen
   wherever the tick happens to wrap. */
.page-title {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-2);
}
.page-title h1 { min-width: 0; }
.status-check {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 2px solid var(--ok);
  background: var(--ok-bg);
  color: var(--ok);
  font-size: 24px;
  font-weight: 800;
  line-height: 1;
  cursor: default;
}
.status-check::after {
  content: attr(data-tip);
  position: absolute;
  left: 0;
  top: calc(100% + var(--s-1));
  z-index: 2;
  max-width: 100%;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--radius);
  background: var(--navy);
  color: #ffffff;
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.3;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.status-check:hover::after,
.status-check:focus::after { opacity: 1; visibility: visible; }

/* --- the protection banner (the dashboard's answer to "am I covered?") ----- */
.banner {
  border-radius: var(--radius-lg);
  padding: var(--pad-card);
  border: 2px solid var(--line);
  background: var(--surface);
  display: flex;
  gap: var(--s-5);
  align-items: flex-start;
}
.banner__mark {
  flex: none;
  width: 56px; height: 56px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 28px;
  line-height: 1;
  background: var(--neutral-bg);
  color: var(--neutral);
  border: 2px solid var(--neutral-line);
}
.banner__body { min-width: 0; }
.banner__title { font-size: var(--t-xl); font-weight: 800; color: var(--navy); margin: 0 0 var(--s-2); }
.banner__text { font-size: var(--t-md); color: var(--muted); margin: 0; }
.banner__actions { margin-top: var(--s-5); }

/* Headline-only banner ("Your protection is active."): one short row at every
   width. The base .banner is a column on phones, which made this one tall. */
.banner[data-compact] {
  flex-direction: row;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
}
.banner[data-compact] .banner__mark { width: 40px; height: 40px; font-size: 20px; }
.banner[data-compact] .banner__title { font-size: var(--t-lg); margin: 0; }

.banner[data-tone="ok"]      { border-color: var(--ok-line);     background: var(--ok-bg); }
.banner[data-tone="ok"] .banner__mark { background: var(--surface); color: var(--ok); border-color: var(--ok); }
.banner[data-tone="warn"]    { border-color: var(--warn-line);   background: var(--warn-bg); }
.banner[data-tone="warn"] .banner__mark { background: var(--surface); color: var(--warn); border-color: var(--warn); }
.banner[data-tone="danger"]  { border-color: var(--danger-line); background: var(--danger-bg); }
.banner[data-tone="danger"] .banner__mark { background: var(--surface); color: var(--danger); border-color: var(--danger); }
.banner[data-tone="info"]    { border-color: var(--info-line);   background: var(--info-bg); }
.banner[data-tone="info"] .banner__mark { background: var(--surface); color: var(--info); border-color: var(--info); }

/* --- status badges -------------------------------------------------------- */
/* Every badge pairs a shape/glyph with words. Colour is never the only
   carrier of meaning — a member with deuteranopia, or a printed page, still
   reads the state. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border-radius: 999px;
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1.5;
  border: 1px solid var(--neutral-line);
  background: var(--neutral-bg);
  color: var(--neutral);
  max-width: 100%;
}
.badge__glyph { font-size: 1em; line-height: 1; }
.badge[data-tone="ok"]     { color: var(--ok);     background: var(--ok-bg);     border-color: var(--ok-line); }
.badge[data-tone="warn"]   { color: var(--warn);   background: var(--warn-bg);   border-color: var(--warn-line); }
.badge[data-tone="danger"] { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-line); }
.badge[data-tone="info"]   { color: var(--info);   background: var(--info-bg);   border-color: var(--info-line); }

/* --- inline messages ------------------------------------------------------ */
.note {
  border-radius: var(--radius);
  padding: var(--pad-card);
  border: 1px solid var(--info-line);
  background: var(--info-bg);
  color: var(--info);
  font-size: var(--t-base);
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
}
.note[data-tone="ok"]     { border-color: var(--ok-line);     background: var(--ok-bg);     color: var(--ok); }
.note[data-tone="warn"]   { border-color: var(--warn-line);   background: var(--warn-bg);   color: var(--warn); }
.note[data-tone="danger"] { border-color: var(--danger-line); background: var(--danger-bg); color: var(--danger); }
.note[data-tone="warm"]   { border-color: #fed7aa;            background: var(--surface-warm); color: var(--ink); }
.note__glyph { flex: none; font-size: 1.25em; line-height: 1.3; }
.note__body { min-width: 0; }
.note__body > * + * { margin-top: var(--s-2); }
.note h3 { font-size: var(--t-md); color: inherit; }
.note:empty { display: none; }

/* --- progress steps ------------------------------------------------------- */
.steps { display: flex; gap: var(--s-2); list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.steps__item {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4) var(--s-2) var(--s-2);
  border-radius: 999px;
  background: var(--neutral-bg);
  border: 1px solid var(--neutral-line);
  color: var(--muted);
  font-size: var(--t-sm);
  font-weight: 700;
  flex: 1 1 min(12rem, 100%);
}
.steps__num {
  flex: none;
  width: 28px; height: 28px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface);
  border: 2px solid var(--neutral-line);
  font-size: var(--t-sm);
}
.steps__item[data-state="done"] { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.steps__item[data-state="done"] .steps__num { border-color: var(--ok); color: var(--ok); }
.steps__item[data-state="current"] { background: var(--surface-warm); border-color: var(--brand); color: var(--brand-ink); }
.steps__item[data-state="current"] .steps__num { border-color: var(--brand-ink); color: var(--brand-ink); }
/* "waiting" is us, not them. Deliberately quieter than "current" so it does not
   read as an outstanding task the member is failing to do. */
.steps__item[data-state="waiting"] { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }
.steps__item[data-state="waiting"] .steps__num { border-color: var(--info); color: var(--info); }

/* --- forms ---------------------------------------------------------------- */
.field { display: block; }
.field + .field { margin-top: var(--s-5); }
.field__label {
  display: block;
  font-size: var(--t-md);
  font-weight: 700;
  color: var(--navy);
  margin-bottom: var(--s-2);
}
.field__optional { font-weight: 600; color: var(--muted); font-size: var(--t-sm); }
.field__hint { font-size: var(--t-sm); color: var(--muted); margin-top: var(--s-2); }
.field__error {
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--danger);
  margin-top: var(--s-2);
  display: flex;
  gap: var(--s-2);
  align-items: flex-start;
}
.field__error:empty { display: none; }

.input, .select, .textarea {
  width: 100%;
  min-height: var(--tap);
  font: inherit;
  font-size: var(--t-md);
  padding: var(--s-3) var(--s-4);
  color: var(--ink);
  background: var(--surface);
  border: 2px solid var(--line-strong);
  border-radius: var(--radius);
}
.input::placeholder { color: var(--muted); opacity: 1; }
.input:hover, .select:hover { border-color: var(--navy); }
.input[aria-invalid="true"] { border-color: var(--danger); background: var(--danger-bg); }
.input:disabled { background: var(--surface-sunken); color: var(--muted); cursor: not-allowed; }

.field-row { display: flex; gap: var(--s-4); flex-wrap: wrap; }
.field-row > .field { flex: 1 1 12rem; margin-top: 0; }
.field-row + .field, .field + .field-row, .field-row + .field-row { margin-top: var(--s-5); }

/* password reveal — a real button with a real name, not an icon */
.input-group { display: flex; gap: var(--s-2); align-items: stretch; }
.input-group .input { flex: 1 1 auto; }
.input-group .btn { flex: none; }

.checkbox {
  display: flex;
  gap: var(--s-4);
  align-items: flex-start;
  padding: var(--pad-card);
  background: var(--surface-warm);
  border: 1px solid #fed7aa;
  border-radius: var(--radius);
}
.checkbox input[type="checkbox"] {
  flex: none;
  width: 44px; height: 44px;
  margin: 0;
  accent-color: var(--brand-ink);
}
.checkbox label { font-size: var(--t-base); line-height: 1.5; font-weight: 600; min-width: 0; }

/* error summary — focusable, announced, links to the offending field */
.error-summary {
  border: 2px solid var(--danger);
  background: var(--danger-bg);
  border-radius: var(--radius);
  padding: var(--s-4) var(--s-5);
}
.error-summary:empty, .error-summary[hidden] { display: none; }
.error-summary h2 { font-size: var(--t-md); color: var(--danger); margin-bottom: var(--s-2); }
.error-summary ul { margin: 0; padding-left: var(--s-5); }
.error-summary a { color: var(--danger); font-weight: 700; }

/* --- tiles (dashboard grid) ----------------------------------------------- */
.tiles {
  /* Wrapping flex, not a grid, so the last row always fills its width: an odd
     tile at the end stretches across instead of leaving a hole beside it. A
     grid cannot do that when tiles are hidden per plan, because the count is
     not known to the stylesheet. */
  display: flex;
  flex-wrap: wrap;
  min-width: 0;
  gap: var(--s-5);
  list-style: none;
  margin: 0;
  padding: 0;
}
.tile {
  position: relative;
  min-width: 0;
  /* Two across at most: 34% means a third tile cannot fit on a row, and 17rem
     is the narrowest a tile reads well, below which they stack. min(100%, ...)
     matters: 17rem doubles to 544px when a member sets text to 200%, which
     pushed the dashboard into a horizontal scroll. */
  flex: 1 1 min(100%, max(17rem, 34%));
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--pad-card);
  box-shadow: var(--shadow);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
/* Badge art from static/badges (generated by tools/make_badges.py). */
.tile__glyph {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: 14px;
  box-shadow: 0 4px 10px rgba(17, 43, 82, 0.18);
}
.tile__title { font-size: var(--t-lg); font-weight: 800; color: var(--navy); margin: 0; }
.tile__text { font-size: var(--t-base); color: var(--muted); margin: 0; }
.tile__actions { margin-top: auto; padding-top: var(--s-3); }
/* Whole-tile link via a stretched pseudo-element: the accessible name comes
   from the real anchor text, and hover is not the only affordance — the
   heading is underlined on hover AND the link is separately focusable. */
.tile__link::after { content: ""; position: absolute; inset: 0; border-radius: var(--radius-lg); }
/* The stretched hit area is the whole tile, but the element itself must still
   measure up on its own — a keyboard or switch user targets the control, not
   the overlay. */
.tile__title .tile__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font: inherit;
  color: var(--navy);
  text-decoration: none;
  text-align: left;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.tile__title .tile__link:hover { color: var(--brand-ink); }
.tile:hover, .tile:focus-within { border-color: var(--brand); box-shadow: var(--shadow-lift); }
.tile:focus-within { outline: var(--focus) solid var(--navy); outline-offset: 2px; }
.tile__link:focus-visible { outline: none; box-shadow: none; }
.tile:hover .tile__link { text-decoration: underline; }

/* --- alert cards ---------------------------------------------------------- */
.alert-list {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  /* minmax(0,1fr), not 1fr: a bare 1fr track still has min-width:auto, so a
     long unbroken alert title pushed the whole card past the viewport once
     text was scaled to 200%. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-4);
}
.alert-card {
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 6px solid var(--neutral-line);
  border-radius: var(--radius);
  padding: var(--pad-card);
  box-shadow: var(--shadow);
}
.alert-card[data-severity="critical"] { border-left-color: var(--danger); }
.alert-card[data-severity="warning"]  { border-left-color: var(--warn); }
.alert-card[data-severity="info"]     { border-left-color: var(--info); }
.alert-card[data-resolved="true"] { background: var(--surface-sunken); border-left-color: var(--ok); }
.alert-card__top {
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  flex-wrap: wrap;
  margin-bottom: var(--s-3);
}
.alert-card__title { font-size: var(--t-lg); margin: 0; flex: 1 1 min(14rem, 100%); min-width: 0; overflow-wrap: anywhere; }
.alert-card__when { font-size: var(--t-sm); color: var(--muted); margin: 0 0 var(--s-3); }
.alert-card__detail { font-size: var(--t-base); color: var(--ink); margin: 0; white-space: pre-line; overflow-wrap: anywhere; }
.alert-card__actions { margin-top: var(--s-4); }
.alert-card dl { margin: var(--s-3) 0 0; display: grid; gap: var(--s-2); }
.alert-card dt { font-size: var(--t-sm); font-weight: 800; color: var(--navy); }
.alert-card dd { margin: 0; font-size: var(--t-base); color: var(--ink); overflow-wrap: anywhere; }

/* --- empty & loading states ----------------------------------------------- */
.empty {
  text-align: center;
  padding: var(--s-10) var(--s-5);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.empty__glyph { font-size: 40px; line-height: 1; margin-bottom: var(--s-3); }
.empty h2, .empty h3 { margin-bottom: var(--s-2); }
.empty p { color: var(--muted); max-width: 44ch; margin-inline: auto; }
.empty .btn-row { justify-content: center; margin-top: var(--s-5); }

.skeleton {
  background: linear-gradient(90deg, var(--neutral-bg) 25%, #e8edf4 37%, var(--neutral-bg) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease infinite;
  border-radius: 8px;
  color: transparent;
  user-select: none;
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
.skeleton--line { height: 1em; margin-bottom: var(--s-2); }
.skeleton--title { height: 1.5em; width: 55%; margin-bottom: var(--s-3); }

/* --- key/value list ------------------------------------------------------- */
/* Mobile-first, deliberately. Stacked is the BASE and two columns are the
   opt-in above 30rem, rather than the reverse: a max-width override of a
   two-column base has to win a cascade it does not reliably win, and the
   failure mode is a label column that eats the card and takes the page
   sideways at 200% text. Starting stacked has no such failure mode. */
.kv { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; margin: 0; }
.kv dt { font-weight: 800; color: var(--muted); margin-top: var(--s-3); }
.kv dt:first-child { margin-top: 0; }
.kv dd { margin: 0; overflow-wrap: anywhere; }

@media (min-width: 30.0625rem) {
  .kv {
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    gap: var(--s-2) var(--s-4);
  }
  .kv dt { margin-top: 0; }
}

/* A .kv whose pairs are wrapped in a div --------------------------------
   /dashboard/exposure builds each pair as <div><dt/><dd/></div>, so the grid
   ITEM is the whole pair rather than the label and the value. The two-column
   template above then lays pairs side by side: "Name" took the auto column
   and "Addresses" was squeezed into what was left. Because that track is
   minmax(0, 1fr) it may shrink below its content, and .kv dd carries
   overflow-wrap:anywhere — so the value wrapped one character per line,
   reading vertically down the right edge of the card.

   Fix: span each wrapper across the row and give IT the label/value grid.
   The bare <dt>/<dd> form used by the admin gate panel is untouched. */
.kv > div { grid-column: 1 / -1; min-width: 0; }
.kv > div + div { margin-top: var(--s-3); }
.kv > div > dt { margin-top: 0; }

@media (min-width: 30.0625rem) {
  .kv > div {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    column-gap: var(--s-4);
    align-items: baseline;
  }
  /* The parent's row-gap already separates the rows. */
  .kv > div + div { margin-top: 0; }
}

/* --- tables (admin) ------------------------------------------------------- */
/* overflow-x:auto alone is not enough. Without min-width:0 the container is
   sized to its content by its flex/grid parent, so instead of the table
   scrolling inside the card, the card grew and took the whole page sideways
   with it. This is the single most common cause of a page that scrolls
   horizontally despite every child "having overflow handled". */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  min-width: 0;
  max-width: 100%;
}
.card > *, .card__header + * { min-width: 0; }
.table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.table caption { text-align: left; font-weight: 700; color: var(--muted); padding-bottom: var(--s-3); }
/* Senior-facing tables. The base .table sits at --t-sm (16px / 12pt), which is
   the floor, not a target. On a page read by people in their eighties the cells
   go to --t-base (18px) with roomier padding — the extra width is absorbed by
   table--stack collapsing to cards on a phone rather than by shrinking type. */
.table--roomy th, .table--roomy td {
  font-size: var(--t-base);
  padding: var(--s-4) var(--s-4);
  line-height: 1.5;
}
.table--roomy thead th { font-size: var(--t-sm); }

.table th, .table td {
  text-align: left;
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.table thead th {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  border-bottom: 2px solid var(--line-strong);
  white-space: nowrap;
}
.table tbody tr:hover { background: var(--surface-sunken); }
.table .num { font-variant-numeric: tabular-nums; }
.mono {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.95em;
  overflow-wrap: anywhere;
}

/* Safety net for prose that quotes a technical value. An identifier like
   INTEGRATION_ADMIN_TOKEN offers no break opportunity, so at 200% text it was
   427px wide inside a 293px card and took the admin console sideways. Prefer
   wrapping the value in .mono; this catches the ones that slip through. */
p, li, dd, .banner__text, .tile__text, .note__body { overflow-wrap: break-word; }

/* Below the breakpoint each row becomes a stacked record. The th text is
   echoed via data-label so no header context is lost on a phone. */
@media (max-width: 48rem) {
  /* The table element itself must leave table layout too. With only the rows
     and cells switched to block/flex, the <table> kept a table's min-content
     width — 628px on a 360px phone — and dragged the whole page sideways. */
  .table--stack, .table--stack tbody { display: block; width: 100%; min-width: 0; }
  /* display:none, not the visually-hidden clip pattern. `position:absolute` on
     a table-header-group is undefined behaviour, and the browser kept laying
     the <th> cells out at full width — they alone held the page at 461px on a
     360px phone. Nothing is lost: each <td> carries its own data-label, which
     is what the stacked view renders as the field name. */
  .table--stack thead { display: none; }
  .table--stack tr {
    display: block;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    padding: var(--s-3);
    margin-bottom: var(--s-3);
  }
  /* min-width:0 + overflow-wrap let a long value wrap inside the stacked
     record. Without them an email address at 200% text was 533px in a 279px
     cell, and the table scrolled sideways instead of the text going onto a
     second line. */
  .table--stack td {
    display: flex;
    gap: var(--s-4);
    border: 0;
    padding: var(--s-2) var(--s-1);
    min-width: 0;
    overflow-wrap: anywhere;
  }
  .table--stack td > * { min-width: 0; }
  .table--stack td::before {
    content: attr(data-label);
    flex: 0 0 min(9rem, 38%);
    font-weight: 800;
    color: var(--muted);
    font-size: var(--t-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }
  .table--stack tbody tr:hover { background: var(--surface); }
}

/* --- dialogs -------------------------------------------------------------- */
dialog.modal {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--s-6);
  max-width: min(34rem, calc(100vw - 2rem));
  width: 100%;
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-lift);
}
dialog.modal::backdrop { background: rgba(2, 6, 23, 0.55); }
dialog.modal h2 { margin-bottom: var(--s-3); }
dialog.modal .btn-row { margin-top: var(--s-6); justify-content: flex-end; }
.modal__target {
  display: block;
  margin: var(--s-4) 0;
  padding: var(--s-3) var(--s-4);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-weight: 700;
  overflow-wrap: anywhere;
}

/* --- secure embed (identity verification) --------------------------------- */
/* Deliberately distinct from Parable's own surfaces: a member is being asked
   to answer personal questions, and they should be able to see exactly where
   Parable stops and the credit bureau begins. */
.secure-frame {
  border: 2px solid var(--navy);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
}
.secure-frame__bar {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  background: var(--navy);
  color: var(--on-dark);
  font-size: var(--t-sm);
  font-weight: 700;
}
.secure-frame__body { position: relative; min-height: 26rem; }
.secure-frame iframe { display: block; width: 100%; height: 34rem; border: 0; background: var(--surface); }
.secure-frame__loading {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center; gap: var(--s-3);
  padding: var(--s-6);
  text-align: center;
  background: var(--surface);
  color: var(--muted);
}
.spinner {
  width: 34px; height: 34px;
  border: 4px solid var(--neutral-line);
  border-top-color: var(--brand-ink);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

/* --- utilities ------------------------------------------------------------ */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
[hidden] { display: none !important; }
.divider { border: 0; border-top: 1px solid var(--line); margin: var(--s-6) 0; }
.pill-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* --- responsive ----------------------------------------------------------- */
@media (max-width: 48rem) {
  :root { --t-3xl: 2rem; --t-2xl: 1.625rem; --t-xl: 1.375rem; }

  .card { border-radius: var(--radius); }
  .banner { flex-direction: column; gap: var(--s-4); }
  .masthead__nav { width: 100%; margin-left: 0; }
  .masthead__nav .btn { flex: 1 1 auto; }
  .btn--stack-block { width: 100%; }
  .secure-frame iframe { height: 30rem; }
}

/* Landscape phones: a fixed tall iframe would push the submit control off
   screen with the on-screen keyboard open. */
@media (max-height: 32rem) {
  .secure-frame iframe { height: 22rem; }
  .secure-frame__body { min-height: 18rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .skeleton { background: var(--neutral-bg); }
}

@media (forced-colors: active) {
  .btn, .card, .tile, .badge, .note, .banner { border: 1px solid ButtonBorder; }
  .alert-card { border-left-width: 6px; }
  .badge__glyph, .note__glyph { forced-color-adjust: auto; }
}

@media print {
  .btn, .masthead__nav, .skip-link { display: none !important; }
  body { background: #fff; }
  .card, .tile, .alert-card { box-shadow: none; border: 1px solid #000; }
}

/* --- clinic booking: a date picker built from native radios ----------------
   Real radios, so keyboard, screen readers and zoom all work with no script. */
.slot-days { display: grid; gap: var(--s-4); margin-top: var(--s-4); }
.slot-day {
  margin: 0;
  min-width: 0;
  padding: var(--pad-card);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.slot-day legend { padding: 0 var(--s-2); font-size: var(--t-md); font-weight: 800; color: var(--navy); }
.slot-day__times { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-2); }
.slot {
  flex: 1 1 12rem;
  min-width: 0;
  min-height: var(--tap);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  font-size: var(--t-md);
  font-weight: 700;
  color: var(--navy);
  background: var(--surface);
  border: 2px solid var(--line-strong);
  border-radius: var(--radius);
  cursor: pointer;
}
.slot:hover { border-color: var(--navy); }
.slot input[type="radio"] { flex: none; width: 28px; height: 28px; margin: 0; accent-color: var(--brand-ink); }
.slot:has(input:checked) { border-color: var(--brand-ink); background: var(--surface-warm); }
.slot:focus-within { outline: 3px solid var(--navy); outline-offset: 2px; }
.slot--taken { justify-content: space-between; cursor: default; color: var(--muted); background: var(--surface-sunken); border: 2px dashed var(--line-strong); }
.slot--taken:hover { border-color: var(--line-strong); }

/* ---------- Legal documents (/terms, /terms/family-memory-care) ---------- */
.legal { line-height: 1.65; }
.legal > * + * { margin-top: var(--s-4); }
.legal h1 { margin-bottom: var(--s-2); }
.legal h2 { margin-top: var(--s-8); font-size: var(--t-lg, 1.35rem); }
.legal ul, .legal ol { padding-left: var(--s-6); }
.legal li + li { margin-top: var(--s-2); }
.legal-meta { color: var(--muted); font-size: var(--t-sm); }
.legal-summary { background: var(--surface-warm); border: 2px solid var(--line-strong); border-radius: var(--radius); padding: var(--s-4) var(--s-5); }
.legal-summary h2 { margin-top: 0; }
/* Disclaimers, limits and indemnities are set apart so they are conspicuous. */
.legal-caps { font-weight: 700; padding: var(--s-4); border-left: 6px solid var(--brand-ink); background: var(--surface-sunken); }
.legal h3 { margin-top: var(--s-6); font-size: var(--t-base); }
.legal-table { width: 100%; border-collapse: collapse; }
.legal-table th, .legal-table td { text-align: left; vertical-align: top; padding: var(--s-3); border-bottom: 1px solid var(--line); }
.legal-table th { background: var(--surface-sunken); }
