/* ============================================================================
   COMPONENTS — Generic, reusable UI components
   Every component renders from config data, never hardcoded content.
   ============================================================================ */

/* ── Buttons ────────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  /* Bumped vertical padding from md → base so every button (.btn--primary,
     --secondary, --text, --destructive) gets the same taller hit target.
     One change here propagates to every button across the app. */
  padding: var(--space-base) var(--space-xl);
  font-family: var(--font-ui);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all var(--transition-fast);
  text-decoration: none;
  border: 2px solid transparent;
  line-height: 1;
}

.btn--primary {
  background: var(--nyc-red);
  color: var(--text-inverse);
  border-color: var(--nyc-red);
}
.btn--primary:hover {
  background: var(--nyc-red-hover);
  border-color: var(--nyc-red-hover);
  text-decoration: none;
  color: var(--text-inverse);
}

.btn--secondary {
  background: var(--surface-white);
  color: var(--nyc-red);
  border-color: var(--nyc-red);
}
.btn--secondary:hover {
  background: var(--nyc-red-bg);
}

.btn--full-width {
  width: 100%;
  justify-content: center;
}

.btn--destructive {
  background: var(--status-denied-bg);
  color: var(--status-denied-text);
  border-color: var(--status-denied-text);
}

.btn--text {
  background: none;
  border: none;
  color: var(--nyc-red);
  /* Match the new .btn vertical padding so Cancel (text variant) sits
     at the same height as Save (primary variant) when they share a
     button-bar row. */
  padding: var(--space-base) 0;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
}
.btn--text:hover {
  text-decoration: underline;
}
/* Destructive text button — for "Discard entry" / cancel-entry actions on the
   add-entry sub-forms (family, jobs, providers, income). Reads error-red, not
   the primary link color, so throwing away the in-progress entry is visually
   flagged as destructive. (NYC pins this to its true red — see tokens-nyc.css —
   because there --nyc-red is Blue 40, not red.) */
.btn--text-danger {
  color: var(--alert-error-text);
}
.btn--text-danger:hover {
  color: var(--alert-error-text);
}

/* ── Icon Link ─────────────────────────────────────────────────────────────── */
/* Text-style action with optional Material Symbol icon. Always flush-left
   (zero horizontal padding) so it aligns with the card edge it lives in.
   Size controls font, icon glyph size, and the icon↔label gap.
   icon-position is handled via flex-direction so the gap stays consistent. */
.icon-link {
  display: inline-flex;
  align-items: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--nyc-red);
  font-family: var(--font-ui);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
  padding: var(--space-md) 0;       /* vertical click target, zero horizontal */
  text-decoration: none;
}
/* Inner spans inherit cursor from the button by spec, but Material Symbols
   font behavior + some browsers' button-default cursor can mask it on the
   icon glyph and the label. Setting cursor: pointer explicitly on the
   children guarantees the hand shows everywhere inside the click target,
   regardless of which child is being hovered. */
.icon-link__icon,
.icon-link__label {
  cursor: pointer;
}
/* Only the text label gets the hover underline — not the icon glyph.
   The icon's baseline doesn't sit on the underline, so including it makes
   the rule look broken (gap or floating line under the icon). */
.icon-link:hover { text-decoration: none; }
.icon-link:hover .icon-link__label { text-decoration: underline; }
.icon-link:disabled,
.icon-link:disabled .icon-link__icon,
.icon-link:disabled .icon-link__label {
  opacity: 0.5;
  cursor: not-allowed;
}

.icon-link__icon {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  font-variation-settings: 'FILL' 0;
}

/* Icon position — left is the natural DOM order; right reverses via flex. */
.icon-link--icon-left  { flex-direction: row; }
.icon-link--icon-right { flex-direction: row; }  /* label rendered first by JS */

/* Sizes — tune font, icon glyph, and gap together. */
/* Icon-link size scale — aligned to the button + type scale so a
   text-style action ("Remove", "Undo") sits at a familiar size next
   to its sibling buttons. Three steps: sm (helper text), md (matches
   .btn--md), lg (matches .btn default = large). */
.icon-link--sm { font-size: var(--font-size-xs); gap: var(--space-xs); }
.icon-link--sm .icon-link__icon { font-size: var(--font-size-sm); }

.icon-link--md { font-size: var(--font-size-sm); gap: var(--space-xs); }
.icon-link--md .icon-link__icon { font-size: var(--font-size-base); }

.icon-link--lg { font-size: var(--font-size-base); gap: var(--space-xs); }
.icon-link--lg .icon-link__icon { font-size: var(--font-size-lg); }

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ── Button size scale ──────────────────────────────────────────────────────
   Two sizes, one step apart. Both share the family's color + radius +
   letter-spacing rules above; only padding + type size change. Used as
   a modifier alongside any variant (.btn--primary.btn--md, etc.).

   .btn       (default, "large") — page-level primary actions: wizard
              Next/Previous, modal Confirm, form Submit. 16px font, 16/24
              padding. Same proportions as the .btn rules above (no
              modifier needed; this is the base class).

   .btn--md   ("medium") — card-level actions: dashboard CTAs, application-
              card actions, secondary inline buttons. Drops ONE step on
              font + padding so the button reads as the action of a card
              rather than the action of the page.

   No tier smaller than --md ships; if a surface needs an action smaller
   than this, it should use a text link (renderIconLink) instead — a
   button below md reads as an icon, not a button. */
.btn--md {
  padding: var(--space-sm) var(--space-lg);
  font-size: var(--font-size-sm);
}

/* Small leading icon for .btn--md (Material Symbols). Scales with the
   medium font tier so the icon sits proportional to the label text. */
.btn__icon-sm {
  font-size: var(--font-size-base);
  vertical-align: middle;
  margin-right: var(--space-xs);
}

/* Small trailing icon for external/navigating links (Material Symbols). */
.btn__icon-ext {
  font-size: var(--font-size-base);
  vertical-align: middle;
  margin-left: var(--space-xs);
}

/* External icon for inline text links. */
.link__icon-ext {
  font-size: 0.85em !important;
  vertical-align: middle;
  margin-left: 2px;
}



/* ── Status Pills ───────────────────────────────────────────────────────────── */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.03em;
  white-space: nowrap;
}

/* All status pills follow the outlined-chip pattern: pale tinted bg + darker
   family text + visible border in same family. DENIED is the destructive
   exception — solid red with white text — because the decision is adverse
   and final. The deadline-pill component uses the identical pattern (see
   .deadline-pill--soft / .deadline-pill--hard) so status pills + deadline
   pills read as one design system. */
.pill--verified    { background: var(--status-verified-bg);    color: var(--status-verified-text);    border: 1px solid var(--status-verified-border); }
.pill--checking    { background: var(--status-checking-bg);    color: var(--status-checking-text);    border: 1px solid var(--status-checking-border); }
.pill--progress    { background: var(--status-progress-bg);    color: var(--status-progress-text);    border: 1px solid var(--status-progress-border); }
.pill--reviews     { background: var(--status-reviews-bg);     color: var(--status-reviews-text);     border: 1px solid var(--status-reviews-border); }
.pill--draft         { background: var(--status-draft-bg);         color: var(--status-draft-text);         border: 1px solid var(--status-draft-border); }
.pill--draft-expired { background: var(--status-draft-expired-bg); color: var(--status-draft-expired-text); border: 1px solid var(--status-draft-expired-border); }
.pill--in-process    { background: var(--status-in-process-bg);    color: var(--status-in-process-text);    border: 1px solid var(--status-in-process-border); }
.pill--approved      { background: var(--status-approved-bg);      color: var(--status-approved-text);      border: 1px solid var(--status-approved-border); }
.pill--waitlisted    { background: var(--status-waitlisted-bg);    color: var(--status-waitlisted-text);    border: 1px solid var(--status-waitlisted-border); }
/* DENIED — destructive variant. Solid family color, white text. No outer
   border (the solid fill is the boundary). */
.pill--denied        { background: var(--status-denied-bg);        color: var(--status-denied-text);        border: 1px solid var(--status-denied-border); }

/* Layout-only helper: when used inside a flex row (e.g. app-card__pills),
   the container's gap handles spacing — no extra margin needed. */
.pill--inline-after-title { margin-left: 0; }

/* Dot indicator inside pills */
.pill__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: pulse-dot 1.5s ease-in-out infinite;
}

@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

/* ── Cards (generic) ────────────────────────────────────────────────────────── */
.card {
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-xl);
}

.card__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  margin-bottom: var(--space-md);
}

.card__body {
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  line-height: var(--line-height-relaxed);
}

/* ── Outcome / Status Cards ────────────────────────────────────────────────── */
.card--outcome {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 480px;
  gap: var(--space-md);
}

.card--outcome .card__icon {
  font-size: 56px;
  color: var(--text-primary);
  font-variation-settings: 'FILL' 1;
  margin-bottom: var(--space-xs);
}

.card--outcome .card__icon--error {
  color: var(--nyc-red);
  font-variation-settings: 'FILL' 0;
}

.card--outcome .card__title {
  font-size: var(--font-size-xl);
  margin: 0;
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
}

.card--outcome .card__title--error {
  color: var(--nyc-red);
}

.card--outcome .card__description {
  color: var(--text-secondary);
  line-height: var(--line-height-relaxed);
  max-width: 580px;
  margin: 0;
  font-size: var(--font-size-sm);
  font-family: var(--font-body);
}

.card--outcome .card__description--large {
  font-size: var(--font-size-md);
  max-width: 640px;
}

.card--outcome .card__link {
  color: var(--nyc-red);
  text-decoration: underline;
  font-family: var(--font-heading);
  font-weight: var(--font-weight-semibold);
}

.card__actions-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  align-items: center;
  margin-top: var(--space-lg);
  width: 100%;
}

/* ── Voucher Card ───────────────────────────────────────────────────────────── */
.voucher-display-card {
  background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
  color: var(--text-inverse);
  padding: var(--space-lg);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-lg);
  box-shadow: var(--shadow-lg);
  width: 100%;
  text-align: left;
}

.voucher-display-card__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  padding-bottom: var(--space-xs);
  margin-bottom: var(--space-md);
}

.voucher-display-card__title {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  letter-spacing: 0.5px;
}

.voucher-display-card__icon {
  font-size: 28px;
}

.voucher-display-card__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md);
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
}

.voucher-display-card__field-label {
  opacity: 0.7;
  font-size: var(--font-size-xs);
}

.voucher-display-card__field-value {
  font-weight: var(--font-weight-bold);
}

.voucher-display-card__field-value--accent {
  font-weight: var(--font-weight-bold);
  color: #86efac;
}

.voucher-display-card__field-full {
  grid-column: 1 / -1;
}

/* Recertification Case Verified Success styling (Phase 7) */
.recert-verified-summary {
  text-align: left;
  width: 100%;
  margin-top: var(--space-lg);
  padding-bottom: var(--space-lg);
  border-bottom: 1px solid var(--border-light);
}

.recert-verified-summary__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md);
  background: var(--panel-info-bg);
  padding: var(--space-md);
  border-radius: var(--radius-lg);
  border: 1px solid var(--panel-info-accent);
  margin-top: var(--space-sm);
}

.recert-verified-summary__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.recert-verified-summary__item--full {
  grid-column: 1 / -1;
}

.recert-verified-summary__label {
  font-family: var(--font-heading);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
}

.recert-verified-summary__value {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--text-primary);
}

.recert-verified-details {
  text-align: left;
  width: 100%;
  margin-top: var(--space-lg);
}

.recert-verified-details__heading {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-bold);
  margin-bottom: var(--space-xs);
  color: var(--text-primary);
}

.recert-verified-details__text {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-relaxed);
  margin-bottom: 0;
}

/* ── Case Validation Form Styles ────────────────────────────────────────────── */
.case-validation-form {
  margin-top: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.form-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.form-section--flex-1 {
  flex: 1;
}

.form-grid-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md);
}

.info-link-container {
  margin-top: var(--space-sm);
}

.info-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-sm);
  color: var(--text-link);
  text-decoration: none;
  font-family: var(--font-heading);
  font-weight: var(--font-weight-medium);
}

.info-link__icon {
  font-size: 18px;
  color: var(--text-link);
  text-decoration: none;
}

.info-link__text {
  text-decoration: underline;
}

.info-link:hover {
  text-decoration: none;
}

.info-link:hover .info-link__text {
  color: var(--text-link-hover);
}

/* Recertification modal custom styles (Phase 7) */
.modal__intro-text {
  margin-bottom: var(--space-md);
  line-height: var(--line-height-relaxed);
}

.modal__checklist-block {
  margin-bottom: var(--space-md);
  text-align: left;
  padding: var(--space-sm) var(--space-md);
}

.modal__checklist-title {
  margin: 0 0 var(--space-2xs) 0 !important;
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
}

.modal__checklist-list {
  margin: 0;
  padding-left: 20px;
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
  color: var(--text-secondary);
}

.modal__footnote-text {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  line-height: var(--line-height-normal);
  text-align: left;
}

/* Recertification page styles */
.recertify-footnote {
  margin-top: var(--space-md);
}

.recertify-actions {
  display: flex;
  gap: var(--space-md);
  margin-top: var(--space-xl);
  flex-wrap: wrap;
}

.dash-block--deadline-hero {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.dash-block--deadline-hero__content {
  flex: 1;
}

.dash-block--deadline-hero__title {
  margin-bottom: var(--space-xs) !important;
}

.dash-block--deadline-hero__body {
  margin: 0 !important;
}

/* ── Application Card ───────────────────────────────────────────────────────── */
.app-card {
  background: var(--surface-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-base);
}

.app-card__header {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  margin-bottom: var(--space-base);
}

.app-card__title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 1;
  gap: var(--space-md);
}

.app-card__pills {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  flex-shrink: 0;
}

.app-card__icon {
  width: 40px;
  height: 40px;
  background: var(--surface-muted);
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.app-card__meta {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
  margin-bottom: var(--space-base);
  font-family: var(--font-ui);
  font-size: var(--font-size-xs);
}

.app-card__meta-label {
  color: var(--text-muted);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
}

.app-card__actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-light);
}

.app-card__actions-left,
.app-card__actions-right {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

/* ── CTA Card (right column) ────────────────────────────────────────────────── */
.cta-card {
  background: var(--surface-card);
  padding: var(--space-xl);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

.cta-card__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  margin-bottom: var(--space-md);
}

.cta-card__body {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  margin-bottom: var(--space-base);
  line-height: var(--line-height-relaxed);
}

/* ── Sidebar Nav (dashboard) ────────────────────────────────────────────────── */
.sidebar-nav {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.sidebar-nav__item {
  margin: 0;
}

.sidebar-nav__divider {
  border-bottom: 1px solid var(--border-light);
  margin: var(--space-sm) 0;
}

.sidebar-nav__link {
  display: flex;
  align-items: center;
  font-family: var(--font-ui);
  gap: var(--space-md);
  padding: var(--space-lg) var(--space-lg);
  color: var(--text-primary);
  text-decoration: none;
  font-size: var(--font-size-sm);
  /* Inactive nav items sit at --font-weight-semibold (600). Bumped up
     from medium (500) so the labels carry enough visual weight against
     the pale page gradient — at 500 they read as helper text instead
     of nav. The active state still steps up further to bold (700)
     below, so the active/inactive hierarchy is preserved. */
  font-weight: var(--font-weight-semibold);
  background: rgba(255, 255, 255, 0.45);
  border-radius: var(--radius-md);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.sidebar-nav__link:hover {
  background: rgba(255, 255, 255, 0.7);
  color: var(--nyc-red);
  text-decoration: none;
}

.sidebar-nav__link:hover .sidebar-nav__icon {
  color: var(--nyc-red);
}

.sidebar-nav__link.is-active {
  color: var(--nyc-red);
  font-weight: var(--font-weight-bold);
  background: rgba(255, 255, 255, 0.8);
}

.sidebar-nav__link.is-active .sidebar-nav__icon {
  color: var(--nyc-red);
}

.sidebar-nav__icon {
  font-size: 22px;
  width: 24px;
  text-align: center;
  color: var(--text-primary);
  flex-shrink: 0;
  font-variation-settings: 'FILL' 1;
}

.sidebar-nav__label {
  flex: 1;
}

/* ── Stepper (wizard sidebar) ───────────────────────────────────────────────── */
.stepper {
  list-style: none;
  padding: 0;
  margin: 0;
  position: relative;
}

.stepper__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  /* Vertical breathing room between steps. Bumped --space-sm (8px) →
     --space-base (16px) so multi-line step labels don't crowd each other.
     If you change this, also update the connector-line top/bottom below
     so the line still meets the next dot center. */
  padding: var(--space-base) 0;
  position: relative;
  margin: 0;
}

/* ── Connector line — runs center-to-center, dots overlay on top ── */
.stepper__item:not(:last-child)::after {
  content: '';
  position: absolute;
  /* Horizontally centered on the 22px dot: (22-2)/2 = 10px */
  left: 10px;
  /* Start at this item's dot center: padding-top(16px) + half-dot(11px) = 27px */
  top: 27px;
  /* End at next item's dot center: same offset below this item's bottom edge
     (next item's padding-top 16px + half-dot 11px = 27px into next item) */
  bottom: -27px;
  width: 2px;
  background: var(--stepper-line-current);
  z-index: 1;
}

/* Completed items get green line */
.stepper__item.is-completed:not(:last-child)::after {
  background: var(--stepper-line);
}

/* Current step gets the red/pink line */
.stepper__item.is-current:not(:last-child)::after {
  background: var(--stepper-line-current);
}

/* ── Dot container (all states share same 22px bounding box) ── */
.stepper__dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 2;
  box-sizing: border-box;
}

/* ── Completed: green filled circle with white checkmark ── */
.stepper__item.is-completed .stepper__dot {
  background: var(--stepper-completed);
  color: var(--text-inverse);
}

.stepper__dot .stepper__check {
  display: none;
}

.stepper__item.is-completed .stepper__dot .stepper__check {
  display: block;
  width: 14px;
  height: 14px;
  fill: white;
}

/* ── Current: solid red/pink filled circle ── */
.stepper__item.is-current .stepper__dot {
  background: var(--stepper-current);
}

/* ── Future: small hollow circle with thin red/pink border ── */
.stepper__item.is-future .stepper__dot {
  background: var(--surface-white);
  border: 1.5px solid var(--stepper-current);
}

/* ── Labels ── */
.stepper__label {
  font-family: var(--font-ui);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-tight);
  padding-top: 2px;
}

.stepper__item.is-current .stepper__label {
  font-weight: var(--font-weight-bold);
}

.stepper__item.is-future .stepper__label {
  color: var(--text-muted);
}

/* ── Modal (generic) ────────────────────────────────────────────────────────── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: var(--z-modal-backdrop);
  opacity: 0;
  visibility: hidden;
  transition: all var(--transition-base);
}

.modal-overlay.is-visible {
  opacity: 1;
  visibility: visible;
}

.modal {
  background: var(--surface-white);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-modal);
  max-width: 520px;
  width: 90%;
  padding: var(--space-2xl);
  transform: translateY(20px) scale(0.97);
  transition: transform var(--transition-base);
  /* Cap the dialog height so long content (e.g. the full consent statement)
     doesn't make the whole modal tall — the body scrolls internally instead
     while the title + actions stay pinned. border-box so the 85vh cap counts
     the padding (otherwise the padding adds on top of the cap). */
  box-sizing: border-box;
  /* min(85vh, 680px): 85vh keeps it on-screen on short viewports, and the
     680px ceiling keeps it a moderate size on tall screens (where 85vh would
     still be huge) — the body scrolls past that point. */
  max-height: min(85vh, 680px);
  display: flex;
  flex-direction: column;
}

@media (min-width: 769px) {
  .modal {
    max-width: 680px;
  }
}

.modal-overlay.is-visible .modal {
  transform: translateY(0) scale(1);
}

.modal__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  margin-bottom: var(--space-base);
  text-transform: none;
}

.modal__body {
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  line-height: var(--line-height-relaxed);
  margin-bottom: var(--space-xl);
  /* Scroll the body, not the whole dialog, when content overflows the capped
     height. min-height:0 lets this flex child actually shrink so it can scroll. */
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
}

/* When a modal has 2+ actions, push the first (dismiss/secondary) to the
   extreme left so it visually separates from the primary action on the right.
   Single-action modals (e.g., scan-failure with just "OK, Continue") are
   unaffected — they stay right-aligned via flex-end. */
.modal__actions > button:first-child:not(:only-child) {
  margin-right: auto;
}

/* ── Alert Banner ───────────────────────────────────────────────────────────── */
.alert-banner {
  padding: var(--space-lg);
  border-radius: var(--radius-md);
  border-left: 4px solid;
  margin-bottom: var(--space-xl);
}

.alert-banner--warning {
  background: var(--alert-warning-bg);
  border-color: var(--alert-warning-border);
  color: var(--alert-warning-text);
}

.alert-banner--info {
  background: var(--alert-info-bg);
  border-color: var(--alert-info-border);
  color: var(--alert-info-text);
}

.alert-banner--error {
  background: var(--alert-error-bg);
  border-color: var(--alert-error-border);
  color: var(--alert-error-text);
}

/* Stronger error tone — used by the two severe dashboard banners
   (DRAFT_EXPIRED and DENIED). Same border + text accents as
   .alert-banner--error; the fill switches to --page-pink-deep so
   these banners visibly separate from the gradient page background
   when they sit at the top of the pinkest portion of the page.
   We share the deeper pink token with the header chrome rather than
   maintaining a second darker error token — both surfaces solve the
   same "stand out from the pink page" problem and a single token
   keeps the system from drifting. Regular in-card error surfaces
   stay on --panel-error-bg. */
.alert-banner--error-strong {
  background: var(--page-pink-deep);
  border-color: var(--alert-error-border);
  color: var(--alert-error-text);
}

.alert-banner--success {
  background: var(--alert-success-bg);
  border-color: var(--alert-success-border);
  color: var(--alert-success-text);
}

/* Neutral grey — for informational recaps that must NOT read as a positive
   outcome (e.g. "we carried your answers over"). Uses the muted surface so it
   reads as context, not a success/warning state. */
.alert-banner--neutral {
  background: var(--surface-muted);
  border-color: var(--border-medium);
  color: var(--text-primary);
}

/* ── Status Banner (alert-banner + structured content) ──────────────────────
   Adds a title row, body paragraph, primary CTA, and a collapsible "Learn
   more" section to the base alert-banner. Used on dashboard + my-applications
   to give the user a state-specific summary with a clear next action. */
.alert-banner--with-icon {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-md);
  align-items: start;
}
.status-banner__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  color: inherit;
  padding-top: 2px;
}
.status-banner__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-bold);
  margin: 0 0 var(--space-xs);
  /* Body-default black for readability and consistency with the
     .status-banner__body below. The parent .alert-banner--{variant}
     sets a tinted text color for the icon (via color: inherit on
     .status-banner__icon), but the title + body read as document
     text, not status text. */
  color: var(--text-primary);
}
.status-banner__body {
  margin: 0 0 var(--space-md);
  /* Explicit body size — don't rely on inheritance from <body>. The
     status-banner is the elevated/loud tier (dashboard application
     status), so body stays at --font-size-base while inline-card
     notifications drop to --font-size-sm. */
  font-size: var(--font-size-base);
  line-height: var(--line-height-normal);
  color: var(--text-primary);
}
.status-banner__actions {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  flex-wrap: wrap;
}
/* Supporting explanation rendered below the CTA on status banners.
   Always visible — no expand/collapse interaction. One step smaller
   than the body so it reads as supporting context rather than a
   competing primary message. Paragraphs stack with token spacing;
   no top border (the body + CTA above already provide grouping).
   The earlier <details>/<summary> "Learn more +" pattern was removed
   because the click-to-expand interaction read as friction on the
   dashboard variants (waitlisted, denied) where the extra context
   is genuinely useful and the user shouldn't have to ask for it. */
.status-banner__detail {
  margin-top: var(--space-md);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
  color: var(--text-primary);
}
.status-banner__detail p { margin: 0 0 var(--space-sm); }
.status-banner__detail p:last-child { margin-bottom: 0; }
.status-banner__detail ul {
  margin: var(--space-sm) 0;
  padding-left: var(--space-xl);
}
.status-banner__detail li { margin-bottom: var(--space-2xs); }
.status-banner__detail li:last-child { margin-bottom: 0; }

/* ── Required Note ───────────────────────────────────────────────────────────
   One-line form-meta sitting at the top of any form to establish the
   project's field-marking convention: required = no marker, optional =
   "(optional)" suffix. Muted typography so it reads as metadata, not
   content. See js/components/required-note.js. */
.required-note {
  font-family: var(--font-ui);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  margin: 0 0 var(--space-xl);
  line-height: var(--line-height-normal);
}

/* ── Entry List ──────────────────────────────────────────────────────────────
   Reusable display for multi-entry form sections (children, household
   members, jobs, providers). See js/components/entry-list.js. The
   component renders the list + add button; the parent step renders the
   inline form panel separately when needed. */
.entry-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-base);
}
.entry-list__rows {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  background: var(--surface-white);
  /* No overflow:hidden — it clipped the NYC icon-button tooltips sitting
     above the first row. Corner rounding is preserved by rounding the
     first/last rows directly (below), so the link-hover background still
     clips to the container's rounded corners. */
}
.entry-list__row {
  display: flex;
  align-items: center;
  gap: var(--space-base);
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
}
.entry-list__row:first-child {
  border-top-left-radius: var(--radius-md);
  border-top-right-radius: var(--radius-md);
}
.entry-list__row:last-child {
  border-bottom: 0;
  border-bottom-left-radius: var(--radius-md);
  border-bottom-right-radius: var(--radius-md);
}
.entry-list__row-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}
.entry-list__row-primary {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
}
.entry-list__row-secondary {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.entry-list__row-actions {
  display: flex;
  gap: var(--space-xs);
  flex-shrink: 0;
}
/* Whole-row link variant — used by the Task List hub for clickable
   task rows. Keeps every other .entry-list__row rule and only adds
   cursor + hover background + keyboard focus ring.

   Why every interaction state explicitly resets text-decoration and
   color: base.css's global `a:hover` rule sets both an underline AND
   a link-color shift on every anchor. That bleeds into the row label
   AND the status pill text inside the link wrapper (double underline
   on the label, underlined pill). The reset below is more specific
   than the global `a:hover`, so it wins. */
.entry-list__row--link,
.entry-list__row--link:link,
.entry-list__row--link:visited,
.entry-list__row--link:hover,
.entry-list__row--link:focus,
.entry-list__row--link:focus-visible,
.entry-list__row--link:active {
  text-decoration: none;
  color: inherit;
}
.entry-list__row--link {
  cursor: pointer;
  transition: background var(--transition-fast);
}
.entry-list__row--link:hover {
  background: var(--surface-hover);
}
/* Hover affordance: underline ONLY the step label, in the label's own
   color (text-primary), not the global link-hover red. The pill and
   hint copy stay untouched. text-decoration-color defaults to
   currentColor; since .entry-list__row-primary owns its color, the
   underline picks that up automatically. */
.entry-list__row--link:hover .entry-list__row-primary {
  text-decoration: underline;
}
.entry-list__row--link:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: -2px;
}
/* Locked variant — non-interactive, dimmed. The unlock-hint copy
   inside .entry-list__row-secondary explains why. */
.entry-list__row--locked {
  opacity: 0.65;
  cursor: not-allowed;
}
.entry-list__row--locked .entry-list__row-primary {
  color: var(--text-secondary);
}
.entry-list__row-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.entry-list__row-action:hover {
  background: var(--surface-hover);
  color: var(--text-primary);
}
.entry-list__row-action:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}
.entry-list__row-action--delete:hover {
  color: var(--nyc-red);
  background: var(--nyc-red-bg);
}
.entry-list__empty {
  padding: var(--space-lg);
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  /* Plain instructional copy — explicitly NOT an alert style (audit #37).
     If the section needs a "you must add at least one" prompt, that's a
     validation message rendered separately by the parent, not the empty
     state. */
}
/* Full-width affordance container — matches the visual weight of the
   empty-state above it, so the Add action reads as a permanent surface
   the user can return to rather than a fleeting link. Overrides
   icon-link's compact-button styling (background / border / padding /
   self-align) without losing its semantics (it's still a <button>
   inside, just dressed as a discoverable card).

   Vertical padding bumped to --space-xl to give the action visual weight
   proportionate to its importance — adding a child / household member /
   job is a primary task on these pages, not a passing micro-action. */
.entry-list__add {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: var(--space-xl) var(--space-lg);
  border: 1.5px dashed var(--border-medium);
  border-radius: var(--radius-md);
  /* Soft tinted resting state — a hair lighter than the hover fill
     (--nyc-red-bg), so the card reads as a gentle accent surface by
     default and deepens slightly on hover. Theme-aware: a pale blue wash
     (#F0F4FF) in the NYC theme, a pale rose wash in the CC theme. */
  background: var(--page-pink-light);
  transition: border-color var(--transition-fast), background var(--transition-fast);
  /* Bump the "+ Add child / Add household member" label up a step from the
     icon-link--md default (sm) — this is a primary action on the page, so
     it should read at body size rather than as a small micro-action. */
  font-size: var(--font-size-base);
  font-weight: 600;
}
.entry-list__add .icon-link__label { font-size: var(--font-size-base); }
.entry-list__add .icon-link__icon { font-size: var(--font-size-lg); }
.entry-list__add:hover {
  border-color: var(--nyc-red);
  border-style: solid;
  background: var(--nyc-red-bg);
}
/* Drop the icon-link's hover underline inside this container — the
   container's own border + background change already signal hover, and
   the underline would compete with that. */
.entry-list__add:hover .icon-link__label {
  text-decoration: none;
}
.entry-list__add:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}

/* ── Chip Toggle ─────────────────────────────────────────────────────────────
   Multi-select pill chips backed by hidden checkboxes (see
   js/components/chip-toggle.js). Visual state driven by :has(:checked)
   to match the project-wide convention — class-based selection bypassed
   for the same stale-state reason radios + checkboxes use this pattern. */
.chip-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.chip-group__label {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
}
.chip-group__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}
.chip-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-sm) var(--space-lg);
  border: 1.5px solid var(--border-medium);
  border-radius: var(--radius-pill);
  background: var(--surface-white);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--text-primary);
  user-select: none;
  transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}
.chip-toggle:hover {
  border-color: var(--nyc-red);
}
.chip-toggle__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0;
  height: 0;
}
.chip-toggle:has(.chip-toggle__input:checked) {
  border-color: var(--nyc-red);
  background: var(--nyc-red);
  color: var(--text-inverse);
}
.chip-toggle:has(.chip-toggle__input:focus-visible) {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}

/* ── Optional-field marker ───────────────────────────────────────────────────
   Shared class used by every form component (form-field, select-field,
   radio-group, checkbox) when caller passes `optional: true`. One rule,
   one source of truth — keeps the marker visually consistent across
   every input type. Muted color + lighter weight + smaller scale so the
   marker reads as metadata and never competes with the question itself. */
/* Required marker — hidden by default (CC marks optional, not required).
   The NYC theme reveals it as a "*" before the label (see tokens-nyc.css). */
.field-required-marker { display: none; }

.field-optional {
  font-family: var(--font-ui);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-normal);
  color: var(--text-muted);
  letter-spacing: 0;
  text-transform: none;
  /* Nudge baseline up slightly so the marker reads as a superscript-ish
     companion to the label rather than sitting awkwardly on the same
     line as bold heading-weight label text. */
  vertical-align: 1px;
}

/* ── Confirm Card ────────────────────────────────────────────────────────────
   Read-only data display surface (see js/components/confirm-card.js).
   Replaces the "render external data as editable inputs with apology
   tooltips" anti-pattern documented in the UX audit (AS8/9/10/11).

   Visual model: light surface + soft border, key/value rows in a dl,
   an interactive footer band carrying the edit-in-source action so the
   data rows themselves have zero affordance to look interactive. */
.confirm-card {
  background: var(--surface-muted);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  margin-bottom: var(--space-base);
}
/* Editable "form" variant of the card — white surface so it reads as the
   editable account cluster, distinct from the grey read-only NYC.ID card.
   Same chrome (shadow + radius) otherwise. */
.confirm-card--form {
  background: var(--surface-white);
}
.confirm-card__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 var(--space-md);
}
.confirm-card__rows {
  display: grid;
  grid-template-columns: minmax(140px, max-content) 1fr;
  column-gap: var(--space-xl);
  row-gap: var(--space-sm);
  margin: 0;
}
.confirm-card__row {
  display: contents;
}
.confirm-card__row-label {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.confirm-card__row-value {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  word-break: break-word;
}
.confirm-card__row-value--missing {
  font-style: italic;
  font-weight: var(--font-weight-normal);
  color: var(--text-muted);
}
/* Edit action band. Sits visually distinct from the data rows so the
   user reads the card as "confirm above, edit below" — there is no
   ambiguity about which surface is interactive. */
/* Single action band below the dotted divider. Holds the footer
   text (left, flex:1) and the source link / button (right, flex-
   shrink:0) on ONE row. Earlier these rendered as two separate
   stacked blocks — the link right-aligned on its own row, the
   footer prose on the line below — which read as visually
   disjointed even though both describe the same affordance. */
.confirm-card__action-row {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  margin-top: var(--space-base);
  padding-top: var(--space-md);
  border-top: 1px dotted var(--border-light);
}
.confirm-card__action-row-text {
  flex: 1 1 auto;
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-normal);
}
.confirm-card__action-row-action {
  flex-shrink: 0;
}
/* In the account FORM the edit-mode action bar (.button-bar--section) must use
   the SAME divider spacing as the read-mode action row, so the separator line
   doesn't jump/flicker when toggling between read and edit. */
.confirm-card--form .button-bar--section {
  margin-top: var(--space-base);
  padding-top: var(--space-md);
  border-top: 1px dotted var(--border-light);
}
/* Smaller Cancel (text) button in the account form's edit bar. Scoped here so
   other inline-edit bars keep their default text-button size. */
.confirm-card--form .button-bar--section .btn--text {
  font-size: var(--font-size-sm);
}
/* Narrow viewports — stack the text above the action so neither
   gets squeezed. Action stays right-aligned within its own row so
   the affordance still feels anchored to the trailing edge. */
@media (max-width: 720px) {
  .confirm-card__action-row {
    flex-direction: column;
    align-items: stretch;
  }
  .confirm-card__action-row-action {
    align-self: flex-end;
  }
}

.confirm-card__source-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--text-link);
  text-decoration: none;
}
.confirm-card__source-link:hover {
  color: var(--text-link-hover);
}
/* Underline the label span, not the flex anchor — keeps the line off the
   trailing icon (the anchor is display:inline-flex). */
.confirm-card__source-link:hover .confirm-card__source-label {
  text-decoration: underline;
}
/* Inline-action variant — used when the source slot is a <button>
   triggering an in-page action (Edit here / Add details) rather than
   navigating away. Only resets the button-specific chrome (background,
   border, padding, cursor); font properties intentionally fall through
   to the .confirm-card__source-link styles above so the button reads
   in the same heading family + weight as the anchor variant. (An
   earlier version used `font: inherit` here, which is a shorthand that
   resets every font property to inherit from the PARENT div — that
   div has no font set, so the browser fell back through to body Lora
   and the button rendered in a serif that didn't match anything else.) */
.confirm-card__source-link--action {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.confirm-card__source-icon {
  /* Render the icon inline with the label baseline, no extra weight. */
  color: currentColor;
}
/* .confirm-card__footer was the standalone block below the source
   line in the earlier two-row layout. The footer text is now rendered
   inside .confirm-card__action-row (via .confirm-card__action-row-text)
   so this standalone class is no longer emitted by the component —
   keeping the rule removed prevents stray usage from re-introducing
   the stacked layout. */

/* Inline edit mode for confirm-card. Same outer surface (border,
   padding, title); inner rows swap for grouped form fields. The
   action bar at the bottom uses renderSectionActionBar — Cancel left,
   Save right — instead of the read-mode single right-aligned link, so
   the visual transition between modes is a content swap, not a
   different layout primitive. */
.confirm-card__edit-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-base);
  margin-top: var(--space-md);
}
/* Street address (wider) + Apt # (narrower) row inside the MyCity
   edit form. Mirrors the asymmetric proportion you see on real address
   forms — full street label and value need room; apt number is a few
   digits. Collapses to a single column at narrow viewports. The
   override on .form-field drops the 320px default cap so each field
   actually fills its grid column. */
.mycity-edit__address-line {
  display: grid;
  grid-template-columns: 3fr 1fr;
  gap: var(--space-xl);
}
.mycity-edit__address-line .form-field {
  max-width: none;
  margin-bottom: 0;
}
@media (max-width: 720px) {
  .mycity-edit__address-line {
    grid-template-columns: 1fr;
    gap: var(--space-base);
  }
}

/* Single-row action band on the empty MyCity card. Mirrors the
   prompt-card pattern (content flex:1 + action flex-shrink:0) so the
   warning note and the "Add Details" button share one visual band
   instead of stacking as two separate sections. The dotted top
   border + spacing match .confirm-card__action-row so the populated
   and empty states share the same divider rhythm. Margin-top provides
   clear breathing room above the note — the earlier version let the
   note crowd directly against the SSN row above. */
.mycity-empty__action-row {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px dotted var(--border-light);
}
.mycity-empty__action-row .inline-note {
  flex: 1 1 auto;
  min-width: 0;
}
.mycity-empty__action-row .btn {
  flex-shrink: 0;
}
/* On narrow viewports there isn't enough room for the note + button
   to share one row legibly, so they stack with the button below.
   Standard responsive pattern used by .about-you__field-pair and
   the ps-referral-switched row elsewhere in the app. */
@media (max-width: 720px) {
  .mycity-empty__action-row {
    flex-direction: column;
    align-items: stretch;
  }
}

/* Mobile: collapse the two-column grid into a stacked label-over-value
   layout so the value isn't squeezed against a fixed-width label column
   at narrow viewports. */
@media (max-width: 600px) {
  .confirm-card__rows {
    grid-template-columns: 1fr;
    row-gap: var(--space-md);
  }
  .confirm-card__row {
    display: block;
  }
  .confirm-card__row-label {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: var(--space-3xs);
  }
}

/* Collapsible "Learn more" — opt-in via alert.collapsible. Used for
   dense reference/policy detail (e.g. the voucher-pause notice) so most
   users skim past the summary + CTA, and the ones who need the legal
   detail expand it on demand. Native <details>/<summary> keeps it
   keyboard-accessible and ships free disclosure semantics to AT. */
.status-banner__details {
  margin-top: var(--space-md);
}
.status-banner__details-summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  cursor: pointer;
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--text-link);
  /* Native marker is a chunky disclosure triangle; replace with our own
     chevron via ::before so the affordance reads as a deliberate control,
     not the browser default. list-style: none hides the marker. */
  list-style: none;
}
.status-banner__details-summary::-webkit-details-marker { display: none; }
.status-banner__details-summary::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  transition: transform var(--transition-fast);
}
.status-banner__details[open] .status-banner__details-summary::before {
  transform: rotate(180deg);
}
.status-banner__details-summary:hover { text-decoration: underline; }
.status-banner__details-summary:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
/* The detail block already has margin-top: var(--space-md) for the
   non-collapsible case. Inside <details> the <summary> already sits
   above it with its own margin, so we tighten the gap here. */
.status-banner__detail--collapsible { margin-top: var(--space-sm); }

/* ── Data Table ─────────────────────────────────────────────────────────────── */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
}

.data-table th {
  background: var(--nyc-red);
  color: var(--text-inverse);
  text-align: left;
  padding: var(--space-md);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-xs);
  letter-spacing: 0.04em;
}

.data-table td {
  padding: var(--space-md);
  border-bottom: 1px solid var(--border-light);
  vertical-align: middle;
}

.data-table tr:hover td {
  background: var(--surface-muted);
}

/* ── Form Fields ────────────────────────────────────────────────────────────── */
.form-group {
  margin-bottom: var(--space-xl);
}

.form-label {
  display: block;
  font-weight: var(--font-weight-bold);
  margin-bottom: var(--space-sm);
  font-size: var(--font-size-base);
}

.form-section-title {
  font-family: var(--font-heading);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: var(--space-sm);
}

.form-label__required {
  color: var(--nyc-red);
  margin-left: 2px;
}

.form-input {
  width: 100%;
  padding: var(--space-md);
  border: none;
  border-bottom: 2px solid var(--border-input);
  font-family: inherit;
  font-size: var(--font-size-base);
  background: transparent;
  transition: border-color var(--transition-fast);
}

.form-input:focus {
  outline: none;
  border-color: var(--border-focus);
}

.form-input--filled {
  color: var(--text-primary);
}

.form-radio-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.form-radio {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  cursor: pointer;
  font-size: var(--font-size-base);
}

.form-radio input[type="radio"] {
  width: 18px;
  height: 18px;
  accent-color: var(--nyc-red);
}

/* ── Checkbox ──────────────────────────────────────────────────────────────── */
.checkbox {
  display: inline-flex;
  /* Align the box to the first line of the label so multi-line labels
     (with optional description below) don't push the box to vertical
     centre — keeps the box anchored next to the first word the user
     reads, which is the convention every native OS checkbox follows. */
  align-items: flex-start;
  gap: var(--space-md);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  color: var(--text-primary);
  user-select: none;
}
.checkbox__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0; height: 0;
}
.checkbox__box {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border: 1.5px solid var(--border-medium);
  border-radius: var(--radius-sm);
  background: var(--surface-white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: transparent;
  transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
  /* Align the 20px box with the optical centre of the first label line.
     This offset used to live as padding-top on .checkbox__content,
     calibrated for small body text — labels in the larger Lora-style
     font (e.g. doc-upload's "Save these documents to my MyCity
     profile" or the Save & MyCity row across the app) have a taller
     cap-height, so the 1px nudge was too small and the box appeared
     to float above the label.

     A 3px top margin lands the box's vertical centre within ~0.5px of
     the first line's cap-height midpoint at BOTH font sizes the app
     currently uses (16px body, ~18px heading-style). If a future
     checkbox adopts a substantially larger label font, override this
     margin in the page-specific context — don't chase a single value
     here that satisfies every size. */
  margin-top: 3px;
}
.checkbox:hover .checkbox__box {
  border-color: var(--nyc-red);
}
/* Visual checked state is driven SOLELY by `:has(:checked)` — the live
   DOM state. The earlier `.is-checked` class fallback was harmless on
   pages that re-render on every change, but on pages that don't,
   clicking a new option left the old class behind, painting two
   checkboxes as checked while the DOM said only one was. Same rationale
   as .radio-option above; native :checked is the source of truth. */
.checkbox:has(.checkbox__input:checked) .checkbox__box {
  border-color: var(--nyc-red);
  background: var(--nyc-red);
  color: var(--text-inverse);
}
.checkbox__tick {
  width: 14px;
  height: 14px;
  display: block;
}
.checkbox__content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  /* Alignment offset moved from here to .checkbox__box → margin-top.
     Anchoring the offset on the box (not the content) makes the
     alignment stable when the content grows past one line — a long
     description below the label no longer drags the label baseline
     out of sync with the box. */
  min-width: 0;
}
.checkbox__label {
  line-height: var(--line-height-normal);
}
/* Optional description line under a checkbox label (e.g. Census-style
   response-option hints on the About You race question). Sits at the
   compact tier with muted colour so the label still leads. */
.checkbox__description {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-normal);
}

/* ── Checkbox button variant + group ───────────────────────────────────────
   The `.checkbox--button` surface chrome (border, fill, selected/hover/focus
   states) is intentionally THEME-SCOPED — it only renders under the NYC theme
   (tokens-nyc.css → .selection-card). Base styling here is layout-only so the
   mycity theme keeps the original bare box+label look it has always had. */
.checkbox--button {
  display: flex;
  width: 100%;
}
.checkbox-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.checkbox-group__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-xs);
}
/* Title mirrors radio-group__label so checkbox and radio prompts read the
   same: Lora-bold at the md size in mycity, Clash/Noto in NYC. */
.checkbox-group__title {
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  color: var(--text-primary);
  line-height: var(--line-height-normal);
}
.checkbox-group__subtitle {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-normal);
}
.checkbox-group__options {
  display: flex;
  flex-direction: column;
  gap: var(--space-base);
}
/* Each option fills the row so multi-line captions don't collide with
   neighbours. */
.checkbox-group__options .checkbox {
  width: 100%;
}
/* Group-level error message ("Why is there an error and how to fix it").
   The pink wrapper surface is NYC-only (tokens-nyc.css); the message itself
   reads consistently in both themes. */
.checkbox-group__error,
.radio-group__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  font-family: var(--font-ui);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  line-height: var(--line-height-tight);
  margin-top: var(--space-2xs);
}
.checkbox-group__error-icon,
.radio-group__error-icon {
  color: var(--nyc-red);
  flex-shrink: 0;
  font-size: 20px;
}

/* ── Form Field (label + input) ────────────────────────────────────────────── */
.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-xl);
  max-width: 320px;
}
.form-field--wide {
  max-width: 100%;
}
.form-field__label {
  /* Lora bold at the md size — same family + size as .radio-group__label
     so every question/field label across the app reads at the same
     visual weight. The user's typed/selected value below uses the
     heading family (Montserrat), creating a consistent ask-vs-answer
     rhythm everywhere a form-field appears. */
  font-family: var(--font-body);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-bold);
  color: var(--text-primary);
}
.form-field__required {
  color: var(--nyc-red);
  margin-left: 2px;
}
.form-field__input {
  /* Input text itself: Montserrat, bold, one size bigger than body so the
     user's typed value reads as the primary content on the line (the label
     above it is the secondary scaffolding). */
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  padding: var(--space-sm) 0;
  border: none;
  /* Border progression:
       resting (empty + unfocused) → grey
       focused                      → darker
       filled + unfocused           → black (commitment cue: the value is set)
     `:placeholder-shown` is the standard CSS handle for "empty"; it's true
     while the placeholder is visible and false once the user types. */
  border-bottom: 1.5px solid var(--border-medium);
  background: transparent;
  color: var(--text-primary);
  outline: none;
  transition: border-color var(--transition-fast);
}
.form-field__input:focus {
  border-bottom-color: var(--text-secondary);
}
.form-field__input:not(:placeholder-shown):not(:focus) {
  border-bottom-color: var(--text-primary);
}
/* Inputs without a placeholder (e.g. date pickers) won't match
   :placeholder-shown logic above; keep them on the darker resting border so
   they don't look unfinished when filled. */
.form-field__input[type="date"]:not(:focus),
.form-field__input[readonly]:not(:focus) {
  border-bottom-color: var(--text-primary);
}
.form-field__input[readonly] {
  color: var(--text-secondary);
  cursor: default;
}
.form-field__helper {
  font-family: var(--font-heading);
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
}

/* Error state — applied at the wrapper level so the label, input border,
   and error message all shift in a single coordinated change. The error
   message replaces helper text in the same slot (renderFormField emits
   one or the other, not both). */
.form-field--error .form-field__label {
  color: var(--nyc-red);
}
.form-field--error .form-field__input,
.form-field--error .form-field__input:focus,
.form-field--error .form-field__input:not(:placeholder-shown):not(:focus) {
  border-bottom-color: var(--nyc-red);
}
/* ── Save confirmation ───────────────────────────────────────────────────────
   Transient "✓ Saved" feedback shown beside a Save button at the point of
   action (see components/save-confirmation.js). Fades in, auto-dismisses, and
   re-animates on every save. Uses the success tokens so it reads correctly in
   both themes. */
.save-confirm {
  /* Same green success pill as the section's "Saved" badge (.income-saved-badge)
     so the confirmation reads as positive in both themes (NYC's success text is
     dark, meant to sit on this green bg). */
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--alert-success-bg);
  color: var(--alert-success-text);
  font-family: var(--font-heading);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.04em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.save-confirm.is-visible {
  opacity: 1;
  transform: none;
}
.save-confirm__icon {
  flex-shrink: 0;
}

/* Matches .radio-group__error / .checkbox-group__error exactly so every
   inline error reads with the same icon + bold-text rhythm across the wizard. */
.form-field__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  font-family: var(--font-ui);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--nyc-red);
  line-height: var(--line-height-tight);
  margin-top: var(--space-xs);
}
.form-field__error-icon {
  color: var(--nyc-red);
  flex-shrink: 0;
  font-size: 20px;
}
/* The cell-phone field is immediately followed by the "Add home/work phone"
   links; the default form-field margin-bottom adds an unwanted gap there. */
.form-field[data-field-name="about-you-phone-cell"] {
  margin-bottom: 0;
}


/* ── Inline Note ───────────────────────────────────────────────────────────
   Short contextual note that sits next to a question or field — for
   downstream-consequence heads-ups, tips, or clarifications the user
   needs at the moment of answering. Distinct from .form-field__helper
   (passive muted hint) and renderPanel (page-level banner). Component:
   js/components/inline-note.js. */
.inline-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
  background: var(--surface-muted);
  border-left: 3px solid var(--border-medium);
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
  color: var(--text-primary);
}
.inline-note__icon {
  font-size: var(--font-size-base);
  flex-shrink: 0;
  margin-top: 0;
  color: var(--text-secondary);
}
.inline-note__body { flex: 1; }
.inline-note__label {
  font-weight: var(--font-weight-bold);
  margin-right: 0.15rem;
}
/* No-icon variant — body sits flush against the left padding so the
   note reads as a simple inset paragraph rather than a stranded right-
   side block where an icon used to be. */
.inline-note--no-icon { gap: 0; }
/* Variants — each shifts the icon color + left-border accent, leaving
   the body text dark so it stays legible. */
.inline-note--tip {
  background: var(--alert-warning-bg);
  border-left-color: var(--alert-warning-border);
}
.inline-note--tip .inline-note__icon { color: var(--alert-warning-text); }
.inline-note--info {
  background: var(--alert-info-bg);
  border-left-color: var(--alert-info-border);
}
.inline-note--info .inline-note__icon { color: var(--alert-info-text); }
.inline-note--warning {
  background: var(--alert-warning-bg);
  border-left-color: var(--alert-warning-border);
}
.inline-note--warning .inline-note__icon { color: var(--alert-warning-text); }
.inline-note--success {
  background: var(--alert-success-bg);
  border-left-color: var(--alert-success-border);
}
.inline-note--success .inline-note__icon { color: var(--alert-success-text); }

/* ── Select Field ──────────────────────────────────────────────────────────
   Styled native <select>. Shares form-field's label/helper structure so the
   two can sit next to each other in a row without visual seams. The wrap
   div anchors the chevron icon to the right edge of the input; the native
   chevron is hidden via appearance:none so we control the visual. */
.select-field__wrap {
  position: relative;
}
.select-field__input {
  /* Native chevron off; ours sits absolutely positioned to the right. */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 100%;
  padding-right: 32px; /* reserve room for the chevron icon */
  cursor: pointer;
}
.select-field__input::-ms-expand {
  display: none; /* IE/Edge legacy native chevron */
}
.select-field__chevron {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  font-size: 20px;
  color: var(--text-secondary);
  pointer-events: none; /* clicks pass through to the underlying <select> */
}
.select-field__input:focus + .select-field__chevron {
  color: var(--text-primary);
}
/* When the select holds a real (non-placeholder) value, treat it like a
   filled form-field — darker resting border. The :has() selector lets us
   key off the option that's currently selected. */
.select-field__input:not([data-empty="true"]):not(:focus) {
  border-bottom-color: var(--text-primary);
}

/* ── File Chips ─────────────────────────────────────────────────────────────── */
.file-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  background: var(--surface-muted);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
}

/* ── Skeleton Loading ───────────────────────────────────────────────────────── */
.skeleton {
  background: linear-gradient(90deg, var(--skeleton-bg) 25%, var(--skeleton-shine) 50%, var(--skeleton-bg) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.8s ease-in-out infinite;
  border-radius: var(--radius-sm);
}

.skeleton--text {
  height: 14px;
  width: 80%;
  margin: var(--space-sm) 0;
}

.skeleton--pill {
  height: 22px;
  width: 90px;
  border-radius: var(--radius-pill);
}

@keyframes skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── Accordion ──────────────────────────────────────────────────────────────── */
.accordion {
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: var(--space-sm);
}

.accordion__trigger {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: var(--space-base) var(--space-lg);
  background: var(--surface-white);
  border: none;
  font-family: var(--font-ui);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.03em;
  cursor: pointer;
  text-align: left;
}

.accordion__trigger:hover {
  background: var(--surface-muted);
}

.accordion__chevron {
  transition: transform var(--transition-fast);
}

.accordion.is-open .accordion__chevron {
  transform: rotate(90deg);
}

.accordion__content {
  display: none;
  padding: var(--space-base) var(--space-lg);
  border-top: 1px solid var(--border-light);
}

.accordion.is-open .accordion__content {
  display: block;
}

/* ── Placeholder Page (unbuilt steps) ───────────────────────────────────────── */
.placeholder-page {
  text-align: center;
  padding: var(--space-5xl) var(--space-2xl);
  color: var(--text-muted);
}

.placeholder-page__icon {
  font-size: 3rem;
  margin-bottom: var(--space-lg);
}

.placeholder-page__hint {
  font-size: var(--font-size-sm);
  margin-top: var(--space-md);
}

/* ── Employer Card (paystub review) ─────────────────────────────────────────── */
.employer-card {
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
  background: var(--surface-white);
}

.employer-card__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-base);
}

.employer-card__name {
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-lg);
}

/* ── Consent Page ──────────────────────────────────────────────────────────── */
/* Plain-language summary sits directly under the H1 — it's what the defect
   log asked for: a quick takeaway so users don't have to parse the full body
   before deciding. Capped at reading-max-width so the line length stays
   readable on wide monitors (the wizard content area can stretch well past
   1000px otherwise). */
.consent-summary {
  max-width: var(--reading-max-width);
  background: var(--panel-info-bg);
  color: var(--panel-info-text);
  border-left: 4px solid var(--panel-info-accent);
  padding: var(--space-base) var(--space-lg);
  border-radius: var(--radius-md);
  margin: var(--space-md) 0 var(--space-xl);
  line-height: var(--line-height-normal);
}

.consent-footer {
  margin-top: var(--space-lg);
  color: var(--text-secondary);
}
.consent-footer a {
  color: var(--text-link);
  font-family: var(--font-ui);
  font-weight: var(--font-weight-bold);
}

/* Required affirmation — single checkbox the user must check to proceed.
   Mirrors the .radio-option card pattern exactly: same padding, shadow,
   selected-state treatment, and two-typeface (Montserrat title + Lora desc)
   typography. The only difference is a square "box" affordance instead of
   the radio's circular "dot". */
.consent-affirm {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-base) var(--space-lg);
  margin: var(--space-xl) 0 0;
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 2px 6px rgba(0,0,0,0.04);
  cursor: pointer;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
  font-family: var(--font-body);
}
.consent-affirm:hover {
  border-color: var(--border-medium);
  box-shadow: 0 2px 4px rgba(0,0,0,0.07), 0 4px 10px rgba(0,0,0,0.05);
}
.consent-affirm.is-selected {
  border: 1px solid var(--nyc-red);
  box-shadow: none;
  background: var(--nyc-red-bg);
}

.consent-affirm__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0; height: 0;
}

/* Square box — same dimensions as the radio dot, but with a small corner
   radius so it reads as a checkbox affordance. Selected state fills with
   NYC red and draws a checkmark via CSS. */
.consent-affirm__box {
  flex-shrink: 0;
  position: relative;
  width: 20px; height: 20px;
  margin-top: 2px;
  border: 2px solid var(--border-medium);
  border-radius: 4px;
  background: var(--surface-white);
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.consent-affirm.is-selected .consent-affirm__box {
  border-color: var(--nyc-red);
  background: var(--nyc-red);
}
.consent-affirm.is-selected .consent-affirm__box::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 0;
  width: 6px;
  height: 11px;
  border: solid var(--text-inverse);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.consent-affirm__content {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1;
  min-width: 0;
}
.consent-affirm__title {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-base);
  color: var(--text-primary);
  line-height: var(--line-height-tight);
}
.consent-affirm__desc {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-normal);
}

/* ── Get Started Page ──────────────────────────────────────────────────────
   Orientation page that merges what used to be Consent + Instructions.
   Sections separate themselves through their heading + content rhythm; we
   don't draw cross-section dividers because the heading shift already does
   that work, and the consent section has its own background which signals
   the boundary.

   Spacing rule: every section uses --space-2xl margin-bottom — the same
   value as .page-intro's margin-bottom — so hero→section, section→section,
   and last-section→button-bar are all visually equal. No more inconsistent
   gaps from mixing padding + margin. */
.get-started__section {
  margin-bottom: var(--space-2xl);
}
.get-started__section:last-of-type {
  margin-bottom: 0;
}

/* Inline placeholder marker — wraps any [bracketed value] in body
   copy that's still awaiting agency confirmation. Italic + dark grey
   so the reader's eye lands on it as draft text without it screaming
   for attention. Keeps surrounding sentence cadence intact. Removed
   at final-publish along with the editorNote block when bracketed
   values get substituted with their finalised numbers. */
.placeholder {
  font-style: italic;
  color: var(--text-muted);
}

/* Compact labeled list used by the "What to expect" section. Bulleted
   so it reads at a glance as a quick reference rather than as prose.
   Bold label leads each line; the rest of the line carries the value.
   Spacing tuned so adjacent bullets don't crowd, but the list overall
   stays denser than the prep checklist above it (which uses its own
   icon-led layout). */
.get-started__expectations {
  margin: 0;
  padding-left: var(--space-xl);
  list-style: disc;
}
.get-started__expectations li {
  margin-bottom: var(--space-sm);
  line-height: var(--line-height-normal);
  color: var(--text-primary);
}
.get-started__expectations li:last-child {
  margin-bottom: 0;
}

.get-started__heading {
  font-family: var(--font-heading);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  margin: 0 0 var(--space-md);
}
.get-started__checklist {
  list-style: none;
  padding: 0;
  margin: 0;
  background: var(--surface-muted);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.get-started__checklist-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-base) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-normal);
}
.get-started__checklist-item:last-child {
  border-bottom: 0;
}
/* Fixed icon container — same width/height on every row so the arrow column
   stays perfectly vertical regardless of Material Symbol glyph metrics or
   how the text wraps. The icon glyph is centered inside this 24×24 box. */
.get-started__checklist-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 24px;
  height: calc(var(--font-size-base) * var(--line-height-normal));
  color: var(--text-primary);
  line-height: 1;
}
.get-started__save-note {
  font-weight: var(--font-weight-bold);
  margin-top: var(--space-md);
}
.get-started__section--consent {
  /* Slightly softer treatment so consent reads as one more piece of prep
     rather than the page's emotional climax. */
  background: var(--page-pink-light);
  border-radius: var(--radius-md);
  padding: var(--space-xl) var(--space-2xl);
}
/* When the consent section is the last block before the button bar, drop
   the button-bar's default top divider + padding so Start Application sits
   directly after the pink panel with the same --space-2xl gap as everywhere
   else on the page. Scoped via the adjacent-sibling combinator so it only
   affects get-started, not other wizard pages. */
.get-started__section--consent + .button-bar {
  border-top: 0;
  padding-top: 0;
  margin-top: var(--space-2xl);
}
.get-started__section--consent .checkbox {
  /* Give the checkbox a bit of breathing room between the summary/details
     above and itself. */
  margin-top: var(--space-md);
}
/* Collapsible legal detail — uses the same +/− affordance as the status
   banner's Learn more so the interaction reads consistently across the app. */
.get-started__legal {
  margin: var(--space-md) 0;
}
.get-started__legal > summary {
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--nyc-red);
  letter-spacing: 0.03em;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}
.get-started__legal > summary::-webkit-details-marker { display: none; }
.get-started__legal > summary::after {
  content: '+';
  font-weight: var(--font-weight-bold);
}
.get-started__legal[open] > summary::after { content: '–'; }
.get-started__legal > summary:hover { text-decoration: underline; }
.get-started__legal-body {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.get-started__legal-body p {
  margin: 0 0 var(--space-sm);
}
.get-started__legal-body ul {
  margin: 0 0 var(--space-md) var(--space-lg);
}
.get-started__legal-body p:last-child,
.get-started__legal-body ul:last-child {
  margin-bottom: 0;
}

/* Audit #84 — Decline alternative block. Sits beneath the consent
   checkbox as a sibling, visually offset so it reads as "here's the
   other way, if the online consent path isn't for you" rather than
   competing with the primary consent action. Muted background +
   left-edge accent border so it's distinct without being loud. No
   heading, no secondary footnote: one concise sentence + one link,
   intentionally kept lightweight so it doesn't compete with the
   consent checkbox above. */
.get-started__decline-alt {
  margin-top: var(--space-lg);
  padding: var(--space-lg) var(--space-lg) var(--space-xl);
  background: var(--surface-white);
  border-left: 3px solid var(--nyc-red);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.get-started__decline-alt-body {
  margin: 0 0 var(--space-3xs);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
}
.get-started__decline-alt-cta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--nyc-red);
  /* Underline lives on the label span, NOT the anchor. The anchor is a flex
     container, and a flex container's text-decoration is drawn across its
     flex items — including the icon — so underlining the anchor would put a
     line under the external-link icon. Underlining only the label keeps the
     icon clean. Same "label-only underline" pattern as .icon-link. */
  text-decoration: none;
}
/* The anchor must stay un-underlined in EVERY state. base.css has a global
   `a:hover { text-decoration: underline }` that would otherwise re-underline
   the flex anchor on hover and put the line back under the icon. Override it
   here (higher specificity) so hover/focus never re-underline the anchor. */
.get-started__decline-alt-cta:hover,
.get-started__decline-alt-cta:focus-visible {
  text-decoration: none;
}
.get-started__decline-alt-label {
  text-decoration: underline;
}
/* Keep the label underlined on hover/focus too — the hover affordance is the
   color shift (base a:hover → --text-link-hover); the underline stays put so
   the link still reads as a link. */
.get-started__decline-alt-cta:hover .get-started__decline-alt-label,
.get-started__decline-alt-cta:focus-visible .get-started__decline-alt-label {
  text-decoration: underline;
}
.get-started__decline-alt-external {
  font-size: 0.85em;
}

/* ── Thank You Page ────────────────────────────────────────────────────────
   Post-submission confirmation. Wrapped in a white card matching the wizard
   content surface so the celebration sits inside the same chrome as the
   rest of the application flow. */
.thank-you__card {
  background: var(--surface-white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-3xl) var(--space-3xl) var(--space-xl);
}
.thank-you__section {
  padding: var(--space-xl) 0;
  border-bottom: 1px solid var(--border-light);
}
.thank-you__section:last-of-type {
  border-bottom: 0;
}
.thank-you__heading {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin: 0 0 var(--space-md);
}
.thank-you__download {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
}
.thank-you__cross-sell-actions {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  flex-wrap: wrap;
  margin-top: var(--space-md);
}
.thank-you__footer-link {
  padding-top: var(--space-lg);
}


/* ── Signature Pad ──────────────────────────────────────────────────────────── */
.signature-pad {
  border: 1px solid var(--border-medium);
  border-radius: var(--radius-md);
  padding: var(--space-3xl) var(--space-2xl);
  margin: var(--space-lg) 0;
  background: var(--surface-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 120px;
}

.signature-pad__label {
  color: var(--text-muted);
  font-style: italic;
}

/* ── Welcome Section ────────────────────────────────────────────────────────── */
.welcome-section {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-xl);
  align-items: center;
  padding: var(--space-xl);
  background: var(--surface-white);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-lg);
}

.welcome-section__title {
  font-family: var(--font-serif);
  font-size: var(--font-size-2xl);
  /* Override h2's default bold — title sits at the same weight as body so it
     reads as a warm greeting, not a section header. Now that body is medium
     by default, this is just inheriting that weight. */
  font-weight: inherit;
  text-transform: none;
  margin-bottom: var(--space-md);
}

.welcome-section__image {
  width: 200px;
  height: 160px;
  border-radius: var(--radius-lg);
  object-fit: cover;
}

/* ── Results Table ─────────────────────────────────────────────────────────
   Generic tabular results component (see js/components/results-table.js).
   Used on the doc-review page for paystub validation results; designed to
   serve other "items + status + actions" tables (document validation,
   eligibility checks, manual review queues, etc.) without modification. */
.results-table-wrap {
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  /* No overflow:hidden — that clipped tooltips and any other absolute-
     positioned popovers that need to escape the table boundary. The inner
     pieces below carry their own border-radius so the rounded-corner look
     is preserved without the clipping side effect.

     overflow-x: auto is the graceful-degradation pair to the
     minmax(0, 1fr) grid track on .layout-wizard / .layout-dashboard.
     The grid never lets the column grow past its fair share, so the
     card stays put; if the table's nowrap cells (Pay Date / Pay Period)
     can't fit, the scrollbar lives INSIDE this wrap rather than pushing
     the card wider. overflow-y is the default `visible` so flag-detail
     expansions, tooltips, and action-stack popovers can still bleed
     past the bottom edge when needed. */
  overflow-x: auto;
  margin-bottom: var(--space-lg);
  background: var(--surface-white);
}
.results-table__heading {
  background: var(--panel-info-bg);
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-primary);
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
  border-top-left-radius: var(--radius-md);
  border-top-right-radius: var(--radius-md);
}
/* Round the bottom corners on whichever cells end up last in the table —
   regular last row or an expansion row. Replaces what overflow:hidden used
   to handle. */
.results-table tbody tr:last-child > .results-table__td:first-child,
.results-table tbody tr:last-child > .results-table__expansion-cell:first-child {
  border-bottom-left-radius: var(--radius-md);
}
.results-table tbody tr:last-child > .results-table__td:last-child,
.results-table tbody tr:last-child > .results-table__expansion-cell:last-child {
  border-bottom-right-radius: var(--radius-md);
}
.results-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-heading);
  /* Table body tier (sm ≈ 14.6px) — matches the prior .ps-table density.
     Keep this here so cells, badges, flags, and previews all inherit unless
     they explicitly override below. */
  font-size: var(--font-size-sm);
}
.results-table__th {
  text-align: left;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.06em;
  /* Informational blue, not the NYC-red link color. Table headers are
     labels (semantically neutral), not interactive links — the red was
     reading as alarming on a page already carrying real warning surfaces
     (flagged-row amber, "N Reviews" pill). --alert-info-text (#0d47a1)
     keeps headers visible and structured without competing with the
     page's actual status signals. */
  color: var(--alert-info-text);
  /* Single inter-column gap: only a RIGHT pad on each cell, so column
     boundaries get one gap instead of the doubled left+right that added up
     across all columns and squeezed the content (esp. the Action links). The
     table's left/right edge insets are restored on the first/last cells below. */
  padding: var(--space-md) var(--space-lg) var(--space-md) 0;
  border-bottom: 1px solid var(--border-light);
  background: var(--surface-white);
  white-space: nowrap;
}
.results-table__th--xnarrow { width: 40px; }
.results-table__th--narrow  { width: 70px; }
/* compact sits between narrow and medium — sized so the Amount column
   reads as "$1,XXX" with breathing room on either side, but doesn't
   claim the full 140px that medium reserves for richer cell content. */
.results-table__th--compact { width: 100px; }
.results-table__th--medium  { width: 140px; }
.results-table__th--wide    { width: 200px; }
/* Two columns flagged `half` split the table's leftover width equally between
   them (auto table layout distributes the free space in proportion to the
   percentage hints). Used by Results + Action so neither hogs the slack while
   the other's content wraps. */
.results-table__th--half    { width: 50%; }
.results-table__th--nowrap,
.results-table__td--nowrap  { white-space: nowrap; }

.results-table__td {
  /* Single right gap only — see .results-table__th note. */
  padding: var(--space-md) var(--space-lg) var(--space-md) 0;
  border-bottom: 1px solid var(--border-light);
  vertical-align: middle;
  color: var(--text-primary);
}
/* Restore the table's outer edge insets: left pad on the first column, the
   last column already carries its right pad as the right edge inset. */
.results-table__th:first-child,
.results-table__td:first-child {
  padding-left: var(--space-lg);
}
.results-table tbody tr:last-child .results-table__td { border-bottom: none; }

/* Row state — highlights the whole row to draw attention */
.results-table__row--flagged .results-table__td {
  background: var(--panel-warning-bg);
}
.results-table__row--verified .results-table__td { /* default fill */ }

/* Fade-in animation when a row just resolved (skeleton → data swap) */
.results-table__row--resolving { animation: results-table-row-fade-in 0.4s ease-out; }
@keyframes results-table-row-fade-in {
  from { background-color: #fdf1f4; }
  to   { background-color: transparent; }
}
.results-table__row--flagged.results-table__row--resolving {
  animation: results-table-flagged-fade-in 0.4s ease-out;
}
@keyframes results-table-flagged-fade-in {
  from { background-color: #fdf1f4; }
  to   { background-color: #fff7e6; }
}

/* Result badge (Verified / Flagged) — inherits sm from .results-table */
.results-table__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-family: var(--font-heading);
}
.results-table__badge--verified { color: var(--status-verified-text); }
.results-table__badge--flagged  { color: var(--alert-warning-text); }
.results-table__badge--pending  { color: var(--text-secondary, #666); font-style: italic; }
.results-table__badge--accepted { color: var(--alert-warning-text); font-style: italic; }
.results-table__badge-icon { font-size: 16px; }
.results-table__badge--verified .results-table__badge-icon { color: var(--status-verified-text); }
.results-table__badge--flagged  .results-table__badge-icon { color: var(--alert-warning-text); }
.results-table__badge--pending  .results-table__badge-icon { color: var(--text-secondary, #666); }
.results-table__badge--accepted .results-table__badge-icon { color: var(--alert-warning-text); }

/* Result flag list (numbered) */
.results-table__flags {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.results-table__flag {
  /* Block flow so multiple flags stack vertically; children use inline flow
     so the info icon sits next to the last word of the label even when the
     label wraps to multiple lines. Avoids the "icon floating in the
     middle" look that flex+align-items:center produces on wrapped rows. */
  display: block;
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
}
.results-table__flag-label {
  display: inline;
  color: var(--text-primary);
  font-weight: var(--font-weight-semibold);
}
/* Wraps the flag label + info icon as a single click target so users get
   an unambiguous affordance: the whole string is interactive, with a dotted
   underline on the text and a hover tooltip prompting "See details".

   display: inline (not inline-flex) is load-bearing: when the label wraps
   to multiple lines ("Incorrect / Name", "Duplicate / Paystub"), inline
   flow lets the info icon sit immediately after the last word ("Name",
   "Paystub") instead of floating vertically-centered to the right of the
   wrapped block. The icon itself uses vertical-align: middle to stay
   on the text baseline; spacing comes from a margin-left on the icon
   (since `gap` doesn't apply to inline boxes). */
.results-table__flag-trigger {
  display: inline;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: default;
}
.results-table__flag-trigger--interactive {
  cursor: pointer;
  transition: color var(--transition-fast);
}
.results-table__flag-trigger--interactive .results-table__flag-label {
  /* Dotted underline cues "more available" without shouting like a link. */
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.results-table__flag-trigger--interactive:hover .results-table__flag-label {
  /* Subtle promotion to a solid underline on hover so the click intent is
     obvious. Keeps the color the same — no need for a separate hover hue. */
  text-decoration-style: solid;
}
.results-table__flag-trigger--interactive.is-expanded {
  /* Match the label color on expand — once the user has clicked through
     to the detail, the trigger has done its job and shouldn't keep
     pulling attention in red. The tooltip already flips to "Hide
     details" so state is clear. */
  color: var(--text-primary);
}
.results-table__flag-info {
  display: inline-flex;
  vertical-align: middle;
  /* Replaces the `gap: 4px` that used to live on .results-table__flag-trigger
     when it was inline-flex. Now that the trigger is plain inline (so the
     icon flows next to the last wrapped word), spacing has to come from
     the icon's own margin. */
  margin-left: 4px;
  color: var(--alert-info-text);
}
.results-table__flag-info .material-symbols-outlined {
  font-size: 20px;
  vertical-align: middle;
}
.results-table__flag-trigger--interactive.is-expanded .results-table__flag-info {
  /* Icon goes black-on-expand to match the label rather than firing in
     red against the calm yellow expansion underneath. */
  color: var(--text-primary);
}

/* Inline expansion row injected by renderResultsTable when row.expansion is
   set. Carries the parent row's state class so it picks up the same alert
   tint (.results-table__expansion--flagged inherits the flagged background
   to maintain visual continuity with the row it belongs to). */
.results-table__expansion-cell {
  padding: 0;
  /* Close the expansion off with the same divider the rest of the table
     uses between rows. Without this, when two flagged rows are stacked
     and the top one is expanded, the expansion's tinted body bleeds
     directly into the next row's tinted body — they read as one merged
     block of background instead of two distinct paystubs. The divider
     gives the next row a clean top edge. */
  border-bottom: 1px solid var(--border-light);
}
.results-table__expansion--flagged .results-table__expansion-cell {
  background: var(--panel-warning-bg);
}
/* When a row is followed by an expansion, drop the row's own bottom border
   so the two read as a single continuous cell instead of two stacked rows
   with a divider between them. Also zero out the row td's bottom padding
   so the visual gap above the expansion's content matches the gap below
   it (otherwise the row's 12px bottom padding stacks on the expansion's
   own top padding, doubling the space above the summary). */
.results-table__row:has(+ .results-table__expansion) > .results-table__td {
  border-bottom: 0;
  padding-bottom: 0;
}
/* Symmetric rule for the row that FOLLOWS an expansion — drop its top
   border (which on most browsers is rendered as the previous row's
   bottom border, but defensively here) and make sure the expansion's
   own bottom border is the only divider between the two paystubs. */
.results-table__expansion + .results-table__row > .results-table__td {
  border-top: 0;
}

/* Action stack (Continue / OR / Upload) */
.results-table__action-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.results-table__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-heading);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--nyc-red);
  text-decoration: none;
  cursor: pointer;
  text-align: left;
}
/* Underline scoped to the label span only — keeps the rule from running
   underneath the Material Symbol icon, which sits above the baseline. */
.results-table__action:hover { text-decoration: none; }
.results-table__action:hover > span:not(.material-symbols-outlined) {
  text-decoration: underline;
}
.results-table__action.is-disabled { opacity: 0.5; cursor: not-allowed; }
.results-table__action-icon { color: var(--nyc-red); }
.results-table__action-or {
  font-family: var(--font-heading);
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  letter-spacing: 0.08em;
}

/* Preview link (eye icon + label) */
.results-table__preview {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--text-link);
  cursor: pointer;
}
.results-table__preview:hover { text-decoration: underline; }
.results-table__preview .material-symbols-outlined { color: var(--text-link); }

/* Skeleton placeholder cells */
.results-table__skeleton {
  height: 14px;
  background: linear-gradient(90deg, var(--skeleton-bg) 0%, var(--skeleton-shine) 50%, var(--skeleton-bg) 100%);
  background-size: 200% 100%;
  border-radius: var(--radius-sm);
  animation: results-table-shimmer 1.4s ease-in-out infinite;
}
.results-table__skeleton--narrow { width: 60%; }
.results-table__skeleton--medium { width: 80%; }
.results-table__skeleton--wide   { width: 90%; }
@keyframes results-table-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── Removed: .info-box ──────────────────────────────────────────────────────
   Replaced by renderPanel({variant:'info', style:'flat'}). The only
   remaining references live in legacy/*.html snapshots, which are
   detached prototypes that don't link this stylesheet. */

/* ============================================================================
   REUSABLE COMPONENTS — extracted from page-specific markup
   Each block here pairs with a render fn under js/components/.
   ============================================================================ */

/* ── Page Intro ────────────────────────────────────────────────────────────── */
/* Title + optional subtitle + description block at the top of every wizard
   step / page. Description is intentionally constrained to 800px so long-form
   prose stays comfortably scannable on wide viewports, and uses --font-size-md
   (one step above body default) for slightly more presence than inline copy. */
.page-intro {
  margin-bottom: var(--space-2xl);
}
.page-intro__title {
  /* Inherits h1 font/size/weight from base.css */
  margin-bottom: var(--space-md);
}
.page-intro__subtitle {
  font-family: var(--font-heading);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.01em;
  margin-top: var(--space-base);
  margin-bottom: var(--space-md);
}
.page-intro__description {
  font-family: var(--font-body);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
  /* 1024px (was 800px). At our 18px root with Lora at --font-size-md
     (~20px), 800px capped lines around 65 characters which felt cramped
     on wide viewports. 1024px gives ~80–90 chars — still comfortable for
     reading prose, uses more of the wizard column on wide screens. */
  max-width: 1024px;
  margin: 0;
  color: var(--text-primary);
}

/* ── Doc Upload Card (per-requirement) ─────────────────────────────────────
   Container for each document requirement on the doc-upload step.
   Composes radio-group + panel + upload-zone + uploaded-files + checkbox
   + form-field (conditional) — see js/pages/wizard/step-doc-upload.js. */
.doc-upload__card,
.doc-forms__card {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: var(--space-xl) var(--space-2xl);
  margin-bottom: var(--space-lg);
}
/* Collapsed acknowledgement state for a referral employer that has at
   least one CFWB-015 attached. The inner success panel + file chip
   carry the visual weight; the outer card only needs to step aside —
   a faint success-tinted left border quietly groups complete cards
   together so users can scan a long list of employers and immediately
   spot which ones still need their attention. */
.doc-forms__card--complete {
  border-left: 3px solid var(--status-verified-border);
}
.doc-upload__heading,
.doc-forms__heading {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  letter-spacing: 0.04em;
  color: var(--text-primary);
  margin-bottom: var(--space-md);
}
/* Two-line card header used on doc-forms cards: employer name as the
   primary anchor (large bold heading), form name + code as a quieter
   sub-line beneath. Lets users with multiple referrals scan by employer
   without parsing the form code on every card. */
.doc-forms__card-header {
  margin-bottom: var(--space-md);
}
.doc-forms__employer {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-lg);
  color: var(--text-primary);
  margin: 0;
  line-height: var(--line-height-tight, 1.2);
}
.doc-forms__form-name {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  margin: var(--space-xs) 0 0;
  line-height: var(--line-height-normal, 1.5);
}
.doc-upload__description,
.doc-forms__description {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height-relaxed);
  color: var(--text-primary);
  margin: 0 0 var(--space-lg);
  max-width: 1024px;
}
/* .doc-upload__select-label removed — superseded by renderRadioGroup's
   built-in `label` prop, which gives every radio question the same
   Lora-bold-md treatment via .radio-group__label. */
.doc-upload__save-row {
  margin: var(--space-lg) 0;
}

/* Downloads row used in doc-forms cards — primary "Download" action plus
   a secondary "Forms in other languages" link, sitting above the upload
   zone. Each link uses the icon-link primitive so spacing + typography
   match the rest of the action vocabulary. */
.doc-forms__downloads {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-lg);
  margin-bottom: var(--space-lg);
}

/* ── Choice Card ───────────────────────────────────────────────────────────── */
/* Container for the "pick a path → see relevant content" pattern. Holds a
   title/prompt, a radio group, and either the selected option's content
   (paired via choice-card__content) or a placeholder prompting the user
   to make a selection. */
.choice-card {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: var(--space-xl) var(--space-2xl);
  margin-bottom: var(--space-lg);
}
.choice-card__title {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin-bottom: var(--space-base);
}
.choice-card__prompt {
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  color: var(--text-primary);
  margin: 0 0 var(--space-base);
}
.choice-card__content {
  margin-top: var(--space-lg);
}
/* Shared placeholder used wherever a section's main content is gated on a
   selection — choice-card AND each doc-upload requirement card use this.
   Dashed neutral border, centered uppercase muted text, mirrors the
   empty-upload-zone look. */
.field-placeholder {
  margin-top: var(--space-lg);
  border: 1px dashed var(--border-medium);
  border-radius: var(--radius-md);
  padding: var(--space-2xl) var(--space-base);
  text-align: center;
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--surface-muted);
}

/* ── Priority self-check panel ─────────────────────────────────────────────── */
/* White card holding the priority questions, the save row, the result, and the
   Cash-Assistance note — one unified container matching the get-started /
   wizard content card (32px radius + generous padding come from tokens-nyc). */
.priority-check__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
.priority-check__save-row {
  display: flex;
  gap: var(--space-md);
  align-items: center;
  flex-wrap: wrap;
}
.priority-check__save-note {
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

/* ── Radio Option ──────────────────────────────────────────────────────────── */
.radio-group {
  display: grid;
  gap: var(--space-base);
  margin-bottom: var(--space-lg);
}
.radio-group--grid   { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.radio-group--stack  { grid-template-columns: 1fr; gap: var(--space-sm); }

/* Question label above the options. Lora bold at the md size — the
   ask is the most important text on the row, so it gets visual weight
   without competing with page-level headings. Description (optional)
   sits below in regular body. */
.radio-group__head {
  /* Always sits on its own full-width row above the options. Without this,
     a grid-variant group (auto-fit columns) would treat the question label as
     just another column alongside the option cards — which then stretch to
     the label's height, ballooning short Yes/No cards. Spanning every column
     keeps the question above and the options at their natural height. */
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-bottom: var(--space-xs);
}
.radio-group__label {
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-md);
  color: var(--text-primary);
  line-height: var(--line-height-normal);
}
.radio-group__required {
  color: var(--nyc-red);
  margin-left: 2px;
}
.radio-group__desc {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-normal);
}

/* Shared base for both variants */
.radio-option {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  cursor: pointer;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
  font-family: var(--font-body);
}

/* Variant: card — boxed, lifted shadow, full padding. Used for branching
   choices where each option is itself a major path (paystubs vs referral). */
.radio-option--card {
  padding: var(--space-base) var(--space-lg);
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 2px 6px rgba(0,0,0,0.04);
}
.radio-option--card:hover {
  border-color: var(--border-medium);
  box-shadow: 0 2px 4px rgba(0,0,0,0.07), 0 4px 10px rgba(0,0,0,0.05);
}
/* Visual selected state is driven SOLELY by `:has(:checked)` — the live
   DOM state. Earlier this rule also accepted `.is-selected` (a class set
   from JS draft state at render time), but combining the two created a
   stale-state bug: when a parent surface doesn't re-render on every
   click (e.g. the two simultaneous Your Family forms), clicking a new
   radio updates the DOM's `:checked` but leaves the old `.is-selected`
   class on the previous option. The CSS then painted BOTH as selected.
   `:has(:checked)` always reflects the actual native state, so it's the
   single source of truth for "which one is on." */
.radio-option--card:has(.radio-option__input:checked) {
  border: 1px solid var(--nyc-red);
  box-shadow: none;
  background: var(--nyc-red-bg);
}

/* Variant: simple — compact dot + inline label, no chrome. Used for picking
   one item from a flat list (e.g. document types on doc-upload). */
.radio-option--simple {
  align-items: center;
  padding: var(--space-xs) 0;
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
}
.radio-option--simple .radio-option__label {
  /* Montserrat (heading family) at the medium size — option labels are
     the user's actual answer, so they get the heading family for weight
     and rhythm, while the question prompt above them stays in Lora
     (body) for the conversational ask. Bumped from base → md so the
     answers don't look smaller than the question they're answering. */
  font-family: var(--font-heading);
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-md);
  color: var(--text-primary);
}
.radio-option--simple .radio-option__dot {
  width: 18px;
  height: 18px;
  margin-top: 0;
}
.radio-option--simple:has(.radio-option__input:checked) .radio-option__dot {
  border-color: var(--nyc-red);
  background: radial-gradient(var(--nyc-red) 0 4px, var(--surface-white) 5px 100%);
}

.radio-option__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0; height: 0;
}

.radio-option__dot {
  flex-shrink: 0;
  width: 20px; height: 20px;
  margin-top: 2px;
  border: 2px solid var(--border-medium);
  border-radius: 50%;
  background: var(--surface-white);
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.radio-option:has(.radio-option__input:checked) .radio-option__dot {
  border-color: var(--nyc-red);
  background: radial-gradient(var(--nyc-red) 0 5px, var(--surface-white) 6px 100%);
}

.radio-option__content {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1;
  min-width: 0;
}
.radio-option__label {
  /* Lora bold for card-variant radio labels, matching the form-field label
     family. The simple variant (.radio-option--simple .radio-option__label
     above) already uses Lora at medium weight. */
  font-family: var(--font-body);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-base);
  color: var(--text-primary);
  line-height: var(--line-height-tight);
}
.radio-option__desc {
  font-family: var(--font-body);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: var(--line-height-normal);
}

/* ── Panel (info + notification) ───────────────────────────────────────────── */
/* Variants control color palette; style controls visual treatment. */
.panel {
  /* The panel container spans whatever its parent gives it (form card,
     wizard column, etc.) so the colored fill always looks proportionate to
     the surrounding layout. The TEXT INSIDE is capped via .panel__content /
     .panel__header for readability — see the rules below. */
  border-radius: var(--radius-md);
  margin-bottom: var(--space-lg);
  font-family: var(--font-body);
  overflow: hidden;
  --panel-bg:     var(--alert-info-bg);
  --panel-text:   var(--alert-info-text);
  --panel-accent: var(--alert-info-border);
  --panel-header-bg: var(--alert-info-border);
  background: var(--panel-bg);
  color: var(--panel-text);
}

/* Variant palettes — each sets the three color slots.
   Info uses panel-specific tokens (softer lavender, body-default black text)
   rather than the alert banner colors. */
.panel--info {
  --panel-bg:     var(--panel-info-bg);
  --panel-text:   var(--panel-info-text);
  --panel-accent: var(--panel-info-accent);
  --panel-header-bg: var(--panel-info-accent);
}
.panel--amber {
  --panel-bg:     #fff4cd;
  --panel-text:   #6b4f00;
  --panel-accent: #f0c93b;
  --panel-header-bg: #f5d56a;
}
.panel--warning {
  --panel-bg:     var(--alert-warning-bg);
  --panel-text:   var(--alert-warning-text);
  --panel-accent: var(--alert-warning-border);
  --panel-header-bg: #f5d56a;
}
.panel--success {
  --panel-bg:     var(--status-verified-bg);
  --panel-text:   var(--status-verified-text);
  --panel-accent: var(--status-verified-border);
  --panel-header-bg: #b3dcc0;
}
.panel--neutral {
  --panel-bg:     var(--surface-muted);
  --panel-text:   var(--text-primary);
  --panel-accent: var(--border-medium);
  --panel-header-bg: #e5e5e5;
}

/* Inner CONTENT cap — applies regardless of how wide the panel container
   itself is. Title, body, bullets, and links all live inside .panel__content
   (or .panel__header for the header-style variant), so capping these two
   covers every text element inside any panel variant. */
.panel__content,
.panel__header {
  max-width: 940px;
}

/* Style: flat — solid fill, no decorations */
.panel--flat .panel__content {
  padding: var(--space-base) var(--space-lg);
}

/* Style: inline — thin colored left border */
.panel--inline {
  border-left: 4px solid var(--panel-accent);
}
.panel--inline .panel__content {
  padding: var(--space-base) var(--space-lg);
}

/* Style: header — darker color band at the top with the title */
.panel--header .panel__header {
  background: var(--panel-header-bg);
  color: var(--panel-text);
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-base);
  padding: var(--space-md) var(--space-lg);
}
.panel--header .panel__content {
  padding: var(--space-base) var(--space-lg);
}

/* Inner content elements */
.panel__title {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-base);
  margin-bottom: var(--space-sm);
  color: var(--panel-text);
}
.panel__body {
  font-size: var(--font-size-base);
  /* Dropped from --line-height-relaxed (1.7) to --line-height-normal
     (1.5). At body scale in a tinted panel, 1.7 leaves too much air
     and pulls the panel out of rhythm with adjacent prompt-cards and
     status-banners (both 1.5). Stays the single normal line-height. */
  line-height: var(--line-height-normal);
  margin: 0;
}
.panel__body + .panel__bullets,
.panel__title + .panel__bullets,
.panel__title + .panel__body {
  margin-top: var(--space-sm);
}
.panel__bullets {
  margin: 0;
  padding-left: var(--space-xl);
}
.panel__bullets li {
  font-size: var(--font-size-base);
  line-height: var(--line-height-normal);
  margin-bottom: var(--space-xs);
}

/* Compact size modifier — every notification on doc-review uses this
   (sm-sized title + body that matches the in-card scale). Replaces the
   four per-page .ps-{flag,variance,shortage,freq}-notice scoped sizing
   wrappers and the three .ps-{flag,variance,freq}-bulk-ack scoped
   wrappers, all of which manually re-declared title/body/bullets at
   --font-size-sm. One rule here, four call sites switch over.

   Renderer: pass `size: 'sm'` to renderPanel(). */
.panel--sm .panel__title,
.panel--sm .panel__body,
.panel--sm .panel__bullets li {
  font-size: var(--font-size-sm);
}
.panel__bullets li:last-child { margin-bottom: 0; }
.panel__links {
  margin-top: var(--space-md);
  display: flex;
  gap: var(--space-lg);
  flex-wrap: wrap;
}
.panel__link {
  color: var(--panel-text);
  font-weight: var(--font-weight-bold);
  text-decoration: underline;
}

/* ── Upload Zone ───────────────────────────────────────────────────────────── */
.upload-zone {
  position: relative;
  border: 1.5px dashed var(--border-medium);
  border-radius: var(--radius-md);
  padding: var(--space-xl) var(--space-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-base);
  background: var(--surface-white);
  margin-bottom: var(--space-sm);
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
.upload-zone:hover { border-color: var(--nyc-red); background: var(--nyc-red-bg); }
.upload-zone.is-drag-over {
  border-color: var(--nyc-red);
  background: var(--nyc-red-bg);
  border-style: solid;
}

/* Hidden native file input — the visible button triggers a click on this. */
.upload-zone__input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* Center group: button + "or drop files" sit together, centered horizontally
   inside the dashed zone (matches Figma). */
.upload-zone__center {
  display: inline-flex;
  align-items: center;
  gap: var(--space-base);
}

.upload-zone__button {
  background: var(--surface-white);
  color: var(--nyc-red);
  border: 1.5px solid var(--nyc-red);
  padding: var(--space-sm) var(--space-base);
  border-radius: var(--radius-sm);
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-sm);
  letter-spacing: 0.04em;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  text-transform: uppercase;
}
.upload-zone__button:hover { background: var(--nyc-red-bg); }
.upload-zone__icon { font-size: var(--font-size-base); }

.upload-zone__hint-inline {
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Audit #85 — Multi-file / combined-PDF clarification line. Sits as a
   full-width sibling between the dashed zone and the formats-hint
   footer so it doesn't fight the footer's left/right two-column
   layout. Slightly stronger color than .upload-zone__hint because
   it's a capability statement (what the user CAN do), not a
   constraint footnote (what the system requires). */
.upload-zone__multi-file-note {
  margin: var(--space-sm) 0 var(--space-base);
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
}

/* Footer row below the dashed zone: accepted-formats hint on the left,
   tutorial link right-aligned. Use space-between on the flex container. */
.upload-zone__footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-lg);
  margin-bottom: var(--space-base);
  flex-wrap: wrap;
}
.upload-zone__hint {
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.upload-zone__tutorial {
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}
.upload-zone__tutorial-prompt { color: var(--text-secondary); }
.upload-zone__tutorial-link {
  color: var(--nyc-red);
  font-weight: var(--font-weight-medium);
  text-decoration: underline;
}

/* ── Uploaded Files List ───────────────────────────────────────────────────── */
.uploaded-files__divider {
  border: 0;
  border-top: 1px solid var(--border-light);
  margin: var(--space-base) 0;
}
.uploaded-files {
  margin-top: 0;
}
.uploaded-files__heading {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-base);
  margin-bottom: var(--space-md);
}
.uploaded-files__row {
  display: flex;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.uploaded-files__chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  background: var(--surface-muted);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: 0;
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.uploaded-files__chip:hover {
  background: var(--surface-hover);
  color: var(--text-primary);
  border-color: var(--border-medium);
}
.uploaded-files__name {
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  text-decoration: none;
  padding: var(--space-sm) var(--space-base);
  cursor: pointer;
}
.uploaded-files__chip--deletable .uploaded-files__name {
  padding-right: var(--space-sm);
}
.uploaded-files__delete {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  padding: var(--space-sm) var(--space-md);
  display: inline-flex;
  align-items: center;
  border-left: 1px solid var(--border-light);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  transition: color var(--transition-fast), background var(--transition-fast);
}
.uploaded-files__delete:hover {
  color: var(--nyc-red);
  background: var(--nyc-red-bg);
}

/* ── Instant CSS Tooltip ─────────────────────────────────────────────────────
   Any element with [data-tooltip] gets a dark floating label on hover.
   Appears instantly (no delay) above the element with a small arrow.
   The tooltip content is the value of the data-tooltip attribute. */
[data-tooltip] {
  position: relative;
}
[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--nyc-dark);
  color: var(--text-inverse);
  font-family: var(--font-ui);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.02em;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  /* Allow longer messages to wrap onto multiple lines. width:max-content
     lets short tooltips stay snug while max-width forces wrapping at a
     comfortable measure instead of producing one super-wide line that
     overflows the page. */
  white-space: normal;
  width: max-content;
  max-width: 260px;
  text-align: center;
  line-height: 1.4;
  pointer-events: none;
  z-index: var(--z-modal);
  box-shadow: 0 4px 12px rgba(0,0,0,0.18);
  animation: tooltip-pop 120ms ease-out;
}
[data-tooltip]:hover::before,
[data-tooltip]:focus-visible::before {
  content: '';
  position: absolute;
  bottom: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: #1a1a2e;
  pointer-events: none;
  z-index: var(--z-modal);
  animation: tooltip-pop 120ms ease-out;
}

/* Results-table action tooltips — these triggers live in the rightmost
   Action column, which sits flush against the boundary of the parent
   .results-table-wrap (overflow-x: auto). A center-anchored tooltip
   extends roughly half its max-width past the trigger's right edge
   and gets clipped by that overflow boundary. Anchor to the trigger's
   right edge instead so the tooltip body grows leftward into the
   table cells beneath (where there's plenty of free space), with the
   arrow shifted toward the right so it still points down at the
   trigger. The action column is the only place this clipping happens,
   so the override scopes tightly via .results-table__action.

   Animation override is load-bearing: the default `tooltip-pop`
   keyframe carries `transform: translateX(-50%)` (correct for the
   center-anchored variant). When applied to a right-anchored tooltip
   whose static transform is `none`, the animation visibly slides the
   bubble in from the left before snapping right at the end. We
   override `animation` to use a translateX-free keyframe so the
   right-anchored variant pops in without a horizontal shift. */
.results-table__action[data-tooltip]:hover::after,
.results-table__action[data-tooltip]:focus-visible::after {
  left: auto;
  right: 0;
  transform: none;
  animation: tooltip-pop-vertical 120ms ease-out;
}
.results-table__action[data-tooltip]:hover::before,
.results-table__action[data-tooltip]:focus-visible::before {
  left: auto;
  /* Arrow sits roughly above the trigger's centre so it still reads as
     pointing AT the action, not floating beside it. The visual centre
     of a short action label ("Continue as is", "Upload") lands around
     this distance from the right edge — small enough to look anchored
     to the trigger, large enough to clear the tooltip's right edge. */
  right: 20px;
  transform: none;
  animation: tooltip-pop-vertical 120ms ease-out;
}
/* Vertical-only pop — same fade + lift as tooltip-pop but without the
   translateX(-50%) keyframe centering that the right-anchored variant
   doesn't want. Keeps the soft entrance without the horizontal slide. */
@keyframes tooltip-pop-vertical {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes tooltip-pop {
  from { opacity: 0; transform: translateX(-50%) translateY(4px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* File-remove trash icon tooltips — same right-anchor pattern as the
   action tooltips above. The trash icon sits at the far-right edge of
   the Action column, so center-anchored tooltips get clipped by the
   table wrap's overflow-x. Anchor to the right edge instead. */
.ps-flag-file-remove[data-tooltip]:hover::after,
.ps-flag-file-remove[data-tooltip]:focus-visible::after {
  left: auto;
  right: 0;
  transform: none;
  animation: tooltip-pop-vertical 120ms ease-out;
}
.ps-flag-file-remove[data-tooltip]:hover::before,
.ps-flag-file-remove[data-tooltip]:focus-visible::before {
  left: auto;
  right: 4px;
  transform: none;
  animation: tooltip-pop-vertical 120ms ease-out;
}

/* ── Prompt Card ───────────────────────────────────────────────────────────── */
.prompt-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xl);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-xl);
  margin-bottom: var(--space-lg);
  font-family: var(--font-body);
}
.prompt-card--action {
  background: var(--surface-muted);
  border: 1px solid var(--border-light);
}
.prompt-card--alt-path {
  /* Quieter than the system --alert-info-* tokens. The alt-path is an
     escape hatch we want available, not advocated for — saturated blue
     reads as a primary informational message and competes for attention
     with whatever issue the user is trying to resolve. Surface + border
     come from --surface-info-soft / --border-info-soft tokens, shared
     with the doc-review timeout body so the "informational alternative"
     palette is consistent system-wide. */
  background: var(--surface-info-soft);
  border: 1px solid var(--border-info-soft);
}
.prompt-card__content {
  flex: 1;
  min-width: 0;
}
.prompt-card__title {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-base);
  margin-bottom: var(--space-sm);
  color: var(--text-primary);
}
.prompt-card__body {
  /* Compact tier body — same scale as the panel--sm notices stacked
     above this card on doc-review, so the alt-path prompt sits in
     the same type rhythm rather than reading one step larger. */
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
  color: var(--text-secondary);
}
.prompt-card__action {
  flex-shrink: 0;
}
.prompt-card__link {
  /* Black + underline (not the system pink). On the alt-path's blue
     background, the red was visually jarring and confused users who
     read it as a destructive/primary action. Black-with-underline
     reads unambiguously as "this is a link, click it."
     Regular weight (not bold) — the link should feel like a quiet
     alternative, not a primary CTA. We want users to resolve the
     issue in front of them first; this is the door, not the
     destination. */
  color: var(--text-primary);
  font-weight: var(--font-weight-normal, 400);
  text-decoration: underline;
  white-space: nowrap;
}
.prompt-card__link:hover,
.prompt-card__link:focus-visible {
  color: var(--text-primary);
  text-decoration-thickness: 2px;
}

/* ============================================================================
   DASHBOARD CONTENT BLOCKS
   Per-state long-form content rendered by js/pages/landing.js between the
   status banner and the application card list. Each block type below has a
   matching renderer in landing.js. The visual language is intentionally
   quieter than .alert-banner — these blocks inform, they don't alert.
   ============================================================================ */

.dash-block {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}

.dash-block__header {
  margin-bottom: var(--space-md);
}

.dash-block__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
  text-transform: none;
  letter-spacing: 0;
}

.dash-block__intro {
  color: var(--text-secondary);
  margin: 0;
}

.dash-block__body {
  color: var(--text-primary);
}
.dash-block__body p { margin: 0 0 var(--space-sm); }
.dash-block__body p:last-child { margin-bottom: 0; }
.dash-block__body ul,
.dash-block__body ol {
  margin: var(--space-sm) 0;
  padding-left: var(--space-xl);
}
.dash-block__body li {
  margin-bottom: var(--space-xs);
}

.dash-block__footer {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  margin: var(--space-md) 0 0;
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-light);
}

.dash-block__note {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  margin: var(--space-xs) 0 0;
  font-style: italic;
}

.dash-block__highlight {
  background: var(--panel-warning-bg);
  border-left: 3px solid var(--panel-warning-accent);
  padding: var(--space-sm) var(--space-md);
  margin-top: var(--space-md);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
}

/* Variant tints — applied to .dash-block--rich.dash-block--<variant>.
   Quieter than alerts (no left bar, just a soft background) so they read as
   informational content, not as alerts. */
.dash-block--info    { background: var(--panel-info-bg); border-color: var(--border-info-soft); }
.dash-block--success { background: var(--panel-success-bg); border-color: var(--panel-success-accent); }
.dash-block--warning { background: var(--panel-warning-bg); border-color: var(--panel-warning-accent); }
.dash-block--error   { background: var(--panel-error-bg); border-color: var(--panel-error-accent); }

/* Grid variants for multi-column blocks */
.dash-block__grid {
  display: grid;
  gap: var(--space-lg);
}
.dash-block__grid--3 {
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 900px) {
  .dash-block__grid--3 { grid-template-columns: 1fr; }
}

.dash-block__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.dash-block__col--with-icon { padding-top: var(--space-xs); }
.dash-block__col-icon {
  font-size: 22px;
  color: var(--nyc-red);
  margin-bottom: var(--space-xs);
}
.dash-block__col-heading {
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dash-block__col-body {
  color: var(--text-primary);
  font-size: var(--font-size-base);
}

/* ── Callout band — short, full-width emphasis with leading icon ─────────── */
.dash-callout {
  display: flex;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-lg);
  align-items: flex-start;
}
.dash-callout--info {
  background: var(--panel-info-bg);
  border: 1px solid var(--border-info-soft);
}
.dash-callout--success {
  background: var(--panel-success-bg);
  border: 1px solid var(--panel-success-accent);
}
.dash-callout--warning {
  background: var(--panel-warning-bg);
  border: 1px solid var(--panel-warning-accent);
}
.dash-callout__icon {
  font-size: 24px;
  color: var(--text-secondary);
  flex-shrink: 0;
  margin-top: 2px;
}
.dash-callout__content {
  flex: 1;
}
.dash-callout__title {
  font-weight: var(--font-weight-semibold);
  margin-bottom: var(--space-2xs);
}
.dash-callout__body {
  color: var(--text-primary);
  font-size: var(--font-size-base);
}
.dash-callout__body a {
  color: var(--text-link);
  font-weight: var(--font-weight-medium);
}

/* ── Forward-looking timeline (waitlist "what the city is doing") ──────────
   Hairline dividers between consecutive entries so each dated news beat
   reads as its own item, not a wall of paragraphs. Padding rather than gap
   spacing so the divider has somewhere to live. */
.dash-timeline {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}
.dash-timeline__item {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: var(--space-lg);
  align-items: start;
  padding: var(--space-lg) 0;
  border-top: 1px solid var(--border-light);
}
.dash-timeline__item:first-child {
  padding-top: 0;
  border-top: 0;
}
.dash-timeline__item:last-child { padding-bottom: 0; }
@media (max-width: 700px) {
  .dash-timeline__item { grid-template-columns: 1fr; gap: var(--space-2xs); }
}
.dash-timeline__date {
  font-family: var(--font-ui);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  padding-top: 2px;
}
.dash-timeline__label {
  font-weight: var(--font-weight-semibold);
  margin-bottom: var(--space-2xs);
}
.dash-timeline__body {
  color: var(--text-primary);
  font-size: var(--font-size-base);
}
.dash-timeline__body strong { font-weight: var(--font-weight-semibold); }

/* ── Methodology block (waitlist selection ordering) ─────────────────────── */
.dash-priority-list {
  margin: 0 0 var(--space-md);
  padding-left: var(--space-xl);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.dash-priority-list li::marker {
  font-weight: var(--font-weight-semibold);
  color: var(--nyc-red);
}
.dash-priority-list__detail {
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}

/* ── Alternatives grid — programs you can still try ──────────────────────── */
.dash-alternatives {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-md);
}

/* ── Sources footer — small, muted, separated from body ──────────────────── */
.dash-sources {
  display: none;
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--border-light);
  color: var(--text-muted);
  font-size: var(--font-size-xs);
}
.dash-sources--inline {
  margin-top: var(--space-2xs);
  padding-top: 0;
  border: 0;
}
.dash-source {
  color: var(--text-muted);
  text-decoration: underline;
  text-decoration-style: dotted;
}
.dash-source + .dash-source { margin-left: var(--space-sm); }
.dash-source:hover { color: var(--text-link); }

/* ── Footnote bar (unauth landing) ───────────────────────────────────────── */
.dashboard-footnote {
  margin-top: var(--space-xl);
  padding: var(--space-md) var(--space-lg);
  background: var(--surface-muted);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  text-align: center;
}
.dashboard-footnote a {
  color: var(--text-link);
}

/* ── Dashboard section heading (above "My Applications" list) ────────────── */
.dashboard-section-heading {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: var(--space-lg) 0 var(--space-md);
  text-transform: none;
  letter-spacing: 0;
}

/* ============================================================================
   PHASE 2 — SIDEBAR ADAPTIVITY: badges, submenus
   ============================================================================ */

.sidebar-nav__badge {
  margin-left: auto;
  min-width: 22px;
  padding: 2px 6px;
  border-radius: 11px;
  background: var(--nyc-red);
  color: var(--text-inverse);
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-bold);
  text-align: center;
  line-height: 1.4;
}

/* Base reset for the sub-menu trigger <button>. Full visual styling
   (font-family / weight / chip surface) is applied by the more-specific
   .sidebar-nav__item--submenu > .sidebar-nav__link--toggle rule further
   down. `font: inherit` was here originally; it's omitted now because
   the body's font is Lora (serif) and the toggle should inherit nothing
   font-related from above — the more-specific rule states the fonts
   explicitly. */
.sidebar-nav__link--toggle {
  border: 0;
  width: 100%;
  text-align: left;
  cursor: pointer;
}
.sidebar-nav__chevron {
  margin-left: auto;
  font-size: 18px !important;
  transition: transform 0.18s ease;
}
.sidebar-nav__link--toggle[aria-expanded="true"] .sidebar-nav__chevron {
  transform: rotate(180deg);
}
.sidebar-nav__submenu {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.22s ease;
}
.sidebar-nav__submenu.is-open {
  max-height: 400px;
}
.sidebar-nav__sublink {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-2xs) var(--space-md) var(--space-2xs) calc(var(--space-md) + 28px);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--font-size-sm);
}
.sidebar-nav__sublink:hover {
  color: var(--text-link);
  background: var(--surface-hover);
}
.sidebar-nav__sublink-ext {
  font-size: 14px !important;
  opacity: 0.7;
}

/* ============================================================================
   PHASE 2 — DEADLINE PILL
   ============================================================================ */

.deadline-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: 2px 10px;
  border-radius: 999px;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  line-height: 1.6;
  vertical-align: middle;
}
.deadline-pill__icon {
  font-size: 14px !important;
}
.deadline-pill--soft {
  background: var(--panel-warning-bg);
  color: var(--alert-warning-text);
  border: 1px solid var(--panel-warning-accent);
}
.deadline-pill--hard {
  background: var(--panel-error-bg);
  color: var(--alert-error-text);
  border: 1px solid var(--panel-error-accent);
}

/* ============================================================================
   PHASE 2 — NOTIFICATION TRAY
   ============================================================================ */

.notif-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin-right: var(--space-sm);
}

.notif-bell {
  position: relative;
  background: none;
  border: 0;
  padding: 6px;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  cursor: pointer;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.notif-bell:hover { background: var(--surface-hover); color: var(--text-primary); }

/* Pip is a perfect 18×18 circle anchored OUTSIDE the bell's top-right
   corner so the bell icon stays fully visible underneath. The 2px ring
   matches the page-header surface so the pip reads as floating above
   the bell, not stamped on top of it. Earlier rules used content-box +
   horizontal padding which produced a wide oval. */
.notif-bell__pip {
  position: absolute;
  top: -3px;
  right: -3px;
  width: 18px;
  height: 18px;
  background: var(--nyc-red);
  color: #fff;
  border: 2px solid var(--page-pink-deep);
  border-radius: 999px;
  box-sizing: border-box;
  padding: 0;
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: var(--font-weight-bold);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
/* Wider variant for "9+" overflow — preserves the rounded ends but no
   longer forces a perfect circle. */
.notif-bell__pip--wide {
  width: auto;
  min-width: 22px;
  padding: 0 4px;
}

.notif-tray {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 360px;
  max-width: calc(100vw - 32px);
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  display: none;
  z-index: 50;
}
.notif-tray.is-open { display: block; }

.notif-tray__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-md);
  border-bottom: 1px solid var(--border-light);
}
.notif-tray__mark-all {
  background: none;
  border: 0;
  color: var(--text-link);
  font-size: var(--font-size-xs);
  cursor: pointer;
  text-decoration: underline;
}
.notif-tray__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 420px;
  overflow-y: auto;
}
.notif-tray__empty {
  padding: var(--space-lg);
  text-align: center;
  color: var(--text-muted);
  font-size: var(--font-size-sm);
}
.notif-tray__item {
  padding: var(--space-md);
  border-bottom: 1px solid var(--border-light);
  cursor: pointer;
  position: relative;
}
.notif-tray__item:last-child { border-bottom: 0; }
.notif-tray__item.is-unread::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 18px;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--nyc-red);
}
.notif-tray__item.is-read { opacity: 0.68; }
.notif-tray__item:hover { background: var(--surface-hover); }
.notif-tray__item-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-bottom: 2px;
}
.notif-tray__item-title {
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
}
.notif-tray__item-ts {
  font-size: var(--font-size-2xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.notif-tray__item-body {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.5;
}
.notif-tray__item-body a { color: var(--text-link); }
.notif-tray__item--warning .notif-tray__item-title { color: var(--alert-warning-text); }
.notif-tray__item--success .notif-tray__item-title { color: var(--alert-success-text); }
.notif-tray__footer {
  padding: var(--space-sm) var(--space-md);
  border-top: 1px solid var(--border-light);
  background: var(--surface-muted);
  font-size: var(--font-size-2xs);
  color: var(--text-muted);
  text-align: center;
}

/* ============================================================================
   PHASE 5 — LL30 language footnote on the NYC bar
   ============================================================================ */

.nyc-bar__right {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.nyc-bar__lang-footnote {
  font-size: var(--font-size-2xs);
  color: var(--text-secondary);
  text-align: right;
  padding: 2px var(--space-base) var(--space-2xs);
  max-width: 100%;
  background: rgba(255,255,255,0.4);
}
.nyc-bar__lang-footnote a {
  color: var(--text-link);
  text-decoration: underline;
}

/* ============================================================================
   PHASE 3 — IMAGERY
   Three render shapes:
     .hero-img      — compact inline image (unauth welcome-section)
     .state-hero    — full-width band at top of auth dashboard
     .accent-img    — sidebar accent inside the right-rail or resource blocks
   Each carries a gradient fallback inline; .--overlay-* tunes the wash on top.
   ============================================================================ */

.welcome-section--hero {
  align-items: stretch;
}

/* Compact inline image — used by the unauth welcome-section */
.hero-img {
  position: relative;
  width: 240px;
  height: 180px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  flex-shrink: 0;
}
.hero-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.hero-img--compact { width: 280px; height: 200px; }
.hero-img__credit {
  position: absolute;
  bottom: 4px;
  right: 6px;
  font-size: 9px;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
  opacity: 0.85;
}
.hero-img__credit a { color: inherit; text-decoration: none; }

/* Full-width state hero band at the top of authenticated dashboard.
   The .state-hero__overlay applies the per-state wash; .--overlay-* mods
   tune its color so the same image reads differently across states. */
.state-hero {
  position: relative;
  width: 100%;
  height: 200px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-lg);
}
.state-hero__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.state-hero__overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.state-hero--overlay-green   .state-hero__overlay { background: rgba(46, 125, 50, 0.45); }
.state-hero--overlay-orange  .state-hero__overlay { background: rgba(230, 144, 60, 0.40); }
.state-hero--overlay-pink    .state-hero__overlay { background: rgba(201, 40, 62, 0.30); }
.state-hero--overlay-charcoal .state-hero__overlay { background: rgba(26, 26, 46, 0.55); }
.state-hero--overlay-desaturated .state-hero__overlay {
  background: rgba(255, 255, 255, 0.20);
  backdrop-filter: saturate(0.55);
  -webkit-backdrop-filter: saturate(0.55);
}
.state-hero--overlay-warm-neutral .state-hero__overlay { background: rgba(245, 230, 210, 0.30); }
.state-hero__credit {
  position: absolute;
  bottom: 6px;
  right: 10px;
  font-size: 10px;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0,0,0,0.5);
  text-decoration: none;
  opacity: 0.7;
  z-index: 1;
}
.state-hero__credit:hover { opacity: 1; }

/* Per-state hero heights — denied uses a calmer shorter band */
.state-hero[data-state="DENIED"] { height: 140px; }
.state-hero[data-state="WAITLISTED"] { height: 160px; }

/* Right-rail accent — smaller, captioned */
.accent-img {
  position: relative;
  width: 100%;
  height: 160px;
  border-radius: var(--radius-md);
  overflow: hidden;
  margin: 0;
}
.accent-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.accent-img__caption {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: var(--space-sm) var(--space-md);
  background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.6) 100%);
  color: #fff;
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
}
.accent-img__credit {
  position: absolute;
  top: 6px;
  right: 8px;
  font-size: 9px;
  color: #fff;
  opacity: 0.7;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.accent-img__credit a { color: inherit; text-decoration: none; }

/* Footnote separator for the unauth footer */
.dashboard-footnote__sep {
  margin: 0 var(--space-sm);
  color: var(--text-muted);
}

/* ============================================================================
   PHASE 4 — TIMELINE (IN_PROCESS application review pipeline)
   Horizontal-ish vertical timeline with completed/current/upcoming dots.
   ============================================================================ */

.dash-block--timeline { padding-top: var(--space-lg); }

.timeline {
  list-style: none;
  padding: 0;
  margin: var(--space-md) 0 0;
}

.timeline__step {
  display: grid;
  grid-template-columns: 32px 1fr;
  gap: var(--space-md);
  align-items: stretch;
  position: relative;
}

.timeline__marker {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 4px;
}

.timeline__dot {
  width: 16px;
  height: 16px;
  border-radius: 999px;
  border: 3px solid var(--border-light);
  background: var(--surface-white);
  z-index: 1;
}

.timeline__line {
  flex: 1;
  width: 2px;
  background: var(--border-light);
  margin-top: 4px;
}

.timeline__content {
  padding-bottom: var(--space-lg);
}

.timeline__label {
  font-weight: var(--font-weight-semibold);
  margin-bottom: 2px;
}

.timeline__body {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  margin-bottom: var(--space-2xs);
}

.timeline__date {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* States */
.timeline__step--completed .timeline__dot {
  background: var(--stepper-completed);
  border-color: var(--stepper-completed);
}
.timeline__step--completed .timeline__line {
  background: var(--stepper-completed);
}
.timeline__step--current .timeline__dot {
  background: var(--nyc-red);
  border-color: var(--nyc-red);
  box-shadow: 0 0 0 4px rgba(201, 40, 62, 0.18);
  animation: timeline-pulse 1.6s infinite;
}
.timeline__step--current .timeline__label {
  color: var(--nyc-red);
}

@keyframes timeline-pulse {
  0%   { box-shadow: 0 0 0 4px rgba(201, 40, 62, 0.18); }
  50%  { box-shadow: 0 0 0 8px rgba(201, 40, 62, 0.08); }
  100% { box-shadow: 0 0 0 4px rgba(201, 40, 62, 0.18); }
}

/* ============================================================================
   PHASE 4 — SUBSCRIBE-UPDATES inline form
   ============================================================================ */

.dash-subscribe {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
  align-items: center;
}
.dash-subscribe__input {
  flex: 1 1 240px;
  min-width: 0;
}
.dash-subscribe__ok {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--alert-success-text);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
}
.dash-subscribe__ok .material-symbols-outlined {
  font-size: 18px !important;
}

/* ============================================================================
   PHASE 6 — MOBILE RESPONSIVE
   The new dashboard components reflow at narrow widths. Most are already
   single-column at small sizes (grid--3 → 1fr at <900px); these tweaks
   handle the rest: state hero, notification tray, sidebar submenu chevron,
   and the alternative grid.
   ============================================================================ */

@media (max-width: 700px) {
  /* State hero — shorter band, drop credit overlay clutter */
  .state-hero { height: 140px; }
  .state-hero[data-state="DENIED"] { height: 110px; }
  .state-hero[data-state="WAITLISTED"] { height: 120px; }
  .state-hero__credit { display: none; }

  /* Welcome section: stack the hero image below the copy */
  .welcome-section--hero,
  .welcome-section {
    grid-template-columns: 1fr;
  }
  .hero-img,
  .hero-img--compact { width: 100%; height: 160px; }

  /* Dashboard blocks: tighten padding so two finger-widths aren't wasted */
  .dash-block { padding: var(--space-md); }

  /* Notification tray: pin to viewport edges so it doesn't overflow */
  .notif-tray {
    position: fixed;
    top: auto;
    bottom: 80px;
    left: var(--space-md);
    right: var(--space-md);
    width: auto;
  }

  /* Alternatives grid: 1 column at narrow sizes (auto-fill already covers
     this when 240px minmax exceeds viewport, but be explicit) */
  .dash-alternatives { grid-template-columns: 1fr; }

  /* Timeline date should sit under the label, not float right */
  .timeline__date { margin-top: var(--space-2xs); }

  /* Language footnote: stack under the bar, not on the right */
  .nyc-bar__lang-footnote { text-align: left; }
}

@media (max-width: 500px) {
  /* Subscribe form: stack the email input over the button so neither truncates */
  .dash-subscribe { flex-direction: column; align-items: stretch; }
  .dash-subscribe__input,
  .dash-subscribe button { width: 100%; }
}

/* ============================================================================
   PHASE 7 — UPDATE-CASE change-type picker
   ============================================================================ */

.update-case-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-md);
  margin-top: var(--space-md);
}

.update-case-card {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  padding: var(--space-md);
  text-align: left;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  font: inherit;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.05s;
}
.update-case-card:hover {
  border-color: var(--nyc-red);
  box-shadow: 0 2px 8px rgba(201, 40, 62, 0.08);
}
.update-case-card:active { transform: translateY(1px); }
.update-case-card.is-active {
  border-color: var(--nyc-red);
  background: var(--nyc-red-bg);
}
.update-case-card__icon {
  font-size: 24px;
  color: var(--nyc-red);
}
.update-case-card__label {
  font-weight: var(--font-weight-semibold);
}
.update-case-card__body {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}

/* ============================================================================
   PHASE 7 — APPLY-PAGE program-card grid
   Replaces the inline-styled cards in pages/apply.js with proper layout.
   ============================================================================ */

.programs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-lg);
}

.program-card {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-md);
}

.program-card--3k {
  border-color: var(--panel-success-accent);
  background: var(--panel-success-bg);
}

.program-card__agency {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  margin: var(--space-2xs) 0 var(--space-sm);
}

.program-card__notice {
  font-size: var(--font-size-sm);
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius-sm);
  background: var(--panel-warning-bg);
  border: 1px solid var(--panel-warning-accent);
  color: var(--alert-warning-text);
  margin-bottom: var(--space-md);
}

/* The "positive" note reads as plain body text — a green tag on the green
   3-K card background failed contrast. No box, no green; black body color. */
.program-card__notice--positive {
  background: transparent;
  border: none;
  padding: 0;
  color: var(--text-primary);
}

.program-card__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin-bottom: var(--space-md);
  padding: var(--space-sm) 0;
  border-top: 1px solid var(--border-light);
  border-bottom: 1px solid var(--border-light);
}
.program-card__meta > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-sm);
}
.program-card__meta-label {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.program-card__meta-value {
  font-size: var(--font-size-sm);
  text-align: right;
}

.program-card__details summary {
  font-size: var(--font-size-sm);
  color: var(--nyc-red);
  cursor: pointer;
  font-weight: var(--font-weight-semibold);
}
.program-card__details p {
  font-size: var(--font-size-sm);
  margin-top: var(--space-sm);
  line-height: 1.6;
}

/* ============================================================================
   OTHER PROGRAMS PAGE
   Vertical list of program rows; each row is a two-column grid (head + body).
   ============================================================================ */

.other-programs {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.other-programs__row {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: var(--space-xl);
  align-items: start;
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
}

.other-programs__label {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-2xs);
}

.other-programs__eligibility {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  margin: 0;
}

.other-programs__body p {
  margin: 0 0 var(--space-md);
}

.other-programs__notes {
  margin: 0 0 var(--space-md);
  padding-left: var(--space-lg);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}

@media (max-width: 700px) {
  .other-programs__row { grid-template-columns: 1fr; gap: var(--space-md); }
}

/* ============================================================================
   QA FIX — gradient-only imagery + tighter hero/welcome relationship.
   Overrides the Phase-3 styles above where they conflict.
   ============================================================================ */

/* Hero band: gradient is always present, optional <img> rides on top.
   When no image is supplied, the gradient alone is the treatment. */
.state-hero {
  position: relative;
  width: 100%;
  height: 160px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-xl);     /* QA-U1 — was --space-lg; widen the gap with welcome-section */
}
.state-hero--compact { height: 140px; }
.state-hero--short   { height: 110px; }
.state-hero[data-state="DENIED"]      { height: 110px; }
.state-hero[data-state="WAITLISTED"]  { height: 120px; }
.state-hero[data-state="DRAFT_EXPIRED"] { height: 110px; }
.state-hero__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* Subtle inset shadow so the gradient reads as an intentional band, not
   accidental blank space. Per-accent saturation tweak via filter. */
.state-hero::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,0.06), inset 0 0 0 1px rgba(0,0,0,0.04);
  border-radius: inherit;
}
.state-hero--accent-desaturated { filter: saturate(0.6); }

/* Inline hero (unauth welcome-section) — same approach, smaller. */
.hero-img {
  position: relative;
  width: 280px; height: 200px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  flex-shrink: 0;
}
.hero-img--compact { width: 280px; height: 200px; }
.hero-img img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}

/* QA-U1: welcome-section card gets its own breathing room from any hero
   band above it (the band already has bottom-margin, this is a belt-and-
   braces). */
.layout-dashboard__main > .welcome-section {
  margin-top: 0;
}

/* Drop accent-image styles to a no-op so any stray invocation collapses
   instead of leaving a dangling figure. */
.accent-img { display: none; }

/* ============================================================================
   QA FIX — sub-menu typography matches parent sidebar items.
   Earlier the sub-menu trigger rendered as a borderless header; QA flagged
   it looked like a different component. Now it inherits the .sidebar-nav__link
   chip styling — same padding, same radius, same hover state.
   ============================================================================ */

/* Sub-menu trigger must be visually indistinguishable from a sibling
   .sidebar-nav__link <a>. Earlier rules let the <button> element inherit
   browser defaults (which fell through to the body's Lora serif) and used
   tighter padding + lighter weight than the rest of the nav. Re-stated
   here as a full override so nothing leaks through. */
.sidebar-nav__item--submenu > .sidebar-nav__link--toggle {
  display: flex;
  align-items: center;
  gap: var(--space-md);                       /* match .sidebar-nav__link */
  width: 100%;
  text-align: left;
  border: 0;
  cursor: pointer;
  font-family: var(--font-ui);                /* Montserrat, not Lora */
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);   /* match inactive nav item */
  color: var(--text-primary);
  padding: var(--space-lg) var(--space-lg);   /* match .sidebar-nav__link */
  background: rgba(255, 255, 255, 0.45);      /* match the chip surface */
  border-radius: var(--radius-md);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.sidebar-nav__item--submenu > .sidebar-nav__link--toggle:hover {
  background: rgba(255, 255, 255, 0.7);
  color: var(--nyc-red);
}
.sidebar-nav__item--submenu > .sidebar-nav__link--toggle:hover .sidebar-nav__icon {
  color: var(--nyc-red);
}
/* The toggle has its own icon — make sure it inherits the same icon
   treatment as <a>-flavored siblings (neutral text color by default,
   blue on hover via the rule above). Earlier the icon was inheriting
   from a parent .sidebar-nav__item context that didn't reset it, so it
   was rendering with the Material-Symbols default tint (pink/red on
   this page). */
.sidebar-nav__item--submenu > .sidebar-nav__link--toggle .sidebar-nav__icon {
  color: var(--text-primary);
}

/* Sub-menu items: indented, lighter, no chip */
.sidebar-nav__submenu {
  padding-top: var(--space-2xs);
}
.sidebar-nav__sublink {
  padding-left: calc(var(--space-md) + 28px);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  text-decoration: none;
  padding-top: var(--space-2xs);
  padding-bottom: var(--space-2xs);
  border-radius: var(--radius-sm);
}
.sidebar-nav__sublink:hover {
  color: var(--text-link);
  background: var(--surface-hover);
}

/* Sidebar item labels never wrap — shorter labels combined with this
   guarantee that "Applications (1)" stays on a single line even when the
   badge is present. */
.sidebar-nav__label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* User-nav dropdown — external link icon (e.g. Provider Portal switch) */
.user-dropdown__ext {
  font-size: 14px !important;
  opacity: 0.55;
  margin-left: var(--space-xs);
  vertical-align: middle;
}

/* State greeting — single-line replacement for the heavy welcome card.
   Sits between the hero band and the status banner. Compact, low-emphasis,
   doesn't compete with the status banner that follows. */
.state-greeting {
  margin: 0 0 var(--space-md);
  color: var(--text-secondary);
  font-size: var(--font-size-base);
  font-family: var(--font-body);
}

/* ============================================================================
   QA FIX — submenu expands within a single chip surface.
   Earlier the trigger button carried the white chip and the expanded
   <ul> floated on the pink page background as orphans. Now the parent
   <li>.sidebar-nav__item--submenu owns the chip when expanded, and the
   trigger + sub-items both render inside it.
   :has() drives the visual state from the open class JS already toggles.
   ============================================================================ */

/* Expanded state — apply the chip to the parent <li>, drop the chip from
   the trigger button so it visually merges into the surface. */
.sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) {
  background: rgba(255, 255, 255, 0.45);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) > .sidebar-nav__link--toggle {
  background: transparent;
  border-radius: 0;
}

.sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) > .sidebar-nav__link--toggle:hover {
  background: rgba(255, 255, 255, 0.4);
}

/* Sub-menu list sits flush against the trigger inside the unified chip,
   with a hairline divider so the boundary still reads. Pad bottom so the
   last sublink doesn't crash into the chip's rounded edge. */
.sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) > .sidebar-nav__submenu {
  padding: var(--space-xs) 0 var(--space-md);
  border-top: 1px solid rgba(0, 0, 0, 0.06);
  background: transparent;          /* inherits the parent <li>'s chip surface */
}

/* Sublink padding tightens so the items don't read as a separate column;
   they're indented under the icon column but breathe inside the chip. */
.sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) .sidebar-nav__sublink {
  padding-top: var(--space-xs);
  padding-bottom: var(--space-xs);
  padding-left: calc(var(--space-lg) + 28px);
  border-radius: 0;
}
.sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) .sidebar-nav__sublink:hover {
  background: rgba(255, 255, 255, 0.7);
}

/* ============================================================================
   STAT-CARD + STAT-GRID
   Distinctive shape from .dash-block — leads with a large numeric value
   instead of a heading. Use to break up "stacked banners" reading on
   pages with multiple independent metrics (e.g. APPROVED's recert
   deadline + co-pay). Grid auto-fits to viewport: side-by-side at desktop,
   stacked at mobile.
   ============================================================================ */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-lg);
  margin-bottom: var(--space-lg);
}

.stat-card {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Accent variants tinted with the panel-* palette, matching the rest of
   the design system. */
.stat-card--warning { background: var(--panel-warning-bg); border-color: var(--panel-warning-accent); }
.stat-card--success { background: var(--panel-success-bg); border-color: var(--panel-success-accent); }
.stat-card--info    { background: var(--panel-info-bg);    border-color: var(--border-info-soft); }
.stat-card--error   { background: var(--panel-error-bg);   border-color: var(--panel-error-accent); }

.stat-card__value {
  font-family: var(--font-heading);
  display: flex;
  align-items: baseline;
  gap: var(--space-xs);
  line-height: 1;
  margin-bottom: var(--space-xs);
}

.stat-card__value-num {
  font-size: 2.75rem;       /* 44px — large enough to read at-a-glance */
  font-weight: var(--font-weight-bold);
  color: var(--text-primary);
  letter-spacing: -0.02em;
}

.stat-card__value-unit {
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  color: var(--text-secondary);
}

.stat-card__label {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.stat-card__body {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  margin: var(--space-xs) 0 0;
  line-height: 1.5;
}

.stat-card__cta {
  margin-top: auto;
  padding-top: var(--space-md);
  color: var(--text-link);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  text-decoration: none;
  align-self: flex-start;
}
.stat-card__cta:hover { text-decoration: underline; }

/* When a stat-grid has sources, the wrap holds both. The grid itself
   collapses its own bottom-margin so the sources line sits closer. */
.stat-grid-wrap { margin-bottom: var(--space-lg); }
.stat-grid-wrap > .stat-grid { margin-bottom: 0; }
.stat-grid-wrap > .dash-sources {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--border-light);
}

/* ============================================================================
   JOURNEY — Horizontal "you are here" lifecycle indicator.
   Page-centerpiece component for WAITLISTED (and reusable for any state
   where position-in-the-system is the user's biggest question). Visually
   distinct from .timeline (vertical, narrow) and .stat-grid (numeric).
   ============================================================================ */

.journey {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}

.journey__header {
  margin-bottom: var(--space-xl);
}

.journey__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
}

.journey__intro {
  margin: 0;
  color: var(--text-secondary);
  text-align: left;
}

/* Horizontal flow of stages. Each stage owns a slice of the width, with
   a connecting line painted underneath the dots. */
.journey__stages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
  gap: 0;
  position: relative;
}

.journey__stage {
  position: relative;
  text-align: center;
  padding: 0 var(--space-xs);
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Connecting line runs along the dot's vertical center, behind the dots.
   Painted per-stage so completed→current can be a gradient. */
.journey__line {
  position: absolute;
  top: 22px;          /* matches dot center: padding-top + dot height/2 */
  left: 50%;
  right: -50%;
  height: 3px;
  background: var(--border-light);
  z-index: 0;
}
.journey__line--end { display: none; }    /* no line after the last stage */
.journey__stage--completed .journey__line {
  background: var(--stepper-completed);
}
.journey__stage--current .journey__line {
  background: linear-gradient(to right,
    var(--nyc-red) 0%, var(--nyc-red) 50%,
    var(--border-light) 50%, var(--border-light) 100%);
}

/* The dot — large enough to read at a glance, centered on the line. */
.journey__dot {
  position: relative;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  background: var(--surface-white);
  border: 3px solid var(--border-light);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  color: var(--text-muted);
  font-weight: var(--font-weight-bold);
}
.journey__dot-icon { font-size: 22px !important; color: #fff; }
.journey__dot-num  { font-size: var(--font-size-base); }

/* Completed state — solid green, white check inside */
.journey__stage--completed .journey__dot {
  background: var(--stepper-completed);
  border-color: var(--stepper-completed);
}

/* Current state — solid red, pulsing ring, larger emphasis */
.journey__stage--current .journey__dot {
  background: var(--nyc-red);
  border-color: var(--nyc-red);
  width: 52px;
  height: 52px;
  margin-top: -4px;          /* keep dot center aligned with line */
}
.journey__dot-pulse {
  width: 14px;
  height: 14px;
  border-radius: 999px;
  background: #fff;
  animation: journey-pulse 1.8s ease-in-out infinite;
}
@keyframes journey-pulse {
  0%, 100% { transform: scale(1);   opacity: 1; }
  50%      { transform: scale(0.7); opacity: 0.7; }
}
.journey__stage--current .journey__dot::after {
  content: '';
  position: absolute;
  inset: -8px;
  border-radius: 999px;
  border: 3px solid rgba(201, 40, 62, 0.18);
  animation: journey-ring 2.2s ease-in-out infinite;
}
@keyframes journey-ring {
  0%, 100% { transform: scale(0.95); opacity: 0.9; }
  50%      { transform: scale(1.12); opacity: 0; }
}

/* Stage label + date under the dot */
.journey__stage-body {
  margin-top: var(--space-md);
  max-width: 18ch;
}
.journey__stage-label {
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
}
.journey__stage--upcoming .journey__stage-label { color: var(--text-muted); }
.journey__stage--current .journey__stage-label  { color: var(--nyc-red); }

.journey__stage-meta {
  margin-top: 2px;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.journey__stage-meta--note {
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-secondary);
  font-style: italic;
  margin-top: var(--space-xs);
  line-height: 1.4;
}

/* "You are here" pill under the current stage */
.journey__here-pill {
  display: inline-block;
  margin-top: var(--space-sm);
  padding: 3px 10px;
  background: var(--nyc-red);
  color: #fff;
  border-radius: 999px;
  font-size: 10px;
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Journey footer — single bottom row carrying both the context line
   (left edge) and the CTA button (right edge). justify-content:
   space-between guarantees the CTA stays anchored to the right
   regardless of how short the context is or how max-width caps it.
   Earlier versions used flex:1 + max-width on the context, which
   left dead space AFTER the CTA when the context didn't fill its
   max-width — pulling the CTA away from the right edge. */
.journey__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border-light);
}
.journey__context {
  flex: 0 1 auto;
  max-width: 50ch;          /* wrap before the CTA's space (was 56ch — trimmed 10%) */
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  text-align: left;
  line-height: 1.5;
}
.journey__context strong { color: var(--text-primary); }
.journey__cta { flex-shrink: 0; }

@media (max-width: 700px) {
  .journey__footer {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-md);
  }
  .journey__cta { align-self: flex-end; }
}

/* Mobile: collapse to a vertical-ish layout. The horizontal flow doesn't
   reduce gracefully at narrow widths — switch the auto-cols to rows. */
@media (max-width: 700px) {
  .journey__stages {
    grid-auto-flow: row;
    gap: var(--space-md);
  }
  .journey__stage {
    flex-direction: row;
    text-align: left;
    align-items: flex-start;
    gap: var(--space-md);
    padding: 0;
  }
  .journey__line {
    top: auto; left: 22px; right: auto; bottom: -50%;
    width: 3px; height: 50%;
  }
  .journey__stage:last-child .journey__line { display: none; }
  .journey__stage-body { margin-top: 0; max-width: none; }
  .journey__stage--current .journey__dot { margin-top: 0; }
}

/* ============================================================================
   PRIORITY-TIERS — Ranked categories with a fallback + exception note.
   Visual: each tier is a row with a large numbered chip on the left and
   the category label + detail on the right. The chip's saturation falls
   slightly as rank descends (Tier 1 deepest red, Tier 3 lighter) to
   reinforce the hierarchy without ever rendering as "Tier 3 is bad."
   ============================================================================ */

.priority-tiers {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}

.priority-tiers__header {
  margin-bottom: var(--space-lg);
}

.priority-tiers__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
}

.priority-tiers__intro {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.5;
}

.priority-tiers__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.priority-tiers__tier {
  display: grid;
  grid-template-columns: 38px 1fr;        /* was 56px — tightened with smaller chip */
  align-items: center;
  gap: var(--space-md);                   /* was --space-lg — closer pairing now */
  padding: var(--space-md) 0;
  border-top: 1px solid var(--border-light);
}
.priority-tiers__tier:first-child {
  padding-top: 0;
  border-top: 0;
}
.priority-tiers__tier:last-child { padding-bottom: 0; }

/* Numbered chip — 60% of the previous size (44px → 26px). Font scales
   from 1.5rem down to 0.875rem so the digit still reads tight inside
   the smaller circle. Saturation gradient by tier preserved. */
.priority-tiers__rank {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  font-family: var(--font-heading);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  background: var(--nyc-red);
  color: #fff;
  line-height: 1;
}
.priority-tiers__tier:nth-child(1) .priority-tiers__rank { background: var(--nyc-red); }
.priority-tiers__tier:nth-child(2) .priority-tiers__rank { background: #d9536b; }   /* red one step lighter */
.priority-tiers__tier:nth-child(3) .priority-tiers__rank { background: #e57f8f; }   /* red two steps lighter */
.priority-tiers__tier:nth-child(4) .priority-tiers__rank { background: #ecaab4; }   /* red three steps lighter */

.priority-tiers__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.priority-tiers__label {
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  font-size: var(--font-size-base);
}

.priority-tiers__detail {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  line-height: 1.5;
}

/* Footer with fallback ("everyone else") + optional exception ("not in the
   system at all"). Sits below a divider; lighter weight than the tiers. */
.priority-tiers__footer {
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-light);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.priority-tiers__fallback {
  margin: 0;
  color: var(--text-primary);
  font-size: var(--font-size-sm);
}
.priority-tiers__exception {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  font-style: italic;
}

.priority-tiers__sources {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--border-light);
  color: var(--text-muted);
  font-size: var(--font-size-xs);
}
.priority-tiers__sources .dash-source + .dash-source { margin-left: var(--space-sm); }

/* ============================================================================
   RECAP — Two-column "lost vs preserved" comparison for state transitions.
   The "kept" column is visually wider and uses success tinting; the "lost"
   column is narrower, muted, and struck-through. Reassurance is the design
   message: what survived outweighs what didn't.
   ============================================================================ */

.recap {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}

.recap__header {
  margin-bottom: var(--space-lg);
}

.recap__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
}

.recap__intro {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* The two panels sit side-by-side at wider viewports — equal width so
   neither column dominates (the icons and variant colors carry the
   semantic weight instead of size). */
.recap__columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-md);
}

/* "Lost" items inside the neutral panel get struck through to reinforce
   the "no longer saved" semantics — the same visual cue as before but
   now living inside the shared panel bullet list. */
.recap__item--lost {
  text-decoration: line-through;
  color: var(--text-muted);
}

/* Footer — same convention as journey footer: content left, CTA right,
   one border-top separator above the row. justify-content: space-between
   keeps the CTA anchored hard-right even when the footer text is short. */
.recap__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border-light);
}
.recap__footer-text {
  flex: 0 1 auto;
  max-width: 50ch;
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  text-align: left;
  line-height: 1.5;
}
.recap__cta { flex-shrink: 0; }

@media (max-width: 700px) {
  .recap__columns { grid-template-columns: 1fr; }
  .recap__footer { flex-direction: column; align-items: stretch; }
  .recap__cta { align-self: flex-end; }
}

/* ============================================================================
   PROGRESS-CARD — Mid-flight wizard progress + resume.
   Segmented bar shows discrete wizard steps with status colors; fraction
   display gives the at-a-glance count; notes list bundles small
   reassurances that used to live in standalone banner callouts.
   ============================================================================ */

.progress-card {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}

.progress-card__header {
  margin-bottom: var(--space-lg);
}

.progress-card__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
}

.progress-card__intro {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* Segmented progress bar — each <span> is one wizard step. Three states:
     completed → solid green
     current   → solid NYC red, pulsing
     upcoming  → light gray
   Segments flex equally; small gaps between them so the divisions read
   as discrete steps rather than a continuous bar. */
.progress-card__bar {
  display: flex;
  gap: 3px;
  margin-bottom: var(--space-md);
}

.progress-card__segment {
  flex: 1;
  height: 10px;
  border-radius: 3px;
  background: var(--border-light);
  transition: background var(--transition-fast);
}
.progress-card__segment--completed { background: var(--stepper-completed); }
.progress-card__segment--current {
  background: var(--nyc-red);
  animation: progress-segment-pulse 1.8s ease-in-out infinite;
}
/* In-progress variant for the task-list hub. Same pulse as --current
   so the bar animates the way users expect a "live" progress signal
   to behave; color is the amber from --status-progress-* (the same
   palette the In progress pill in the body uses) so the sidebar bar
   and the body pills share a single visual language. */
.progress-card__segment--in-progress {
  background: var(--status-progress-text);
  animation: progress-segment-pulse 1.8s ease-in-out infinite;
}
.progress-card__segment--upcoming { background: var(--border-light); }

@keyframes progress-segment-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.65; }
}

/* Fraction display — big number + denominator + label */
.progress-card__fraction {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
  flex-wrap: wrap;
}

.progress-card__fraction-stat {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2xs);
  font-family: var(--font-heading);
  line-height: 1;
}

.progress-card__fraction-num {
  font-size: 2.5rem;       /* 40px — large enough to register at a glance */
  font-weight: var(--font-weight-bold);
  color: var(--text-primary);
  letter-spacing: -0.02em;
}

.progress-card__fraction-sep {
  font-size: var(--font-size-base);
  color: var(--text-muted);
  font-weight: var(--font-weight-medium);
}

.progress-card__fraction-denom {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-semibold);
  color: var(--text-secondary);
}

.progress-card__fraction-label {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: var(--font-weight-semibold);
}

/* When the fraction is the final block (no notes, no footer), its
   own bottom margin would leave a dead air gap inside the card.
   Reset it so the bottom of the fraction sits flush with the card's
   internal padding. Same reason applies to the bar when it's the
   only segmented element. */
.progress-card__fraction:last-child {
  margin-bottom: 0;
}
.progress-card__bar:last-child {
  margin-bottom: 0;
}

/* Reassurance notes — small checkmark items. Replaces what used to be
   a separate "Documents you've used before are ready to reuse" callout. */
.progress-card__notes {
  list-style: none;
  margin: 0 0 var(--space-md);
  padding: var(--space-md);
  background: var(--surface-muted);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
.progress-card__note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-xs);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.4;
}
.progress-card__note-icon {
  font-size: 16px !important;
  color: var(--stepper-completed);
  flex-shrink: 0;
  margin-top: 2px;
}

/* Footer — same convention as journey/recap: text left, CTA right,
   border-top separator above. */
.progress-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  margin-top: var(--space-md);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border-light);
}
.progress-card__footer-text {
  flex: 0 1 auto;
  max-width: 50ch;
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  text-align: left;
  line-height: 1.5;
}
.progress-card__cta { flex-shrink: 0; }

@media (max-width: 700px) {
  .progress-card__footer { flex-direction: column; align-items: stretch; }
  .progress-card__cta { align-self: flex-end; }
}

/* ============================================================================
   UNAUTH HERO — true 50/50 grid with full-height image on the right.
   Previous version put the image in a small 320px box that left most of
   the right side empty — visually broken on a marketing-style landing.
   The grid now forces the image column to match the copy column's height;
   align-items:stretch ensures the placeholder/photo fills its grid cell.
   ============================================================================ */

.welcome-section--hero {
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  gap: var(--space-2xl);
}

.welcome-section--hero > div:first-child {
  display: flex;
  flex-direction: column;
  justify-content: center;     /* vertically center the copy column */
}

.welcome-section__lede {
  max-width: 56ch;
  margin: var(--space-md) 0 0;
  color: var(--text-secondary);
  line-height: 1.5;
  font-size: var(--font-size-base);
}

.welcome-section__cta-row {
  display: flex;
  gap: var(--space-md);
  margin-top: var(--space-lg);
  flex-wrap: wrap;
}

@media (max-width: 900px) {
  .welcome-section--hero {
    grid-template-columns: 1fr;
    gap: var(--space-lg);
  }
}

/* Intentional image-placeholder block. Photo-style gradient + dashed
   border + camera glyph + subject caption. Designers see exactly what
   kind of photo this slot expects. */
.image-placeholder {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  background: linear-gradient(135deg, #f9e0e6 0%, #fde2c5 50%, #fdd6e3 100%);
  border: 1.5px dashed rgba(0, 0, 0, 0.18);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  margin: 0;
  color: rgba(0, 0, 0, 0.55);
  text-align: center;
  overflow: hidden;
}

.image-placeholder--hero {
  width: 100%;
  height: 100%;
  min-height: 320px;
}

.hero-photo {
  width: 100%;
  min-height: 320px;
  border-radius: var(--radius-lg);
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}

.image-placeholder__icon {
  font-size: 36px !important;
  opacity: 0.45;
}

.image-placeholder__caption {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  font-style: normal;
}

.image-placeholder__label {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.image-placeholder__subject {
  font-size: var(--font-size-xs);
  color: rgba(0, 0, 0, 0.5);
  line-height: 1.4;
  font-style: italic;
}

/* ============================================================================
   IMAGE-BAND — Full-width visual interlude between content blocks.
   Photo-style layout: media slot is the dominant layer; a vertical
   dark scrim runs bottom-to-top so text sits on a readable dark
   gradient regardless of what photo (or placeholder) is underneath.
   Pull-quote + attribution + CTA align bottom-left like a real
   editorial caption — not a floating panel.
   ============================================================================ */

.image-band {
  position: relative;
  width: 100%;
  min-height: 320px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-lg);
  display: flex;
  align-items: center;              /* vertical center: balanced top/bottom */
  isolation: isolate;
}

/* ── Merged eligibility flow: when image-band is directly followed by
   elig-check, stack them as one visual unit ─────────────────────────── */
.image-band + .elig-check {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  border: none;
  margin-top: -1px;
}
.image-band:has(+ .elig-check) {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  margin-bottom: 0;
}

.image-band__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.image-band__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Placeholder content (camera glyph + caption) sits in the RIGHT half,
   right-aligned and vertically centered. Left half is reserved for the
   overlay copy. The 50% left padding keeps the camera content from
   overlapping the quote. */
.image-band__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: var(--space-sm);
  border: 1.5px dashed rgba(255, 255, 255, 0.2);
  padding: var(--space-lg) var(--space-2xl);
  padding-left: 50%;
  text-align: right;
}

.image-band__placeholder-icon {
  font-size: 44px !important;
  color: rgba(255, 255, 255, 0.55);
}

.image-band__placeholder-text {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  text-align: right;
  color: rgba(255, 255, 255, 0.85);
  max-width: 60ch;
}

.image-band__placeholder-label {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.image-band__placeholder-subject {
  font-size: var(--font-size-xs);
  color: rgba(255, 255, 255, 0.7);
  font-style: italic;
  line-height: 1.4;
}

/* Dark scrim — left-to-right gradient keeps the text side dark and
   the photo side clear so both copy and image are legible. */
.image-band__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    to right,
    rgba(0, 0, 0, 0.82) 0%,
    rgba(0, 0, 0, 0.6) 40%,
    rgba(0, 0, 0, 0.2) 65%,
    transparent 100%
  );
  pointer-events: none;
}

/* Overlay text — sits in the LEFT portion of the band, vertically centered
   by the parent flex (align-items: center). */
.image-band__overlay {
  position: relative;
  z-index: 2;
  width: 55%;
  padding: var(--space-2xl);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  color: #fff;
  box-sizing: border-box;
}

.image-band__quote {
  font-family: var(--font-heading);
  font-size: 2rem;                  /* 32px — dramatic, magazine-cover scale */
  font-weight: var(--font-weight-bold);
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0;
  color: #fff;
  max-width: 25ch;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}

.image-band__attribution {
  font-size: var(--font-size-xs);
  font-style: normal;
  color: rgba(255, 255, 255, 0.85);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: var(--font-weight-semibold);
}

.image-band__cta {
  align-self: flex-start;
  margin-top: var(--space-xs);
}

@media (max-width: 700px) {
  .image-placeholder--hero { min-height: 220px; }
  .image-band { min-height: 280px; align-items: flex-end; }
  .image-band__overlay {
    width: 100%;
    padding: var(--space-xl) var(--space-lg);
  }
  .image-band__quote { font-size: 1.5rem; max-width: 18ch; }
  /* On mobile the placeholder caption is hidden — overlay text dominates
     and there's not enough horizontal room for a side-by-side layout.
     When a real photo lands the gradient/photo still shows; only the
     "PHOTO PLACEHOLDER" caption is suppressed for visual cleanliness. */
  .image-band__placeholder { display: none; }
}

/* ============================================================================
   PROGRAM-COMPARE — 4-up side-by-side decision cards.
   Each card carries the same fields in the same slots so eyes scan across
   the grid horizontally to compare. Status pills reuse the application
   status pill family so the visual language is consistent.
   ============================================================================ */

.program-compare {
  margin-bottom: var(--space-lg);
}

.program-compare__header {
  margin-bottom: var(--space-lg);
}

.program-compare__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
}

.program-compare__intro {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.5;
  max-width: 70ch;
}

.program-compare__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-md);
}

.program-card-x {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.program-card-x__head { display: flex; flex-direction: column; gap: var(--space-xs); }

.program-card-x__name {
  font-family: var(--font-heading);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-bold);
  margin: 0;
  line-height: 1.3;
}

.program-card-x__tagline {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.4;
}

/* Definition-list of key facts (Ages, Income) — labels uppercase, values
   normal-weight. Stacked vertically for narrow cards; the row pattern
   would crowd at 4-up. */
.program-card-x__facts {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: 0;
  padding: var(--space-sm) 0;
  border-top: 1px solid var(--border-light);
  border-bottom: 1px solid var(--border-light);
}

.program-card-x__fact {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.program-card-x__fact-label {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0;
}

.program-card-x__fact-value {
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  margin: 0;
  line-height: 1.4;
}

.program-card-x__status {
  /* The .pill comes inline; just position it */
  display: flex;
}

.program-card-x__footnote {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  font-style: italic;
  line-height: 1.4;
  margin: 0;
}

/* CTA pinned to bottom of card, full-width within the card padding */
.program-card-x__footer {
  margin-top: auto;
}
.program-card-x__cta {
  width: 100%;
  justify-content: center;
}

/* ============================================================================
   ELIGIBILITY-CHECK — Interactive 3-question quick check (unauth landing).
   Question cards in a horizontal row at desktop; result panel below
   updates live as the user toggles answers. Distinct from any other
   block — the only actually interactive content block on the unauth
   landing.
   ============================================================================ */

.elig-check {
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}

.elig-check__header { margin-bottom: var(--space-lg); }

/* Title on the left, a direct route to the full screener on the right —
   for users who'd rather skip the 3-question widget. */
.elig-check__header-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
}
.elig-check__skip-link {
  color: var(--text-link);
  font-family: var(--font-ui);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  text-decoration: underline;
  white-space: nowrap;
}
.elig-check__skip-link:hover { color: var(--text-link-hover); }

.elig-check__title {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  margin: 0 0 var(--space-xs);
}

.elig-check__intro {
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* Question cards — 3-up at desktop, stacked at mobile. Each card has a
   numbered chip on the left and the question + toggle row on the right. */
.elig-check__questions {
  list-style: none;
  margin: 0 0 var(--space-lg);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}

@media (max-width: 900px) {
  .elig-check__questions { grid-template-columns: 1fr; }
}

.elig-check__question {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-md);
  padding: var(--space-md);
  background: var(--surface-muted);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
}

.elig-check__q-number {
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: var(--nyc-red);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: var(--font-weight-bold);
  font-size: var(--font-size-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  line-height: 1;
}

.elig-check__q-body { display: flex; flex-direction: column; gap: var(--space-xs); }

.elig-check__q-label {
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  font-size: var(--font-size-sm);
  line-height: 1.4;
}

.elig-check__q-hint {
  color: var(--text-muted);
  font-size: var(--font-size-xs);
  line-height: 1.4;
}

/* Yes/No toggle row — segmented control style */
.elig-check__toggle-row {
  display: inline-flex;
  margin-top: var(--space-xs);
  border-radius: var(--radius-pill);
  overflow: hidden;
  border: 1px solid var(--border-medium);
  align-self: flex-start;
}

.elig-check__toggle {
  appearance: none;
  -webkit-appearance: none;
  background: var(--surface-white);
  border: 0;
  padding: var(--space-2xs) var(--space-md);
  font-family: var(--font-ui);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.elig-check__toggle + .elig-check__toggle {
  border-left: 1px solid var(--border-medium);
}
.elig-check__toggle:hover { background: var(--surface-hover); color: var(--text-primary); }
.elig-check__toggle.is-active {
  background: var(--nyc-red);
  color: #fff;
}
.elig-check__toggle.is-active:hover {
  background: var(--nyc-red-hover);
  color: #fff;
}

/* Result panel — sits below the questions. Color + icon change per state. */
.elig-check__result {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-light);
  background: var(--surface-muted);
  transition: background var(--transition-fast), border-color var(--transition-fast);
}
/* The panel stays out of the DOM flow until the user starts answering —
   display:flex would otherwise defeat the [hidden] attribute. */
.elig-check__result[hidden] { display: none; }

.elig-check__result[data-state="likely"] {
  background: var(--panel-success-bg);
  border-color: var(--panel-success-accent);
}
.elig-check__result[data-state="partial"],
.elig-check__result[data-state="maybe"] {
  background: var(--panel-info-bg);
  border-color: var(--border-info-soft);
}
.elig-check__result[data-state="unlikely"] {
  background: var(--panel-warning-bg);
  border-color: var(--panel-warning-accent);
}

.elig-check__result-icon {
  font-size: 28px !important;
  flex-shrink: 0;
  color: var(--text-muted);
}
.elig-check__result[data-state="likely"]   .elig-check__result-icon { color: var(--alert-success-text); }
.elig-check__result[data-state="partial"]  .elig-check__result-icon,
.elig-check__result[data-state="maybe"]    .elig-check__result-icon { color: var(--alert-info-text); }
.elig-check__result[data-state="unlikely"] .elig-check__result-icon { color: var(--alert-warning-text); }

.elig-check__result-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-width: 0;
}

.elig-check__result-headline {
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  font-size: var(--font-size-base);
  line-height: 1.35;
}

.elig-check__result-body {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  line-height: 1.5;
}
.elig-check__result-body a {
  color: var(--text-link);
  font-weight: var(--font-weight-medium);
}

/* CTA row inside the result panel — buttons need a real gap between them
   and clear separation from the body copy above. */
.elig-check__result-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-top: var(--space-xs);
}
.elig-check__result-ctas[hidden] { display: none; }

/* ============================================================================
   MOBILE — HAMBURGER + NAV DRAWER
   Off-canvas left drawer that replicates the desktop sidebar for narrow
   viewports. Hidden on desktop (>768px); on mobile the only way an
   authenticated user reaches their dashboard sections.
   ============================================================================ */

/* Hamburger trigger — only visible on mobile. Lives inside .page-header__inner
   to the left of the breadcrumb. */
.nav-drawer-trigger {
  display: none;                      /* shown on mobile only */
  background: none;
  border: 0;
  padding: 8px;
  margin-right: var(--space-sm);
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--text-primary);
  min-width: 44px;
  min-height: 44px;
  align-items: center;
  justify-content: center;
}
.nav-drawer-trigger:hover { background: var(--surface-hover); }
.nav-drawer-trigger .material-symbols-outlined {
  font-size: 24px;
}

@media (max-width: 768px) {
  .nav-drawer-trigger { display: inline-flex; }
}

/* Drawer panel — slides in from the left at <=768px */
.nav-drawer {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 84vw;
  max-width: 320px;
  background: var(--surface-white);
  box-shadow: 2px 0 16px rgba(0, 0, 0, 0.18);
  transform: translateX(-100%);
  transition: transform 0.22s ease;
  z-index: 60;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  visibility: hidden;
}
.nav-drawer.is-open {
  transform: translateX(0);
  visibility: visible;
}

/* Hide drawer entirely on desktop — the sidebar handles navigation there */
@media (min-width: 769px) {
  .nav-drawer,
  .nav-drawer-backdrop { display: none !important; }
}

.nav-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  transition: opacity 0.22s ease;
  z-index: 59;
  pointer-events: none;
}
.nav-drawer-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.nav-drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-light);
}
.nav-drawer__header strong {
  font-family: var(--font-heading);
  font-size: var(--font-size-md);
}

.nav-drawer__close {
  background: none;
  border: 0;
  padding: 8px;
  border-radius: 999px;
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}
.nav-drawer__close:hover { background: var(--surface-hover); color: var(--text-primary); }

.nav-drawer__list {
  list-style: none;
  margin: 0;
  padding: var(--space-sm) 0;
}

.nav-drawer__item { margin: 2px var(--space-sm); }

.nav-drawer__link {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-md);   /* ≥44px tall touch target */
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-base);
  text-decoration: none;
  min-height: 44px;
}
.nav-drawer__link:hover { background: var(--surface-hover); color: var(--text-link); }

.nav-drawer__item--sub .nav-drawer__link {
  padding-left: calc(var(--space-md) + 28px);
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}

.nav-drawer__icon {
  font-size: 22px;
  color: var(--text-primary);
  width: 24px;
  text-align: center;
}
.nav-drawer__item--sub .nav-drawer__icon { color: var(--text-secondary); }

.nav-drawer__label { flex: 1; }

.nav-drawer__badge {
  margin-left: auto;
  min-width: 22px;
  padding: 2px 6px;
  border-radius: 11px;
  background: var(--nyc-red);
  color: #fff;
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-bold);
  text-align: center;
  line-height: 1.4;
}

.nav-drawer__divider {
  margin: var(--space-sm) var(--space-lg);
  height: 1px;
  background: var(--border-light);
  list-style: none;
}

.nav-drawer__group-label {
  list-style: none;
  margin: var(--space-md) var(--space-lg) var(--space-xs);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

/* ============================================================================
   MOBILE — Pill + app-card responsive tweaks.
   At ≤480 a long status pill on the same line as a title forces the
   parent row to push past viewport. Two interventions:
     1. Pills can wrap (override the nowrap default at narrow widths)
     2. The container row wraps too, so the pill drops to a new line
        when there isn't room next to the title
   ============================================================================ */

@media (max-width: 480px) {
  .pill {
    white-space: normal;            /* allow long status text to wrap */
  }
  .pill--inline-after-title {
    margin-left: 0;
    margin-top: var(--space-2xs);   /* drops to its own line when wrapping */
  }
  /* App-card header is flex-row at desktop; at mobile it stacks so the
     title + pill+deadline + meta each get their own line. */
  .app-card__header {
    flex-wrap: wrap;
  }
}

/* ============================================================================
   MOBILE — Collapse fixed grid columns that didn't have media queries.
   These shipped as desktop-only grids and broke at narrow widths.
   ============================================================================ */

@media (max-width: 700px) {
  /* App-card meta: 3 columns of case-number / dates pre-collapse to 1 col
     so the date strings have room. */
  .app-card__meta {
    grid-template-columns: 1fr;
    gap: var(--space-xs);
  }
  /* Income/Wizard form grid 2-col → 1-col */
  .income-form-grid {
    grid-template-columns: 1fr !important;
  }
}

@media (max-width: 480px) {
  /* App-card actions stack vertically — primary CTAs already full-width */
  .app-card__actions {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-sm);
  }
  .app-card__actions-left,
  .app-card__actions-right {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }
  .app-card__actions-right .btn { width: 100%; }
}

/* ============================================================================
   MOBILE — Touch-target sweep on chrome elements.
   Bumps targets that fail the WCAG 2.5.5 / iOS HIG 44px minimum at narrow
   widths. Desktop sizing preserved — these rules only apply at mobile.
   ============================================================================ */

@media (max-width: 768px) {
  /* Notification bell — 36→44, pip stays floating outside */
  .notif-bell {
    width: 44px;
    height: 44px;
  }
  .notif-bell__pip {
    top: 0;
    right: 0;
  }

  /* Sidebar sub-menu sublinks — tap-friendly height */
  .sidebar-nav__sublink {
    padding-top: var(--space-sm);
    padding-bottom: var(--space-sm);
    min-height: 44px;
  }
  .sidebar-nav__item--submenu:has(.sidebar-nav__submenu.is-open) .sidebar-nav__sublink {
    padding-top: var(--space-sm);
    padding-bottom: var(--space-sm);
  }

  /* Eligibility-check Yes/No toggles — meet 44px */
  .elig-check__toggle {
    padding: var(--space-sm) var(--space-lg);
    min-height: 44px;
    min-width: 64px;
  }

  /* Icon-link text actions (Withdraw, Remove, View) — 44 min */
  .icon-link {
    padding: var(--space-sm) 0;
    min-height: 44px;
  }

  /* Language toggle — 44 min */
  .nyc-bar__lang {
    min-height: 44px;
    padding-left: var(--space-md);
    padding-right: var(--space-md);
  }

  /* User-nav profile button (name + avatar) — already taller via padding,
     but ensure 44 minimum on small viewports where touch matters more. */
  .user-nav__profile {
    min-height: 44px;
  }
}

/* ============================================================================
   LANGUAGE TOGGLE — popover for LL30 framing (tap-fallback for the
   hover-only title tooltip).
   ============================================================================ */

.nyc-bar__right { position: relative; }

.nyc-bar__lang-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 320px;
  max-width: calc(100vw - var(--space-md) * 2);
  background: var(--surface-white);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
  padding: var(--space-md);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  display: none;
  z-index: 50;
}
.nyc-bar__lang-popover.is-open { display: block; }
.nyc-bar__lang-popover p { margin: 0 0 var(--space-sm); line-height: 1.5; }
.nyc-bar__lang-popover a {
  color: var(--text-link);
  font-weight: var(--font-weight-semibold);
}
