/* Volunteer phone bank module.
   Palette and rhythm come from the campaign's own design tokens, so this matches
   .campaign-form-card and the /volunteer hero rather than approximating them. */

/*
 * Built on the campaign's own tokens (frontend/hubspot/design-tokens.css), not
 * hand-picked values. An earlier version invented its own red, slightly off the real
 * maroon, and a flat 8px radius with no border, so next to a real campaign form it read
 * as a different site. Every colour below falls back to a literal so the module still
 * renders correctly outside the CMS.
 */
.lf-vpb {
  --red: var(--lf-maroon, #960018);
  --red-dark: var(--lf-maroon-deep, #620010);
  --cream: var(--lf-cream, #f4f0ec);
  --ink: var(--lf-ink, #171717);
  --white: var(--lf-white, #fff);
  --rule: var(--lf-maroon-soft-22, rgba(150, 0, 24, 0.22));
  --rule-soft: var(--lf-maroon-soft-10, rgba(150, 0, 24, 0.1));
  --muted: #5f5a54;
  --shadow: var(--lf-shadow, 0 5px 18px rgba(38, 20, 20, 0.07));
  --motion: var(--lf-motion-base, 200ms) var(--lf-motion-ease, cubic-bezier(0.2, 0.7, 0.3, 1));
  --radius: 16px;              /* the campaign form-card radius */
  max-width: 720px;
  margin: 0 auto;
}

.lf-vpb *, .lf-vpb *::before, .lf-vpb *::after { box-sizing: border-box; }

/*
 * THEME RESET — scoped to .lf-vpb, and written with :where() for a reason.
 *
 * This module is styled as if it were the only thing on the page, because in the dev
 * harness it is. Inside HubSpot it is not: Focus_Plus ships element-level rules that
 * reach straight into this markup, because every label and legend here sits inside <form>.
 *
 *   form label     { display: inline-table; margin-bottom: 10px }  <- breaks flex rows
 *   form legend    { margin-bottom: 10px }
 *   form fieldset  { max-width: 100% !important }
 *   fieldset       { padding: .35em .75em .625em }
 *   legend         { display: table }
 *   h1..h6         { margin: 0 0 1em }                            <- 1em gutter on every heading
 *                                                                   (this module only
 *                                                                    renders h2/h3)
 *   dl, dt, dd     { margin: 1em 0 }                          <- the recap list
 *
 * Every one of those is a bare element selector at 0,0,x, so a single class beats it and
 * the naive fix is "style it with a class". The naive fix is wrong in the other
 * direction: `.lf-vpb label` is 0,1,1, which OUTRANKS every `.lf-vpb__*` component rule
 * at 0,1,0. Written that way this reset set display:block on the flex consent row and
 * blanked the submit button's background -- silently, because the rules are valid.
 *
 * :where() contributes zero specificity, so each rule below lands at 0,1,0: it beats the
 * theme's element selectors, ties with the component rules, and loses to them on source
 * order. This block therefore MUST stay above the component rules; the test enforces it.
 */
.lf-vpb :where(fieldset) { border: 0; margin: 0; padding: 0; min-width: 0; }
.lf-vpb :where(legend)   { display: block; float: none; width: auto; padding: 0; margin: 0; }
.lf-vpb :where(label)    { display: block; margin: 0; font-size: 1rem;
                           color: var(--ink); text-transform: none; }
.lf-vpb :where(h2, h3) { margin: 0; }
.lf-vpb :where(p)  { margin: 0; }
.lf-vpb :where(ol) { margin: 0; padding-left: 1.15rem; }
.lf-vpb :where(dl, dt, dd) { margin: 0; }
.lf-vpb :where(button) { font: inherit; color: inherit; background: none; border: 0;
                         border-radius: 0; text-transform: none; cursor: pointer;
                         /* `appearance` and `-webkit-appearance` are separate properties, so
                            the shorthand `appearance: none` does NOT cancel the theme's
                            `-webkit-appearance: button`. Safari keeps drawing native button
                            chrome over the maroon fill unless both are stated. */
                         appearance: none; -webkit-appearance: none; }
.lf-vpb :where(input, select) { font: inherit; }

/*
 * The `hidden` attribute is this module's single source of truth for visibility, and it
 * must beat every author `display:` rule inside the module.
 *
 * An author rule wins over the user agent's `[hidden] { display: none }`. So any element
 * styled with an explicit display value becomes UNHIDEABLE via the attribute — that is how
 * the spinner never went away (`.lf-vpb__loading { display: flex }`) and how the step
 * transitions silently stopped working (`.lf-vpb__step--on { display: block }`). Both were
 * set correctly in JS and both did nothing.
 *
 * `!important` is the blunt instrument and it is the right one here: the alternative is
 * auditing every display rule against every attribute write, which is the check that
 * already failed twice.
 */
.lf-vpb [hidden] { display: none !important; }

.lf-vpb__head { margin-bottom: 1.35rem; }
.lf-vpb__title { font-size: clamp(1.3rem, 2.6vw, 1.7rem); margin: 0 0 .35rem; letter-spacing: -.01em; }
.lf-vpb__intro p, .lf-vpb__intro { margin: 0; color: var(--muted); font-size: .95rem; }

/* The whole flow sits in ONE card, matching .campaign-form-card on the campaign's own
   forms: maroon-tinted border, 16px radius, soft shadow. Previously each step had its
   own ad-hoc box and the module read as a stack of unrelated widgets. */
.lf-vpb__step {
  display: none;
  background: var(--white);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
}
.lf-vpb__step:not([hidden]) { display: block; animation: lf-vpb-in var(--motion) both; }

/* ── steps ─────────────────────────────────────────────────────────────────
   The `hidden` ATTRIBUTE is the single source of truth for which step is showing.

   This module previously used two mechanisms at once: `show()` set `hidden`, and a
   `.lf-vpb__step--on` class carried `display: block`. That is broken, because an author
   rule beats the user-agent's `[hidden] { display: none }` — so the attribute was inert
   and visibility depended entirely on classList.toggle succeeding. Any failure there
   leaves the flow on step 1 with no calendar and no visible error.


/* ── questions ─────────────────────────────────────────────────────────── */
/* The step is already a card, so a question needs no border of its own -- a second box
   inside a box is what made this read as a stack of unrelated widgets. */
.lf-vpb__q { border: 0; padding: 0; margin: 0 0 1.1rem; }
.lf-vpb__q-title { font-weight: 600; padding: 0; margin: 0 0 .15rem; font-size: 1rem; }
.lf-vpb__q-hint { color: var(--muted); font-size: .88rem; margin: 0 0 .6rem; }

.lf-vpb__opts { display: grid; gap: .5rem; grid-template-columns: 1fr 1fr; }
.lf-vpb__opts--stack { grid-template-columns: 1fr; gap: .5rem; }
@media (max-width: 560px) { .lf-vpb__opts { grid-template-columns: 1fr; } }

/* Stacked: label and note on one line, so a returning caller scanning for "No" finds it
   without reading the note under each option. */
.lf-vpb__opts--stack .lf-vpb__opt-body { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.lf-vpb__opts--stack .lf-vpb__opt-note { margin-top: 0; }
.lf-vpb__opts--stack .lf-vpb__opt-label { flex: 0 0 2.4rem; }

/* ── step counter ──────────────────────────────────────────────────────── */
/* Shown from step 2 onward only. On step 1 "Step 1 of 5" is noise before the
   volunteer has done anything. */
.lf-vpb__counter { margin: 0 0 .7rem; font-size: .72rem; font-weight: 700; letter-spacing: .07em;
                   text-transform: uppercase; color: var(--muted); }

.lf-vpb__opt { position: relative; display: block; }
.lf-vpb__opt input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.lf-vpb__opt-body {
  display: block; border: 1px solid var(--rule); border-radius: 10px;
  padding: .7rem .9rem; background: var(--white);
  transition: border-color var(--lf-motion-fast, 140ms), background var(--lf-motion-fast, 140ms);
}
.lf-vpb__opt:hover .lf-vpb__opt-body { background: var(--cream); }
.lf-vpb__opt-label { display: block; font-weight: 600; font-size: .95rem; }
.lf-vpb__opt-note { display: block; font-size: .84rem; color: var(--muted); margin-top: .1rem; }
.lf-vpb__opt input:checked + .lf-vpb__opt-body { border-color: var(--red); background: var(--red); color: var(--white); box-shadow: none; }
.lf-vpb__opt input:checked + .lf-vpb__opt-body .lf-vpb__opt-note { color: var(--cream); }
.lf-vpb__opt input:focus-visible + .lf-vpb__opt-body { outline: 3px solid var(--rule); outline-offset: 2px; }

/*
 * HOVER CASCADE — the theme wins here, and it wins on every button.
 *
 * Focus_Plus ships, in one rule:
 *   .button:hover, .cta_button:hover, .hs-button:hover, button:hover,
 *   form input[type=submit]:hover  { background:#f4f0ec; border:3px #000;
 *                                    border-radius:8px; color:#000; ... }
 *
 * The bare `button:hover` in that list is specificity (0,1,1) -- ONE pseudo-class plus
 * ONE element. Every resting rule in this module is a single class, (0,1,0). So on hover
 * the theme outranks the module's own base styles and silently imposes its own:
 *   - color:#000, so the maroon primary button turned BLACK ON MAROON, and the selected
 *     calendar chip turned black text on dark maroon (measured: rgb(0,0,0) on
 *     rgb(98,0,16) -- effectively unreadable).
 *   - border-radius:8px, so every 5px and 10px radius jumped to 8px on hover, which is
 *     why a chip visibly swelled the moment the pointer arrived.
 *
 * The list rows are labels, not buttons, so only .lf-vpb__btn is reachable by that rule.
 *   - background and border, which the module's own :hover rules outrank -- so those two
 *     were the ONLY properties that looked right.
 *
 * Restating `color` in the hover rules below is not redundancy; without it the theme
 * wins it. Specificity (0,3,0) beats (0,1,1).
 */
.lf-vpb__btn:hover:not(:disabled) {
  background: var(--red-dark); border-color: var(--red-dark); color: var(--white);
  border-radius: 10px;
}
/* The list replaces the calendar, so the month-arrow and chip hover rules that lived here
   are gone with it. The two rules above are the only hovers left that the theme can reach. */

/* ── identity fields ───────────────────────────────────────────────────── */
.lf-vpb__who { display: grid; gap: .85rem; grid-template-columns: 1fr 1fr; margin: 1.25rem 0 1rem; }
/* Identity is now its own step, so the top margin is unnecessary. */
.lf-vpb__who[data-details] { margin-top: 1.2rem; }
.lf-vpb__field { display: block; }
.lf-vpb__field--wide { grid-column: 1 / -1; }
@media (max-width: 560px) {
  .lf-vpb__who { grid-template-columns: 1fr; }
  .lf-vpb button.lf-vpb__btn,
  .lf-vpb .lf-vpb__btn {
    width: 100% !important;
    min-width: 100% !important;
    min-height: 56px !important;
    padding: 1rem 1.25rem !important;
    font-size: 1.1rem !important;
  }
}
.lf-vpb__label { display: block; font-weight: 600; font-size: .88rem; margin-bottom: .3rem; }
.lf-vpb__req { font-style: normal; color: var(--red); }
/* One rule for both controls. The riding select sits in the same grid and the theme has a
   `form select { width: 100% }` rule of its own, so it needed the treatment anyway.
   These were 1.5px / 7px -- heavier and tighter than every other control in the module,
   which is what made the identity step read as pasted in from somewhere else. */
/*
 * Scoped TWICE on purpose.
 *
 * Focus_Plus ships:
 *   form input[type="email"], form input[type="file"], form input select,
 *   form textarea { border: 3px #000; padding: 20px; ... }
 *
 * Two separate problems, and the first one is why these fields had no borders at all:
 *
 *  1. `border: 3px #000` is INVALID CSS. The border shorthand requires a style, and with
 *     only a width and a colour the whole declaration is discarded by the parser. So the
 *     theme did not override our border -- it never applied one. Measured in Chrome:
 *     border computed to "0px none", which is why the inputs looked like bare labels.
 *     Nobody can fix this by adding a stronger border rule, because there is no rule to
 *     beat; the value is thrown away before the cascade.
 *
 *  2. `padding: 20px` in that same rule is valid, and `form input[type=...]` is (0,1,2)
 *     against our (0,1,1) -- so the theme won and every field was 65px tall with huge
 *     gaps between label, box and error text.
 *
 * `.lf-vpb .lf-vpb__field input` is (0,2,1) and beats (0,1,2). Verified by reading the
 * matched rules back out of Chrome, not by assuming.
 */
.lf-vpb .lf-vpb__field input,
.lf-vpb .lf-vpb__field select {
  width: 100%; padding: .6rem .75rem; border: 1px solid var(--rule);
  border-radius: 8px; font: inherit; font-size: .95rem;
  background: var(--white); color: var(--ink);
  transition: border-color var(--lf-motion-fast, 140ms);
}
.lf-vpb .lf-vpb__field input:focus,
.lf-vpb .lf-vpb__field select:focus {
  outline: none; border-color: var(--red); box-shadow: 0 0 0 3px var(--rule-soft);
}
.lf-vpb__riding { grid-column: 1 / -1; }
/* Only reached when the postal code could not be matched, so it gets a visible edge to
   explain why it appeared rather than looking like an arbitrary extra question. */
.lf-vpb__riding:not([hidden]) { padding: .85rem; border: 1px solid var(--rule); border-radius: 10px; background: var(--cream); }
.lf-vpb__hint { display: block; font-size: .82rem; color: var(--muted); margin-top: .3rem; }
.lf-vpb__err { display: block; font-size: .85rem; color: var(--red); margin-top: .3rem; min-height: 0; }
.lf-vpb__err:empty { display: none; }

/* ── buttons ───────────────────────────────────────────────────────────── */
/*
 * Forced full-width and touch target minimums.
 *
 * Focus_Plus theme overrides ship:
 *   button, .button, .hs-button, form input[type="submit"] {
 *     width: auto;
 *     padding: 10px 30px;
 *   }
 *   button:disabled { background: #d0d0d0; color: #e6e6e6; }
 *
 * The theme's `width: auto` rule loads after the module stylesheet, which collapsed the
 * primary "Book it" CTA button to just the narrow text width (~110px) instead of stretching
 * edge-to-edge across the card on mobile, making the click/tap zone tiny and hard to find.
 *
 * Scoping `.lf-vpb button.lf-vpb__btn` with `width: 100% !important`, `display: flex !important`,
 * and `min-height: 52px !important` guarantees an unmistakable, thumb-friendly tap target
 * across all viewport sizes.
 */
.lf-vpb__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  box-sizing: border-box;
  min-height: 52px;
  margin-top: 1.25rem;
  background: var(--red);
  color: var(--white);
  border: 1px solid var(--red);
  border-radius: 10px;
  padding: 0.85rem 1.25rem;
  font: 700 1.05rem/1.2 inherit;
  text-align: center;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background var(--lf-motion-fast, 140ms), border-color var(--lf-motion-fast, 140ms), transform var(--lf-motion-fast, 140ms);
}

.lf-vpb button.lf-vpb__btn,
.lf-vpb .lf-vpb__btn {
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  width: 100% !important;
  min-width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
  min-height: 52px !important;
  padding: 0.85rem 1.25rem !important;
  font-size: 1.05rem !important;
  font-weight: 700 !important;
  text-align: center !important;
}

.lf-vpb button.lf-vpb__btn:hover:not(:disabled),
.lf-vpb .lf-vpb__btn:hover:not(:disabled) {
  background: var(--red-dark) !important;
  border: 1px solid var(--red-dark) !important;
  border-radius: 10px !important;
  padding: 0.85rem 1.25rem !important;
  color: var(--white) !important;
  transform: none !important;
}

.lf-vpb button.lf-vpb__btn:active:not(:disabled),
.lf-vpb .lf-vpb__btn:active:not(:disabled) {
  transform: none !important;
}

.lf-vpb button:hover,
.lf-vpb .lf-vpb__btn:hover,
.lf-vpb .lf-vpb__pager-btn:hover,
.lf-vpb .lf-vpb__opt:hover,
.lf-vpb .lf-vpb__list-opt:hover,
.lf-vpb a:hover {
  transform: none !important;
}

.lf-vpb button.lf-vpb__btn:disabled,
.lf-vpb .lf-vpb__btn:disabled,
.lf-vpb__btn:disabled {
  background: #ede8e1 !important;
  border-color: #dfd8cd !important;
  color: #8c8277 !important;
  opacity: 1 !important;
  cursor: not-allowed !important;
  box-shadow: none !important;
}

.lf-vpb__btn--ghost {
  display: flex;
  background: none !important;
  color: var(--muted) !important;
  border-color: transparent !important;
  font-weight: 500 !important;
  font-size: 0.95rem !important;
  min-height: 44px !important;
  margin-top: 0.6rem !important;
  box-shadow: none !important;
}

.lf-vpb__btn--ghost:hover:not(:disabled) {
  background: var(--cream) !important;
  color: var(--ink) !important;
  border-color: transparent !important;
  text-decoration: underline !important;
}

.lf-vpb__btn:focus-visible {
  outline: 3px solid var(--rule);
  outline-offset: 2px;
}

/* ── banners ───────────────────────────────────────────────────────────── */
.lf-vpb__banner { margin: .9rem 0 0; padding: .7rem .85rem; border-radius: 10px; font-size: .9rem;
                  background: var(--cream); color: var(--ink); border: 1px solid var(--rule-soft); }
.lf-vpb__banner--warn { background: var(--cream); border-color: var(--rule); color: var(--red-dark); }
.lf-vpb__banner--err { background: var(--cream); border-color: var(--rule); color: var(--red-dark); font-weight: 500; }

/* ── loading ───────────────────────────────────────────────────────────── */
/* ── training sessions: a list, not a calendar ─────────────────────────────────
   Superseded: training sessions now render INSIDE the calendar cells like shifts do.
   Google Calendar shows the events inside the day, which is what makes a week legible at
   a glance. The old list hid the calendar entirely and the old calendar hid the time. */
.lf-vpb__sub-foot { margin: .7rem 0 0; font-size: .84rem; color: var(--muted); }

/* The shift calendar earns its place: the day carries no time, so the day is the choice
   and the time comes after. Both panes keep the same slot vocabulary once a day is set. */
.lf-vpb__loading { display: flex; align-items: center; gap: .7rem; color: var(--muted); padding: 2rem 0; }
.lf-vpb__spinner { width: 18px; height: 18px; border: 2px solid var(--rule); border-top-color: var(--red);
                  border-radius: 50%; animation: lf-vpb-spin .8s linear infinite; }
@keyframes lf-vpb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .lf-vpb__spinner { animation: none; } }

.lf-vpb__sub { font-size: 1.05rem; margin: 0 0 .25rem; }
.lf-vpb__sub-intro { color: var(--muted); margin: 0 0 .9rem; font-size: .9rem; }

/* ── calendar ──────────────────────────────────────────────────────────── */
/* ── slot list ─────────────────────────────────────────────────────────────
   Replaces the month grid. Vertical rhythm, full-width rows, and nothing that
   requires horizontal room, so the list is as usable on a phone as on a desktop.
   Times are set as a two-column grid so the labels line up down the page: a list of
   ragged times is much harder to scan than a column of them. */
.lf-vpb__list-month + .lf-vpb__list-month { margin-top: 1.1rem; }
.lf-vpb__list-month-label {
  margin: 0 0 .5rem; font-size: .78rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
  padding-bottom: .3rem; border-bottom: 1px solid var(--rule-soft);
}
.lf-vpb__list-day { display: flex; gap: .75rem; align-items: baseline; }
.lf-vpb__list-day + .lf-vpb__list-day { margin-top: .35rem; }
.lf-vpb__list-day-label {
  /* Wide enough for "Wed 14 Oct" on one line. It was 4.6rem, which wrapped the label to
     two lines and knocked every time out of alignment with the one above it. */
  flex: 0 0 6.2rem; font-size: .88rem; font-weight: 600; color: var(--ink);
  padding-top: .62rem; white-space: nowrap;
}
.lf-vpb__list-opts { display: grid; gap: .4rem; flex: 1 1 auto; min-width: 0; }

.lf-vpb__list-opt { position: relative; display: block; }
.lf-vpb__list-opt input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.lf-vpb__list-opt-body {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  border: 1px solid var(--rule); border-radius: 10px; padding: .6rem .8rem;
  background: var(--white);
  transition: border-color var(--lf-motion-fast, 140ms), background var(--lf-motion-fast, 140ms);
}
.lf-vpb__list-opt:hover .lf-vpb__list-opt-body { background: var(--cream); }
.lf-vpb__list-opt-time { font-weight: 600; font-size: .95rem; font-variant-numeric: tabular-nums; }
.lf-vpb__list-opt-note { font-size: .84rem; color: var(--muted); }

/* Selected: a solid fill plus a maroon left edge, so the chosen row is obvious even
   where the fill is hard to see. */
.lf-vpb__list-opt input:checked + .lf-vpb__list-opt-body {
  background: var(--red); border-color: var(--red); color: var(--white);
  box-shadow: inset 3px 0 0 var(--red-dark);
}
.lf-vpb__list-opt input:checked + .lf-vpb__list-opt-body .lf-vpb__list-opt-note { color: var(--cream); }
.lf-vpb__list-opt input:focus-visible + .lf-vpb__list-opt-body { outline: 3px solid var(--rule); outline-offset: 2px; }

/* Unavailable: still shown, still readable, plainly not a choice. A hidden slot makes a
   volunteer think the day is closed; this says the slot exists and why it is not open. */
.lf-vpb__list-opt--off .lf-vpb__list-opt-body {
  background: var(--cream); border-color: var(--rule-soft); color: var(--muted); cursor: not-allowed;
}
.lf-vpb__list-opt--off .lf-vpb__list-opt-time { font-weight: 500; }
.lf-vpb__list-opt--off input { cursor: not-allowed; }

/* ── pager ────────────────────────────────────────────────────────────────
   Three columns so the "Page 2 of 9" label stays optically centred as the button
   labels change width ("Earlier" vs "Later"). */
.lf-vpb__pager {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: .4rem;
  margin: 1rem 0 0; padding-top: .85rem; border-top: 1px solid var(--rule-soft);
}
.lf-vpb__pager-btn {
  display: inline-block; justify-self: start; padding: .5rem .7rem; font: 600 .85rem/1 inherit;
  /* At 390px the pager row is tight and "Earlier" wrapped onto two lines, overflowing the
     button. One line each, and the middle column shrinks instead. */
  white-space: nowrap;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--white); color: var(--ink); cursor: pointer;
  transition: background var(--lf-motion-fast, 140ms), border-color var(--lf-motion-fast, 140ms);
}
.lf-vpb__pager-btn:last-child { justify-self: end; }
.lf-vpb__pager-btn:hover:not(:disabled) { background: var(--cream); border-color: var(--red); }
.lf-vpb__pager-btn:disabled { opacity: .4; cursor: not-allowed; }
/* `button` in the Focus_Plus theme is (0,0,1), which loses to a class, but the theme also
   forces -webkit-appearance on buttons. Restated so the pager looks the same as the rest. */
.lf-vpb__pager-btn { appearance: none; -webkit-appearance: none; border-radius: 8px; }
.lf-vpb__pager-btn:focus-visible { outline: 3px solid var(--rule); outline-offset: 2px; }
.lf-vpb__pager-mid { text-align: center; line-height: 1.25; }
.lf-vpb__pager-page { display: block; font-size: .84rem; font-weight: 600; color: var(--ink); }
.lf-vpb__pager-range { display: block; font-size: .76rem; color: var(--muted); }

/* A day with nothing bookable on it is noise: every row greyed tells the volunteer nothing
   they did not already know from the first greyed row. */
.lf-vpb__list-day--out { opacity: .75; }

@media (max-width: 420px) {
  .lf-vpb__list-day { display: block; }
  .lf-vpb__list-day-label { flex: none; padding-top: 0; margin-bottom: .3rem; }
}

.lf-vpb__echo { margin: .8rem 0 0; padding: .55rem .8rem; background: var(--cream); border-radius: 8px;
                font-size: .9rem; color: var(--muted); }
.lf-vpb__echo:empty { display: none; }
/* The shift calendar states plainly that there is nothing special about the dates, because
   "some days are missing" and "every weekday is bookable" read very differently and the
   grid alone cannot convey the difference. */
.lf-vpb__cal-note { margin: .55rem 0 0; font-size: .84rem; color: var(--muted); }
.lf-vpb__echo b { color: var(--ink); }
.lf-vpb__empty { margin: .8rem 0 0; padding: .6rem .8rem; background: var(--cream); border-radius: 8px;
                 font-size: .9rem; color: var(--muted); }

/* ── confirmation ──────────────────────────────────────────────────────── */
.lf-vpb__done { text-align: center; padding-top: .5rem; }
.lf-vpb__tick { display: flex; align-items: center; justify-content: center; width: 46px; height: 46px;
                border-radius: 50%; background: #1d6b3f; color: #fff; font-size: 1.4rem; margin: 0 auto .8rem; }
.lf-vpb__recap { border: 1px solid var(--rule); border-radius: 12px; overflow: hidden; margin: 1.2rem 0; }
.lf-vpb__recap > div { display: flex; justify-content: space-between; gap: 1rem; padding: .6rem .85rem; font-size: .9rem; }
.lf-vpb__recap > div:nth-child(odd) { background: var(--cream); }
.lf-vpb__recap dt { color: var(--muted); }
.lf-vpb__recap dd { margin: 0; font-weight: 600; text-align: right; }

/* The chosen slot, echoed on the identity step so the volunteer can see what they are
   confirming without going back. */
.lf-vpb__recap--inline { margin: 0 0 .25rem; }
.lf-vpb__recap--inline > div { padding: .65rem .85rem; font-size: .92rem; }
.lf-vpb__recap-k { color: var(--muted); margin-right: .5rem; }
.lf-vpb__recap--inline b { font-weight: 600; }

/* A disabled time slot — greyed rather than hidden, so a volunteer at 19:30 can see the
   19:00 slot existed and why it is not available. */
.lf-vpb__slot--off { opacity: .5; pointer-events: none; }
/* ── consent ────────────────────────────────────────────────────────────────
   A checkbox, not an extra field in the grid. It is required, so it has to read as a
   deliberate act rather than another box to fill — hence a larger target, a visible box
   rather than a browser default, and the secondary line sitting inside the label so the
   whole thing is one click target. */
.lf-vpb__consent { grid-column: 1 / -1; margin-top: .4rem; }
.lf-vpb__check {
  display: flex; gap: .7rem; align-items: flex-start; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 10px; padding: .75rem .9rem;
  background: var(--cream);
  transition: border-color var(--lf-motion-fast, 140ms), background var(--lf-motion-fast, 140ms);
}
.lf-vpb__check:hover { background: var(--white); }
.lf-vpb__check input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.lf-vpb__check-box {
  flex: 0 0 auto; width: 1.25rem; height: 1.25rem; margin-top: .1rem;
  border: 1px solid var(--rule); border-radius: 4px; background: var(--white);
  position: relative; transition: background .12s, border-color .12s;
}
.lf-vpb__check-box::after {
  content: ''; position: absolute; left: .34rem; top: .06rem;
  width: .35rem; height: .68rem; border: solid #fff; border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0); transition: transform .12s;
}
.lf-vpb__check input:checked ~ .lf-vpb__check-box { background: var(--red); border-color: var(--red); }
.lf-vpb__check input:checked ~ .lf-vpb__check-box::after { transform: rotate(45deg) scale(1); }
.lf-vpb__check input:focus-visible ~ .lf-vpb__check-box { outline: 3px solid var(--rule); outline-offset: 2px; }
.lf-vpb__check-text { font-size: .95rem; }
.lf-vpb__check-note { display: block; color: var(--muted); font-size: .86rem; margin-top: .2rem; }

.lf-vpb__next { margin: 0 0 .5rem; padding-left: 1.2rem; }
.lf-vpb__next li { margin: .45rem 0; color: var(--muted); }
.lf-vpb__next strong { color: var(--ink); }