/* Warmly Invited design system — B05 */
:root {
  /* Alpha palette: the beta is born green, not blue. */
  --accent: #2d6a4f;
  --accent-dark: #1f4d38;
  --accent-soft: #e8f1ea;
  --bg: #faf9f7;
  --surface: #ffffff;
  --ink: #26221e;
  --muted: #716a62;
  --faint: #a89f96;
  --line: #eae5df;

  /* Semantic state colors and their named surfaces. */
  --yes: #2b7349;
  --no: #b3423a;
  --no-dark: #8f2f2a;
  --maybe: #8f6323;
  --yes-tint: #e3f2e9;
  --no-tint: #f7e5e3;
  --maybe-tint: #f7eeda;
  --neutral-tint: #efece8;
  --warning: #8f6323;
  --warning-tint: #fff8e8;

  /* Contact channels are semantic too; no screen owns another blue/gold. */
  --channel-email: #3e6d93;
  --channel-email-tint: #edf5fb;
  --channel-email-line: #d8e4f0;
  --channel-text: #866226;
  --channel-text-tint: #fbf5e8;
  --channel-text-line: #e7d8ba;

  --radius: 10px;
  --radius-control: 8px;
  --shadow: 0 2px 10px rgba(40, 30, 20, 0.07);
  --shadow-lift: 0 8px 28px rgba(40, 30, 20, 0.14); /* toast and outbox surfaces */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-serif: Georgia, "Times New Roman", serif; /* invitation notes; donation footer/dialog brand+title are the mock-mandated host-chrome exception */
  --body-size: 16px;
  --body-leading: 1.45;
  --caption-size: 13px;

  /* The only three shared heading sizes. */
  --heading-1-size: 26px;
  --heading-2-size: 19px;
  --heading-3-size: 16px;
  --heading-leading: 1.2;

  /* Never-jump dimensions. Keep these stable while copy or state changes. */
  --slot-error-height: 20px;
  --slot-hint-height: 34px;
  --control-width-sm: 96px;
  --control-width-md: 148px;
  --control-width-lg: 190px;

  /* KWB-328: the Send-feedback pill's corner (see .feedback-btn and the corner rule). */
  --feedback-top: 8px; /* a page with a top bar may centre the pill on it */
  --feedback-clear-top: 56px; /* 8 inset + 38 pill + 10 air: a page column starts below this */
  --feedback-clear-right: 148px; /* 8 inset + 128 pill + 12 air: a top bar's controls end before this */
  --feedback-clear-bottom: 60px; /* KWB-332, phones: 12 inset + 38 pill + 10 air: the end of a page and a pinned bar's content stop above this (plus the safe area) */
}

*, *::before, *::after { box-sizing: border-box; }
html { min-height: 100%; }
body {
  min-height: 100%;
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--body-size);
  line-height: var(--body-leading);
}
button, input, select, textarea { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
button { cursor: pointer; }

/* Three heading sizes; use these classes or the matching semantic element. */
h1, .heading-1 {
  margin: 0;
  font-size: var(--heading-1-size);
  line-height: var(--heading-leading);
  letter-spacing: -0.02em;
}
h2, .heading-2 {
  margin: 0;
  font-size: var(--heading-2-size);
  line-height: var(--heading-leading);
  letter-spacing: -0.01em;
}
h3, .heading-3 {
  margin: 0;
  font-size: var(--heading-3-size);
  line-height: var(--heading-leading);
}
/* Inline invitation-card SVGs share this stylesheet's layout primitives. */
.invite-card-svg { display: block; }
.page-title { margin-bottom: 4px; }
.page-subtitle { margin: 0 0 26px; color: var(--muted); }

/* App chrome and controls carried forward from the alpha. */
.page-shell {
  width: min(1080px, 100%);
  margin: 0 auto;
  padding: 30px 28px 60px;
}
.card-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px;
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 18px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-control);
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.btn-primary { background: var(--accent); color: var(--surface); }
.btn-primary:hover { background: var(--accent-dark); }
.btn-secondary { background: var(--surface); border-color: var(--line); }
.btn-secondary:hover { border-color: var(--faint); }
.btn-ghost { background: transparent; color: var(--muted); }
.btn-ghost:hover { color: var(--ink); }
.btn:disabled { cursor: default; opacity: 0.45; }
.btn-sm { min-height: 44px; padding: 0 12px; font-size: 13px; }

.field, label.field { display: block; margin-bottom: 16px; }
.field-label, label.field > span {
  display: block;
  margin-bottom: 6px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
input[type="text"], input[type="email"], input[type="tel"], input[type="date"],
input[type="time"], input[type="number"], input[type="search"], textarea, select {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
}
input:focus, textarea:focus, select:focus, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
textarea { min-height: 72px; resize: vertical; }
select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 34px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 16px) 19px, calc(100% - 11px) 19px;
  background-repeat: no-repeat;
  background-size: 5px 5px, 5px 5px;
}

/* One chip primitive. Variants change semantics; modifiers change size only. */
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 58px;
  height: 24px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.chip-sm { min-width: 52px; height: 22px; padding: 0 8px; font-size: 11px; }
.chip-md { min-width: 58px; height: 24px; padding: 0 10px; font-size: 12px; }
.chip-lg { min-width: 72px; height: 32px; padding: 0 12px; font-size: 13px; }
.chip-yes { background: var(--yes-tint); color: var(--yes); }
.chip-no { background: var(--no-tint); color: var(--no); }
.chip-maybe { background: var(--maybe-tint); color: var(--maybe); }
.chip-neutral { background: var(--neutral-tint); color: var(--muted); }
.chip-email { border-color: var(--channel-email-line); background: var(--channel-email-tint); color: var(--channel-email); }
.chip-text { border-color: var(--channel-text-line); background: var(--channel-text-tint); color: var(--channel-text); }

/* One empty-state primitive. The copy must name the empty thing. */
.empty {
  display: flex;
  min-height: 150px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 22px;
  color: var(--muted);
  text-align: center;
}
/* Size modifiers preserve the shared empty structure while reserving known footprints. */
.empty-lg { min-height: 300px; }
.empty-icon {
  display: inline-flex;
  width: 36px;
  height: 36px;
  align-items: center;
  justify-content: center;
  margin-bottom: 5px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 18px;
}
.empty-title { color: var(--ink); font-weight: 700; }
.empty-copy { font-size: 13px; }

/* One treatment per action class: primary, secondary, and destructive. */
a.link {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  font-size: 13px;
  font-weight: 700;
  text-underline-offset: 2px;
}
a.link-action { color: var(--accent); }
a.link-action:hover { color: var(--accent-dark); }
a.link-secondary { color: var(--muted); text-decoration: none; }
a.link-secondary:hover { color: var(--ink); text-decoration: underline; }
a.link-danger { color: var(--no); }
a.link-danger:hover { color: var(--no-dark); }

/* Never-jump helpers. Empty slots stay in flow and retain their footprint. */
.reserved-slot { display: block; }
.error-slot {
  display: block;
  min-height: var(--slot-error-height);
  margin: 5px 0 0;
  color: var(--no);
  font-size: 12px;
  line-height: var(--slot-error-height);
}
.hint-slot {
  display: block;
  min-height: var(--slot-hint-height);
  margin: 7px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 17px;
}
.error-slot[data-empty="true"],
.hint-slot[data-empty="true"],
.reserved-slot:empty { visibility: hidden; }
/* KWB-207: a hint that sits between a field's label and its control. "label.field > span"
   (0,0,1,2) beats ".hint-slot" (0,0,1,0), so an in-label hint inherits the uppercase label
   treatment unless a rule at least as specific says otherwise. It is content-sized rather
   than carrying .hint-slot's reserve: this copy is static server text the island never
   rewrites or empties, so there is nothing to reserve against and the reserve would only
   push the control away from its own hint. */
.field-hint, label.field > span.field-hint {
  min-height: 0;
  margin: 0 0 6px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: normal;
  line-height: 17px;
  text-transform: none;
}
/* A sub-field block that belongs to an off switch keeps its whole box: turning a
   setting off must not move anything below it (B48 fixed footprint). */
.cr-dependent-body[data-off="true"] { visibility: hidden; }
.control-sm, .control-md, .control-lg { flex: 0 0 auto; }
.control-sm { flex-basis: var(--control-width-sm); width: var(--control-width-sm); }
.control-md { flex-basis: var(--control-width-md); width: var(--control-width-md); }
.control-lg { flex-basis: var(--control-width-lg); width: var(--control-width-lg); }
.tabular-nums { font-variant-numeric: tabular-nums; }
.touch-target { min-width: 44px; min-height: 44px; }
.status-region {
  display: block;
  min-height: 40px;
  padding: 8px 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  color: var(--muted);
  font-size: 12px;
  line-height: 20px;
}
.status-region.is-success { border-color: var(--yes); background: var(--yes-tint); color: var(--accent-dark); }
.status-region.is-error { border-color: var(--no); background: var(--no-tint); color: var(--no-dark); }
.status-region:empty { visibility: hidden; }
.status-message.is-success { color: var(--accent-dark); }
.status-message.is-error { color: var(--no-dark); }
.caption { color: var(--muted); font-size: var(--caption-size); }

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

/* Host-only donation footer and coming-soon dialog (B49; binding mock). */
footer.product-footer {
  margin: 0;
  max-width: none;
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding: 28px 24px 32px;
  color: var(--muted);
  font-size: 14px;
}
.product-footer .footer-inner {
  display: flex;
  max-width: 640px;
  margin: 0 auto;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.product-footer .footer-word { color: var(--muted); }
.product-footer .host-account-line {
  display: flex;
  flex: 0 0 100%;
  min-height: 44px;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.product-footer .host-account-label {
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}
.product-footer .host-account-link {
  color: var(--accent-dark);
  font-size: 13px;
  font-weight: 600;
}
.host-signout-form { margin: 0; }
.host-signout-btn {
  display: inline-flex;
  flex: 0 0 112px;
  width: 112px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 9px 12px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: transparent;
  color: var(--accent-dark);
  font: 600 14px var(--font);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.host-signout-btn:hover { background: var(--accent); color: var(--surface); }
.host-signout-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.product-footer .brand {
  color: var(--ink);
  font-family: var(--font-serif);
}
.donate-btn {
  display: inline-flex;
  flex: 0 0 128px;
  width: 128px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 9px 0;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-soft);
  color: var(--accent-dark);
  font: 600 14px var(--font);
  text-align: center;
  text-decoration: none;
  transition: background 140ms ease, color 140ms ease;
}
.donate-btn:hover { background: var(--accent); color: var(--surface); }
.donate-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
dialog.donate-dialog {
  width: calc(100vw - 48px);
  max-width: 380px;
  margin: auto;
  padding: 32px 32px 28px;
  border: none;
  border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}
dialog.donate-dialog::backdrop { background: rgba(38, 34, 30, 0.35); }
@media (prefers-reduced-motion: no-preference) {
  dialog.donate-dialog[open] { animation: donate-rise 180ms ease-out; }
  @keyframes donate-rise {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
  }
}
.dialog-kicker {
  margin-bottom: 10px;
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.dialog-title {
  margin-bottom: 10px;
  font-family: var(--font-serif);
  font-size: 22px;
  font-weight: 400;
}
.dialog-body { margin-bottom: 22px; color: var(--muted); font-size: 15px; }
.dialog-actions { display: flex; justify-content: flex-end; }
.dialog-close {
  width: 128px;
  min-height: 44px;
  padding: 9px 0;
  border: none;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--surface);
  font: 600 14px var(--font);
  text-align: center;
}
.dialog-close:hover { background: var(--accent-dark); }
.dialog-close:focus-visible { outline: 2px solid var(--accent-dark); outline-offset: 2px; }
/* KWB-304: Send feedback. A fixed corner button and one panel; nothing here jumps, and the
   panel moves only where the host drags it. */
.feedback-btn {
  position: fixed;
  top: var(--feedback-top);
  right: 8px;
  z-index: 30;
  width: 128px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font: 500 13px var(--font);
  text-align: center;
  white-space: nowrap;
  box-shadow: var(--shadow);
  cursor: pointer;
}
.feedback-btn:hover { color: var(--ink); border-color: var(--faint); }
.feedback-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* KWB-329: a non-modal panel (dialog.show()), not a modal: no backdrop, the page behind it
   scrolls and takes clicks, so the host can show what he is writing about. Docked bottom-right
   (top/left auto clear the UA's inset-inline: 0) until the title is dragged; the script then
   writes --feedback-panel-left/top through the CSSOM, keeps them 8px inside the viewport and
   sets data-placed. Above the pill and every sticky bar; host modals sit above it in the top
   layer. It needs its own edge now nothing dims the page. */
dialog.feedback-dialog {
  position: fixed;
  top: auto;
  right: 24px;
  bottom: 24px;
  left: auto;
  z-index: 40;
  width: calc(100vw - 48px);
  max-width: 420px;
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  margin: 0;
  padding: 28px 32px 28px;
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}
dialog.feedback-dialog[data-placed="true"] {
  top: var(--feedback-panel-top);
  right: auto;
  bottom: auto;
  left: var(--feedback-panel-left);
  max-height: calc(100vh - 16px);
  max-height: calc(100dvh - 16px);
}
/* Lifted while it is being dragged, so the move reads as picking the panel up. */
dialog.feedback-dialog[data-dragging="true"] { box-shadow: var(--shadow-lift), 0 12px 32px rgba(38, 34, 30, 0.18); }
/* A browser without <dialog> support would render the box open at the foot of every page. */
dialog.feedback-dialog:not([open]) { display: none; }
/* html2canvas marks pseudo-elements with these classes and injects a nonce-less <style> the
   CSP blocks; without this the copy it screenshots would lay out ::before/::after twice. */
.___html2canvas___pseudoelement_before::before,
.___html2canvas___pseudoelement_after::after { content: "" !important; display: none !important; }
@media (prefers-reduced-motion: no-preference) {
  dialog.feedback-dialog[open] { animation: donate-rise 180ms ease-out; }
}
.feedback-form { display: block; }
/* The kicker and title are the drag handle. The grip is absolutely placed and fixed-size, so
   showing or hiding it moves nothing. */
.feedback-handle {
  position: relative;
  cursor: move;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.feedback-grip {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 18px;
  color: var(--faint);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
}
.feedback-grip::before {
  content: "";
  width: 8px;
  height: 12px;
  background-image: radial-gradient(circle, var(--faint) 1.2px, transparent 1.6px);
  background-size: 4px 4px;
}
.feedback-dialog .dialog-body { margin-bottom: 16px; }
.feedback-label {
  display: block;
  margin-bottom: 6px;
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
}
.feedback-text {
  display: block;
  width: 100%;
  min-height: 132px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: 400 var(--body-size)/var(--body-leading) var(--font);
  resize: none;
}
.feedback-text:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.feedback-text:disabled { opacity: 0.6; }
.feedback-status {
  min-height: var(--slot-hint-height);
  margin: 8px 0 6px;
  color: var(--muted);
  font-size: 13px;
  line-height: 17px;
}
.feedback-status[data-state="error"] { color: var(--no); }
.feedback-status[data-state="sent"] { color: var(--yes); }
.feedback-actions { display: flex; justify-content: flex-end; gap: 10px; }
.feedback-cancel,
.feedback-send {
  min-height: 44px;
  padding: 9px 0;
  border-radius: var(--radius);
  font: 600 14px var(--font);
  text-align: center;
  cursor: pointer;
}
.feedback-cancel {
  width: 100px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
}
.feedback-cancel:hover { color: var(--ink); border-color: var(--faint); }
.feedback-send {
  width: 128px;
  border: none;
  background: var(--accent);
  color: var(--surface);
}
.feedback-send:hover { background: var(--accent-dark); }
.feedback-send:disabled { background: var(--accent); opacity: 0.55; cursor: default; }
.feedback-cancel:focus-visible,
.feedback-send:focus-visible { outline: 2px solid var(--accent-dark); outline-offset: 2px; }
/* KWB-329: on a phone the panel is a full-width bottom sheet, clear of the home indicator, and
   it does not move: the sheet rule comes after the placed rule and matches it, so it wins. */
@media (max-width: 560px) {
  dialog.feedback-dialog, dialog.feedback-dialog[data-placed="true"] {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    max-width: none;
    max-height: calc(100vh - 12px);
    max-height: calc(100dvh - 12px);
    padding: 22px 20px calc(18px + env(safe-area-inset-bottom, 0px));
    border-width: 1px 0 0;
    border-radius: calc(var(--radius) + 4px) calc(var(--radius) + 4px) 0 0;
  }
  .feedback-handle { cursor: auto; touch-action: auto; }
  .feedback-grip { display: none; }
}
/* KWB-328: above the phone breakpoint the Send-feedback pill owns the top-right corner box
   on every page that carries it (body.has-feedback, server-rendered, so the reveal moves
   nothing). The page's opening column starts below the pill; a page-level top bar keeps its
   right-hand controls clear of it. No width maths: it holds for every column width and with
   a classic scrollbar. A page must open with its <main> or a top <header> (asserted in
   test/feedback.test.ts). The query is the exact complement of the phone query, so no
   fractional width falls between them. */
@media not all and (max-width: 560px) {
  body.has-feedback > main:first-child { padding-top: var(--feedback-clear-top); }
  body.has-feedback > header:first-child { padding-right: var(--feedback-clear-right); }
}
@media (max-width: 560px) {
  /* A phone has no spare top-right: page titles run edge to edge there, so the button
     drops to the bottom corner, clear of the safe area. Still fixed, still 128 × 38. */
  .feedback-btn { top: auto; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); right: 12px; }
  /* KWB-332: the bottom of the page reserves the pill's lane instead. The end of every page
     that carries the pill, and the one bar pinned to the bottom edge (the Guests & Send seam,
     guests.ts KWB-221), stop --feedback-clear-bottom above the viewport's bottom. So at max
     scroll, and while the seam is pinned, nothing sits under the pill. Keyed to the
     server-rendered class, so the reveal moves nothing. The seam rule ignores data-sticky
     because the seam's box must not depend on pinning (KWB-221). It lives here, not in
     guests.ts, and depends on that class name; a test pins the seam as the only bottom-anchored bar. */
  body.has-feedback { padding-bottom: calc(var(--feedback-clear-bottom) + env(safe-area-inset-bottom, 0px)); }
  body.has-feedback .send-seam { padding-bottom: calc(var(--feedback-clear-bottom) + env(safe-area-inset-bottom, 0px)); }
}
@media (max-width: 560px) {
  footer.product-footer { padding-right: 18px; padding-left: 18px; }
  .product-footer .footer-inner { align-items: flex-start; }
  .product-footer .host-account-line { justify-content: space-between; }
}

/* Dev showcase layout; it composes the same primitives future screens use. */
.token-showcase {
  max-width: 900px;
  margin: 0 auto;
  padding: 32px 24px 64px;
}
.token-showcase-intro { max-width: 620px; margin-bottom: 28px; }
.token-showcase-section { margin-top: 22px; }
.token-showcase-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.token-showcase-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.token-showcase-probe {
  min-height: 44px;
  margin-top: 12px;
  padding: 10px 12px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 13px;
}
.token-showcase-code {
  display: inline-block;
  padding: 2px 5px;
  border-radius: 4px;
  background: var(--neutral-tint);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
}

/* B17 create/edit screen. It composes B05 tokens and keeps every validation
   message in a reserved slot so typing never moves the controls underneath. */
.create-screen { max-width: 1180px; }
.create-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 320px);
  align-items: start;
  gap: 24px;
}
.create-main { min-width: 0; }
.create-section { margin-bottom: 18px; }
.create-step {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  margin-bottom: 22px;
}
.create-step-number {
  display: inline-flex;
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-dark);
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
}
.create-step-heading {
  display: block;
  color: var(--ink);
  font-size: var(--heading-2-size);
  font-weight: 800;
  line-height: var(--heading-leading);
}
.create-step-subtitle { display: block; margin-top: 2px; color: var(--muted); font-size: 13px; }
.cr-required { color: var(--no); }
.cr-optional { color: var(--faint); font-size: 11px; font-style: normal; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; }
.cr-when-note { margin: 0 0 8px; }
/* KWB-262: the date error is a full-row grid child, so the row gap is 0 and the block's
   trailing space moves off the labels and onto the container. A grid item's margins never
   collapse with its children's, so leaving "margin-bottom: 16px" on the labels would push the
   error 21px below the input it describes and slide it under the warning box. The 22px here
   is not a free parameter: it collapses with ".cr-warning"'s adjacent "margin-top: -8px" to
   the 14px gap this layout has always had. */
.cr-fields-three { display: grid; grid-template-columns: 1.35fr 1fr; gap: 0 12px; margin-bottom: 22px; }
.cr-fields-three .field { min-width: 0; margin-bottom: 0; }
/* "min-width: 0" is load-bearing: without it the item's automatic min-content floor widens
   the full-row track past the viewport at 360px and drags the date input with it. No
   "overflow-wrap" — this slot's invariant is one line, and breaking mid-word would convert a
   budget failure into a silent 20px page shift instead of visible overflow. */
.cr-fields-three > .cr-date-error { grid-column: 1 / -1; min-width: 0; }
/* KWB-307: the class keeps its name (KWB-279 depends on it) but the row now holds four fields in
   a 2 × 2 grid, each error slot a full row under the pair it belongs to: Date | Start time, the
   date error, End date | End time, the end-date error. Every child is placed by class. The 16px
   above the second pair is the rhythm a field keeps below its own error slot. */
.cr-fields-three > .cr-when-date { grid-column: 1; grid-row: 1; }
.cr-fields-three > .cr-when-start { grid-column: 2; grid-row: 1; }
.cr-fields-three > .cr-date-error { grid-row: 2; }
.cr-fields-three > .cr-when-end-date { grid-column: 1; grid-row: 3; }
.cr-fields-three > .cr-when-end { grid-column: 2; grid-row: 3; }
.cr-fields-three > .cr-end-date-error { grid-column: 1 / -1; grid-row: 4; min-width: 0; }
.cr-fields-three > .cr-when-end-date, .cr-fields-three > .cr-when-end { margin-top: 16px; }
/* Clear sits in the End date label row, out of flow, and is hidden rather than removed while the
   field is empty, so showing it moves nothing. The label is shortened by its width so the two
   never overlap; the hit area extends past the glyphs without reaching the input below. */
.cr-when-end-date { position: relative; }
.cr-when-end-date > .field-label { padding-right: 52px; }
.cr-end-date-clear {
  position: absolute;
  top: -2px;
  right: 0;
  width: 48px;
  height: 17px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  line-height: 17px;
  text-align: right;
}
.cr-end-date-clear::after { position: absolute; inset: -12px -8px 0 -8px; content: ""; }
.cr-end-date-clear:hover { text-decoration: underline; }
.cr-end-date-clear[data-empty="true"] { visibility: hidden; }
#event-description { scroll-margin-top: 96px; }
.cr-warning {
  min-height: var(--slot-hint-height);
  margin: -8px 0 4px;
  padding: 6px 10px;
  border-left: 3px solid var(--warning);
  border-radius: 0 var(--radius-control) var(--radius-control) 0;
  background: var(--warning-tint);
  color: var(--warning);
  font-size: 12px;
  line-height: 18px;
}
.cr-warning span { display: block; }
.cr-field-error { color: var(--no); }
.cr-field-error[data-empty="false"] { min-height: var(--slot-error-height); }
/* KWB-262: "label.field > span" (0,0,1,2) outranks ".error-slot" (0,0,1,0), so a field error
   rendered inside its own label inherits the uppercase label treatment — 11px, 700 weight,
   0.08em tracking, muted colour — which is both wrong and about 1.3x wider per character
   than the 12px sentence case the slot reserves a line for. Restore the slot's treatment at
   matching specificity, the same escape ".field-hint" uses above. Layout-neutral for the two
   slots that stay in their labels: those labels are plain blocks in a padded ".card-panel",
   where the dropped "margin-bottom: 6px" already collapsed into the label's own 16px.
   No "color" here: a filled slot already takes "--no-dark" from ".status-message.is-error"
   (0,0,2,0), and declaring "color" at (0,0,2,2) would win instead and paint the two
   still-in-label slots a different red from the moved one. */
label.field > span.cr-field-error {
  margin: 5px 0 0;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
}
input.is-invalid, textarea.is-invalid { border-color: var(--no); }
.cr-setting-block { margin-bottom: 22px; }
.cr-setting-label { color: var(--ink); font-size: 14px; font-weight: 800; }
.cr-setting-copy { margin-top: 3px; color: var(--muted); font-size: 13px; line-height: 1.45; }
.cr-setting-explanation { margin-top: 11px; color: var(--muted); font-size: 12px; }
.cr-advanced-settings { border-top: 1px solid var(--line); }
.cr-advanced-summary {
  display: flex;
  min-height: 44px;
  align-items: center;
  gap: 10px;
  color: var(--accent);
  cursor: pointer;
  font-weight: 800;
  list-style: none;
}
.cr-advanced-summary::-webkit-details-marker { display: none; }
.cr-advanced-summary::before {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  content: "";
  transform: rotate(-45deg);
  transition: transform .12s ease;
}
.cr-advanced-settings[open] .cr-advanced-summary::before { transform: rotate(45deg); }
.cr-advanced-content { padding-bottom: 2px; }
.cr-visibility-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 11px; }
.cr-visibility-card {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr);
  gap: 2px 9px;
  min-height: 96px;
  padding: 13px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  text-align: left;
}
.cr-visibility-card:hover, .cr-visibility-card.is-selected { border-color: var(--accent); }
.cr-visibility-card.is-selected { background: var(--accent-soft); }
.cr-visibility-card:disabled { cursor: not-allowed; opacity: .72; }
.cr-radio { width: 15px; height: 15px; margin-top: 2px; border: 2px solid var(--faint); border-radius: 50%; }
.cr-visibility-card.is-selected .cr-radio { border: 4px solid var(--accent); }
.cr-visibility-title { font-size: 13px; font-weight: 800; line-height: 1.25; }
.cr-visibility-copy { grid-column: 2; color: var(--muted); font-size: 12px; line-height: 1.4; }
.cr-visibility-lock { margin-top: 10px; color: var(--muted); font-size: 12px; line-height: 1.45; }
.cr-setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  min-height: 68px;
  padding: 15px 0;
  border-top: 1px solid var(--line);
}
.cr-first-row { border-top: 0; padding-top: 0; }
.cr-setting-row > div:first-child { min-width: 0; }
.cr-sub-setting { display: grid; grid-template-columns: 1fr minmax(160px, 220px); align-items: center; gap: 14px; margin: -3px 0 2px; padding: 0 0 0 16px; border-left: 2px solid var(--accent-soft); }
.cr-switch {
  position: relative;
  width: 64px;
  height: 44px;
  flex: 0 0 64px;
  padding: 9px 4px;
  border: 0;
  border-radius: 999px;
  background: var(--line);
  transition: background .12s ease;
}
.cr-switch.is-on { background: var(--accent); }
.cr-switch-knob { display: block; width: 26px; height: 26px; border-radius: 50%; background: var(--surface); box-shadow: 0 1px 3px rgba(40, 30, 20, .22); transition: transform .12s ease; }
.cr-switch.is-on .cr-switch-knob { transform: translateX(30px); }
.cr-design-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px; }
.cr-design-tile {
  min-width: 0;
  padding: 7px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  text-align: left;
}
.cr-design-tile:hover, .cr-design-tile.is-selected { border-color: var(--accent); }
.cr-design-tile.is-selected { background: var(--accent-soft); }
.cr-design-thumb { display: block; overflow: hidden; border-radius: 5px; }
.cr-design-thumb svg { width: 100%; height: auto; }
.cr-photo-input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.cr-design-thumb .cr-photo-thumb { display: block; width: 100%; aspect-ratio: 250 / 340; object-fit: cover; }
.cr-design-name { display: block; margin-top: 7px; font-size: 12px; font-weight: 800; }
.cr-design-mood { display: block; min-height: 32px; color: var(--muted); font-size: 11px; line-height: 1.35; }
.cr-preview-column { min-width: 0; }
.cr-preview-sticky { position: sticky; top: 18px; }
.cr-preview-label { margin: 0 0 9px; color: var(--muted); font-size: 11px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase; }
.cr-preview { display: flex; justify-content: center; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow); }
.cr-preview svg { width: 100%; max-width: 250px; height: auto; }
.cr-preview-status { min-height: 28px; margin-top: 9px; }
.cr-preview-note { color: var(--faint); font-size: 12px; line-height: 1.4; text-align: center; }
/* KWB-148: let feedback grow down while the action buttons stay top-anchored. */
.cr-actions { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 10px; margin-top: 6px; }
.cr-action-feedback { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: 0; min-width: 0; }
.cr-action-feedback .cr-field-error { min-width: 0; margin-top: 0; overflow-wrap: anywhere; }
.cr-action-feedback .cr-field-error[data-empty="false"] { margin-bottom: 10px; }
.cr-action-buttons { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 10px; }
.cr-save-status { min-width: 0; min-height: 40px; margin: 0; }
.cr-save-status.is-error { color: var(--no); }
.cr-card-templates { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.cr-potluck-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(70px, 88px) auto 44px; align-items: center; gap: 8px; margin-bottom: 9px; }
.cr-potluck-name, .cr-potluck-qty { min-width: 0; }
.cr-potluck-need, .cr-potluck-stored { color: var(--muted); font-size: 12px; white-space: nowrap; }
.cr-potluck-stored { min-width: 65px; font-variant-numeric: tabular-nums; }
.cr-potluck-remove { width: 44px; height: 44px; border: 0; background: transparent; color: var(--muted); font-size: 24px; line-height: 1; }
.cr-potluck-remove:hover { color: var(--no); }
.cr-potluck-empty { min-height: 44px; padding: 11px 0; color: var(--muted); font-size: 13px; }
.cr-conflict { max-width: 680px; margin: 20px auto; }
.cr-conflict-list { display: grid; grid-template-columns: max-content 1fr; gap: 8px 18px; margin: 24px 0; }
.cr-conflict-list dt { color: var(--muted); font-size: 12px; font-weight: 800; }
.cr-conflict-list dd { margin: 0; }
.cr-conflict-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.host-page-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 24px; }
.host-event-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.host-event-card { display: grid; gap: 3px; color: var(--ink); text-decoration: none; }
.host-event-card:hover { border-color: var(--accent); }
.host-event-card strong { font-size: var(--heading-2-size); }
.host-event-card > span:not(.host-event-status) { color: var(--muted); font-size: 13px; }
.host-event-status { width: fit-content; margin-bottom: 4px; padding: 3px 8px; border-radius: 999px; background: var(--accent-soft); color: var(--accent-dark); font-size: 11px; font-weight: 800; text-transform: capitalize; }
.event-placeholder-card { display: grid; gap: 4px; max-width: 560px; margin: 10px 0 18px; }
.event-placeholder-card span { color: var(--muted); font-size: 13px; }
/* KWB-313: "Describe your event" grows with its text (TEXTAREA_AUTOGROW_ISLAND) up to 16
   lines, or 60% of the viewport if that is smaller, then scrolls inside. 23px is the 20px of
   vertical padding plus the 3px of border. The stable gutter keeps the text from re-wrapping
   when the inner scrollbar appears at the cap. All of it is keyed on the island's marker:
   without JavaScript the box keeps today's plain rows box, uncapped, with its grip. */
#event-description[data-autogrow="on"] { max-height: min(calc(16 * 1.45em + 23px), 60vh); overflow-y: auto; scrollbar-gutter: stable; }
/* The island overrides any manual height on the next keystroke, so the grip goes once it is
   fitting. Without JavaScript the base "textarea { resize: vertical }" stays. */
textarea[data-autogrow="on"] { resize: none; }
/* The character counter under the description. Its line is always reserved, so crossing the
   show threshold moves nothing. "label.field > span" is the KWB-262 specificity trap: without
   the second selector the counter would render as an 11px bold uppercase label. */
.cr-note-count, label.field > span.cr-note-count { display: block; min-height: 20px; margin: 5px 0 0; color: var(--muted); font-size: 12px; font-weight: 400; letter-spacing: normal; line-height: 20px; text-align: right; text-transform: none; white-space: nowrap; }
.cr-note-count[data-empty="true"] { visibility: hidden; }
.cr-note-count[data-at-limit="true"] { color: var(--ink); }
@media (max-width: 920px) {
  .create-layout { grid-template-columns: 1fr; }
  .cr-preview-column { order: -1; }
  .cr-preview-sticky { position: static; }
  .cr-preview { max-width: 320px; margin: 0 auto; }
  .cr-preview-status, .cr-preview-note { max-width: 320px; margin-right: auto; margin-left: auto; text-align: center; }
  .cr-design-grid { grid-template-columns: repeat(5, minmax(92px, 1fr)); overflow-x: auto; padding-bottom: 4px; }
}
@media (max-width: 620px) {
  .create-screen { padding-top: 22px; }
  .host-page-heading { flex-direction: column; }
  .host-event-grid { grid-template-columns: 1fr; }
  .card-panel { padding: 17px; }
  .cr-fields-three { grid-template-columns: 1fr; gap: 0; }
  /* KWB-262: stacked, auto-placement would drop the date error below End time, where it reads
     as a complaint about End time. Place all four explicitly — named classes, not
     :nth-child(), which breaks silently the first time a field is inserted — and reinstate the
     16px inter-field rhythm the container margin took off the labels, on the two fields that
     now follow the error. The error stays 5px under its own input and 16px above the next
     label: 3.2x closer to the field it describes. */
  .cr-fields-three > .cr-when-date { grid-column: 1; grid-row: 1; }
  .cr-fields-three > .cr-date-error { grid-row: 2; }
  .cr-fields-three > .cr-when-start { grid-column: 1; grid-row: 3; }
  /* KWB-307: the End date and its error follow Start time, so each error still sits under its
     own field, and End time closes the stack. */
  .cr-fields-three > .cr-when-end-date { grid-column: 1; grid-row: 4; }
  .cr-fields-three > .cr-end-date-error { grid-row: 5; }
  .cr-fields-three > .cr-when-end { grid-column: 1; grid-row: 6; }
  .cr-fields-three > .cr-when-start, .cr-fields-three > .cr-when-end-date, .cr-fields-three > .cr-when-end { margin-top: 16px; }
  /* KWB-180: phones keep the full explanatory copy, so the slot reserves the two lines that
     copy actually wraps to at the 360px floor (265px content width).
     2 × 18px line-height + 12px vertical padding, box-sizing: border-box. */
  .cr-warning { min-height: 48px; }
  .cr-visibility-options { grid-template-columns: 1fr; }
  .cr-sub-setting { grid-template-columns: 1fr; gap: 4px; padding-bottom: 10px; }
  .cr-potluck-row { grid-template-columns: minmax(0, 1fr) 70px 44px; }
  .cr-potluck-need { display: none; }
  .cr-potluck-stored { grid-column: 1 / 3; grid-row: 2; }
  .cr-potluck-remove { grid-column: 3; grid-row: 1 / 3; }
  .cr-actions { grid-template-columns: minmax(0, 1fr); align-items: start; }
  .cr-action-feedback { grid-template-columns: minmax(0, 1fr); }
  .cr-action-buttons { order: -1; justify-content: flex-end; }
}
/* KWB-313: phones cap the growing description at 10 lines. */
@media (max-width: 620px) {
  #event-description[data-autogrow="on"] { max-height: min(calc(10 * 1.45em + 23px), 60vh); }
}
/* KWB-314: the guest question's "?" and the dialog it opens. The button's 44px hit area is
   pulled back by negative margins to a zero-height margin box, so the label row is exactly as
   tall as a plain label. The dialog is a modal overlay: nothing in the page moves when it opens,
   and the page is not scroll-locked (dropping a classic scrollbar would shift the layout). Its
   slots are clamped inside reserved heights, so it is one size whatever the host has typed. */
.cr-label-row { display: flex; align-items: center; gap: 4px; margin-bottom: 6px; }
.cr-label-row > .field-label { min-width: 0; margin-bottom: 0; }
.cr-help-btn { display: grid; flex: 0 0 44px; place-items: center; width: 44px; height: 44px; margin: -22px -10px -22px -6px; padding: 0; border: 0; background: transparent; color: var(--accent-dark); cursor: pointer; }
.cr-help-glyph { display: grid; place-items: center; width: 24px; height: 24px; border: 1.5px solid var(--line); border-radius: 50%; background: var(--surface); font: 800 13px/1 var(--font); }
.cr-help-btn:hover .cr-help-glyph { border-color: var(--faint); }
.cr-help-btn:focus-visible { outline: none; }
.cr-help-btn:focus-visible .cr-help-glyph { outline: 2px solid var(--accent); outline-offset: 2px; }
dialog.cr-help-dialog {
  width: calc(100vw - 32px);
  max-width: 440px;
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  margin: auto;
  padding: 28px;
  overflow-y: auto;
  overscroll-behavior: contain;
  border: none;
  border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}
dialog.cr-help-dialog:not([open]) { display: none; }
dialog.cr-help-dialog::backdrop { background: rgba(38, 34, 30, 0.35); }
@media (prefers-reduced-motion: no-preference) {
  dialog.cr-help-dialog[open] { animation: donate-rise 180ms ease-out; }
}
.cr-help-dialog .dialog-body { margin-bottom: 16px; }
.cr-sketch { display: grid; gap: 8px; max-width: 360px; margin: 0 auto 16px; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); font-size: 13px; line-height: 18px; }
.cr-sketch-card { min-width: 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); }
.cr-sketch-heading { margin-bottom: 4px; color: var(--ink); font-size: 14px; font-weight: 800; }
.cr-sketch-slot { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.cr-sketch-card > .cr-sketch-slot { height: 36px; color: var(--muted); -webkit-line-clamp: 2; }
.cr-sketch-choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin: 6px 0 10px; }
.cr-sketch-choice { padding: 4px 0; border: 1px solid var(--line); border-radius: 6px; color: var(--ink); font-size: 12px; font-weight: 700; text-align: center; white-space: nowrap; }
.cr-sketch-choice[data-picked="true"] { border-color: var(--accent-dark); background: var(--accent-dark); color: var(--surface); }
.cr-sketch-label { margin-bottom: 6px; color: var(--muted); font-size: 12px; font-weight: 800; }
/* The highlight is outline and background only, never a border or padding change, and the
   "Your question" tag word carries it without relying on colour. */
.cr-sketch-question { margin-bottom: 6px; padding: 6px 8px; border-radius: 6px; background: var(--accent-soft); outline: 2px solid var(--accent); }
.cr-sketch-tag { display: block; color: var(--accent-dark); font-size: 10px; font-weight: 800; letter-spacing: 0.08em; line-height: 16px; text-transform: uppercase; }
.cr-sketch-question > .cr-sketch-slot { height: 54px; color: var(--ink); font-weight: 600; -webkit-line-clamp: 3; }
/* After both slot rules, and one class more specific, so the muted empty state always wins. */
.cr-sketch .cr-sketch-slot[data-empty="true"] { color: var(--faint); font-style: italic; }
.cr-sketch-box { min-height: 40px; padding: 8px 10px; border: 1.5px solid var(--line); border-radius: 6px; background: var(--surface); color: var(--faint); font-size: 12px; }
.cr-help-aside { margin: 0 0 8px; color: var(--muted); font-size: 13px; line-height: 18px; }
.cr-help-actions { align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
.cr-help-links { min-width: 0; }
.cr-help-actions .dialog-close { flex: 0 0 128px; }
/* KWB-315: the edit-only link to the saved preview, in the action row's left slot. */
.cr-help-link { color: var(--accent-dark); font-size: 14px; font-weight: 700; line-height: 20px; text-underline-offset: 3px; }
.cr-help-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.cr-help-caption { display: block; margin-top: 2px; color: var(--muted); font-size: 12px; line-height: 16px; }
@media (max-width: 620px) {
  dialog.cr-help-dialog { padding: 20px; }
}
@media (max-width: 620px) {
  .page-shell, .token-showcase { padding-right: 18px; padding-left: 18px; }
  .token-showcase-grid { grid-template-columns: 1fr; }
}
/* KWB-326: separators for inline link rails. Each is one collapsible space whose dot is painted as
   its background, so the space is the only break opportunity between items; a space that
   lands at a line end is removed, which leaves no dot to lead or trail a wrapped line. The links
   either side never split mid-label. Markup: RAIL_SEPARATOR. */
.rail-sep {
  word-spacing: 0.57em;
  /* Dropped 0.12em below the line box centre to sit where the old "·" glyph sat. */
  background: radial-gradient(circle at 50% calc(50% + 0.12em), currentColor 0.9px, transparent 1.4px) no-repeat;
}
a:has(+ .rail-sep), .rail-sep + a { white-space: nowrap; }


.host-page {
  max-width: min(1080px, 100%);
}
.host-page-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px 24px;
  margin-bottom: 28px;
}
.host-page-heading {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.host-page-heading .page-title {
  margin: 0 0 6px;
  line-height: var(--heading-leading);
}
.host-page-heading .page-subtitle {
  margin: 0;
  line-height: 1.45;
}
.host-page-header > .btn {
  flex: 0 0 auto;
  min-width: var(--control-width-md);
  white-space: nowrap;
}
.host-event-list { display: grid; gap: 18px; }
.host-event-row {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  align-items: start;
  gap: 20px;
  min-width: 0;
}
.host-event-card { width: 120px; max-width: 100%; overflow: hidden; }
.host-event-card .invite-card-svg,
.host-manage-card .invite-card-svg { width: 100%; height: auto; }
.host-event-meta {
  display: flex;
  min-width: 0;
  flex-direction: column;
  align-items: flex-start;
}
.host-event-title {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  max-width: 100%;
  color: var(--ink);
  font-size: var(--heading-2-size);
  font-weight: 700;
  line-height: var(--heading-leading);
  overflow-wrap: anywhere;
  text-decoration: none;
}
.host-event-title:hover { color: var(--accent); }
.host-event-when {
  min-height: 24px;
  margin: 6px 0 4px;
  color: var(--muted);
  font-size: var(--caption-size);
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.host-event-status {
  min-height: 22px;
  margin: 0 0 8px;
  font-size: var(--caption-size);
  font-weight: 700;
  line-height: 1.45;
}
.host-event-status[data-state="sent"] { color: var(--yes); }
.host-event-status[data-state="unsent"] { color: var(--maybe); }
.host-past {
  margin-left: 6px;
  vertical-align: middle;
}
/* KWB-220: same grid as the sibling when/status lines, so a row that gains the marker
   keeps its neighbours where they were. */
.host-event-pending {
  min-height: 24px;
  margin: 0 0 6px;
  color: var(--accent);
  font-size: var(--caption-size);
  font-weight: 600;
  line-height: 1.45;
}
.host-event-pending-count { font-variant-numeric: tabular-nums; }
.host-tally-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  min-height: 24px;
  margin-bottom: 14px;
}
.host-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-height: 44px;
  align-items: center;
}
.host-manage-layout {
  display: grid;
  grid-template-columns: minmax(0, 230px) minmax(0, 1fr);
  align-items: start;
  gap: 24px;
  min-width: 0;
  margin-top: 14px;
}
.host-manage-card { width: 230px; max-width: 100%; min-width: 0; }
.host-card-links {
  min-height: 44px;
  margin-top: 8px;
  color: var(--muted);
  font-size: var(--caption-size);
  text-align: center;
}
.host-cancel-trigger {
  min-height: 44px;
  padding: 9px 0;
  border: 0;
  background: transparent;
  color: var(--no);
  font: inherit;
  font-weight: 700;
  line-height: 26px;
  text-decoration: underline;
  text-underline-offset: 2px;
  vertical-align: middle;
  cursor: pointer;
}
.host-cancel-trigger:hover { color: var(--no-dark); }
.host-cancel-trigger:focus-visible { outline: 2px solid var(--no-dark); outline-offset: 3px; }
.host-cancel-dialog {
  width: calc(100vw - 36px);
  max-width: 520px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}
.host-cancel-dialog::backdrop { background: rgba(38, 34, 30, 0.35); }
.host-cancel-dialog .field { margin-bottom: 16px; }
.host-cancel-dialog textarea { min-height: 128px; resize: vertical; }
/* KWB-316: the cancel message grows as the host types, to 10 lines, then scrolls inside. The
   cap is 45vh at most so the dialog's title, copy and buttons still fit a landscape phone.
   Keyed on the island's marker: without JavaScript the box is the plain 128px box with its grip. */
.host-cancel-dialog textarea[data-autogrow="on"] { max-height: min(calc(10 * 1.45em + 23px), 45vh); overflow-y: auto; scrollbar-gutter: stable; }
.host-cancel-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  min-height: 44px;
  margin-top: 18px;
}
.host-cancel-submit {
  border-color: var(--no);
  background: var(--no);
  color: var(--surface);
}
.host-cancel-submit:hover { border-color: var(--no-dark); background: var(--no-dark); }
.host-cancel-submit:disabled { border-color: var(--muted); background: var(--muted); }
.host-cancel-status { margin-top: 14px; }
.host-cancelled-status { margin: 10px 0 14px; }
.host-cancel-warning { display: block; margin-top: 6px; color: var(--no); }
/* KWB-311: "Delete this draft" for an event that never went out. Same danger register as Cancel. */
.host-delete-trigger {
  min-height: 44px;
  padding: 9px 0;
  border: 0;
  background: transparent;
  color: var(--no);
  font: inherit;
  font-weight: 700;
  line-height: 26px;
  text-decoration: underline;
  text-underline-offset: 2px;
  vertical-align: middle;
  cursor: pointer;
}
.host-delete-trigger:hover { color: var(--no-dark); }
.host-delete-trigger:focus-visible { outline: 2px solid var(--no-dark); outline-offset: 3px; }
.host-delete-dialog {
  width: calc(100vw - 36px);
  max-width: 460px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}
.host-delete-dialog::backdrop { background: rgba(38, 34, 30, 0.35); }
.host-delete-dialog .dialog-body { margin-bottom: 0; }
.host-delete-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  min-height: 44px;
  margin-top: 18px;
}
.host-delete-submit {
  border-color: var(--no);
  background: var(--no);
  color: var(--surface);
}
.host-delete-submit:hover { border-color: var(--no-dark); background: var(--no-dark); }
.host-delete-submit:disabled { border-color: var(--muted); background: var(--muted); }
/* Reserved for the longest failure sentence at 360px (three lines), so an error never grows the
   centred dialog and moves its buttons. */
.host-delete-status { min-height: 78px; margin-top: 14px; }
/* KWB-310: "Copy to a new event". One dialog per page, filled from the trigger while closed, so
   opening it moves nothing on the page. Every control has a fixed width and every message slot
   is reserved, so toggling the switch, erroring and going busy move nothing inside it either. */
.host-copy-trigger {
  min-height: 44px;
  padding: 9px 0;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 700;
  line-height: 26px;
  text-decoration: underline;
  text-underline-offset: 2px;
  vertical-align: middle;
  cursor: pointer;
}
.host-copy-trigger:hover { color: var(--ink); }
.host-copy-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.host-copy-dialog {
  width: calc(100vw - 36px);
  max-width: 520px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}
.host-copy-dialog::backdrop { background: rgba(38, 34, 30, 0.35); }
.host-copy-dialog .dialog-title { overflow-wrap: anywhere; }
.host-copy-dialog .dialog-body { margin-bottom: 18px; }
.host-copy-date { margin-bottom: 6px; }
.host-copy-hint { min-height: 17px; margin: 0 0 18px; color: var(--muted); font-size: 12px; line-height: 17px; }
.host-copy-guests-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; }
.host-copy-guests-label { min-width: 0; font-weight: 700; }
/* Scoped past .host-control-slot, which is declared later with the same specificity and would
   otherwise hand the switch its 190px settings-row width. */
.host-copy-guests-head .host-copy-switch-slot { flex: 0 0 104px; width: 104px; min-width: 104px; }
.host-copy-dialog[data-guests="none"] .host-switch:disabled { cursor: not-allowed; }
.host-copy-guests-detail { margin-top: 4px; color: var(--muted); font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
/* Two lines of 20px, reserved at every width: the longest failure sentence wraps to two in the
   298px a 390px phone leaves, and a status that grew would push the buttons down. */
.host-copy-status { min-height: 40px; margin-top: 14px; font-size: 13px; line-height: 20px; }
.host-copy-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  min-height: 44px;
  margin-top: 8px;
}
/* The edit screen's landing banner after a copy (src/views/create.ts). Server-rendered once,
   before the page paints, so it never appears or leaves under the host's eyes. */
.cr-copied-notice { margin: -12px 0 22px; font-size: 13px; }
.cr-copied-notice strong { color: var(--ink); }
.host-manage-main { min-width: 0; }
.host-panel { margin-bottom: 18px; min-width: 0; }
.host-panel-heading { margin-bottom: 12px; }
/* KWB-297: the roster heading carries the spreadsheet download. It wraps under the heading on a
   phone rather than squeezing it, and its width is fixed so nothing about it ever moves.
   KWB-331: Excel is the button; the CSV stays one quiet link beside it. 240px leaves headroom
   for "Download spreadsheet (Excel)" in Windows' Segoe UI. */
.host-panel-heading-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; margin-bottom: 12px; }
.host-panel-heading-row .host-panel-heading { margin-bottom: 0; }
.host-guest-export-actions { display: flex; flex: 0 0 auto; flex-wrap: wrap; align-items: center; gap: 4px 12px; max-width: 100%; }
.host-guest-export { flex: 0 0 auto; width: 240px; max-width: 100%; justify-content: center; white-space: nowrap; }
.host-guest-export-csv { display: inline-flex; align-items: center; justify-content: center; }
.host-manage-title { overflow-wrap: anywhere; }
.host-manage-subtitle {
  min-height: 24px;
  margin: 5px 0 4px;
  color: var(--muted);
  font-size: var(--caption-size);
  overflow-wrap: anywhere;
}
.host-manage-description {
  min-height: 24px;
  margin: 4px 0 8px;
  color: var(--ink);
  font-size: var(--caption-size);
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.host-manage-description-copy { white-space: pre-wrap; }
/* A long note used to push Guests & Send off a short phone. Clamped to four lines until the
   host asks for the rest; the max-height repeats the clamp for engines without line-clamp. */
.host-manage-description-copy[data-expanded="false"] {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  max-height: calc(4 * 1.45em);
  overflow: hidden;
}
/* Fixed width: the label swaps between Show more and Show less, and neither the control nor
   anything beside it may move when it does. */
.host-manage-description-toggle {
  display: block;
  width: 92px;
  margin: 4px 0 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.host-manage-description-toggle[hidden] { display: none; }
.host-manage-description-link { white-space: nowrap; }
.host-manage-description[data-state="set"] .host-manage-description-link {
  display: block;
  margin: 4px 0 0;
}
.host-delivery-line {
  min-height: 24px;
  margin: 2px 0 10px;
  font-size: 13px;
  font-weight: 700;
}
.host-delivery-line[data-state="sent"] { color: var(--yes); }
.host-delivery-line[data-state="unsent"] { color: var(--maybe); }
/* KWB-220: a callout in the panel's own vocabulary — one accent, the status-region border
   and tint. The success and failure tints are state, not a second brand colour. */
.host-pending-invites {
  margin: 12px 0;
  padding: 14px 16px;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-soft);
}
.host-pending-invites[data-state="sent"] { border-color: var(--yes); background: var(--yes-tint); }
.host-pending-invites[data-state="error"] { border-color: var(--no); background: var(--no-tint); }
/* KWB-233: a reserved slot for the undeliverable count, present and empty when there is none,
   so the notice keeps one height in every state. The count is an a.link (44px touch target),
   so the slot reserves 44px — which also holds the sentence on two lines at a 360px phone. */
.host-pending-invites-undeliverable {
  display: flex;
  align-items: center;
  min-height: 44px;
  margin: 0 0 4px;
  color: var(--no-dark);
  font-size: var(--caption-size);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.host-pending-invites-undeliverable[data-empty="true"] { visibility: hidden; }
/* Two reserved lines. Every sentence this slot can hold fits inside two lines at a 360px
   phone (measured at the panel's 252px content width), so pending, sent, and error are the
   same height and nothing below the notice moves when the state changes. */
.host-pending-invites-copy {
  min-height: 2.9em;
  margin: 0 0 4px;
  color: var(--ink);
  line-height: 1.45;
  overflow-wrap: anywhere;
}
/* A count going 9 → 10 must not move the words after it. */
.host-pending-invites-count { font-variant-numeric: tabular-nums; font-weight: 700; }
/* The receipt gets its own reserved slot rather than growing the copy box: present and
   empty while nothing has been sent, filled after a send, identical height either way. */
.host-pending-invites-receipt {
  min-height: 2.9em;
  margin: 0 0 10px;
  color: var(--muted);
  font-size: var(--caption-size);
  font-variant-numeric: tabular-nums;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.host-pending-invites-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  min-height: 44px;
}
/* Measured in Chrome at 14px/700: "Send invitations" needs 150px of button, 2px more than
   control-md offers, so the slot is control-lg and every label is held on one line. */
.host-pending-invites .btn { white-space: nowrap; }
.host-stat-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  min-width: 0;
  margin: 12px 0 14px;
}
.host-stat {
  flex: 1 1 82px;
  min-width: 0;
}
.host-stat-value {
  min-height: 32px;
  color: var(--ink);
  font-size: 24px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.host-stat-label {
  color: var(--muted);
  font-size: 11px;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.host-deadline,
.host-visibility {
  min-height: 20px;
  margin: 4px 0;
  color: var(--muted);
  font-size: var(--caption-size);
  overflow-wrap: anywhere;
}
.host-deadline { color: var(--maybe); font-weight: 600; }
.host-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 44px;
  margin-bottom: 12px;
}
.host-filter {
  font-size: 12.5px;
  text-decoration: none;
}
.host-filter:hover,
.host-filter[aria-current="page"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}
.host-guest-list { min-width: 0; }
.host-guest-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(92px, auto);
  align-items: start;
  gap: 12px;
  min-width: 0;
  min-height: 76px;
  padding: 11px 2px;
  border-top: 1px solid var(--line);
}
.host-guest-row:first-child { border-top: none; }
.host-guest-main { min-width: 0; }
.host-guest-name { font-size: 14.5px; font-weight: 700; overflow-wrap: anywhere; }
.host-guest-contact {
  min-height: 20px;
  margin-top: 1px;
  color: var(--faint);
  font-size: 12.5px;
  overflow-wrap: anywhere;
}
.host-guest-note {
  min-height: 20px;
  margin-top: 4px;
  color: var(--muted);
  font-size: 13px;
  font-style: italic;
  overflow-wrap: anywhere;
}
/* KWB-330: who the guest is bringing, in the guest's own words. Host-only. Not italic: it is
   a list of names, not a quote. */
.host-guest-plus-ones {
  min-height: 20px;
  margin-top: 4px;
  color: var(--muted);
  font-size: 13px;
  overflow-wrap: anywhere;
}
.host-guest-opened {
  display: flex;
  min-height: 20px;
  align-items: center;
  margin-top: 3px;
  color: var(--muted);
  font-size: 12px;
}
/* KWB-269: the guest has been to the invitation — said in the delivery line's own register,
   so the host reads it rather than skims past it. The word carries the meaning; the weight
   is emphasis on top of it. */
.host-guest-opened[data-state="opened"] { color: var(--ink); font-weight: 700; }
.host-guest-opened[data-empty="true"] { visibility: hidden; }
/* KWB-270: the guest asked for their link. Context under the opened line, in the same reserved
   box, and deliberately without its emphasis: it is a hint about delivery, not a milestone. */
.host-guest-asked {
  display: flex;
  min-height: 20px;
  align-items: center;
  margin-top: 3px;
  color: var(--muted);
  font-size: 12px;
}
.host-guest-asked[data-empty="true"] { visibility: hidden; }
.host-guest-delivery {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 20px;
  margin-top: 3px;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.35;
}
.host-guest-delivery-label { color: var(--ink); font-weight: 800; }
.host-guest-delivery[data-state="suppressed"] .host-guest-delivery-label,
.host-guest-delivery[data-state="not-sending"] .host-guest-delivery-label,
.host-guest-delivery[data-state="unsubscribed"] .host-guest-delivery-label { color: var(--muted); }
.host-guest-delivery[data-state="undeliverable"] .host-guest-delivery-label,
.host-guest-delivery[data-state="unreachable"] .host-guest-delivery-label { color: var(--no-dark); }
.host-guest-delivery-detail { overflow-wrap: anywhere; }
.host-guest-side {
  display: flex;
  min-width: 92px;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
}
.host-guest-side .chip { max-width: 100%; }
.host-guest-empty { min-height: 150px; }
.host-potluck-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(100px, auto);
  align-items: start;
  gap: 12px;
  min-width: 0;
  min-height: 58px;
  padding: 9px 2px;
  border-top: 1px solid var(--line);
}
.host-potluck-row:first-of-type { border-top: none; }
.host-potluck-label { font-weight: 600; overflow-wrap: anywhere; }
.host-potluck-names {
  min-height: 20px;
  margin-top: 3px;
  color: var(--faint);
  font-size: var(--caption-size);
  overflow-wrap: anywhere;
}
.host-potluck-count {
  min-width: 100px;
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.host-setting-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
  min-height: 58px;
  padding: 7px 0;
  border-top: 1px solid var(--line);
}
.host-setting-row:first-of-type { border-top: none; }
.host-setting-copy { min-width: 0; }
.host-setting-label { font-weight: 700; }
.host-setting-detail {
  min-height: 20px;
  color: var(--muted);
  font-size: 13px;
  overflow-wrap: anywhere;
}
.host-control-slot {
  display: inline-flex;
  width: var(--control-width-lg);
  min-width: var(--control-width-lg);
  min-height: 44px;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radius-control);
  color: var(--faint);
  font-size: 12px;
  text-align: center;
}
.host-switch {
  display: inline-flex;
  width: 100%;
  min-width: 100%;
  min-height: 44px;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  text-align: left;
}
.host-switch[aria-checked="true"] { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-dark); }
.host-switch:disabled { cursor: wait; opacity: .65; }
.host-switch-track {
  display: inline-flex;
  width: 34px;
  height: 20px;
  flex: 0 0 34px;
  align-items: center;
  padding: 3px;
  border-radius: 999px;
  background: var(--faint);
}
.host-switch[aria-checked="true"] .host-switch-track { background: var(--accent); }
.host-switch-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--surface); transform: translateX(0); transition: transform 160ms ease; }
.host-switch[aria-checked="true"] .host-switch-thumb { transform: translateX(14px); }
.host-switch-label { width: 32px; text-align: center; }
.host-switch-status { width: 100%; height: 20px; min-height: 20px; margin-top: 4px; overflow: hidden; color: var(--muted); font-size: 12px; line-height: 20px; text-overflow: ellipsis; white-space: nowrap; }
.host-switch-status[data-empty="true"] { visibility: hidden; }
.host-switch-status[data-error="true"] { color: var(--no); }
/* KWB-223. The three-state RSVP-notification control stacks its copy above its segments:
   three 104px segments do not fit the 190px host-control-slot the two-state switches use. */
.host-setting-row-stacked { flex-direction: column; align-items: stretch; }
.host-rsvp-notify-segmented { display: flex; flex-wrap: wrap; gap: 6px; min-height: 44px; }
.host-rsvp-notify-segment {
  flex: 0 0 104px;
  width: 104px;
  min-height: 44px;
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: inherit;
  font: inherit;
  font-size: 13px;
  font-weight: 750;
  text-align: center;
  cursor: pointer;
}
.host-rsvp-notify-segment[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.host-rsvp-notify-segment:disabled { cursor: wait; opacity: .65; }
.host-rsvp-notify-segment:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* The explanatory sentence changes length with the state, and the longest of the six wraps to
   three lines on a ~320-360px column. Reserved at 3 × line-height so pressing a segment cannot
   shove the page — this min-height is the layout-stability guarantee, not decoration. */
.host-rsvp-notify-status { min-height: 60px; line-height: 20px; }
/* The account page is one panel wide; the switch sits in the same reserved slot as the
   event-settings switches so On and Off cannot move anything. */
.host-account-email { color: var(--muted); overflow-wrap: anywhere; }
.host-empty {
  min-height: 180px;
  margin-bottom: 18px;
}
.broadcast-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0 14px;
}
.broadcast-form-grid .field { min-width: 0; }
/* KWB-249: a <fieldset> carries the group semantics the five checkboxes need, but arrives
   with browser chrome (border, padding, an intrinsic min-width that breaks grid columns).
   Reset it to nothing and dress the <legend> like any other field label. */
.broadcast-audience-field { border: 0; padding: 0; margin: 0 0 16px; min-width: 0; }
.broadcast-audience-field legend {
  display: block;
  padding: 0;
  margin-bottom: 6px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.broadcast-audience-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
/* A fixed row height and a fixed count slot: ticking a box reflows nothing, and 9 -> 10
   never moves the label beside it. */
.broadcast-audience-option { min-height: 32px; }
.broadcast-audience-check { display: flex; align-items: center; gap: 8px; min-height: 32px; cursor: pointer; }
.broadcast-audience-name { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 500; }
.broadcast-audience-count {
  flex: 0 0 auto;
  min-width: 3ch;
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* The live recipient line exists at every state, including 0 recipients. */
.broadcast-audience-total {
  min-height: 22px;
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 13px;
  line-height: 22px;
  font-variant-numeric: tabular-nums;
}
/* Both nouns are stacked in one grid cell so the slot is exactly as wide as "guests" and
   "guest" ↔ "guests" cannot move the text after it. Visibility, never display. */
.broadcast-audience-noun { display: inline-grid; }
.broadcast-audience-noun > span { grid-area: 1 / 1; }
.broadcast-audience-noun[data-one="true"] > .broadcast-audience-noun-many,
.broadcast-audience-noun[data-one="false"] > .broadcast-audience-noun-one { visibility: hidden; }
/* Space reserved when nothing is undeliverable — visibility, never display, so the line
   cannot change width when the aside appears. Same trick as .send-undeliverable. */
.broadcast-audience-undeliverable[data-empty="true"] { visibility: hidden; }
/* KWB-316: the 72px rest comes from min-height, not a fixed height: rows="2" is 69.4px, under
   the floor, so the box rests at exactly 72px with or without JavaScript. The island grows it
   to 8 lines, then it scrolls inside; the cap, scroll and gutter are keyed on its marker. */
.broadcast-message-field textarea { min-height: 72px; border-radius: var(--radius-control); }
.broadcast-message-field textarea[data-autogrow="on"] { max-height: min(calc(8 * 1.45em + 23px), 60vh); overflow-y: auto; scrollbar-gutter: stable; }
.broadcast-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; min-height: 54px; }
.broadcast-actions .hint-slot { flex: 1 1 300px; margin: 0; }
.broadcast-status { min-height: 40px; margin-top: 4px; }
.broadcast-status.is-error { color: var(--no); }
.broadcast-preview { min-height: 220px; margin-top: 12px; padding: 14px; border-top: 1px solid var(--line); border-radius: var(--radius-control); background: var(--bg); }
/* KWB-136: operator's explicit empty-Blast collapse; preserve hidden/display:none so dead space is not reserved. */
.broadcast-preview[hidden], .broadcast-history[hidden] { display: none; }
.broadcast-preview-heading { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 30px; }
.broadcast-preview-heading h3 { margin: 0; }
.broadcast-preview-summary { min-height: 20px; margin: 6px 0; color: var(--muted); font-size: 13px; line-height: 1.45; }
.broadcast-recipient-list { min-height: 72px; max-height: 180px; overflow: auto; margin: 8px 0; padding: 0; list-style: none; }
.broadcast-recipient-row { display: flex; align-items: center; gap: 10px; min-height: 32px; padding: 6px 0; border-top: 1px solid var(--line); }
.broadcast-recipient-row:first-child { border-top: 0; }
.broadcast-recipient-name { min-width: 0; overflow-wrap: anywhere; font-weight: 700; }
.broadcast-recipient-channel { flex: 0 1 auto; margin-left: auto; color: var(--muted); font-size: 12px; white-space: nowrap; }
.broadcast-preview-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; min-height: 44px; }
.broadcast-history { min-height: 44px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); color: var(--muted); font-size: 13px; line-height: 1.45; }
/* KWB-253: the blast record. The list keeps one row's height at zero blasts so the panel
   below it does not move when the host's first blast lands. */
.broadcast-recent { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.broadcast-recent h3 { margin: 0 0 8px; }
.broadcast-recent-list { min-height: 64px; margin: 0; padding: 0; list-style: none; }
.broadcast-recent-row { min-height: 56px; padding: 8px 0; border-top: 1px solid var(--line); }
.broadcast-recent-row:first-child { border-top: 0; }
.broadcast-recent-head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 4px 12px; min-height: 20px; }
.broadcast-recent-audience { font-weight: 700; }
.broadcast-recent-meta { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.broadcast-recent-excerpt { overflow: hidden; margin: 2px 0 0; color: var(--muted); font-size: 13px; line-height: 20px; min-height: 20px; text-overflow: ellipsis; white-space: nowrap; }
.broadcast-recent-empty { display: flex; align-items: center; min-height: 56px; color: var(--muted); font-size: 13px; }
.broadcast-recent-more { min-height: 18px; margin: 8px 0 0; color: var(--muted); font-size: 12px; line-height: 18px; }
.broadcast-recent-more[data-empty="true"] { visibility: hidden; }
@media (max-width: 760px) {
  .host-manage-layout { grid-template-columns: minmax(0, 1fr); }
  .host-manage-card { width: 230px; }
}
/* KWB-263: phones keep the full explanatory copy (operator ruling, KWB-180 §10.0), so the
   Blast hint reserves the three lines that copy actually wraps to at the 360px floor
   (288px of content). 3 × 17px line-height, no padding, no border, box-sizing: border-box.
   Scoped to this slot rather than .hint-slot, which every other hint in the app shares. */
@media (max-width: 620px) {
  .broadcast-actions .hint-slot { min-height: 51px; }
}
@media (max-width: 530px) {
  .host-page { padding-right: 18px; padding-left: 18px; }
  .host-event-row { grid-template-columns: 96px minmax(0, 1fr); gap: 12px; }
  .host-event-card { width: 96px; }
  .host-stat-grid { gap: 8px; }
  .host-stat { flex-basis: calc(50% - 8px); }
  .host-stat-value { font-size: 22px; }
  .host-guest-row { grid-template-columns: minmax(0, 1fr) minmax(84px, auto); gap: 8px; }
  .host-guest-side { min-width: 84px; }
  .host-potluck-row { grid-template-columns: minmax(0, 1fr) minmax(88px, auto); gap: 8px; }
  .host-potluck-count { min-width: 88px; }
  .host-control-slot { width: min(var(--control-width-lg), 100%); min-width: min(var(--control-width-lg), 100%); }
  /* KWB-253: narrow enough that the time would squeeze the audience label — let it wrap
     onto its own line instead; the row keeps its reserved height either way. */
  .broadcast-recent-meta { white-space: normal; }
}

/* KWB-291: the Manage roster's reply control. Every row's side column is one fixed width, so a
   chip that grows from "No reply yet" to "Declined · marked by you" never narrows the name. */
[data-reply-controls="true"] .host-guest-list .host-guest-row { grid-template-columns: minmax(0, 1fr) 184px; }
[data-reply-controls="true"] .host-guest-list .host-guest-side { width: 184px; min-width: 184px; }
/* The side column is a flex column, so the wrapper's flex basis would be its height: pin
   the box by width and height instead. */
.host-guest-list .guest-reply-actions { display: flex; align-items: center; justify-content: flex-end; flex: none; width: 140px; height: 44px; min-height: 44px; }
.host-guest-list .guest-reply-actions .btn { flex: 0 0 140px; width: 140px; min-width: 140px; white-space: nowrap; }
.host-guest-list .guest-reply-actions .btn[aria-expanded="true"] { border-color: var(--accent); background: var(--accent-soft); }
.host-guest-list .guest-reply-form { grid-column: 1 / -1; min-width: 0; margin-top: 8px; padding: 10px 12px; border-radius: var(--radius-control); background: var(--bg); }
.host-guest-list .guest-reply-lead { min-height: 36px; margin: 0 0 8px; color: var(--muted); font-size: 12.5px; line-height: 18px; overflow-wrap: anywhere; }
.host-guest-list .guest-reply-lead[data-empty="true"] { visibility: hidden; }
.host-guest-list .guest-reply-notice { min-height: 36px; margin: 0 0 8px; color: var(--accent-dark); font-size: 12.5px; line-height: 18px; overflow-wrap: anywhere; }
.host-guest-list .guest-reply-notice[data-empty="true"] { visibility: hidden; }
.host-guest-list .reply-response { min-width: 0; margin: 0 0 8px; padding: 0; border: 0; }
.host-guest-list .reply-response legend { margin-bottom: 4px; color: var(--muted); font-size: 10px; font-weight: 800; }
.host-guest-list .reply-response-segmented { display: flex; flex-wrap: wrap; gap: 6px; min-height: 44px; }
.host-guest-list .reply-response-segment { flex: 0 0 96px; width: 96px; min-height: 44px; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); color: inherit; font: inherit; font-size: 12px; font-weight: 750; cursor: pointer; }
.host-guest-list .reply-response-segment[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.host-guest-list .reply-response-segment:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* Party size only for Coming or Maybe. Hidden, not removed, so choosing Not coming never
   lifts Clear, Cancel and Save. */
.host-guest-list .reply-party { display: flex; flex-wrap: wrap; gap: 10px; min-height: 66px; }
.host-guest-list .reply-party[data-empty="true"] { visibility: hidden; }
/* KWB-330: who the guest is bringing, only on an event that asks. Reserved like the party
   block and shown only when the party has someone extra, so Coming, Not coming and the
   steppers never lift Clear, Cancel and Save. */
.host-guest-list .reply-names { display: grid; align-content: start; gap: 4px; min-height: 66px; margin-top: 8px; }
.host-guest-list .reply-names[data-empty="true"] { visibility: hidden; }
.host-guest-list .reply-names-label { color: var(--muted); font-size: 10px; font-weight: 800; }
.host-guest-list .reply-names input { width: 100%; max-width: 420px; min-height: 44px; box-sizing: border-box; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); color: var(--ink); font: inherit; font-size: 16px; }
.host-guest-list .reply-stepper { display: grid; gap: 4px; }
.host-guest-list .reply-stepper-label { color: var(--muted); font-size: 10px; font-weight: 800; }
.host-guest-list .reply-stepper-controls { display: inline-grid; grid-template-columns: 44px minmax(3ch, 1fr) 44px; align-items: center; width: 124px; min-height: 44px; border: 1px solid var(--line); border-radius: var(--radius-control); overflow: hidden; }
.host-guest-list .reply-stepper button { width: 44px; height: 44px; border: 0; background: var(--surface); color: inherit; font: inherit; font-size: 20px; line-height: 1; cursor: pointer; }
.host-guest-list .reply-stepper button:disabled { color: var(--muted); cursor: not-allowed; opacity: .58; }
.host-guest-list .reply-stepper output { min-width: 3ch; font-variant-numeric: tabular-nums; text-align: center; }
.host-guest-list .guest-reply-form .inline-edit-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.host-guest-list .guest-reply-form .inline-edit-actions .error-slot { flex: 1 1 220px; min-width: 0; margin-top: 0; overflow-wrap: anywhere; }
/* The Clear and Save labels both swap in place; fixed widths reserve the longer copy. */
.host-guest-list .guest-reply-form .reply-clear, .host-guest-list .guest-reply-form .reply-save { flex: 0 0 156px; width: 156px; min-width: 156px; white-space: nowrap; }
@media (max-width: 530px) {
  [data-reply-controls="true"] .host-guest-list .host-guest-row { grid-template-columns: minmax(0, 1fr) 140px; }
  [data-reply-controls="true"] .host-guest-list .host-guest-side { width: 140px; min-width: 140px; }
  /* 140px cannot hold the longest chip on one line, so the chip wraps inside a slot that
     always reserves two lines — a longer status never pushes the button down. */
  [data-reply-controls="true"] .host-guest-list .host-guest-status { display: flex; justify-content: flex-end; min-height: 36px; }
  [data-reply-controls="true"] .host-guest-list .host-guest-status .chip { height: auto; min-height: 22px; padding-top: 3px; padding-bottom: 3px; line-height: 14px; white-space: normal; text-align: right; }
}


.host-invite-intro { margin-bottom: 12px; color: var(--muted); font-size: 13px; }
.host-invite-form { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.host-invite-form .host-invite-input { flex: 1 1 200px; min-width: 0; width: auto; }
.host-invite-form .host-invite-submit { min-height: 44px; }
/* Three reserved lines: the longest outcome sentence wraps to three on a 360px phone. */
.host-invite-status {
  min-height: 60px;
  margin-top: 4px;
  font-size: 13px;
  line-height: 20px;
  overflow-wrap: anywhere;
}
.host-invite-status[data-empty="true"] { visibility: hidden; }
.host-invite-list-heading { margin: 12px 0 8px; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.host-invite-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 13px; }
.host-invite-table th {
  padding: 6px 8px 6px 0;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-align: left;
  text-transform: uppercase;
}
.host-invite-table td { padding: 8px 8px 8px 0; border-bottom: 1px solid var(--line); vertical-align: top; overflow-wrap: anywhere; }
.host-invite-table .host-invite-col-date { width: 96px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.host-invite-table .host-invite-col-status { width: 104px; padding-right: 0; }
.host-invite-table td.host-invite-col-status[data-consumed="true"] { color: var(--accent-dark); }
.host-invite-table td.host-invite-col-status[data-consumed="false"] { color: var(--muted); }
.host-invite-empty td { color: var(--muted); }


.outbox-page { max-width: min(1080px, 100%); }
.outbox-nav { min-height: 44px; margin-bottom: 12px; }
.outbox-page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.outbox-page-head > div { min-width: 0; }
.outbox-page-head .page-subtitle { margin-bottom: 0; }
.outbox-private-note { padding-top: 8px; color: var(--muted); font-size: 12px; white-space: nowrap; }
.outbox-tabs { display: flex; flex-wrap: wrap; gap: 6px; min-height: 44px; margin-bottom: 12px; }
.outbox-tab {
  display: inline-flex;
  width: var(--control-width-md);
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0 12px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
}
.outbox-tab:hover, .outbox-tab[aria-current="page"] { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-dark); }
.outbox-panel { min-width: 0; padding: 0; overflow: hidden; }
.outbox-email-layout, .outbox-email-list, .outbox-email-detail { min-height: 560px; }
.outbox-email-list { min-width: 0; }
.outbox-email-row {
  display: grid;
  grid-template-columns: minmax(180px, 1.05fr) minmax(200px, 1.55fr) minmax(190px, 1fr);
  gap: 14px;
  align-items: center;
  width: 100%;
  min-height: 94px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  text-align: left;
  text-decoration: none;
}
.outbox-email-row:hover { background: #f8faf8; }
.outbox-email-sender, .outbox-email-subject, .outbox-recipient { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.outbox-email-sender strong, .outbox-email-subject strong, .outbox-recipient strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.outbox-email-to, .outbox-recipient-address { overflow-wrap: anywhere; color: var(--muted); font-size: 12px; }
.outbox-email-subject strong { font-size: 14px; }
.outbox-email-badges, .outbox-detail-badges, .outbox-message-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.outbox-email-record { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; min-width: 0; }
.outbox-email-time, .outbox-detail-time, .outbox-message-time { color: var(--muted); font-size: 12px; white-space: nowrap; }
.outbox-email-status-copy, .outbox-status-copy { color: var(--muted); font-size: 12px; line-height: 1.35; }
.outbox-email-status-copy { flex-basis: 100%; text-align: right; }
.outbox-kind-chip { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-dark); }
.outbox-status-queued, .outbox-status-sending, .outbox-status-deferred { color: var(--maybe); }
.outbox-status-sent, .outbox-status-delivered { color: var(--yes); }
.outbox-status-bounced, .outbox-status-complained, .outbox-status-failed { color: var(--no-dark); }
.outbox-status-suppressed, .outbox-status-cancelled, .outbox-status-unknown { color: var(--muted); }
.outbox-email-detail { max-width: 760px; margin: 0 auto; padding: 22px 28px 28px; }
.outbox-detail-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 20px; }
.outbox-detail-top .link { flex: 0 0 auto; }
.outbox-detail-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 7px; color: var(--muted); font-size: 12px; text-align: right; }
.outbox-mail-head { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 3px 10px; margin-bottom: 18px; color: var(--muted); font-size: 13px; }
.outbox-mail-head strong, .outbox-mail-head .outbox-recipient-name { color: var(--ink); }
.outbox-mail-label { color: var(--faint); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }
.outbox-mail-subject { margin-bottom: 12px; overflow-wrap: anywhere; }
.outbox-mail-status { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-height: 30px; margin-bottom: 15px; }
.outbox-mail-body { padding-bottom: 18px; color: var(--muted); font-size: 14px; line-height: 1.65; white-space: pre-line; overflow-wrap: anywhere; }
.outbox-card-preview { width: 250px; max-width: 100%; margin: 2px auto 18px; }
.outbox-honest-footer { padding-top: 15px; border-top: 1px solid var(--line); color: var(--faint); font-size: 11.5px; text-align: center; }

/* The phone frame deliberately keeps the alpha's preview silhouette. */
.outbox-text-frame { width: min(440px, 100%); min-height: 560px; margin: 26px auto; border: 8px solid #292725; border-radius: 35px; background: #f6f5f4; box-shadow: var(--shadow-lift); overflow: hidden; }
.outbox-text-notch { width: 112px; height: 22px; margin: 0 auto -22px; position: relative; z-index: 2; border-radius: 0 0 14px 14px; background: #292725; }
.outbox-text-bar { display: flex; height: 63px; align-items: center; justify-content: space-between; padding: 28px 18px 9px; border-bottom: 1px solid #dfddda; background: rgba(255, 255, 255, .75); color: #282624; font-size: 13px; }
.outbox-text-bar span { color: #4b83c7; font-size: 22px; line-height: 1; }
.outbox-text-thread { display: flex; min-height: 490px; flex-direction: column; justify-content: flex-end; gap: 16px; padding: 22px 16px 28px; }
.outbox-text-message { align-self: flex-start; max-width: 96%; }
.outbox-message-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 5px 9px; color: #8b8884; font-size: 10px; }
.outbox-message-meta strong { color: #595754; }
.outbox-message-to { max-width: 100%; overflow-wrap: anywhere; color: #595754; }
.outbox-message-badges .chip { font-size: 10px; }
.outbox-message-status-copy { flex-basis: 100%; margin-left: 9px; color: #716a62; line-height: 1.35; }
.outbox-text-bubble { padding: 10px 13px; border-radius: 18px 18px 18px 5px; background: #e4e3e1; color: #2e2c29; font-size: 13px; line-height: 1.45; overflow-wrap: anywhere; }
.outbox-text-empty { display: flex; min-height: 560px; align-items: center; }
@media (max-width: 760px) {
  .outbox-page-head { flex-direction: column; gap: 4px; }
  .outbox-private-note { padding-top: 0; }
  .outbox-email-row { grid-template-columns: minmax(0, 1fr) minmax(170px, .9fr); gap: 8px 12px; }
  .outbox-email-sender { grid-column: 1 / -1; }
  .outbox-email-record { justify-content: flex-start; }
}
@media (max-width: 530px) {
  .outbox-page { padding-right: 18px; padding-left: 18px; }
  .outbox-email-row { grid-template-columns: minmax(0, 1fr); min-height: 0; padding: 14px 16px; }
  .outbox-email-sender, .outbox-email-subject, .outbox-email-record { grid-column: auto; }
  .outbox-email-record { justify-content: flex-start; }
  .outbox-email-time { white-space: normal; }
  .outbox-email-detail { padding-right: 18px; padding-left: 18px; }
  .outbox-detail-top { flex-direction: column; }
  .outbox-detail-meta { align-items: flex-start; text-align: left; }
}
