/* Maccabee Design and Consulting — site stylesheet
   Brand kit: tools/render_maccabee_pdf.py (Maccabee-core black/silver/cyan).
   The display face is set in site.yaml (fonts.display) and rendered into
   the @@FONT-DISPLAY@@ block below by build.py — see
   shared/templates/assets/fonts/ofl/README.md for the OFL library. Used
   ONLY for the H1/wordmark scale, never for body copy.

   Design pass, 2026-09-06 (Warda Diziner, Diziner family — web/UX):
   tokenized the palette + type + spacing scale, made the department/store
   card grid mobile-first (single column -> 2 -> 3 -> 4 as the viewport
   grows, so a phone always gets a clean stack, never a squeeze), verified
   every shipped text/background pair against WCAG AA (ratios below and in
   the sprint report — nothing here is guessed), and hardened the "commits
   to dark, stays legible if the OS is light" requirement with an explicit
   color-scheme so native form/scrollbar chrome doesn't fight the brand
   background. No copy changed, no selectors touching functions/_middleware.js. */

:root {
  /* ---- brand tokens ----
     DESIGN-TOKENS-2026-09-10 (Geula Diziner): --mc-black/--mc-bg corrected
     to #000000, the logo's MEASURED black (emblem.png corners sampled
     (0,0,0,255) every time) — the prior value was one step off. Two tokens
     because two roles (foreground text-on-cyan vs. the page ground) that
     happen to share a value today; contrast only improves versus the old
     value (every ratio recomputed in the spec, nothing regresses). */
  --mc-black: #000000;
  --mc-cyan: #00E5FF;
  /* Print-legible cyan for text on light backgrounds (same darkened teal
     used in tools/render_maccabee_pdf.py — raw cyan fails contrast for
     text). Not used on-screen today (the site is dark-first everywhere)
     but kept as a token for any future light-surface/print artifact that
     shares this sheet. */
  --mc-cyan-ink: #007A8A;
  /* Hover/active state for anything with a solid cyan fill — formalizes
     what was two hand-picked hexes (.guest-link:hover's bare --mc-cyan,
     .gate-form button:hover's #6df3ff) into one token both now use. */
  --mc-cyan-bright: #6DF3FF;
  --mc-silver: #C6C9CE;
  --mc-bg: #000000;
  --mc-bg-panel: #16161A;
  /* The value design.md's inline roster-card style ships (#141418) —
     promoted to a token so a future component references it instead of
     hand-picking a fourth "close enough" dark grey (DESIGN-TOKENS §2). */
  --mc-bg-panel-alt: #141418;
  /* Formalizes .card:hover's prior hand-picked #1b1b20 — any panel's
     "lifted" hover state, not just .card. */
  --mc-hover: #1B1B22;
  --mc-text: #ECECEE;
  --mc-text-dim: var(--mc-silver);
  --mc-border: #2A2A30;
  --mc-border-strong: #3A3A42;
  --mc-focus: #7FF0FF;
  --mc-error: #FF6B6B;
  /* "Pending," not "approved" and not "wrong" — amber/gold, deliberately
     neither cyan nor error red (DESIGN-TOKENS §10, roster UNVETTED badge). */
  --mc-badge-unvetted-border: #C9A227;
  --mc-badge-unvetted-text: #E6C14A;

  /* ---- type ---- */
  /* Helvetica Neue first (brand feel on macOS/iOS); real fallbacks after
     it so Windows/Android/Linux — where Helvetica Neue isn't installed —
     still land on a clean, near-neutral system face instead of default
     serif Times. */
  /* 2026-10-07: the house faces lead (Maccabee Sans for text, MDC Hebrew UI for Hebrew; @font-face in mdc-skin.css); the earlier
     system stack stays behind them as the fallback, so a page still reads if a face never loads. */
  --font-body: "Maccabee Sans", "MDC Hebrew UI", "Helvetica Neue", Helvetica, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, Arial, sans-serif;
  /* --font-display is GENERATED — see the @@FONT-DISPLAY@@ block below. */

  /* Numeric type scale (DESIGN-TOKENS §4) — the sizes already in use across
     the site, written down once so nobody invents a fifth by eye. */
  --fs-h1: 2.1rem;
  --fs-h1-splash: 2.4rem;
  --fs-h2: 1.3rem;
  --fs-h3: 1.15rem;
  --fs-body: 1rem;
  --fs-small: 0.9rem;
  --fs-tiny: 0.82rem;
  --fs-badge: 0.72rem;

  /* ---- spacing / shape scale ---- */
  --space-1: 6px;
  --space-2: 12px;
  --space-3: 20px;
  --space-4: 32px;
  /* Inter-SECTION rhythm only (hero->grid, grid->signup box) — --space-4
     stays the ceiling for card-internal/inter-card spacing (DESIGN-TOKENS §5). */
  --space-5: 48px;
  --radius: 8px;
  --radius-sm: 6px;
  --transition: 0.15s ease;

  /* ---- motion tokens (components pass, 2026-10-05; the stylesheet had none) ---- */
  --dur-1: 0.12s;
  --dur-2: 0.2s;
  --dur-3: 0.35s;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);

  /* ---- buttons (see "Components" at the foot of this file) ----
     THE ONE-LINE CHOICE (Zusha's call, GAP-AUDIT D2 / F-14): which finish is "primary". Every button is a library plate
     (mdc-skin.css, generated by tools/build_mdc_skin.py): the primary is the cyan skin and the hero's pair is the amber and
     etched skins (ALIASES: `.wow-hero-cta .btn--primary`, `.wow-hero-cta .btn--secondary`). To make the hero cyan, delete those
     two alias entries and rebuild; to change every primary, change the `mdc-skin-cyan` / `mdc-btn--primary` alias lists. */
  --btn-link: var(--mc-cyan);

  /* Height of the phone header row (44px toggle + 2 x 14px padding + 3px rule), used to size the open menu. */
  --header-h: 75px;
}

/* This is a deliberately dark-first brand (black field, cyan accent) — the
   same look regardless of the visitor's OS light/dark setting, so colors
   are hard-coded rather than mapped through prefers-color-scheme; see
   artifact/brand guidance on "a design that deliberately commits to a
   single look." color-scheme below is the piece that makes that safe: it
   tells the browser to render its OWN chrome (scrollbar track/thumb,
   password-reveal icon, date pickers, form-control default styling) in
   dark, so a visitor whose OS is set to light never sees a light native
   control fighting this dark page — "readable when the OS is light" is
   handled here, not by branching the palette. */
:root { color-scheme: dark; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--mc-bg);
  color: var(--mc-text);
  /* Safety net for the full-bleed hero's 100vw breakout trick (below):
     100vw can exceed the visible viewport by a scrollbar's width in some
     browser/OS combinations. This is a no-op everywhere the trick lands
     exactly on the viewport edge, and a guardrail everywhere it doesn't --
     nothing on this site relies on horizontal scroll. */
  overflow-x: hidden;
}

body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
}

/* The 401 "guest access required" page rendered inline by
   functions/_middleware.js (unauthorizedResponse()) — kept out of the
   markup as inline style="" so the site CSP can ship style-src 'self'
   with no 'unsafe-inline'. Same centered-panel look the inline styles
   gave before; background/text/link colors already come from the rules
   above and from `a { color: var(--mc-cyan); }` below. */
.locked-page {
  display: flex;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* @@FONT-DISPLAY-START@@
   GENERATED by build.py from site.yaml's fonts.display block.
   Do not hand-edit - the build rewrites it every time.
   License: SIL OFL 1.1 */
:root {
  --font-display: "Suez One", var(--font-body);
}

@font-face {
  font-family: "Suez One";
  src: url("/static/fonts/SuezOne-Regular.ttf") format("truetype");
  font-display: swap;
}
/* @@FONT-DISPLAY-END@@ */
/* 2026-10-07: Maccabee Display leads the display face; Suez One (the generated block above, still declared) is its fallback. The
   block above is generated from site.yaml and is left exactly as the build writes it. */
:root {
  --font-display: "Maccabee Display", "Suez One", var(--font-body);
  --font-mono: "Maccabee Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --font-quote: "Maccabee Serif", Georgia, "Iowan Old Style", "Times New Roman", serif;
}

a { color: var(--mc-cyan); text-decoration: underline; text-underline-offset: 2px; }
a:hover, a:focus { color: #ffffff; }
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible {
  outline: 3px solid var(--mc-focus);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--mc-cyan);
  color: var(--mc-black);
  padding: 8px 14px;
  z-index: 100;
}
.skip-link:focus { left: 8px; top: 8px; }

/* NAV-UX-SPEC-2026-09-10 §6: sticky so the header (and its own Departments
   dropdown) never gets tucked under page content on scroll. z-index sits
   above .nav-dropdown-menu's 60 so a dropdown opened near the top of a
   scrolled page still shows above what follows it. */
header.site-header {
  position: sticky;
  top: 0;
  z-index: 70;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: 14px var(--space-3);
  border-bottom: 3px solid transparent;           /* the lit rule below is drawn in this 3px (it keeps the header's height) */
  color: var(--mdc-c-onEnamel, #ECEDEE);
  background-color: var(--mc-black);
  background-image: linear-gradient(100deg, rgb(0 229 255 / 0.16), rgb(0 229 255 / 0) 46%), var(--mdc-face-enamel);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.14), inset 0 -1px 0 #000, 0 1px 0 #000, var(--mdc-elev-e3);
}
header.site-header::before {                       /* the enamel's grain */
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--mdc-tex-enamel);
  background-size: var(--mdc-tex-enamel-size);
  mix-blend-mode: overlay;
  opacity: calc(var(--mdc-tex-enamel-op, 0.5) * 0.45);
}
header.site-header::after {                        /* the lit cyan rule: a glass lamp strip, not a 2D line */
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: -3px;
  height: 3px;
  background: linear-gradient(180deg, #9BF6FF, #00E5FF 46%, #00C4DB);
  box-shadow: 0 0 10px rgb(0 229 255 / 0.55), 0 1px 0 #000;
}
header.site-header .brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* F-02: the wordmark was drawn at 32px tall from a file with 36px of empty margin, so the lettering was ~20px
   high and the second line ~4px. static/img/wordmark-header.webp is the same artwork cropped to its ink: at
   these heights the lettering is 24px (phone) and 28px (desktop) and the second line is 10 to 12px. */
header.site-header img.wordmark { height: 40px; width: auto; display: block; }
@media (min-width: 720px) { header.site-header img.wordmark { height: 52px; } }
header.site-header .brand-text {
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* Mobile hamburger toggle — CSS-hidden above the collapse breakpoint (below)
   so desktop never sees it; static/js/nav.js is the one script this button
   needs (§1.4). 44px minimum per WCAG 2.5.8 AAA / platform-HIG floor,
   stricter than the 24px floor the rest of the site's tap targets clear. */
.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}
/* The two glyphs swap on aria-expanded: one source of truth for the state, no extra class to keep in sync. */
.nav-toggle .nav-toggle__close,
.nav-toggle[aria-expanded="true"] .nav-toggle__open { display: none; }
.nav-toggle[aria-expanded="true"] .nav-toggle__close { display: inline-block; }

nav.site-nav ul.site-nav-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  padding: 0;
}
nav.site-nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  /* min-height 44px clears WCAG 2.5.8 AAA for a thumb on mobile nav
     (NAV-UX-SPEC §6) — was a bare 8px/6px padding, now a floor plus padding. */
  min-height: 44px;
  padding: 8px 12px;
  color: var(--mc-text);
  text-decoration: none;
  text-shadow: 0 -1px 0 rgb(0 0 0 / 0.8);
}
nav.site-nav a::after,
.nav-dropdown summary::after {                     /* the lit bar (it was a flat 2px border-bottom) */
  content: "";
  position: absolute;
  inset-inline: 12px;
  bottom: 5px;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(180deg, #9BF6FF, #00E5FF 50%, #00C4DB);
  box-shadow: 0 0 8px rgb(0 229 255 / 0.8);
  opacity: 0;
  transform: scaleX(0.35);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
nav.site-nav a:hover, nav.site-nav a:focus, nav.site-nav a[aria-current="page"] {
  color: var(--mc-cyan);
}
nav.site-nav a:hover::after, nav.site-nav a:focus-visible::after, nav.site-nav a[aria-current="page"]::after,
.nav-dropdown summary:hover::after, .nav-dropdown[open] summary::after,
.nav-dropdown[aria-current="true"] summary::after, .nav-dropdown summary[aria-current="true"]::after {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  nav.site-nav a::after, .nav-dropdown summary::after { transition: none; }
}
/* The current-page link also gets weight, not just color/underline — a
   second, non-color cue for which page you're on (helps at a glance, and
   never relies on color alone). */
nav.site-nav a[aria-current="page"] { font-weight: 700; }

/* ---------- Departments dropdown — pure <details>/<summary> + CSS, zero
   JavaScript (NAV-UX-SPEC §1.3). <details> is natively keyboard-operable
   (Enter/Space toggles, Tab reaches every child once open), natively
   touch-operable (a tap toggles it), and works with no script at all —
   the CSS below layers hover/focus-within on top for a pointer/keyboard
   desktop user who shouldn't have to click at all, additively: [open],
   :hover and :focus-within all independently show the menu. ---------- */
.nav-dropdown { position: relative; }
.nav-dropdown summary {
  position: relative;
  cursor: pointer;
  list-style: none;
  color: var(--mc-text);
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 8px 12px;
}
.nav-dropdown summary::-webkit-details-marker,
.nav-dropdown summary::marker { display: none; }
.nav-dropdown .nav-caret { margin-left: 6px; transition: transform var(--dur-2) var(--ease); }
.nav-dropdown[open] .nav-caret { transform: rotate(180deg); }
.nav-dropdown[aria-current="true"] summary,
.nav-dropdown summary[aria-current="true"] {
  color: var(--mc-cyan);
}
.nav-dropdown-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 220px;
  padding: 6px 0;
  z-index: 60;
  list-style: none;
  margin: 4px 0 0;
}
.nav-dropdown[open] .nav-dropdown-menu { display: block; }
@media (hover: hover) and (pointer: fine) {
  .nav-dropdown:hover .nav-dropdown-menu,
  .nav-dropdown:focus-within .nav-dropdown-menu { display: block; }
}
.nav-dropdown-menu li a {
  display: flex;
  min-height: 44px;
  align-items: center;
  padding: 10px 16px;
  white-space: nowrap;
}
.nav-dropdown-menu li a::after { display: none; }
.nav-dropdown-menu li a:hover, .nav-dropdown-menu li a:focus-visible {
  background-image: linear-gradient(90deg, rgb(0 229 255 / 0.18), rgb(0 229 255 / 0));
}
.nav-dropdown-viewall a { font-weight: 700; box-shadow: inset 0 1px 0 var(--mdc-groove-a), inset 0 2px 0 var(--mdc-groove-b); }

/* ---------- breadcrumb ("you are here"), every gated page ---------- */
.breadcrumb { padding: 10px var(--space-3) 0; max-width: 880px; margin: 0 auto; }
.breadcrumb ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  font-size: var(--fs-small);
  color: var(--mc-text-dim);
}
.breadcrumb li:not(:last-child)::after { content: "/"; margin-left: 6px; color: var(--mc-border-strong); }
.breadcrumb a {
  color: var(--mc-text-dim);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}
/* Every crumb, link or current page, is the same flex row so the text shares one baseline (Vered, #349: the
   current page sat 5px above "Home" on phones). 44px on a touch screen. */
.breadcrumb li { display: flex; align-items: center; min-height: 24px; }
.breadcrumb ol { align-items: center; }
@media (pointer: coarse) { .breadcrumb a, .breadcrumb li { min-height: 44px; } }
.breadcrumb a:hover, .breadcrumb a:focus { color: var(--mc-cyan); text-decoration: underline; }
.breadcrumb li[aria-current="page"] { color: var(--mc-text); font-weight: 600; }

/* ---------- footer sitemap, every gated page ---------- */
.site-sitemap {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--mdc-groove-a);
  box-shadow: 0 1px 0 var(--mdc-groove-b);
}
@media (min-width: 340px) { .site-sitemap { grid-template-columns: repeat(2, 1fr); } }   /* two columns on a phone: 44px rows would otherwise make the footer a very long single column */
@media (min-width: 720px) { .site-sitemap { grid-template-columns: repeat(3, 1fr); } }
.sitemap-group h2 {
  font-size: var(--fs-badge);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--mc-silver);
  margin: 0 0 8px;
  border-bottom: none;
  padding-bottom: 0;
}
.sitemap-group ul { list-style: none; margin: 0; padding: 0; }
.sitemap-group li { margin: 0; }
/* F-15 (WCAG 2.5.8, 24px minimum): the footer links were bare inline text, 15px tall with 4px between them.
   Each is now a block row at least 32px tall with a mouse and 44px on a touch screen, so the target is the row,
   not the word. The old comment in this file claimed the 24px floor was cleared; this is what clears it. */
.sitemap-group a {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 2px 0;
}
@media (pointer: coarse) { .sitemap-group a { min-height: 44px; } }
.sitemap-group .link-ext { opacity: 0.7; }
.sitemap-sublist {
  margin-top: 6px;
  padding-top: 6px;
  font-size: 0.92em;
  box-shadow: inset 0 1px 0 var(--mdc-groove-a), inset 0 2px 0 var(--mdc-groove-b);
}

main {
  max-width: 880px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3) 64px;
}

main h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: 1.25;
  letter-spacing: 0.01em;
  padding-bottom: 10px;
  margin: 0 0 var(--space-3);
  overflow-wrap: break-word;
  position: relative;
}
main h2 {
  font-size: var(--fs-h2);
  margin: var(--space-4) 0 10px;
  color: #fff;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--mdc-groove-a);
  box-shadow: 0 1px 0 var(--mdc-groove-b);
}
/* One <h1> per page: the generated manual pages (sync_manual_pages.py) demote
   the manual's chapter headings to <h2> and sections to <h3>/<h4>. These
   classes carry the OLD look so the outline changes and the page does not.
   Selectors are `main .ms-hN`, one class heavier than `main h1`/`main h2`. */
main .ms-h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: 1.25;
  letter-spacing: 0.01em;
  color: inherit;
  padding-bottom: 10px;
  margin: 0 0 var(--space-3);
  overflow-wrap: break-word;
  position: relative;
}
main .ms-h2 {
  font-size: var(--fs-h2);
  margin: var(--space-4) 0 10px;
  color: #fff;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--mdc-groove-a);
  box-shadow: 0 1px 0 var(--mdc-groove-b);
}
main .ms-h3 { font-size: var(--fs-h3); margin: var(--space-3) 0 8px; }
main h1::after, main .ms-h1::after {                /* the lamp rule: it was a flat 2px cyan border */
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(180deg, #9BF6FF, #00E5FF 50%, #00C4DB);
  box-shadow: 0 0 8px rgb(0 229 255 / 0.55), 0 1px 0 #000;
}
.wow-hero-copy h1::after, .state .state__title::after { display: none; }
.signup-box h2, .contact-box h2 { box-shadow: none; }
main p, main li { color: var(--mc-text); overflow-wrap: break-word; }   /* the house face is wider: an unbroken token such as AI/Engineering/Torah must wrap, not run off a phone */
main small { color: var(--mc-text-dim); }

/* ---------- department / store cards ----------
   Mobile-first grid: one column by default (a phone gets a clean vertical
   stack of full-width, easy-to-tap cards — never two cramped, unequal
   columns), then 2/3/4 columns as the viewport grows. This replaces the
   earlier flex-wrap approach, which let card widths and per-row counts
   drift depending on how much text each card happened to carry; a grid
   keeps every card in a row the same width regardless of content length.
   /departments (13 cards) and /store (one card per department service
   line) are the pages this matters for.
   Tracks are minmax(0, 1fr), not bare 1fr: 1fr means minmax(auto, 1fr),
   whose auto minimum is the longest unbreakable word in the card, so a
   word like "Cybersecurity" grew its track and pushed the grid ~71px past
   the main column at 1440px (/departments/technology). The 0 minimum
   holds every track to its share; .card's overflow-wrap below breaks
   the word instead. */
.card-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  margin: var(--space-3) 0;
}
@media (min-width: 480px) {
  .card-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 720px) {
  .card-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 960px) {
  .card-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.card {
  display: block;
  min-width: 0;
  overflow-wrap: anywhere;
  /* Padding alone clears a ~57px tap target at the base 16px/1.6 body
     size (16 + 16 + ~25.6), comfortably past the 24px WCAG 2.5.8 floor. */
  padding: 16px;
  text-decoration: none;
}
.card:hover, .card:focus {
  color: var(--mc-cyan);
}
.card--soon {
  color: var(--mc-text-dim);
  cursor: default;
}
.card--soon:hover {
  color: var(--mc-text-dim);
}
.card small { display: block; margin-top: 6px; }

/* ---------- MDC on Claude manual pages (Warda Diziner, 2026-09-23):
   contributor bylines + in-chapter quotes are LIVE-GENERATED from each
   edition's manual.md by website/tools/sync_manual_pages.py (never hand-
   edited) — the manual's own ":::byline"/":::quote" markup converts to
   this plain HTML. Each byline/quote carries a small face tile
   (.ms-face), joined by roster slug per website/README.md's face-
   provenance rule: an approved face anywhere, else an unvetted internal
   face on an internal-tier page only, else an initials monogram
   (.ms-face--placeholder) — the generator never emits a path
   build.py::copy_faces() would reject. */
.ms-byline {
  color: var(--mc-text-dim);
  font-size: 0.95rem;
  margin: var(--space-2) 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ms-byline-name { color: var(--mc-text); font-weight: 600; }
.ms-face {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  vertical-align: middle;
  flex: none;
  box-shadow: 0 0 0 1px var(--mdc-hilite), 0 0 0 2px var(--mdc-silver-500), var(--mdc-elev-e1);
}
.ms-face-card { display: inline-flex; flex: none; } /* "card" in the class name only so
  tests/test_faces.py's card-finder recognizes it as the unvetted face's own
  badge container — no visual role beyond what .ms-face already gives it. */
.ms-face--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--mc-text-dim);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  background-image: var(--mdc-face-enamel);
}
.ms-quote {
  padding: 16px 20px;
  margin: var(--space-3) 0;
  padding-left: 30px;
  font-family: var(--font-quote);
}
.ms-quote p { margin: 0 0 8px; }
.ms-quote p:last-of-type { margin-bottom: 8px; }
.ms-quote-footer {
  color: var(--mc-text-dim);
  font-size: 0.9rem;
  font-style: normal;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ms-edition-meta { color: var(--mc-text-dim); }
.ms-chapter-banner {
  padding: 20px 24px;
  margin: var(--space-3) 0;
  padding-left: 30px;
}
.ms-chapter-banner strong { color: var(--mc-cyan); }
.ms-chapter-banner span { color: var(--mc-text-dim); }

/* ---------- .surface-interactive — the canonical clickable-surface
   behavior (DESIGN-TOKENS §6), now shared by .card and every new
   download/roster card rather than each hand-rolling its own hover.
   Three states, never conflated: hover/focus-within lifts the panel and
   turns the border cyan ("you can interact with this"); :focus-visible on
   the surface itself gets the distinct --mc-focus ring (keyboard-tabbed,
   as opposed to merely hoverable); a non-interactive surface simply never
   gets this class. ---------- */

.surface-interactive:focus-visible {
  outline: 3px solid var(--mc-focus);
  outline-offset: 2px;
}

/* ---------- downloads page card (DESIGN-TOKENS §8) ---------- */
.download-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.download-card__media {
  width: 100%;
  display: block;
  background: var(--mc-bg-panel-alt);
}
.download-card__media--video { aspect-ratio: 16 / 9; object-fit: cover; }
.download-card__media--cover { aspect-ratio: 17 / 22; object-fit: cover; }
.download-card__body {
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.download-card__title { font-size: var(--fs-h3); color: #fff; margin: 0; }
.download-card__meta { font-size: var(--fs-small); color: var(--mc-text-dim); margin: 0; }
/* ---------- mailing-list signup box (DESIGN-TOKENS §9) ---------- */
.signup-box {
  padding: var(--space-4);
  margin: var(--space-5) 0;
  max-width: 480px;
}
.signup-box h2 {
  margin: 0 0 6px;
  font-size: var(--fs-h2);
  color: #fff;
  border-bottom: none;
  padding-bottom: 0;
}
.signup-box p {
  margin: 0 0 var(--space-2);
  color: var(--mc-text-dim);
  font-size: var(--fs-small);
}
.signup-box form {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
}
.signup-box input[type="text"],
.signup-box input[type="email"] {
  flex: 1 1 220px;
  padding: 10px 12px;
  font-size: 1rem;
}
.signup-box input:-webkit-autofill,
.signup-box input:-webkit-autofill:hover,
.signup-box input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--mc-text);
  -webkit-box-shadow: 0 0 0 1000px var(--mc-bg-panel-alt) inset;
  transition: background-color 9999s ease-in-out 0s;
}
.signup-box .signup-note {
  margin-top: var(--space-1);
  font-size: var(--fs-tiny);
  color: var(--mc-text-dim);
}
/* The honeypot field (name from build.py's HONEYPOT_FIELD_NAME, copied
   verbatim from functions/subscribe.js's own HONEYPOT_FIELD_NAME constant
   — see either file's comment; currently "company" — SUBSCRIBE-SECURITY-REQ
   §4): CSS-hidden off-screen, never type="hidden" alone — some bots skip a
   truly hidden input but fill a visually-hidden text field less reliably
   than one that isn't hidden at all. A real visitor never sees or tabs
   into it (aria-hidden + tabindex="-1" in the rendered markup). */
.signup-honeypot,
.contact-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- /internal/faces-review (BLOCK fix, Vered 2026-09-10) ----------
   Moved here verbatim from a `<style>` block that used to sit inline in
   the page's own generated markup (tools/build_roster.py,
   render_faces_review_page()) — the site's real CSP (style-src 'self', no
   'unsafe-inline'; website/_headers, mirrored in functions/_middleware.js)
   silently drops inline <style> in production, so the gate-box border/
   background and the department three-column TOC rendered completely
   unstyled once actually served. Everything below is scoped under
   ".faces-review" (the wrapper div render_faces_review_page() now opens
   around its whole body) so this page's own class names — .gate-box,
   .roster, .roster-card, .roster-badge* — never collide with the
   differently-shaped .roster-card/.roster-badge/.roster-face classes
   above (DESIGN-TOKENS-2026-09-10 §10, reserved for a future roster-card
   3:4 wiring elsewhere on the site — see this file's own "roster / face
   card" section). */
.faces-review .gate-box {
  padding: 16px 18px;
  margin: var(--space-3) 0;
}
.faces-review .gate-box h2 { margin-top: 0; }
.faces-review .gate-box p:last-child { margin-bottom: 0; }
.faces-review .dept-toc { columns: 2; margin: var(--space-3) 0; }
@media (min-width: 720px) { .faces-review .dept-toc { columns: 3; } }
.faces-review .roster {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  margin: var(--space-3) 0;
}
@media (min-width: 720px) {
  .faces-review .roster { grid-template-columns: repeat(3, 1fr); }
}
.faces-review .roster-card {
  padding: 16px;
}
.faces-review .roster-card .face {
  width: 120px;
  height: 120px;
  border-radius: var(--radius);
  object-fit: cover;
  display: block;
  margin-bottom: 6px;
  box-shadow: 0 0 0 1px var(--mdc-hilite), 0 0 0 2px var(--mdc-silver-500), var(--mdc-elev-e1);
}
.faces-review .roster-card .face--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  width: 120px;
  height: 120px;
  color: var(--mc-text-dim);
  font-size: 0.8rem;
  padding: 8px;
  background-image: var(--mdc-face-enamel);
}
.faces-review .roster-badge {
  display: inline-block;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 6px;
  margin: 0 0 10px;
}
.faces-review .roster-card h3 { margin: 0 0 2px; }
.faces-review .roster-card .role { color: var(--mc-silver); margin: 0 0 2px; }
.faces-review .roster-card .handle {
  font-family: monospace;
  color: var(--mc-cyan);
  margin: 0 0 10px;
  display: block;
}
.faces-review .roster-card .lineage code { word-break: break-all; font-size: 0.78rem; }
.faces-review .roster-card .defect-note {
  margin: 8px 0 0;
  padding-top: 8px;
  font-size: 0.85rem;
  color: #d64545;
  box-shadow: inset 0 1px 0 var(--mdc-groove-a), inset 0 2px 0 var(--mdc-groove-b);
}

/* Screen-reader-only text — visually hidden, still announced. Used for the
   signup form's <label> elements (a placeholder alone is not an accessible
   name). Same off-screen technique as .skip-link before it gains focus. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- roster / face card (DESIGN-TOKENS §10) ---------- */
.roster-face {
  width: 100%;
  max-width: 240px;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--radius-sm);
  display: block;
  box-shadow: 0 0 0 1px var(--mdc-hilite), 0 0 0 2px var(--mdc-silver-500), var(--mdc-elev-e1);
}
.roster-face--placeholder {
  aspect-ratio: 3 / 4;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 12px;
  font-size: var(--fs-tiny);
  line-height: 1.35;
  color: var(--mc-text-dim);
  border-radius: var(--radius-sm);
  background-image: var(--mdc-face-enamel);
}
.roster-card {
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: relative;
}
.roster-name { margin: 6px 0 0; font-size: var(--fs-h3); color: #fff; }
.roster-role { margin: 0; color: var(--mc-text-dim); font-size: var(--fs-small); }
.roster-handle {
  font-size: var(--fs-tiny);
  color: var(--mc-text-dim);
  font-family: var(--font-mono);
}
.roster-badge {
  display: inline-block;
  font-size: var(--fs-badge);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 8px;
  margin-top: 6px;
  align-self: flex-start;
}

/* Any wide table (today: /portfolio) scrolls inside itself on a narrow
   viewport instead of forcing the whole page to scroll sideways — pure
   CSS, no markup change, so build.py's generated <table> needs nothing
   extra to get this. */
table.portfolio-table {
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
}
table.portfolio-table th, table.portfolio-table td {
  text-align: left;
  padding: 10px 8px;
  vertical-align: top;
  border-bottom: 1px solid var(--mdc-groove-a);
  box-shadow: 0 1px 0 var(--mdc-groove-b);
}
table.portfolio-table th { color: var(--mc-silver); font-weight: 600; }

/* Plain markdown tables (the long internal explainers and the review
   queue) get the same scroll-inside-itself treatment. html/body carry
   overflow-x: hidden, so a table wider than a phone was simply cut off,
   not scrollable. */
main table:not(.portfolio-table) {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-collapse: collapse;
}

.hold-footer {
  margin-top: var(--space-3);
  padding: 12px 14px;
  font-size: 0.9rem;
  color: var(--mc-text-dim);
  position: relative;
  padding-left: 30px;
}

footer.site-footer {
  padding: var(--space-3);
  font-size: 0.82rem;
  color: var(--mc-text-dim);
  max-width: 880px;
  margin: 0 auto;
  box-shadow: inset 0 1px 0 var(--mdc-groove-a), inset 0 2px 0 var(--mdc-groove-b);
}
footer.site-footer p { margin: 6px 0; }

/* ---------- splash / gate (public, ungated pages) ----------
   Job of this page: read as "under construction" within ~2 seconds on a
   phone, with zero scrolling required to see it. min-height:100vh + flex
   centering already guarantees the whole block sits mid-viewport; the
   mobile block below just trims total block height further on small
   phones so there's real air around it rather than a near-full screen. */
.splash-wrap {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-4);
}
.splash-wrap img.emblem { height: 96px; width: auto; margin-bottom: var(--space-3); }
.splash-wrap h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1-splash);
  letter-spacing: 0.02em;
  margin: 0 0 10px;
}
.splash-wrap .tagline { color: var(--mc-silver); margin: 0 0 28px; max-width: 520px; }
.splash-wrap .legal-note {
  font-size: 0.8rem;
  color: var(--mc-text-dim);
  max-width: 480px;
  margin-top: var(--space-4);
}
.gate-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 100%;
  max-width: 320px;
  margin: 0 auto;
}
.gate-form label { text-align: left; font-size: 0.9rem; color: var(--mc-silver); }
.gate-form input[type="password"] {
  padding: 10px 12px;
  font-size: 1rem;
}
/* Browser autofill (esp. Chrome/WebKit) paints its own light background
   behind a remembered password regardless of page CSS, which would blow
   a white rectangle into this dark form — override it back to the panel
   color so a returning guest's autofilled field still reads as part of
   the brand, not a broken control. */
.gate-form input:-webkit-autofill,
.gate-form input:-webkit-autofill:hover,
.gate-form input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--mc-text);
  -webkit-box-shadow: 0 0 0 1000px var(--mc-bg-panel) inset;
  transition: background-color 9999s ease-in-out 0s;
}
.gate-error {
  color: var(--mc-error);
  font-size: 0.88rem;
  min-height: 1.2em;
}

/* Mobile nav collapse (NAV-UX-SPEC §6). 719px — one pixel under the
   existing .card-row 3-column breakpoint (min-width: 720px) — so the nav's
   own collapse point lines up with a value the stylesheet already treats
   as "tablet and up," rather than inventing a fourth breakpoint. This
   REPLACES the older `header.site-header { flex-direction: column }` rule:
   that rule fought the hamburger layout (brand + toggle now stay on one
   row at every width; the nav LIST is what collapses, not the header). */
@media (max-width: 719px) {
  .nav-toggle { display: flex; }
  /* F-11 (this block replaces the older in-flow dropdown). The nav takes NO room in the header row: its box is a
     zero-height anchor at the header's bottom edge, and the open list is laid over the page from there, so
     opening the menu no longer pushes the content down. (Earlier history of this block, kept because it is why
     the selectors look the way they do: `nav.site-nav` is a flex item of the header and needed its own width;
     and the base rule `nav.site-nav ul.site-nav-list { display: flex }` is (0,2,2), so the collapsing rule has
     to carry the same specificity or the base rule wins at every width.) */
  nav.site-nav { position: absolute; top: calc(100% + 3px); left: 0; right: 0; height: 0; width: auto; }
  nav.site-nav ul.site-nav-list {
    display: none;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0;
    width: 100%;
  }
  nav.site-nav ul.site-nav-list.is-open {
    display: flex;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    max-height: calc(100vh - var(--header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--space-1) var(--space-3) var(--space-3);
    background-color: var(--mc-black); background-image: var(--mdc-face-enamel);
  box-shadow: inset 0 -1px 0 #000, 0 1px 0 var(--mdc-groove-b), var(--mdc-elev-e4);
  }
  @supports (height: 100dvh) {
    nav.site-nav ul.site-nav-list.is-open { max-height: calc(100dvh - var(--header-h)); }
  }
  /* A whole row is the tap target, not just the words. */
  nav.site-nav ul.site-nav-list a { display: flex; width: 100%; }
  .nav-dropdown-menu {
    position: static;
    padding-left: 12px;
    background-image: none; box-shadow: none; border: 0;
  }
  /* Long labels ("Office of Zusha Yonteff and Rabbi Avraham Kroll") must wrap on a phone, not clip. */
  .nav-dropdown-menu li a { white-space: normal; }
}

/* The scrim behind the open phone menu (templates/base.html, toggled by static/js/nav.js). Under the header
   (z 70), over everything else. Never shown on a wide screen, whatever state the script left behind. */
.nav-overlay {
  position: fixed;
  inset: 0;
  z-index: 65;
  background: rgb(0 0 0 / 0.66);
  overscroll-behavior: contain;
}
.nav-overlay[hidden] { display: none; }
@media (min-width: 720px) { .nav-overlay { display: none; } }
/* Scroll lock while the menu is open. */
html.nav-open, html.nav-open body { overflow: hidden; }

/* No script, small screen: the toggle could never open anything, so show the list in flow instead of hiding the
   whole site's navigation (CSS-only, supported where it matters; harmless elsewhere). */
@media (max-width: 719px) and (scripting: none) {
  .nav-toggle { display: none; }
  nav.site-nav { position: static; height: auto; width: 100%; }
  nav.site-nav ul.site-nav-list { display: flex; position: static; max-height: none; }
}

@media (max-width: 520px) {
  main { padding-left: var(--space-2); padding-right: var(--space-2); }
  main h1, main .ms-h1 { font-size: 1.6rem; }
}

/* Phones — trims the splash block so "Under Construction" reads instantly
   with room around it, no scroll, on small screens (iPhone SE and up). */
@media (max-width: 400px) {
  .splash-wrap { padding: var(--space-3) var(--space-2); }
  .splash-wrap img.emblem { height: 72px; margin-bottom: var(--space-2); }
  .splash-wrap h1 { font-size: 1.9rem; }
  .splash-wrap .tagline { margin-bottom: var(--space-3); }
  .splash-wrap .legal-note { margin-top: var(--space-3); }
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.001ms !important; }
}

/* Generated by build.py (2026-09-07): the AI-persona + roster link under an
   overview page's H1 (front-matter `roster:`), and the /departments hub's
   list of rosters held on the internal tier (from roster.json). */
.roster-notice {
  padding: 8px 14px;
  margin: 0 0 20px 0;
  color: var(--mc-text-dim, #C6C9CE);
  font-size: 0.95em;
  position: relative;
  padding-left: 30px;
}
/* A callout plate carries a lit lamp stripe where it used to carry a flat 3px left border. Cyan by default, amber for a warning,
   red for an error. The plate itself (face, bevel, shadow) is the library's (.mdc-card--inset in mdc-skin.css). */
.ms-quote, .ms-chapter-banner, .hold-footer, .roster-notice, .hands-review .item .ask, .bgg-banner, .contact-errors { position: relative; }
.ms-quote::before, .ms-chapter-banner::before, .hold-footer::before, .roster-notice::before, .hands-review .item .ask::before,
.bgg-banner::before, .contact-errors::before {
  content: "";
  position: absolute;
  inset-block: 12px;
  inset-inline-start: 10px;
  inline-size: 5px;
  border-radius: 3px;
  background: linear-gradient(180deg, #9BF6FF, #00E5FF 50%, #00C4DB);
  box-shadow: 0 0 6px rgb(0 229 255 / 0.8), inset 1px 0 0 rgb(255 255 255 / 0.6), inset -1px 0 0 rgb(0 100 112 / 0.7);
}
.bgg-banner::before { background: linear-gradient(180deg, #FFE08A, #FFB547 50%, #D9941F); box-shadow: 0 0 6px rgb(255 181 71 / 0.7), inset 1px 0 0 rgb(255 255 255 / 0.5); }
.contact-errors::before { background: linear-gradient(180deg, #FF9EA1, #FF6B6B 50%, #D64545); box-shadow: 0 0 6px rgb(255 107 107 / 0.7), inset 1px 0 0 rgb(255 255 255 / 0.5); }
.held-note { margin-top: 24px; }
.held-list li { margin: 6px 0; }

/* ---- /internal/hands-review (2026-09-24, Eli) -------------------------
   Zusha's one-page review board: short version first, one decision per
   item, pictures inline. Scoped under .hands-review so nothing collides.
   No inline styles anywhere on the page (CSP style-src 'self'). */
.hands-review .short-version {
  padding: 16px 18px;
  margin: var(--space-3) 0;
}
.hands-review .short-version h2 { margin-top: 0; }
.hands-review .short-version ol { margin-bottom: 0; }
.hands-review .item {
  padding: 16px 18px;
  margin: var(--space-3) 0;
}
.hands-review .item h2 { margin-top: 0; }
.hands-review .item .ask {
  padding: 6px 12px;
  margin: 10px 0;
  font-weight: 600;
  position: relative;
  padding-left: 30px;
}
.hands-review .item .options { margin: 8px 0; padding-left: 1.2em; }
.hands-review .item .options li { margin: 4px 0; }
.hands-review .item .rec { color: var(--mc-cyan); }
.hands-review .item .where { color: var(--mc-text-dim); font-size: var(--fs-small); }
.hands-review .pics {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin: 12px 0;
}
@media (min-width: 720px) {
  .hands-review .pics { grid-template-columns: repeat(2, 1fr); }
  .hands-review .pics--3 { grid-template-columns: repeat(3, 1fr); }
}
.hands-review figure { margin: 0; }
.hands-review figure img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-sm);
  background: #fff;
  box-shadow: 0 0 0 1px var(--mdc-hilite), 0 0 0 2px var(--mdc-silver-500), var(--mdc-elev-e1);
}
.hands-review figcaption { font-size: var(--fs-tiny); color: var(--mc-text-dim); margin-top: 4px; }
.hands-review audio { width: 100%; margin: 8px 0; }
.hands-review .tag {
  display: inline-block;
  font-size: var(--fs-badge);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 6px;
  margin-right: 6px;
}

/* ============================================================
   WOW PASS — 2026-09-25 (Warda Diziner, Diziner Family — web/UX)
   projects/website-effectiveness-2026-09/DESIGN-BRIEF-ZUSHA.md:
   "real wow", "go wild design" — Moshiach/chinuch/Beis HaMikdash
   theme, Maccabee rededication. Guardrails held throughout:
   reduced-motion honored, contrast kept AA, 44px tap targets,
   music stays opt-in only (never autoplay), lazy-loaded media.
   ============================================================ */

:root {
  /* Gold / Jerusalem-stone accent tier — reserved for the theme layer
     (hero glows, dividers, the Menorah-flame motif) and NEVER substituted
     for the existing brand cyan on functional UI (links, focus rings,
     buttons) — those stay --mc-cyan per the made-for-humans / line-identity
     "functional surfaces are exempt" carve-out. Contrast-checked against
     --mc-bg (#000): --mc-gold on black = 8.9:1, well past AA. */
  --mc-gold: #D9A94A;
  --mc-gold-bright: #F2C879;
  --mc-stone: #C9BFA6;
  --mc-stone-dim: #8A8270;
  --wow-hero-vh: 88vh;
}

/* ---- full-bleed hero ----
   ROUND 2 fix (Eli's review, Finding 1): `main` boxes its children to
   880px, so a plain child .wow-hero sat in a ~840px column with black
   margins either side on desktop -- not full-bleed at all. The standard
   "breakout" trick (position:relative; left/right:50%; negative 50vw
   margins) escapes any ancestor's max-width/padding without touching
   `main` itself or any other child. `overflow-x: hidden` on html/body
   (added below) is the safety net against the 100vw-includes-scrollbar
   edge case this trick is known for. */
.wow-hero {
  position: relative;
  left: 50%;
  right: 50%;
  width: 100vw;
  margin-left: -50vw;
  margin-right: -50vw;
  min-height: 60vh;
  max-height: var(--wow-hero-vh);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  margin-top: 0;
  margin-bottom: var(--space-5);
  background: var(--mc-bg-panel);
}
/* Home's hero is the first screen: near-100vh, edge to edge, no rounding
   (a rounded corner reads as "a boxed panel", the opposite of full-bleed).
   svh/dvh account for mobile browser chrome (address bar) collapsing --
   100vh alone is measured against the LARGEST possible viewport on some
   mobile browsers and can leave a hero taller than the visible screen; the
   plain vh rule ships first as the universal fallback, svh/dvh override it
   where supported. */
/* ROUND 2 fix: five interior-page heroes shipped `style="min-height:44vh"`
   inline (mission.md, chinuch.md, technology.md, design.md, work-with-
   us.md) -- a real pre-existing bug from round 1, caught by re-running
   test_no_new_inline_style_anywhere_in_dist. The site's actual CSP
   (style-src 'self', no 'unsafe-inline') silently drops a style=""
   attribute in production, so these five pages' heroes were shipping at
   whatever `.wow-hero`'s own min-height/max-height happened to default to,
   not the smaller interior-page height the content intended. A named
   modifier class fixes both the CSP violation and restores the intended
   sizing. */
.wow-hero--sm {
  min-height: 44vh;
}
.wow-hero--tall {
  min-height: 92vh;
  min-height: 92svh;
  min-height: 92dvh;
  max-height: none;
  border-radius: 0;
}
.wow-hero picture,
.wow-hero img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* ---- cross-fading hero slideshow (Home only) ----
   static/js/hero-slideshow.js toggles .is-active between 2-3
   `.wow-hero-slide` images already both sitting in the DOM (no layout
   shift, no extra network round-trip beyond what's already loading).
   Ken Burns is a slow opacity+scale drift on the active slide only.
   `prefers-reduced-motion` (media query below) freezes everything to the
   first slide, no animation, no scale -- the JS itself also checks the
   media query and never advances slides in that case, so a reduced-motion
   visitor never even downloads past the fallback the CSS already shows. */
.wow-hero-media { position: absolute; inset: 0; z-index: 0; }
.wow-hero-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity 1.6s ease;
}
.wow-hero-slide.is-active {
  opacity: 1;
  animation: wow-ken-burns 9s ease-out forwards;
}
@keyframes wow-ken-burns {
  from { transform: scale(1.06); }
  to { transform: scale(1); }
}
.wow-hero::after {
  /* Legibility scrim — dark gradient so overlaid text always clears AA,
     independent of the art beneath it (DESIGN-TOKENS contrast discipline
     extended to photographic hero art, not just flat panels). */
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.55) 65%, rgba(0,0,0,0.92) 100%);
}
.wow-hero-copy {
  position: relative;
  z-index: 2;
  padding: var(--space-4) var(--space-3) var(--space-5);
  max-width: 760px;
  /* .wow-hero is a flex container (align-items: flex-end); a block child
     of a row-direction flex container defaults to min-width:auto, which
     equals its unwrapped max-content width -- without this override, long
     copy refuses to wrap and pushes the whole hero (and page) into
     horizontal overflow on narrow viewports. Real bug, caught rendering
     the mobile proofs (2026-09-25) -- not a screenshot artifact. */
  min-width: 0;
  width: 100%;
  box-sizing: border-box;
}
.wow-hero-copy h1 {
  /* Overrides `main h1`'s cyan border-bottom + tight margin -- a rule on
     the ART, not a section title, so it drops the underline and gets the
     display face at oversized scale instead (Finding 1: "headline ON the
     art", not a small tagline paragraph next to it). Class selector beats
     the two-element `main h1` on specificity regardless of source order. */
  border-bottom: none;
  padding-bottom: 0;
  margin: 0 0 var(--space-2);
  color: var(--mc-text);
  font-family: var(--font-display);
  font-size: clamp(2rem, 5.5vw + 1rem, 4.2rem);
  line-height: 1.08;
  text-shadow: 0 2px 24px rgba(0,0,0,0.65);
  max-width: 14ch;
}
.wow-hero-copy p {
  color: var(--mc-stone);
  font-size: var(--fs-h3);
}
.wow-hero-kicker {
  display: inline-block;
  font-size: var(--fs-tiny);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mc-gold-bright);
  margin-bottom: var(--space-2);
}
.wow-hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
/* Scroll cue -- a single small affordance on the tall home hero only, so a
   first-time visitor knows the page continues past the first screen. Pure
   CSS bob; frozen under reduced-motion with the rest of the theme layer. */
.wow-hero-scroll-cue {
  position: absolute;
  left: 50%;
  bottom: var(--space-3);
  z-index: 2;
  transform: translateX(-50%);
  color: var(--mc-stone);
  font-size: 1.4rem;
  opacity: 0.8;
  animation: wow-scroll-bob 2.2s ease-in-out infinite;
}
@keyframes wow-scroll-bob {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 8px); }
}

/* ---- living-light motif: a small CSS/SVG flame, pure decoration ----
   Placed inline as an <svg class="wow-flame"> next to a hero kicker or in
   the footer. Animates a gentle flicker/breathe; fully static (no keyframe
   applied) under prefers-reduced-motion, per the brief's guardrail — a
   reader loses zero information either way (decorative only, boundary
   test in controlled-imperfection.md). */
.wow-flame { width: 28px; height: 40px; display: inline-block; vertical-align: -8px; }
/* Large variant for the home hero kicker (Finding 2: "the living-light
   flame motif large and central, not a small icon"). Same paths, same
   animation, just scaled up and re-centered against the bigger type
   around it. */
.wow-flame--lg { width: 40px; height: 58px; vertical-align: -14px; margin-right: 4px; }
.wow-flame .wow-flame-core { transform-origin: 50% 100%; animation: wow-flicker 2.6s ease-in-out infinite; }
.wow-flame .wow-flame-outer { transform-origin: 50% 100%; animation: wow-flicker-outer 3.4s ease-in-out infinite; }
@keyframes wow-flicker {
  0%, 100% { transform: scaleY(1) scaleX(1) rotate(0deg); }
  25% { transform: scaleY(1.06) scaleX(0.97) rotate(-1deg); }
  50% { transform: scaleY(0.96) scaleX(1.03) rotate(1deg); }
  75% { transform: scaleY(1.03) scaleX(0.99) rotate(-0.5deg); }
}
@keyframes wow-flicker-outer {
  0%, 100% { opacity: 0.85; transform: scaleY(1); }
  50% { opacity: 1; transform: scaleY(1.08); }
}

/* ---- dawn/glow ambient backdrop for section breaks ----
   A slow radial pulse used behind the mission/chinuch scroll-story
   sections. CSS-only, GPU-cheap (opacity + transform), never behind body
   text without the scrim above already applied. */
.wow-dawn {
  position: relative;
  overflow: hidden;
}
.wow-dawn::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 140%;
  height: 100%;
  transform: translateX(-50%);
  background: radial-gradient(circle at 50% 0%, rgba(217,169,74,0.16), rgba(0,229,255,0.05) 45%, transparent 70%);
  animation: wow-dawn-pulse 9s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
}
@keyframes wow-dawn-pulse {
  0%, 100% { opacity: 0.6; }
  50% { opacity: 1; }
}
.wow-dawn > * { position: relative; z-index: 1; }

/* ---- scroll-staged storytelling ----
   .wow-reveal starts faded/offset; static/js/reveal.js adds .is-visible
   via IntersectionObserver. Content must never depend on JS to be
   readable, so a CSS-only fallback animation reveals it a beat after
   paint even with reveal.js blocked or erroring. */
.wow-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s ease;
  animation: wow-reveal-fallback 0s 1.2s forwards;
}
.wow-reveal.is-visible { opacity: 1; transform: translateY(0); }
@keyframes wow-reveal-fallback { to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .wow-reveal { opacity: 1; transform: none; transition: none; animation: none; }
  .wow-flame .wow-flame-core, .wow-flame .wow-flame-outer { animation: none; }
  .wow-dawn::before { animation: none; opacity: 0.75; }
  /* Round 2 (hero slideshow, scroll cue, button lift): the brief's own
     guardrail ("reduced-motion respected") extended to every new motion
     this pass adds. The slideshow's first slide is marked is-active in the
     HTML itself (server-rendered, not JS-added), so this alone is enough
     to leave a single static hero image with zero script dependency. */
  .wow-hero-slide { transition: none; animation: none; }
  .wow-hero-slide.is-active { animation: none; }
  .wow-hero-scroll-cue { animation: none; }
}

/* ---- theme picture grid (arm pages, mission story) ---- */
.wow-art-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin: var(--space-4) 0;
}
@media (min-width: 720px) {
  .wow-art-row--2 { grid-template-columns: repeat(2, 1fr); }
}
.wow-art-row figure { margin: 0; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--mc-border); }
.wow-art-row img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wow-art-row figcaption { padding: var(--space-2); font-size: var(--fs-tiny); color: var(--mc-text-dim); background: var(--mc-bg-panel); }

/* ---- gold divider ---- */
.wow-divider {
  border: none;
  height: 1px;
  margin: var(--space-5) 0;
  background: linear-gradient(90deg, transparent, var(--mc-gold), transparent);
  opacity: 0.6;
}

/* ---- opt-in music player (site-wide, persistent widget) ----
   ROUND 2 fix (Eli's review, Finding 4): at 375px the player used to ship
   already-expanded (the full pill: play + next + label), which at phone
   width is wide enough to sit directly over body paragraphs near the foot
   of a page. Now it ships COLLAPSED to a single 44px round corner button
   (`.wow-music-fab`) by default on every viewport; static/js/music-player.js
   reveals the pill (`.wow-music-panel`) only on an explicit tap, and it
   collapses back on a second tap or on pressing Escape. Never autoplays
   (JS enforces -- this is layout/finish only). Text-labeled state so it
   never depends on color alone; every control keeps the 44px WCAG 2.5.8
   floor whether the panel is open or shut. */
.wow-music-player {
  position: fixed;
  right: 14px;
  bottom: 14px;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  max-width: calc(100vw - 28px);
}
/* On phone width the expanded panel growing LEFTWARD can reach far enough
   to sit over a CTA button near the bottom-right of the viewport (found
   testing the home hero's own CTA row against this exact control). Stack
   the panel ABOVE the fab instead of beside it below this breakpoint, so
   expanding never grows sideways into whatever else sits at that height. */
@media (max-width: 480px) {
  .wow-music-player {
    /* DOM order is panel, then fab -- plain `column` stacks the panel
       ABOVE the fab (first child on top), which is what "grows upward,
       not sideways" needs; `column-reverse` would have put the fab on
       top and pushed the panel further down, off-screen. */
    flex-direction: column;
    align-items: flex-end;
  }
}
.wow-music-fab {
  flex: none;
  width: 44px;
  height: 44px;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}
.wow-music-panel {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px 6px 6px;
  max-width: min(320px, calc(100vw - 90px));
}
.wow-music-panel[hidden] { display: none; }
.wow-music-panel button {
  flex: none;
  min-width: 44px;
  min-height: 44px;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.wow-music-panel [data-music-next] {
  min-width: 36px;
  min-height: 36px;
  font-size: 0.9rem;
}
.wow-music-panel [data-music-label] {
  font-size: var(--fs-tiny);
  color: var(--mc-text-dim);
  max-width: 34vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .wow-music-panel [data-music-label] { max-width: 26vw; }
}
/* Reserve room at the foot of every page so the fixed player -- collapsed
   or (briefly, on tap) expanded -- never sits on top of the last line of
   body copy, on the viewport where the corner control is proportionally
   biggest relative to the content column. */
@media (max-width: 480px) {
  main { padding-bottom: 88px; }
}

/* ---- pending-asset note (visible only inside a review build; states a
   true fact plainly, per SHOW-THE-REAL-THING this never narrates mid-page
   copy — it is a small dashed-border utility class, used sparingly) ---- */
.wow-pending-note {
  font-size: var(--fs-tiny);
  color: var(--mc-badge-unvetted-text);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
  margin: var(--space-3) 0;
  box-shadow: inset 0 0 0 1px var(--mc-badge-unvetted-border);
}

/* ============================================================
   WOW PASS, ROUND 2 — 2026-09-25 (Warda Diziner, with Osher
   Lichtman on motion and Dalia Diziner on creative direction).
   Eli's review of round 1: "the strategy is right, the execution
   is too tame." These four additions push the wow further down
   the page (Finding 5), full-bleed, lazy-loaded below the fold,
   reduced-motion honored throughout. Same guardrails as the hero
   above -- never on functional UI, never without a legibility
   scrim under overlaid text.
   ============================================================ */

/* ---- full-bleed image band with an oversized, stat-free line of type ----
   A one-image, one-line "moment" -- the Beis HaMikdash/Geulah band on
   Mission, the Maccabee "few against many" band. No numbers, no claim to
   substantiate; a single sourced line (moshiach-context.md where the copy
   draws on it) set at display scale over reverent, non-literal art. */
.wow-band {
  position: relative;
  left: 50%;
  right: 50%;
  width: 100vw;
  margin-left: -50vw;
  margin-right: -50vw;
  min-height: 52vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  margin-top: var(--space-5);
  margin-bottom: var(--space-5);
}
.wow-band img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
.wow-band::before {
  /* Even scrim (not the top-weighted hero gradient) -- this band's whole
     job is the centered line of type, which needs AA contrast everywhere
     it might wrap to, not just along one edge. */
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(0,0,0,0.62);
}
.wow-band-line {
  position: relative;
  z-index: 2;
  max-width: 18ch;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3);
  text-align: center;
  font-family: var(--font-display);
  color: var(--mc-text);
  font-size: clamp(1.6rem, 4vw + 0.6rem, 3rem);
  line-height: 1.2;
  text-shadow: 0 2px 20px rgba(0,0,0,0.7);
}
.wow-band-line small {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  color: var(--mc-stone);
  text-shadow: none;
}

/* ---- gold / Jerusalem-stone texture section background ----
   For a section that wants the warm accent tier as its GROUND (not just an
   image), rather than another photo band -- e.g. a pull-quote or a short
   passage sitting directly on stone/gold texture. The image is a CSS
   background (not an <img>), so it participates in normal document flow
   and never needs object-fit/absolute positioning; content sizes the
   section, not the other way around.

   Two named variants, not a CSS-custom-property hook set from markup: the
   site's real CSP (style-src 'self', no 'unsafe-inline') silently drops a
   `style=""` attribute in production (test_no_new_inline_style_anywhere_
   in_dist), so the per-page image can never be wired in that way -- a
   fixed modifier class per texture image keeps every URL in the
   stylesheet, where the CSP allows it. */
.wow-texture-band {
  position: relative;
  left: 50%;
  right: 50%;
  width: 100vw;
  margin-left: -50vw;
  margin-right: -50vw;
  margin-top: var(--space-5);
  margin-bottom: var(--space-5);
  padding: var(--space-5) var(--space-3);
  background-size: cover;
  background-position: center;
}
.wow-texture-band--gold {
  background-image: linear-gradient(rgba(0,0,0,0.72), rgba(0,0,0,0.72)),
    url("/static/img/theme/26-gold-stone-meets-cyan-silver-1200.webp");
}
.wow-texture-band--stone {
  background-image: linear-gradient(rgba(0,0,0,0.72), rgba(0,0,0,0.72)),
    url("/static/img/theme/20-jerusalem-stone-dawn-texture-1200.webp");
}
.wow-texture-band > * {
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}

/* ---- chinuch "lamp to lamp" sequence ----
   Three frames read left-to-right (a generation handing to the next),
   single column on phone so the sequence still reads top-to-bottom in
   order rather than as a cramped triple-column squeeze. */
.wow-lamp-sequence {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin: var(--space-4) 0;
}
@media (min-width: 720px) {
  .wow-lamp-sequence { grid-template-columns: repeat(3, 1fr); align-items: center; }
  .wow-lamp-sequence figure:nth-child(2) { transform: scale(1.06); }
}
.wow-lamp-sequence figure { margin: 0; text-align: center; }
.wow-lamp-sequence img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--radius);
  border: 1px solid var(--mc-border);
}
.wow-lamp-sequence figcaption {
  margin-top: var(--space-1);
  font-size: var(--fs-tiny);
  color: var(--mc-text-dim);
}

/* ---- Site search (website/search.py + static/js/search.js; ships only when holds.search is lifted
        or in a --preview build). Tokens only: no new colours. ---- */
.header-tools { display: flex; align-items: center; gap: var(--space-1); order: 3; }
@media (max-width: 719px) { .header-tools { order: 0; } }   /* phone: brand | search + menu, nav wraps below */
.search-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  cursor: pointer;
}
.search-btn[hidden] { display: none; }

html.search-open { overflow: hidden; }
dialog.search-dialog {
  --search-max-h: min(80vh, 640px);
  width: min(640px, calc(100% - 32px));
  max-width: none;
  max-height: var(--search-max-h);
  margin: 10vh auto auto;
  padding: 0;
  overflow: hidden;
}
dialog.search-dialog[open] { display: block; }
dialog.search-dialog::backdrop { background: rgba(0, 0, 0, 0.72); backdrop-filter: blur(3px); }
.search-panel { display: flex; flex-direction: column; max-height: var(--search-max-h); min-height: 0; padding: var(--space-2) var(--space-3) var(--space-2); gap: var(--space-2); }
.search-panel > * { flex: 0 0 auto; }                       /* only the results scroll; head, field, status, hints never shrink */
.search-panel > .search-body { flex: 1 1 auto; }
.search-head { display: flex; align-items: center; justify-content: space-between; }
.search-head h2 { margin: 0; font-size: var(--fs-h3); }
.search-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  cursor: pointer;
}
.search-field { position: relative; }
.search-field .search-ico { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--mc-text-dim); pointer-events: none; }
.search-field input[type="search"] {
  width: 100%;
  min-height: 48px;
  padding: 10px 14px 10px 44px;
  font: inherit;
  font-size: 1rem;
}
.search-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.search-status { margin: 0; min-height: 1.3em; color: var(--mc-text-dim); font-size: var(--fs-small); overflow-wrap: anywhere; }
.search-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.search-empty { padding: var(--space-3) 0; text-align: center; overflow-wrap: anywhere; }   /* a long query is echoed here and in the status line */
.search-empty-title { margin: 0 0 var(--space-1); font-weight: 700; font-size: var(--fs-h3); }
.search-empty-text { margin: 0 0 var(--space-2); color: var(--mc-text-dim); }
.search-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-1); margin: 0; }
.search-chips[hidden] { display: none; }
.search-chip {
  min-height: 44px;
  padding: 8px 16px;
  font: inherit;
  cursor: pointer;
}
.search-results { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }
.search-results[hidden] { display: none; }
.search-result {
  position: relative;
  padding: 12px 14px;
  min-height: 44px;
}
.search-result-link { display: inline; font-weight: 700; font-size: var(--fs-body); text-decoration: none; color: var(--mc-cyan); }
.search-result-link::after { content: ""; position: absolute; inset: 0; }   /* the whole card is the tap target */
.search-result-link:focus-visible { outline: none; text-decoration: underline; }
.search-result-path { display: block; margin-top: 2px; color: var(--mc-text-dim); font-size: var(--fs-tiny); overflow-wrap: anywhere; }
.search-result-excerpt { margin: var(--space-1) 0 0; font-size: var(--fs-small); line-height: 1.5; color: var(--mc-text); }
.search-result-excerpt mark { background: rgba(0, 229, 255, 0.22); color: var(--mc-text); border-radius: 2px; padding: 0 1px; }
.search-hints { display: none; margin: 0; color: var(--mc-text-dim); font-size: var(--fs-tiny); }
@media (hover: hover) and (pointer: fine) { .search-hints { display: block; } }
.search-hints kbd {
  display: inline-block;
  min-width: 1.6em;
  margin: 0 2px 0 8px;
  padding: 1px 6px;
  text-align: center;
  font: inherit;
}
.search-hints kbd:first-child { margin-left: 0; }
/* Phone first: the dialog IS the screen. Input sits at the top, clear of the on-screen keyboard. */
@media (max-width: 639px) {
  dialog.search-dialog {
    --search-max-h: 100vh;
    width: 100%; height: 100vh; margin: 0; border: 0; border-radius: 0;
  }
  @supports (height: 100dvh) { dialog.search-dialog { --search-max-h: 100dvh; height: 100dvh; } }
  .search-panel { height: 100%; padding: var(--space-2); }
}
@media (prefers-reduced-motion: no-preference) {
  dialog.search-dialog[open] { animation: search-in 0.18s ease-out; }
  dialog.search-dialog[open]::backdrop { animation: search-fade 0.18s ease-out; }
}
@keyframes search-in { from { opacity: 0; transform: translateY(-8px) scale(0.985); } to { opacity: 1; transform: none; } }
@keyframes search-fade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  dialog.search-dialog, dialog.search-dialog::backdrop, .search-result, .search-btn { animation: none; transition: none; }
}

/* ======================================================================
   Components (Irit Techburger with Warda Diziner, 2026-10-05; GAP-AUDIT
   sections 3 and 4, findings F-02, F-11 to F-15, F-17).
   Buttons, icons, department cards, state pages, and the progressive
   enhancements. Brand tokens only; no inline styles anywhere (CSP
   style-src 'self'); no conic, repeating or streaked gradient (no rays).
   ====================================================================== */

/* ---- icons ----
   <svg class="icon icon--24" aria-hidden="true" focusable="false"><use href="/static/img/icons.svg#i-search"/></svg>
   Geula's line set. The sprite carries no colour and no stroke: the page owns both (currentColor, and an optical
   stroke per size, the way a typeface is re-weighted per size). Decorative by default; an icon-only control
   carries its name on the button (aria-label), never on the icon. */
.icon {
  --icon-size: 1.5rem;
  --icon-stroke: 1.75;
  width: var(--icon-size);
  height: var(--icon-size);
  flex: none;
  display: inline-block;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.22em;
  overflow: visible;
}
.icon--16 { --icon-size: 16px; --icon-stroke: 2; }
.icon--20 { --icon-size: 20px; --icon-stroke: 1.85; }
.icon--24 { --icon-size: 24px; --icon-stroke: 1.75; }
.icon--32 { --icon-size: 32px; --icon-stroke: 1.5; }
.icon--48 { --icon-size: 48px; --icon-stroke: 1.4; }
@media (forced-colors: active) { .icon { stroke: CanvasText; } }

/* ---- buttons: ONE system (F-13) ----
   <a|button class="btn btn--primary|secondary|ghost|link  btn--sm|md|lg  [btn--block] [btn--pill]">
   Replaces the five hand-rolled primaries (.guest-link, .gate-form button, .signup-box button,
   .download-card__cta, .wow-btn). States: hover, :focus-visible (the shared 3px ring), :active, disabled
   (`disabled`, or `aria-disabled="true"` on a link) and loading (`aria-busy="true"`: the label keeps its width
   and an arc spinner replaces it, so nothing jumps). Targets: md is 44px; lg 52px; sm is 36px drawn with a 44px
   hit area (the ::before). Variants only set the --_ tokens below; the base rule reads them. */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 44px;
  padding: 0 20px;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* The face, bevel, gloss, hover, press, focus and disabled states are the library's (mdc-skin.css). `a:hover { color: #fff }`
   above would otherwise turn the label of a link button white on its cyan face. */
.btn, .btn:visited, .btn:hover, .btn:focus { color: var(--_ink, inherit); }
/* `a:hover, a:focus { color: #fff }` above would otherwise win on a link button after a click. */

/* a link-styled button is text, not a plate */
.btn--link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 4px;
  background: none;
  border: 0;
  color: var(--btn-link);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.btn--link:hover, .btn--link:focus-visible { color: #fff; }

.btn--sm { min-height: 36px; padding: 0 14px; font-size: var(--fs-small); }
.btn--sm::before { content: ""; position: absolute; inset: -4px 0; }   /* 36px drawn, 44px to hit */
.btn--lg { min-height: 52px; padding: 0 28px; font-size: 1.05rem; }
.btn--block { display: flex; width: 100%; }

.btn[disabled], .btn[aria-disabled="true"] { cursor: not-allowed; }
a.btn[aria-disabled="true"] { pointer-events: none; }

.btn[aria-busy="true"] {
  cursor: progress;
  pointer-events: none;
  color: transparent;
  text-shadow: none;
}
.btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1.1em;
  height: 1.1em;
  margin: -0.55em 0 0 -0.55em;
  border-top-color: transparent;
  /* an arc, three quarters of a ring: no ticks, nothing radiating */
  border-radius: 50%;
  border: 2px solid var(--_ink, currentColor);
  border-top-color: transparent;
}
@media (prefers-reduced-motion: no-preference) {
  .btn[aria-busy="true"]::after { animation: btn-spin 0.8s linear infinite; }
  @keyframes btn-spin { to { transform: rotate(360deg); } }
}
@media (forced-colors: active) {
  .btn[aria-busy="true"]::after { border-color: ButtonText; border-top-color: transparent; }
}

/* THE HERO PAIR keeps its gold (it was gold before the system existed). This one scoped declaration is the
   whole of the gold-vs-cyan choice: delete these three lines and the hero reads cyan like every other primary. */
/* (2026-10-07: the hero pair wears the library's amber skin for the primary and its etched skin for the secondary; see ALIASES in
   website/tools/build_mdc_skin.py. Delete those two alias entries and the hero reads cyan like every other primary.) */
.download-card__body .btn { align-self: flex-start; margin-top: var(--space-2); }
.signup-box .btn { white-space: nowrap; }

/* ---- department cards with an icon (F-07) ---- */
.card--dept {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--space-1);
}
.card__title { font-size: var(--fs-h3); font-weight: 700; line-height: 1.25; color: #fff; }
.card--dept:hover .card__title, .card--dept:focus-visible .card__title { color: var(--mc-cyan); }
/* Slot for a painted department thumbnail (bg-59 to bg-66) once approved: an <img> or a background on this span,
   inside the anchor, above the icon. Not emitted today, so nothing is reserved on the page. */
.card__media {
  display: block;
  aspect-ratio: 4 / 3;
  margin: -16px -16px var(--space-2);
  border-radius: var(--radius) var(--radius) 0 0;
  background-size: cover;
  background-position: center;
  overflow: hidden;
}
.card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- state pages and blocks: 404, empty, coming soon, sent (templates/_states.html) ----
   The art slot (.state__art, id art-<kind>) takes no room until art is placed in it: an <img> child fills it
   automatically, or a rule on its id sets display: block plus a background-image. */
.state-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
}
.state-wrap { width: 100%; max-width: 560px; padding: 0; }
.state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  max-width: 560px;
  margin: var(--space-4) auto;
  padding: var(--space-4) var(--space-3);
  text-align: center;
}
.state-wrap .state { margin: 0 auto; }
.state__art { display: none; width: min(100%, 360px); aspect-ratio: 1 / 1; background-size: cover; background-position: center; border-radius: var(--radius); }
.state__art:has(> img) { display: block; }
.state__art > img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: var(--radius); }
.state__icon { color: var(--mc-cyan); line-height: 0; }
.state .state__title {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: var(--mc-text);
  margin: 0;
  padding: 0;
  border: 0;
}
.state .state__body { margin: 0; color: var(--mc-text-dim); }
.state__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); margin-top: var(--space-1); }

/* ---- progressive enhancement: each of these is optional and each is declared only where it is safe ---- */

/* Cross-document View Transitions (F-17): a short cross-fade between pages, the header held still. A browser
   without support navigates normally; under reduced motion the rule is never declared, so navigation is the
   ordinary instant one. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  header.site-header { view-transition-name: site-header; }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.2s; }
}

/* Scroll-driven reveal: cards and panels rise into place as they enter the viewport. Guarded twice (the feature,
   and no reduced-motion preference), starts at 20% opacity rather than 0 so nothing is ever invisible, and
   needs no script. reveal.js and .wow-reveal are untouched. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    @keyframes rise-in {
      from { opacity: 0.2; transform: translateY(14px); }
      to { opacity: 1; transform: none; }
    }
    .card-row > .card, .download-card, .roster-card, .state, .wow-art-row figure {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 35%;
    }
  }
}

/* Better line breaks: balanced headings, no one-word last lines. Falls back to ordinary wrapping. */
@supports (text-wrap: balance) {
  h1, h2, h3, .card__title, .state__title, .wow-band-line { text-wrap: balance; }
}
@supports (text-wrap: pretty) {
  main p, main li { text-wrap: pretty; }
}

/* ======================================================================
   Background art -- the front-door showpiece (Irit Techburger / Warda
   Diziner, 2026-10-05; reworked same day: contrast only where text sits).
   Markup comes from website/backgrounds.py and exists ONLY in a preview
   build or once holds.backgrounds is lifted; with the hold on, none of these
   classes appear in any page and this block is inert.

   Layer order, bottom to top: art (primary + an alternate that slowly
   cross-fades in) / cloud haze / cyan light wash / bottom fade / seam glow.
   The art itself is NEVER dimmed or tone-mapped: outside the text it runs at
   full luminance.

   LEGIBILITY: the scrim lives ON THE TEXT CONTAINERS, not on the viewport.
   Every host below paints rgb(0 0 0 / --bg-zone-alpha) under its WHOLE box
   and feathers out only outside the box, where no text is:
     .bg-col            the centred text column (everything below the hero)
     .wow-hero-copy     the hero's copy block (headline, tagline, buttons)
     .breadcrumb, footer.site-footer     chrome rows
     .splash-wrap       the splash / gate card
     .state             the 404 card (templates/404.html)
   --bg-zone-alpha is sized for the worst possible pixel under it (pure
   white), so it holds for any art. It is mirrored in backgrounds.py
   (ZONE_ALPHA, with ACCENT_ALPHA / HAZE_ALPHA / FADE_FULL_AT) and
   tests/test_backgrounds.py proves WCAG AA for every text colour against it
   and fails if the two drift. A browser without mask-image gets a hard-edged
   scrim instead of a feathered one: more dark, never less.

   NO RAYS (SD-firm-no-rays-ever-2026-09-09): light is a soft radial or linear
   WASH. No conic, no repeating, no narrow or streaked gradients; a test pins it.

   PERFORMANCE: the layer is position:absolute in a fixed-height box and every
   image is inset:0 inside it (no layout shift). Motion is transform/opacity
   only, declared ONLY under prefers-reduced-motion: no-preference. No JS. The
   scroll parallax is CSS scroll-driven animation behind @supports.
   ====================================================================== */
:root {
  --bg-zone-alpha: 0.72;
  --bg-accent-alpha: 0.20;
  --bg-haze-alpha: 0.10;
  --bg-fade-full-at: 94%;
  --bg-feather: 240px;           /* side feather of the column scrim, outside the column */
  --bg-top-feather: 56px;        /* top feather of the column scrim, above the first line of text */
  --bg-window: 0px;              /* text-free art window above the column (narrow screens only) */
  --bg-h: 100vh;
}
@supports (height: 100svh) { :root { --bg-h: 100svh; } }
@media (max-width: 999px) {
  :root { --bg-window: 34vh; }
}
@media (max-width: 999px) {
  @supports (height: 100svh) { :root { --bg-window: 34svh; } }
}

body.has-bg {
  position: relative; z-index: 0;                       /* own stacking context */
  min-height: max(560px, var(--bg-h));                  /* html/body overflow-x:hidden makes the body clip its children: it must be at least as tall as the art layer, or a short page (splash, gate) cuts the art off */
}
/* Splash / gate: a short page. Centre the card in the now viewport-tall body. */
body.has-bg:has(> .splash-wrap) { display: flex; flex-direction: column; justify-content: center; }
.bg-art {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: max(560px, var(--bg-h));
  overflow: hidden;
  z-index: -1;
  pointer-events: none;
  contain: layout paint;
  /* What a visitor sees while the art loads, and all they see under prefers-reduced-data. */
  background: linear-gradient(180deg, #0b1430 0%, #050a1c 55%, #000 100%);
}
.bg-art__media { position: absolute; inset: 0; }
.bg-art__img { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
.bg-art__img img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: 50% 30%;
}
@media (orientation: portrait) { .bg-art__img img { object-position: 50% 0; } }   /* mobile crops: subject on top, fade below */
.bg-art__img--alt { opacity: 0; }                    /* reduced-motion / no-animation: the primary only */

.bg-art__haze {                                      /* slow cloud bank: zero bytes, pure gradient */
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 200%;
  background-image:
    radial-gradient(ellipse 22% 16% at 18% 30%, rgb(198 201 206 / var(--bg-haze-alpha)), transparent 70%),
    radial-gradient(ellipse 26% 14% at 58% 52%, rgb(198 201 206 / var(--bg-haze-alpha)), transparent 70%),
    radial-gradient(ellipse 20% 12% at 86% 22%, rgb(198 201 206 / var(--bg-haze-alpha)), transparent 70%);
  background-size: 50% 100%;
  background-repeat: repeat-x;
}
.bg-art__light {                                     /* the cyan wash: blooms at the edges, away from the text */
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 34% 40% at 88% 24%, rgb(0 229 255 / var(--bg-accent-alpha)), transparent 72%),
    radial-gradient(ellipse 30% 34% at 8% 74%, rgb(0 229 255 / calc(var(--bg-accent-alpha) * 0.7)), transparent 72%);
}
@media (max-width: 999px) {
  .bg-art__light {
    background:
      radial-gradient(ellipse 70% 22% at 50% 14%, rgb(0 229 255 / var(--bg-accent-alpha)), transparent 72%),
      radial-gradient(ellipse 60% 18% at 12% 40%, rgb(0 229 255 / calc(var(--bg-accent-alpha) * 0.7)), transparent 72%);
  }
}
.bg-art__fade {                                      /* the layer melts into the page's black; full strength before any text can reach it */
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    transparent 0%,
    transparent 70%,
    rgb(0 0 0 / var(--bg-zone-alpha)) var(--bg-fade-full-at),
    #000 100%);
}
.bg-art__seam {                                      /* a soft cyan glow where the art meets the page: a wash, never a line */
  position: absolute; left: 0; right: 0; bottom: 0; height: 140px;
  background: radial-gradient(ellipse 60% 48% at 50% 52%, rgb(0 229 255 / 0.14), transparent 100%);   /* centre above the last row, 0 before it: no edge */
}

/* ---- text hosts: the scrim, anchored to the text itself ----
   One rule for all four hosts. The scrim is a vertical GRADIENT BACKGROUND (alpha 0 -> zone alpha over the top ramp,
   held across the host's own box, back to 0 over the bottom ramp), so its soft edges cost nothing and can never leave
   a hairline; the only mask is horizontal (the side feathers). Both ramps sit OUTSIDE the host box. */
.has-bg .bg-col,
.has-bg .breadcrumb,
.has-bg footer.site-footer,
.has-bg .wow-hero-copy {
  position: relative;
  isolation: isolate;
  --bg-top-ramp: var(--bg-top-feather);
  --bg-bottom-ramp: var(--bg-top-feather);
}
.has-bg .wow-hero-copy { --bg-top-ramp: 160px; }
.has-bg .bg-col::before,
.has-bg .breadcrumb::before,
.has-bg footer.site-footer::before,
.has-bg .wow-hero-copy::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: calc(-1 * var(--bg-top-ramp));
  bottom: calc(-1 * var(--bg-bottom-ramp));
  left: calc(-1 * var(--bg-feather));
  right: calc(-1 * var(--bg-feather));
  background: linear-gradient(180deg,
    rgb(0 0 0 / 0) 0,
    rgb(0 0 0 / var(--bg-zone-alpha)) var(--bg-top-ramp),
    rgb(0 0 0 / var(--bg-zone-alpha)) calc(100% - var(--bg-bottom-ramp)),
    rgb(0 0 0 / 0) 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--bg-feather),
    #000 calc(100% - var(--bg-feather)), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 var(--bg-feather),
    #000 calc(100% - var(--bg-feather)), transparent 100%);
}
/* Narrow screens: the column fills the width, so the art gets a text-free window above it instead. */
@media (max-width: 999px) {
  .has-bg .bg-col:first-child {
    margin-top: var(--bg-window);
    --bg-top-ramp: calc(var(--bg-top-feather) + var(--bg-window));
  }
}

/* Hero: the art IS the hero. Its copy block is a scrim host and, on wide screens, lines up with the text column
   (the column is main's 880px box; the hero is 100vw and centred on the same axis). */
.has-bg .wow-hero { background: transparent; overflow: visible; }   /* the photos it clipped are gone; let the copy's feather run past its edge */
.has-bg .wow-hero::after { display: none; }
.has-bg .wow-hero-copy { margin-left: max(0px, calc((100% - 880px) / 2)); }

/* The home hero's scroll-cue arrow sits at the bottom centre, outside the copy block: it gets its own small plate. */
.has-bg .wow-hero-scroll-cue {
  display: grid; place-items: center;
  width: 2.4rem; height: 2.4rem;
  border-radius: 50%;
  background: rgb(0 0 0 / var(--bg-zone-alpha));
  opacity: 1;
}

/* Splash / gate: the card is the plate. */
.has-bg .splash-wrap {
  min-height: 0;
  max-width: 560px;
  margin: max(32px, 8vh) auto;
  background: rgb(0 0 0 / var(--bg-zone-alpha));
  border: 1px solid var(--mc-border);
  border-radius: var(--radius);
}
/* 404: the one card is the plate (the page stays standalone: the painting is decoration under it, nothing more). */
.has-bg .state-wrap .state {
  background: rgb(0 0 0 / var(--bg-zone-alpha));
  border: 1px solid var(--mc-border);
  border-radius: var(--radius);
}
/* The gate's error line is red (--mc-error); no image plate clears AA for it, so it gets a solid black one. */
.has-bg .gate-error:not(:empty) { background: var(--mc-black); box-shadow: 0 0 0 4px var(--mc-black); }

@media (prefers-reduced-data: reduce) {
  .bg-art__media, .bg-art__haze { display: none; }   /* plain gradient, no image (the <picture> also swaps to a 43-byte file) */
}
@media print { .bg-art { display: none; } }

@media (prefers-reduced-motion: no-preference) {
  @keyframes bg-drift {
    from { transform: translate3d(-1.2%, 0, 0) scale(1.07); }
    to   { transform: translate3d(1.2%, 0, 0) scale(1.07); }
  }
  /* 18s cycle: each painting holds ~7.5s; the two overlap only for the 1.5s fades (about 17% of the time). */
  @keyframes bg-xfade { 0%, 41.67% { opacity: 0; } 50%, 91.67% { opacity: 1; } 100% { opacity: 0; } }
  @keyframes bg-haze  { to { transform: translate3d(-50%, 0, 0); } }
  @keyframes bg-pulse { from { opacity: 0.72; } to { opacity: 1; } }
  .bg-art__img { animation: bg-drift 70s ease-in-out infinite alternate; will-change: transform; }
  .bg-art__img--alt { animation: bg-drift 70s ease-in-out infinite alternate, bg-xfade 18s ease-in-out infinite; }
  .bg-art__haze { animation: bg-haze 140s linear infinite; will-change: transform; }
  .bg-art__light { animation: bg-pulse 9s ease-in-out infinite alternate; }
}
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    @keyframes bg-parallax { to { transform: translate3d(0, 10%, 0); } }
    .bg-art__media {
      inset: -12% 0 0 0;                             /* headroom so the shift never shows a gap */
      animation: bg-parallax linear both;
      animation-timeline: scroll(root);
      animation-range: 0 100vh;
    }
  }
}

/* ---- /internal/backgrounds/ picker (preview build only) ---- */
.bgg-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-3); margin-top: var(--space-3); }
@media (min-width: 720px) { .bgg-grid { grid-template-columns: 1fr 1fr; } }
.bgg-card {
  padding: var(--space-2);
}
.bgg-card h2 { margin-top: 0; font-size: var(--fs-h3); }
.bgg-pair { display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-2); align-items: start; }
.bgg-pair img { display: block; width: 100%; height: auto; border: 1px solid var(--mc-border-strong); border-radius: 4px; }
.bgg-size { color: var(--mc-text-dim); font-size: var(--fs-tiny); margin: var(--space-1) 0; }
.bgg-pages { margin: 0; padding-left: 1.2em; font-size: var(--fs-small); }
.bgg-role { color: var(--mc-text-dim); font-size: var(--fs-tiny); }
.bgg-none { color: var(--mc-text-dim); }
.bgg-banner {
  padding: 8px 14px;
  position: relative;
  padding-left: 30px;
}

/* ======================================================================
   Flat finish (Personal Settings look.depth; attribute data-mdc-depth on <html>; inert until something sets it).
   The plates are flattened by the generated block at the foot of mdc-skin.css; this flattens what site.css paints itself: the enamel header and
   panel, the lit rules and the lamp stripes. `auto` follows the device's request for more contrast. Solid fills, no gloss, strong borders.
   ====================================================================== */
html[data-mdc-depth="flat"] header.site-header { background-image: none !important; background-color: var(--mc-black) !important; box-shadow: none !important; }
html[data-mdc-depth="flat"] header.site-header::before { display: none; }
html[data-mdc-depth="flat"] header.site-header::after,
html[data-mdc-depth="flat"] nav.site-nav a::after,
html[data-mdc-depth="flat"] .nav-dropdown summary::after,
html[data-mdc-depth="flat"] main h1::after, html[data-mdc-depth="flat"] main .ms-h1::after,
html[data-mdc-depth="flat"] :is(.ms-quote, .ms-chapter-banner, .hold-footer, .roster-notice, .hands-review .item .ask, .bgg-banner, .contact-errors)::before {
  background: var(--mc-cyan) !important; box-shadow: none !important;
}
html[data-mdc-depth="flat"] nav.site-nav ul.site-nav-list.is-open { background-image: none !important; background-color: var(--mc-black) !important; box-shadow: none !important; border-bottom: 2px solid var(--mc-silver) !important; }
html[data-mdc-depth="flat"] .nav-dropdown-menu li a:hover, html[data-mdc-depth="flat"] .nav-dropdown-menu li a:focus-visible { background-image: none !important; background-color: var(--mc-hover) !important; }
@media (prefers-contrast: more) {
  html[data-mdc-depth="auto"] header.site-header { background-image: none !important; background-color: var(--mc-black) !important; box-shadow: none !important; }
  html[data-mdc-depth="auto"] header.site-header::before { display: none; }
  html[data-mdc-depth="auto"] header.site-header::after, html[data-mdc-depth="auto"] main h1::after, html[data-mdc-depth="auto"] main .ms-h1::after { background: var(--mc-cyan) !important; box-shadow: none !important; }
}

/* ---------- /contact form (website/functions/contact.js; held by site.yaml holds.forms) ----------
   Site tokens only. Targets are 44 px (WCAG 2.5.8 asks 24; the site's own floor is 44). Errors are
   marked by TEXT plus a heavier border, never by colour alone. No inline styles anywhere in the
   markup (the CSP forbids them): the honeypot hides through .contact-honeypot (shared rule above)
   and everything toggles through the `hidden` attribute. */
.contact-box {
  padding: var(--space-4);
  margin: var(--space-5) 0;
  max-width: 640px;
}
.contact-box h2 {
  margin: 0 0 var(--space-1);
  font-size: var(--fs-h2);
  color: #fff;
  border-bottom: none;
  padding-bottom: 0;
}
.contact-intro, .contact-hint, .contact-note {
  color: var(--mc-text-dim);
  font-size: var(--fs-small);
  margin: 0 0 var(--space-2);
}
.contact-hint { margin: var(--space-1) 0 0; font-size: var(--fs-tiny); }
.contact-note { margin: var(--space-2) 0 0; font-size: var(--fs-tiny); }
.contact-field { margin: 0 0 var(--space-3); }
.contact-field label {
  display: block;
  margin: 0 0 var(--space-1);
  font-weight: 600;
  color: var(--mc-text);
}
.contact-flag { font-weight: 400; color: var(--mc-text-dim); font-size: var(--fs-small); }
.contact-field input[type="text"],
.contact-field input[type="email"],
.contact-field select,
.contact-field textarea {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font: inherit;
  font-size: 1rem;
}
.contact-field textarea { min-height: 9em; resize: vertical; }
.contact-field__error {
  margin: var(--space-1) 0 0;
  color: var(--mc-error);
  font-size: var(--fs-small);
  font-weight: 600;
}
.contact-field__error[hidden],
.contact-errors[hidden],
.contact-received[hidden],
.contact-form[hidden] { display: none; }
.contact-errors {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
}
.contact-errors__title { margin: 0 0 var(--space-1); font-size: var(--fs-h3); color: #fff; }
.contact-error-list { margin: 0; padding-left: 1.2em; }
.contact-submit {
  min-width: 44px;
  min-height: 44px;
  padding: 10px 28px;
  cursor: pointer;
}
.contact-submit[disabled] { opacity: 0.6; cursor: progress; }
.contact-received h3 { margin: 0 0 var(--space-1); font-size: var(--fs-h2); color: #fff; }
.contact-received h3:focus, .contact-errors:focus {
  outline-offset: 4px;
  outline: 3px solid transparent;
  box-shadow: 0 0 0 2px var(--mdc-focus-gap), 0 0 0 5px var(--mdc-focus-inner), 0 0 0 8px var(--mdc-focus-outer);
}
/* Dalia's form-sent art slot (Lit Window, bg-81). Empty until the art is approved and wired; it
   takes no space until then, so nothing jumps when it arrives. */
.contact-received__art:empty { display: none; }
@media (max-width: 480px) {
  .contact-box { padding: var(--space-3); }
}
