/* ==========================================================================
   Textum Media — booking.css
   Page-only rules for /booking. Loaded after site.css.

   Everything here either lays out the two-column form page or extends a
   shared component (.field, .pill, .steps, .notice) for this page. No new
   parallel components, no hex, no raw px except 1px hairlines.

   CONTENTS
     01 Page shell + two-column grid
     02 Form column
     03 Form layout & controls
     04 Interest pills (checkbox group)
     04b The when block: calendar + time cards
     04c Calendar
     04d Time-of-day cards
     04e Honeypot
     05 Submit row + inline notice
     06 Success panel
     07 Sticky aside
   ========================================================================== */


/* == 01 Page shell + two-column grid ====================================== */

/* The page head already carries the top rhythm and a hairline, so the form
   section opens tighter than a standard --space-section. */
.bk-section {
  padding-block: var(--space-9);
}

.bk-grid {
  align-items: start;
}


/* == 02 Form column ======================================================= */

.bk-main > * + * {
  margin-top: var(--space-6);
}

.bk-main__title {
  font-size: var(--fs-xl);
  max-width: 18ch;
}

.bk-main__intro {
  font-size: var(--fs-sm);
  max-width: var(--measure);
}

/* <noscript> is inline by default; the notice inside needs a block box. */
.bk-noscript {
  margin-top: var(--space-4);
}


/* == 03 Form layout & controls ============================================ */

.bk-form {
  display: grid;
  gap: var(--space-5);
}

.bk-form__row {
  display: grid;
  gap: var(--space-4);
}

/* Two fields abreast as soon as the column is wide enough for a comfortable
   input; the page grid itself does not stack until 901px. */
@media (min-width: 601px) {
  .bk-form__row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* One generous control height across text, date and select so the rows line
   up. textarea keeps the 140px minimum from site.css. */
.bk-form input.field__input,
.bk-form select.field__input {
  min-height: var(--space-7);
}

/* Safari/iOS centres the value inside a date input and shrinks it; keep it
   reading like every other text field. */
.bk-date::-webkit-date-and-time-value {
  text-align: left;
}

.bk-date::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: .55;
}

.bk-date:hover::-webkit-calendar-picker-indicator {
  opacity: 1;
}


/* == 04 Interest pills (checkbox group) ==================================== */

.bk-choice__legend {
  display: block;
  margin-bottom: var(--space-1);
}

.bk-choice__hint {
  margin-top: 0;
  margin-bottom: var(--space-2);
}

.bk-choice__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* The <label> is the hit area; the visually-hidden checkbox inside it stays
   focusable and keeps the visible checked state on its own pill. */
.bk-choice__option {
  display: inline-flex;
  cursor: pointer;
}

.bk-choice__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--space-5);
  padding: var(--space-1) var(--space-2);
  font-size: var(--fs-sm);
  background-color: var(--surface-card);
  color: var(--fg);
  user-select: none;
}

/* The check mark: drawn but invisible until the box is checked, so picking
   a pill never reflows its neighbours. */
.bk-choice__check {
  width: 14px;
  height: 14px;
  flex: none;
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform: scale(.6);
}

.bk-choice__option:hover .bk-choice__pill {
  border-color: var(--fg);
}

.bk-choice__input:checked + .bk-choice__pill {
  background-color: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
}

.bk-choice__input:checked + .bk-choice__pill .bk-choice__check {
  opacity: 1;
  transform: none;
}

/* The checkbox itself is clipped to a hairline, so the ring has to be drawn
   on the pill that stands in for it. */
.bk-choice__input:focus-visible + .bk-choice__pill {
  outline: var(--focus-width) solid var(--ring);
  outline-offset: var(--focus-offset);
}


/* == 04b The when block: calendar + time cards ============================
   Two columns as soon as there is room for both, stacked below that. No
   breakpoint needed: auto-fit does it from the available width, which is
   what actually matters inside a 7-of-12 form column.
   ======================================================================== */

.bk-when {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
  align-items: start;
}

.bk-when__slot {
  border: 0;
  padding: 0;
  min-inline-size: 0;
}

.bk-when__legend {
  padding: 0;
}


/* == 04c Calendar =========================================================
   Mounted by booking.js in place of the native date control. Soft by
   construction: one hairline around a card, circular days, a tinted hover
   and no rules between cells.
   ======================================================================== */

/* The host reserves the grid's height from first paint. The native date
   input is hidden the moment we know JavaScript is running (the inline flag
   in <head>), so nothing is painted that is about to be replaced. */
.bk-cal-host {
  display: none;
}

.js .bk-cal-host {
  display: block;
  /* Measured height of the mounted grid: head + weekday row + six 40px rows
     + footer + padding. Six rows always render, so this does not vary. */
  min-height: 431px;
}

.js .bk-when__date .bk-date {
  display: none;
}

/* booking.js adds .no-cal if the grid throws while it is being built: the
   native control comes back and the reserved box goes away. */
.no-cal .bk-cal-host {
  display: none;
}

.no-cal .bk-when__date .bk-date {
  display: block;
}

.bk-cal {
  max-width: 380px;
  padding: var(--space-3);
  background-color: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
}

.bk-cal:focus {
  outline: none;
}

.bk-cal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1);
  margin-bottom: var(--space-2);
}

.bk-cal__month {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-tight);
  text-align: center;
  flex: 1 1 auto;
}

.bk-cal__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background-color: transparent;
  color: var(--fg-2);
}

.bk-cal__nav:hover:not(:disabled) {
  background-color: var(--signal-soft);
  border-color: transparent;
  color: var(--signal);
}

.bk-cal__nav:disabled {
  opacity: .3;
  cursor: not-allowed;
}

.bk-cal__chev {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  stroke-width: 1.5;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bk-cal__row {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}

.bk-cal__wd {
  padding-block: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-mono);
  line-height: 1.4;
  color: var(--fg-2);
  text-align: center;
}

.bk-cal__cell {
  display: grid;
  place-items: center;
  height: 40px;
}

.bk-cal__day {
  display: flex;
  align-items: center;
  justify-content: center;
  width: min(40px, 100%);
  height: 100%;
  aspect-ratio: 1;
  border: 0;
  border-radius: var(--r-pill);
  background-color: transparent;
  font-family: var(--font-text);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--fg);
}

.bk-cal__day:hover:not(:disabled) {
  background-color: var(--signal-soft);
  color: var(--signal);
}

.bk-cal__day:disabled {
  color: var(--fg-2);
  opacity: .38;
  cursor: not-allowed;
}

/* Beyond the booking window (180 days) — same treatment as a past day. */
.bk-cal__cell.is-beyond .bk-cal__day {
  color: var(--fg-2);
}

/* Today and tomorrow: inside the minimum lead time (BOOKING_MIN_DAYS_AHEAD),
   so they are real, unbooked dates rather than gone like a past day — muted
   the same way, but struck through so "too soon" reads differently from
   "already over". */
.bk-cal__cell.is-unavailable .bk-cal__day {
  color: var(--fg-2);
  text-decoration: line-through;
  text-decoration-color: var(--line-2);
}

/* Weekends read one step quieter, so the working week leads the eye. The
   :not(.is-selected) matters: without it this outranks the selected rule
   and a picked Saturday keeps the muted grey on its blue fill. */
.bk-cal__cell.is-weekend:not(.is-selected) .bk-cal__day:not(:disabled) {
  color: var(--fg-2);
}

/* Today: a hairline ring, not a fill — the fill belongs to the selection. */
.bk-cal__cell.is-today .bk-cal__day {
  box-shadow: inset 0 0 0 1px var(--line-2);
}

.bk-cal__cell.is-selected .bk-cal__day,
.bk-cal__cell.is-selected .bk-cal__day:hover {
  background-color: var(--signal);
  box-shadow: none;
  color: var(--on-signal);
  font-weight: var(--fw-semibold);
}

.bk-cal__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1) var(--space-2);
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}

.bk-cal__summary {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-mono);
  line-height: 1.5;
  color: var(--fg-2);
}

.bk-cal__summary.is-set {
  color: var(--fg);
}

.bk-cal__today {
  padding: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-mono);
  color: var(--link);
  border-bottom: 1px solid var(--rule-2);
}

.bk-cal__today:hover {
  border-bottom-color: currentColor;
}

/* The calendar carries the invalid state for the date field, since the real
   input is hidden once the grid is mounted. */
.field--invalid .bk-cal {
  border-color: var(--fg);
  border-width: 2px;
  padding: calc(var(--space-3) - 1px);
}


/* == 04d Time-of-day cards ================================================
   Three radios rendered as cards. The input stays in the DOM (clipped, not
   display:none) so the group keeps native arrow-key behaviour and focus.
   ======================================================================== */

.bk-slots {
  display: grid;
  gap: var(--space-2);
}

.bk-slot {
  display: block;
  cursor: pointer;
}

.bk-slot__body {
  display: grid;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
  background-color: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.bk-slot:hover .bk-slot__body {
  border-color: var(--line-2);
  background-color: var(--ink-tint);
}

.bk-slot__name {
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--fg);
}

.bk-slot__time {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: var(--tracking-mono);
  color: var(--fg-2);
}

.bk-slot__note {
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--fg-2);
}

/* Checked: a tinted card with a signal edge drawn inside, so nothing moves. */
.bk-slot__input:checked + .bk-slot__body {
  background-color: var(--signal-soft);
  border-color: var(--signal);
  box-shadow: inset 0 0 0 1px var(--signal);
}

.bk-slot__input:checked + .bk-slot__body .bk-slot__name {
  color: var(--signal);
}

/* The radio is clipped to a hairline, so the card shows the focus ring. */
.bk-slot__input:focus-visible + .bk-slot__body {
  outline: var(--focus-width) solid var(--ring);
  outline-offset: var(--focus-offset);
}

.field--invalid .bk-slot__body {
  border-color: var(--fg);
}


/* == 04e Honeypot ========================================================
   Off-screen rather than display:none, so a bot that ignores hidden fields
   still fills it. Never shown, never focusable, never announced.
   ======================================================================== */

.bk-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}


/* == 05 Submit row + inline notice ======================================== */

/* The live region is always in the DOM so screen readers announce a server
   error the moment it lands; it must not open a gap while it is empty. */
.bk-form__notice:empty {
  display: none;
}

.bk-form__actions {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--rule);
}

.bk-form__promise {
  max-width: var(--measure);
}

.bk-submit[disabled] .arrow {
  transform: none;
}


/* == 06 Success panel ===================================================== */

/* .notice--success is a signal-blue block, so the default blue focus ring
   would be invisible on it — the panel takes focus after a successful post. */
.bk-success {
  --ring: var(--on-signal);
  padding: var(--space-4);
  /* The panel takes focus and scrolls itself in; keep it clear of the
     sticky nav. */
  scroll-margin-top: calc(var(--nav-h) + var(--space-4));
}

.bk-success__body > * + * {
  margin-top: var(--space-2);
}

.bk-success__when {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--line-inv-2);
  border-bottom: 1px solid var(--line-inv-2);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: var(--tracking-mono);
}


/* == 07 Sticky aside ====================================================== */

.bk-aside {
  align-self: start;
  padding-top: var(--space-6);
  border-top: 1px solid var(--rule);
}

.bk-aside__sticky > * + * {
  margin-top: var(--space-5);
}

.bk-aside__title {
  font-size: var(--fs-xl);
  max-width: 16ch;
}

/* .steps goes two-column at 901px, which is far too wide for a 4-column
   aside — keep the numbered rows stacked and tighter at every width. */
.bk-steps .steps__item {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-1);
  padding-block: var(--space-3);
}

.bk-steps .steps__title {
  font-size: var(--fs-md);
  margin-bottom: 0;
}

.bk-direct {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-4);
  border-top: 1px solid var(--rule);
}

.bk-direct__list {
  display: grid;
  gap: var(--space-1);
}

.bk-direct__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: var(--tracking-mono);
  color: var(--link);
  text-decoration: none;
}

.bk-direct__link:hover {
  color: var(--link-hover);
  text-decoration: underline;
  text-underline-offset: .2em;
}

.bk-direct__icon {
  width: var(--space-3);
  height: var(--space-3);
  stroke: currentColor;
  stroke-width: 1.5;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The page grid goes two-column at 901px (site.css), but a 4-of-12 aside is
   too narrow to hold its own heading and hint text at that width — "Twenty
   minutes, four things." and the closing hint both broke into three ragged
   lines in a ~200px column. Stay single-column through that range and only
   split once the aside has enough room to read comfortably. */
@media (min-width: 901px) and (max-width: 1099px) {
  .bk-main,
  .bk-aside {
    grid-column: 1 / -1;
  }
}

@media (min-width: 1100px) {
  /* On desktop the aside sits beside the form, so the mobile separator is
     replaced by a hairline that travels with the sticky block. */
  .bk-aside {
    padding-top: 0;
    border-top: 0;
  }

  .bk-aside__sticky {
    position: sticky;
    top: calc(var(--nav-h) + var(--space-4));
    padding-left: var(--space-6);
    border-left: 1px solid var(--rule);
  }
}


/* == 08 Motion ============================================================
   Calendar feedback only. Everything here is decorative and sits inside the
   reduced-motion guard; the grid works identically without it.
   ======================================================================== */

@media (prefers-reduced-motion: no-preference) {
  .bk-cal__nav,
  .bk-cal__day,
  .bk-cal__today,
  .bk-slot__body {
    transition:
      background-color var(--dur) var(--ease),
      border-color var(--dur) var(--ease),
      box-shadow var(--dur) var(--ease),
      color var(--dur) var(--ease),
      transform var(--dur) var(--ease);
  }

  .bk-slot:hover .bk-slot__body {
    transform: translateY(-1px);
  }

  /* The picked day lands rather than blinking on. */
  .bk-cal__cell.is-just-picked .bk-cal__day {
    animation: bk-pick var(--dur-slow) var(--ease);
  }

  /* Rows settle in when the month turns — added by booking.js on nav only,
     so picking a day never re-animates the whole grid. */
  .bk-cal__grid.is-turning .bk-cal__row:not(.bk-cal__row--head) {
    animation: bk-month-in var(--dur-slow) var(--ease) both;
  }

  .bk-cal__grid.is-turning .bk-cal__row:nth-child(3) { animation-delay: 30ms; }
  .bk-cal__grid.is-turning .bk-cal__row:nth-child(4) { animation-delay: 60ms; }
  .bk-cal__grid.is-turning .bk-cal__row:nth-child(5) { animation-delay: 90ms; }
  .bk-cal__grid.is-turning .bk-cal__row:nth-child(6) { animation-delay: 120ms; }
  .bk-cal__grid.is-turning .bk-cal__row:nth-child(7) { animation-delay: 150ms; }
}

@keyframes bk-pick {
  from { transform: scale(.7); }
  to { transform: scale(1); }
}

@keyframes bk-month-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
