/* ==========================================================================
   book-picker.css, the three-step slot picker.

   ONE FILE, THREE SITES, BYTE-IDENTICAL, like book.js beside it, on two
   pages each (the #book card on index.html and the standalone /book.html).
   So it may only read custom properties that exist on ALL THREE sites, and
   everything site-specific arrives through a var() fallback:

     on every site   --paper --paper-2 --paper-3 --ink --accent --accent-deep
                     --accent2 --accent2-ink --muted --muted-ink --rule
                     --on-accent --font-body --font-display --font-label
                     (+ tokens.css: --label)
     legal only      --r-card 16px, --r-btn 999px   -> the rounded language
     mnc only        --error  #A3231D                -> the invalid-field red

   The two radii are why the controls come out square on ssa and mnc and
   rounded on legal without a per-site file: min() clamps legal's 16px card
   radius to something a 44px control can wear, and the default arm of each
   var() is the 2px ssa and mnc give every button.

   There is no inline <style> and no style="" anywhere: mnc and legal are
   served under `style-src 'self'` with no 'unsafe-inline', which blocks a
   style attribute exactly as hard as a style block. Every state the script
   drives is a class, an aria-pressed value, or a `hidden` attribute. It
   writes no CSS at all: the one CSSOM height write this file used to need
   went with the slide it tweened.

   Loaded AFTER the site stylesheet so it wins the cascade, and on ssa after
   book.css too (which repairs that site's JS-gated heading reveal).
   ========================================================================== */

/* ⚠ LOAD-BEARING, NOT HOUSEKEEPING. `hidden` is the picker's only mechanism
   for "this state is not on screen", and the UA rule behind it is a plain
   `display: none` that ANY class with a display wins against. Several on
   these pages do: .btn is inline-flex (the email button IS a .btn), the
   step, times and nearby containers are all flex, and so is the tablist -
   which ships `hidden` for the no-JavaScript case and would otherwise show
   two tabs that nothing is listening to. Without this, a "hidden" state
   renders. Scoped to the picker, the card's tabs and the email route, so
   the rest of the page is untouched. */
#picker[hidden],
#picker [hidden],
.bk__tabs[hidden],
.bk__panel[hidden],
[data-book-fallback][hidden],
[data-book-picker][hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   0. THE #book CARD IS ONE CARD, NOT A ROW OF TWO

   ⚠ THIS IS THE ONE RULE IN THIS FILE THAT REACHES OUTSIDE THE PICKER, and
   it is here rather than in ssa.css / mnc.css / legal.css on purpose: all
   three declare the identical `.book { display: grid; grid-template-columns:
   1fr 1fr }` for the #book section, all three now hold a single tabbed card
   in it, and this file is already loaded after all three and is already
   byte-identical across them. One rule here beats three concurrent edits to
   files another agent is working in.

   Each site keeps its own `gap`, which becomes the vertical rhythm between
   the card and whatever context sits above it. The `grid-column: 1 / -1`
   rules those sites carry for the card are now redundant but harmless.
   -------------------------------------------------------------------------- */

/* ONE COLUMN, THE FULL MEASURE. The 720px cap that used to be here is gone:
   with the card laid out in two panes there is nothing empty left in it to
   cap away, and the calendar-plus-times pair needs the room. */
.book { grid-template-columns: minmax(0, 1fr); }

/* --------------------------------------------------------------------------
   0b. The tabs. "Book a time" | "Write to us", one panel at a time.

   The tablist ships `hidden` in the markup and is un-hidden by book.js, and
   both panels ship visible - so with no JavaScript the card shows the
   picker's email fallback AND the site's own form, stacked, and offers no
   control that cannot work. Nothing here is driven by :target or a hash.
   -------------------------------------------------------------------------- */

.bk__tabs {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin: 0;
  border-bottom: 1px solid var(--rule, #dcd8cf);
}

.bk__tab {
  appearance: none;
  cursor: pointer;
  /* Jake's rule: every section fits one screen, and #book is the tallest.
     The tab row is chrome, so it pays first. */
  padding: 9px 14px 10px;
  margin-bottom: -1px;
  background: none;
  border: 1px solid transparent;
  border-bottom: 2px solid transparent;
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--muted-ink, #6b655c);
}
.bk__tab:hover { color: var(--ink, #1f1d1a); }
/* aria-selected is the state, so the picture and the announcement cannot
   disagree. Colour is not the only signal: the selected tab also carries the
   rule under it. */
.bk__tab[aria-selected="true"] {
  color: var(--ink, #1f1d1a);
  border-bottom-color: var(--accent, #24504a);
}
.bk__tab:focus-visible {
  outline: 2px solid var(--accent2, var(--accent, #24504a));
  outline-offset: 2px;
}

/* The sites' own email forms are written to stand as a card of their own
   (ssa's .form carries a border, a raised background and 28px of padding).
   Inside this card that would draw a second box around the second tab, so
   the box is dropped and the card it now lives in provides it. Scoped to the
   panel: the same form elsewhere on the page is untouched. */
.bk__panel .form {
  border: 0;
  background: none;
  padding: 0;
  border-radius: 0;
}

.bk__panel { margin-top: 16px; }
.bk__panel:focus { outline: none; }
.bk__panel > :first-child { margin-top: 0; }

/* --------------------------------------------------------------------------
   0c. THE TWO PANES.

   At 900px and up the card is two columns at every step. The LEFT pane is
   the calendar, all the way through: the reader picks a day, the day stays
   put with its cell pressed while they pick a time beside it, and it is
   still there - with the chosen day and time restated under the month grid -
   while they type. The RIGHT pane carries whichever of the quiet aside, that
   day's times or the form belongs to the step.

   There is no declared min-height and there must not be one. The calendar is
   in EVERY step's set, so the left pane holds the card's height by itself at
   all three steps: a five-week month is the measured floor, a six-week month
   is 52px taller, and a form that outruns its month grid grows the card on
   step three. A fixed floor on top of that could only add white to a section
   that has to fit one screen.

   Below 900px the panes collapse to one column and the blocks stack in DOM
   order - which is why the left pane comes first in the markup: calendar,
   then the summary, then times and form.
   -------------------------------------------------------------------------- */

.bk__panes { display: grid; gap: 22px; }
/* a grid item's `auto` minimum is what lets a long word or a wide table push
   a column past its track; neither pane has any business doing that. */
.bk__pane { min-width: 0; }

@media (min-width: 900px) {
  .bk__panes {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 32px;
  }
  /* the calendar fills its pane rather than sitting in the left third of it */
  .bk__cal { max-width: none; }
  .bk__day { min-height: 44px; }
}

/* The quiet right-hand pane at step one: what the call is, and what happens
   after it. Top-left of the pane, per the layout note. */
.bk__aside > :first-child { margin-top: 0; }

/* The two ways back out of step three, directly under the summary line that
   they change. Both sit under the month grid, so the space above belongs to
   the summary and there is nothing below them in the pane. */
.bk__backs {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin: 0;
}

/* --------------------------------------------------------------------------
   1. The shell
   -------------------------------------------------------------------------- */

.bk { margin-top: 26px; }

/* Inside the booking card on each index.html, where a rule and a title row
   already sit right above the picker. The standalone /book.html needs the
   bigger gap; a card does not. */
.bk--card { margin-top: 14px; }

/* The fallback line inside that card: shown until real slots land, and again
   if availability fails, so the card is never empty. It replaced
   .book-embed__alt, which said the same thing behind the retired iframe. */
.bk__alt {
  margin: 18px 0;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--muted-ink, #6b655c);
}

/* The one announced line: loading, "just taken", field summaries. It carries
   no box until it says something, so an empty region takes no room and never
   pushes the calendar down on load. */
.bk__status {
  margin: 0 0 18px;
  font-family: var(--font-label, ui-monospace, monospace);
  font-size: var(--label, 13px);
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--ink, #1f1d1a);
}
.bk__status:empty { display: none; }

/* --------------------------------------------------------------------------
   2. The blocks inside the panes

   There is no slide and nothing is absolutely positioned: a block is either
   in its step's set and in flow, or `hidden`. The calendar is in every set,
   so it holds the card's height and a step change moves nothing but the
   content of the pane beside it.
   -------------------------------------------------------------------------- */

/* Blocks are focused programmatically when they arrive, so the ring would be
   a ring nobody asked for. :focus-visible still applies to a keyboard route
   into the same node. */
.bk__step:focus,
.bk__bail:focus,
.bk__done:focus { outline: none; }

.bk__stepbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  max-width: 560px;
  margin: 0 0 16px;
}
.bk__stephead { margin: 0; }

.bk__back {
  appearance: none;
  padding: 0;
  border: 0;
  border-bottom: 1px solid currentColor;
  background: none;
  cursor: pointer;
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 14px;
  line-height: 1.3;
  color: var(--accent2-ink, var(--accent, #24504a));
}
.bk__back:hover { color: var(--accent-deep, #1b3d38); }

/* --------------------------------------------------------------------------
   3. Step one: a real month calendar
   -------------------------------------------------------------------------- */

.bk__cal { max-width: 392px; }

.bk__calbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 14px;
}
.bk__month {
  margin: 0;
  font-family: var(--font-display, Georgia, serif);
  font-size: 19px;
  line-height: 1.2;
  color: var(--ink, #1f1d1a);
}

.bk__nav {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  cursor: pointer;
  font-size: 17px;
  line-height: 1;
  color: var(--ink, #1f1d1a);
  background: var(--paper, #fff);
  border: 1px solid var(--rule, #dcd8cf);
  border-radius: min(var(--r-card, 2px), 10px);
}
.bk__nav:hover:not([disabled]) {
  border-color: var(--accent, #24504a);
  color: var(--accent, #24504a);
}
/* Bounded to the availability window: there is nothing before the first free
   day or after the last, and the button says so rather than doing nothing. */
.bk__nav[disabled] {
  color: var(--muted, #8c877d);
  border-color: var(--rule, #dcd8cf);
  background: transparent;
  cursor: not-allowed;
}

.bk__dow,
.bk__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.bk__dow { margin: 0 0 6px; }
.bk__dow span {
  text-align: center;
  font-family: var(--font-label, ui-monospace, monospace);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-ink, #6b655c);
}

.bk__day {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0;
  appearance: none;
  cursor: pointer;
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 15px;
  font-weight: 500;
  line-height: 1;
  color: var(--ink, #1f1d1a);
  background: var(--paper-2, #fff);
  border: 1px solid var(--rule, #dcd8cf);
  border-radius: min(var(--r-card, 2px), 10px);
}
.bk__day:hover:not([disabled]) { border-color: var(--accent, #24504a); }

/* The leading and trailing cells of the grid. Not buttons, not announced. */
.bk__day--blank {
  background: none;
  border-color: transparent;
  cursor: default;
}

/* A day the calendar has nothing free on - past, weekend, or simply full.
   It keeps its number so the month still reads as a month, but it carries no
   surface, no border and no pointer, and it is `disabled` as well as
   aria-disabled so it is not a tab stop either. */
.bk__day[disabled] {
  background: transparent;
  border-color: transparent;
  color: var(--muted, #8c877d);
  font-weight: 400;
  cursor: not-allowed;
}

/* Today, marked but not shouted: a rule under the number. Never the only
   signal for anything - it says where you are, not what you can do. */
.bk__day--today { box-shadow: inset 0 -2px 0 var(--accent2, var(--accent, #24504a)); }

.bk__day[aria-pressed="true"] {
  background: var(--accent, #24504a);
  border-color: var(--accent, #24504a);
  color: var(--on-accent, #fff);
}

/* Said once, under the calendar: "45 min · Singapore time". */
.bk__dur {
  margin: 20px 0 0;
  font-family: var(--font-label, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-ink, #6b655c);
}

/* --------------------------------------------------------------------------
   4. Step two: that day's times, two to a row

   A grid, not a column: ten slots in five rows are all on screen at once,
   which is the point - a single vertical list made the reader scroll for the
   afternoon. No max-width, so the pair of columns fills its pane.
   -------------------------------------------------------------------------- */

.bk__times,
.bk__nearby {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.bk__nearby { margin: 0 0 8px; }

.bk__slot {
  width: 100%;
  min-height: 48px;
  padding: 13px 16px;
  appearance: none;
  cursor: pointer;
  text-align: center;
  color: var(--ink, #1f1d1a);
  background: var(--paper, #fff);
  border: 1px solid var(--rule, #dcd8cf);
  border-radius: var(--r-btn, 2px);
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 15.5px;
  font-weight: 500;
  line-height: 1.2;
}
.bk__slot:hover {
  border-color: var(--accent, #24504a);
  color: var(--accent, #24504a);
}
/* The two alternatives a 409 offers. They can be on another day, so they
   show the whole label, and they sit on the recessed surface to read as a
   different kind of thing from the list below them. */
.bk__slot--near { background: var(--paper-3, #f2efe7); }
.bk__slot[aria-pressed="true"] {
  background: var(--accent, #24504a);
  border-color: var(--accent, #24504a);
  color: var(--on-accent, #fff);
}
.bk__slot[aria-pressed="true"]:hover {
  background: var(--accent-deep, #1b3d38);
  color: var(--on-accent, #fff);
}

/* --------------------------------------------------------------------------
   5. Step three: who is coming
   -------------------------------------------------------------------------- */

/* What the reader has chosen, restated under the month grid it was chosen
   from - and above the two links that change it. */
.bk__summary {
  margin: 24px 0 6px;
  font-family: var(--font-label, ui-monospace, monospace);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  color: var(--accent, #24504a);
}

.bk__form { max-width: 560px; }

.bk__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

.bk__field { margin: 0 0 18px; }
.bk__pair .bk__field { margin: 0; }

.bk__label {
  display: block;
  margin: 0 0 7px;
  font-family: var(--font-label, ui-monospace, monospace);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-ink, #6b655c);
}
.bk__opt { letter-spacing: 0.04em; text-transform: none; }

.bk__input {
  width: 100%;
  min-height: 46px;
  padding: 11px 14px;
  color: var(--ink, #1f1d1a);
  background: var(--paper, #fff);
  /* ssa derives a real control edge from ink and paper; mnc and legal use
     --muted for the same boundary. */
  border: 1px solid var(--field-line, var(--muted, #8c877d));
  border-radius: min(var(--r-card, 2px), 8px);
  font-family: var(--font-body, system-ui, sans-serif);
  font-size: 16px;
  line-height: 1.4;
}
.bk__textarea { min-height: 46px; resize: vertical; }

/* Every site draws its own focus ring, but not on the same selectors: mnc
   sets one globally, ssa lists them, legal covers .btn and .link only. So
   the picker states its own, in whichever colour the site focuses in -
   --accent2 is the accent itself on ssa and mnc, and legal's blue. */
.bk__day:focus-visible,
.bk__slot:focus-visible,
.bk__nav:focus-visible,
.bk__back:focus-visible {
  outline: 2px solid var(--accent2, var(--accent, #24504a));
  outline-offset: 2px;
}
.bk__input:focus-visible {
  outline: 2px solid var(--accent2, var(--accent, #24504a));
  outline-offset: 1px;
  border-color: var(--accent2, var(--accent, #24504a));
}

/* A disabled Confirm is what a reader sees after a 409, and all three sites
   style their primary button as a solid accent fill that says nothing about
   being disabled. Told apart by fill and text colour rather than by opacity:
   a washed-out accent drops under 4.5:1, and --muted-ink is documented AA on
   --paper-3 on every one of the three palettes. */
.bk__form button[disabled] {
  background: var(--paper-3, #f2efe7);
  border-color: var(--rule, #dcd8cf);
  color: var(--muted-ink, #6b655c);
  cursor: not-allowed;
}

/* Colour is never the only signal: the handler also writes the message below
   the field, announces a summary, and moves focus to the first bad control. */
.bk__input[aria-invalid="true"] {
  border-color: var(--error, #a3231d);
  box-shadow: inset 0 0 0 1px var(--error, #a3231d);
}

.bk__err {
  display: block;
  margin-top: 6px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--error, #a3231d);
}
.bk__err:empty { display: none; }

/* The honeypot the collector screens on (field name `website`, sent empty).
   Off-screen rather than display:none-d: a bot that reads the computed style
   skips a hidden field, and a hidden-but-reachable one is what actually
   catches form fillers. aria-hidden, tabindex and autocomplete="off" keep it
   away from a keyboard, a screen reader and a password manager alike. */
.bk__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.bk__fine { margin-top: 16px; }

/* --------------------------------------------------------------------------
   6. The two end states: the email bail-out, and the confirmation
   -------------------------------------------------------------------------- */

.bk__bail {
  padding: 22px 24px;
  background: var(--paper-2, #fffdf9);
  border: 1px solid var(--rule, #dcd8cf);
  border-radius: min(var(--r-card, 2px), 16px);
}
.bk__bail > :first-child { margin-top: 0; }

.bk__done {
  padding: 26px 26px 28px;
  background: var(--paper-2, #fffdf9);
  border: 1px solid var(--accent, #24504a);
  border-radius: min(var(--r-card, 2px), 16px);
}
.bk__donehead { margin: 0 0 12px; }
.bk__doneslot {
  margin: 0 0 10px;
  font-family: var(--font-label, ui-monospace, monospace);
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--accent, #24504a);
}

/* --------------------------------------------------------------------------
   7. Phones. One pane, the calendar and the time grid take the full column.
   -------------------------------------------------------------------------- */

@media (max-width: 560px) {
  .bk { margin-top: 28px; }
  .bk__cal { max-width: none; }
  .bk__day { min-height: 42px; font-size: 14.5px; }
  .bk__dow span { font-size: 9.5px; letter-spacing: 0.06em; }
  .bk__pair { grid-template-columns: 1fr; gap: 0; }
  .bk__pair .bk__field { margin: 0 0 18px; }
  .bk__bail,
  .bk__done { padding: 20px 18px; }
  /* two tabs, and they must not crowd on a 320px card */
  .bk__tab { padding: 11px 12px 12px; font-size: 14.5px; }
}

/* --------------------------------------------------------------------------
   8. Motion, and only under no-preference. Under `reduce` the script shows
      the arriving blocks with no class at all, so there is nothing here to
      undo. The steps no longer slide: the calendar stays put across the
      whole flow, so a step change is a change of content in the pane beside
      it and a fade of the arriving block is the only honest thing to animate.
   -------------------------------------------------------------------------- */

@keyframes bk-fade-up {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
@keyframes bk-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: no-preference) {
  /* the tab switch and the step change: the same plain opacity fade, on the
     arriving thing only. Both classes are added by book.js and neither is
     ever present under `reduce`. */
  .bk__panel--in,
  .bk__fade { animation: bk-fade 200ms ease both; }
  .bk__tab { transition: color 120ms ease, border-color 120ms ease; }

  .bk__day,
  .bk__slot,
  .bk__nav,
  .bk__input {
    transition: background-color 120ms ease, border-color 120ms ease,
                color 120ms ease, box-shadow 120ms ease;
  }
  .bk__bail,
  .bk__done { animation: bk-fade-up 240ms cubic-bezier(0.4, 0, 0.2, 1) both; }
}
