/* Public booking pages — /b/{token} and /b/manage/{token}. Standalone, zero app chrome.

   Sibling of publicform.css and deliberately built the same way: neutrals come from
   tokens.css, and every brand-carrying value comes from the --pf-* variables the page
   injects from BrandingService. The two surfaces share one vocabulary because they are
   one product to the person looking at them — the booking page had been the only
   anonymous surface with its own hand-rolled greys, and it showed.

   TYPEFACE. The design this implements is drawn in Playfair Display + Bricolage
   Grotesque, and this page now renders in them. It did not until 2026-08-30: those two
   families came from Google Fonts, loaded by _Layout, and an anonymous page may not hand a
   visitor's IP to a font CDN on our customer's behalf — so the display face fell back to
   the system serif and the UI face to the system sans, a deliberate divergence from the
   drawings. Self-hosting was the fix and it is done: tokens.css carries a generated
   font-face block over wwwroot/fonts, so every page that loads tokens.css gets the real
   faces from our own origin, with no request leaving it. Nothing here had to change.
   The BRAND face is a separate question and still resolves to a device stack — see
   BrandFonts, which is bound by email, not by the CDN rule. */

.bk {
    margin: 0;
    background: var(--paper);
    font-family: var(--pf-font, var(--font-sans));
    color: var(--text);
    -webkit-text-size-adjust: 100%;
}

/* ── Stage, brand bar, card, badge — the shell every state renders inside ───────── */

.bk-stage { min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 40px 16px 56px; box-sizing: border-box; }
.bk-col { width: 100%; max-width: 600px; display: flex; flex-direction: column; align-items: center; }

.bk-brand { width: 100%; margin: 0 0 18px; display: flex; align-items: center; gap: 10px; }
.bk-mark { width: 30px; height: 30px; border-radius: 9px; background: var(--pf-accent, var(--accent-deep)); display: grid; place-items: center; color: var(--pf-on-accent, #fff); font-family: var(--font-display); font-size: 17px; font-weight: 600; flex-shrink: 0; }
.bk-logo { max-height: 44px; max-width: 220px; width: auto; height: auto; display: block; }
.bk-name { font-family: var(--pf-font-head, var(--font-display)); font-size: 19px; font-weight: var(--pf-head-weight, 600); letter-spacing: -0.01em; color: var(--text); }

.bk-card { width: 100%; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-xl); box-shadow: var(--shadow-lg); padding: 32px 34px; box-sizing: border-box; }
.bk-card--narrow { max-width: 520px; }

.bk-badge { margin-top: 20px; font-size: 12.5px; color: var(--muted-2); }
.bk-badge strong { font-size: 14px; color: var(--text-2); }

/* ── Type ──────────────────────────────────────────────────────────────────────── */

.bk-h1 { margin: 0 0 8px; font-family: var(--pf-font-head, inherit); font-size: 26px; font-weight: var(--pf-head-weight, 700); letter-spacing: -0.01em; line-height: 1.15; color: var(--text); }
/* A heading here is usually a date and a time, and the Editorial serif draws old-style figures
   by default: the 5 of "Thursday 5 November" dropped below the line and "10:00" read small. Lining
   figures, as the website's serif headings have; a sans face is unchanged. */
.bk-h1, .bk-name, .bk-masthead-name, .bk-empty h2 { font-variant-numeric: lining-nums; }
.bk-lead { margin: 0 0 22px; font-size: 14.5px; line-height: 1.6; color: var(--text-muted); }
.bk-lead strong { font-weight: 600; color: var(--text-2); overflow-wrap: anywhere; }
.bk-h { margin: 22px 0 10px; font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }

/* ── Progress rail ─────────────────────────────────────────────────────────────── */

.bk-steps { display: flex; align-items: center; gap: 6px; margin-bottom: 22px; flex-wrap: wrap; }
.bk-step { display: inline-flex; align-items: center; gap: 7px; padding: 5px 11px 5px 6px; border-radius: 999px; background: var(--card); border: 1px solid var(--border); font-size: 12px; font-weight: 600; color: var(--text-faint); text-decoration: none; }
/* --muted-2, not --text-faint. The ramp in tokens.css is audited against --paper, --card
   and the rail; --border-soft is darker than all three, and an 11px numeral on it measured
   4.34:1. One step up the same ramp clears it without breaking the ramp's order. */
.bk-step i { width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; font-size: 11px; font-weight: 700; font-style: normal; background: var(--border-soft); color: var(--muted-2); }
.bk-step--done { color: var(--text-2); }
/* ⚠ NEUTRAL ink on the tint, not the accent's. --pf-accent-link is darkened until it
   clears 4.5:1 on WHITE, which is the right target for a link on a white card and is a
   hair short on the accent TINT — measured 4.46:1 at 12px/650 on a pale-yellow brand,
   which is an AA fail on a chip that renders on every single page load. The chip is
   still unmistakably the accent's: it keeps the accent tint as its ground and the accent
   itself as its border. Do not "restore" the accent ink here without first giving the
   brand an ink checked against the tint rather than against white — that is an eighth
   --ws-* value and a change to nine public surfaces, not a colour swap. */
.bk-step--done i { background: var(--pf-accent-tint, var(--accent-tint)); color: var(--text-2); }
.bk-step--now { background: var(--pf-accent-tint, var(--accent-tint)); border-color: var(--pf-accent, var(--accent-deep)); font-weight: 650; color: var(--text-2); }
.bk-step--now i { background: var(--pf-accent, var(--accent-deep)); color: var(--pf-on-accent, #fff); }
.bk-step-sep { width: 14px; height: 1px; background: var(--border-2); flex-shrink: 0; }

/* ── Choice rows (services, staff) ─────────────────────────────────────────────── */

.bk-rows { display: flex; flex-direction: column; gap: 8px; }
.bk-row { display: flex; align-items: center; gap: 14px; padding: 14px 16px; border: 1.5px solid transparent; box-shadow: inset 0 0 0 1px var(--border); border-radius: var(--radius); background: var(--card); text-decoration: none; color: inherit; }
/* The inset shadow carries the 1px resting border so the 1.5px selected border costs no
   reflow — selecting a row must not nudge every row below it. */
.bk-row[aria-current="true"] { border-color: var(--pf-accent, var(--accent-deep)); box-shadow: none; background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-row:focus-visible { outline: 2px solid var(--pf-accent-deep, var(--accent-ink)); outline-offset: 2px; }
.bk-radio { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--border-2); box-sizing: border-box; flex-shrink: 0; }
.bk-row[aria-current="true"] .bk-radio { border: 5px solid var(--pf-accent, var(--accent-deep)); background: var(--card); }
.bk-row-body { flex: 1; min-width: 0; }
.bk-row-name { display: block; font-size: 15px; font-weight: 650; color: var(--text); }
.bk-row-sub { display: block; font-size: 13px; color: var(--muted-2); margin-top: 2px; }
/* Neutral, for the reason .bk-step--now is neutral: --pf-accent-link clears 4.5:1 on WHITE
   and lands at 4.46 on the accent tint, which is what a selected row's ground is. The row
   is unmistakably selected without it — tint, border and a filled radio all say so. */
.bk-row[aria-current="true"] .bk-row-sub { color: var(--text-2); }
.bk-row[aria-current="true"] .bk-row-sub--soft { color: var(--muted-2); }
.bk-row-meta { font-size: 12.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; flex-shrink: 0; }

.bk-row-price { font-size: 15px; font-weight: 650; color: var(--text-2); font-variant-numeric: tabular-nums; flex-shrink: 0; }
.bk-row[aria-current="true"] .bk-row-price { color: var(--text); }

.bk-avatar { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; font-size: 13.5px; font-weight: 700; flex-shrink: 0; background: var(--relation-soft); color: var(--relation-ink); }
.bk-avatar--photo { object-fit: cover; }

/* The reference. Mono because it gets read out and typed back in, and `user-select: all` so
   one tap takes the whole thing rather than a word of it. */
.bk-ref-label { margin: 0 0 2px; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); }
.bk-ref { margin: 0 0 10px; font-family: var(--font-mono); font-size: 30px; font-weight: 700; letter-spacing: .03em; line-height: 1.2; color: var(--pf-accent-link, var(--accent-ink)); user-select: all; }
.bk-ref-hint { margin: 0 0 22px; font-size: 13.5px; color: var(--text-muted); }

/* ── The confirmation lands ────────────────────────────────────────────────────────
   The one moment on this page where somebody's anxiety resolves, and it arrived with no
   more ceremony than a paragraph. The reference overshoots and settles — the same landing
   the grid's summary values get, on the one number this page exists to produce — and the
   detail follows once it is readable.

   Safe to animate on load, unlike anything in the app: this block only renders when a
   booking has JUST been taken, so the page's existence IS the change. It is also drawn on
   /b/manage's confirmation, which is why it lives here rather than in either page — a
   component added to one and not the other is how the two come to disagree about one
   booking. Reduced motion is already covered by the blanket `.bk *` rule at the foot of
   this file, so there is nothing extra to declare. */
.bk-ref { animation: bkRefLand 520ms cubic-bezier(.2, 1.08, .32, 1) 160ms both; }
@keyframes bkRefLand {
    0%   { opacity: 0; transform: translateY(-9px) scale(.72); }
    62%  { opacity: 1; transform: translateY(0) scale(1.06); }
    100% { opacity: 1; transform: none; }
}
.bk-ref-label { animation: bkRefUp 300ms ease-out both; }
.bk-ref-hint  { animation: bkRefUp 320ms ease-out 480ms both; }
@keyframes bkRefUp { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.bk-ref-inline { font-family: var(--font-mono); font-size: 13.5px; user-select: all; }
.bk-avatar--any { background: var(--card); border: 1px solid var(--pf-accent, var(--accent-deep)); font-size: 15px; color: var(--pf-accent-link, var(--accent-ink)); }

/* ── Week rail and time grid ───────────────────────────────────────────────────── */

.bk-week { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.bk-week-label { font-size: 13px; font-weight: 650; color: var(--text-2); }
.bk-week-nav { display: flex; gap: 6px; }
.bk-arrow { width: 30px; height: 30px; border-radius: 9px; border: 1px solid var(--border); background: var(--card); display: grid; place-items: center; font-size: 13px; color: var(--text-2); text-decoration: none; }
.bk-arrow:hover { background: var(--card-2); }

.bk-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-bottom: 22px; }
.bk-day { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 9px 0 8px; border-radius: var(--radius); border: 1.5px solid transparent; box-shadow: inset 0 0 0 1px var(--border); background: var(--card); text-decoration: none; }
.bk-day-dow { font-size: 10.5px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.bk-day-num { font-size: 16px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.bk-day-free { font-size: 10.5px; font-weight: 600; color: var(--pf-accent-link, var(--accent-ink)); }
.bk-day--scarce .bk-day-free { color: var(--rollup-ink); }
.bk-day--empty { box-shadow: inset 0 0 0 1px var(--border-soft); background: var(--card-2); }
/* --ink-300 is a legacy blue-grey outside the audited ramp and measured 1.86:1 here, on a
   date numeral. An empty day is information — "she doesn't work Mondays" is the reason the
   day is drawn at all rather than removed — so it has to be readable. --text-faint is the
   quietest tier that passes, which is exactly what this wants. */
.bk-day--empty .bk-day-num, .bk-day--empty .bk-day-free { color: var(--text-faint); }
.bk-day[aria-current="true"] { border-color: var(--pf-accent, var(--accent-deep)); box-shadow: none; background: var(--pf-accent, var(--accent-deep)); }
.bk-day[aria-current="true"] .bk-day-dow { color: var(--pf-on-accent, #fff); opacity: .8; }
.bk-day[aria-current="true"] .bk-day-num { color: var(--pf-on-accent, #fff); }
.bk-day[aria-current="true"] .bk-day-free { color: var(--pf-on-accent, #fff); opacity: .85; }

.bk-dayhead { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; }
.bk-dayhead-l { font-size: 13px; font-weight: 650; color: var(--text-2); }
.bk-dayhead-r { font-size: 12px; color: var(--text-faint); }

.bk-times { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.bk-slot { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 12px 0; border: 1.5px solid transparent; box-shadow: inset 0 0 0 1px var(--border-2); border-radius: var(--radius); background: var(--card); font: inherit; font-size: 15px; font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; cursor: pointer; }
.bk-slot[aria-pressed="true"] { border-color: var(--pf-accent, var(--accent-deep)); box-shadow: none; background: var(--pf-accent-tint, var(--accent-tint)); font-weight: 700; }
.bk-slot:focus-visible { outline: 2px solid var(--pf-accent-deep, var(--accent-ink)); outline-offset: 2px; }

/* ── Notices ───────────────────────────────────────────────────────────────────── */

.bk-nudge { margin-top: 18px; display: flex; align-items: flex-start; gap: 9px; padding: 11px 13px; border-radius: var(--radius); background: var(--card-2); border: 1px solid var(--border-soft); }
/* Neutral, the same rule .bk-step--now follows: --pf-accent-link clears 4.5:1 on WHITE
   and measured 4.41 on --card-2, which is what a nudge sits on. It also reserves the
   accent for things a reader can PRESS — this glyph is a flourish beside an aside. */
.bk-nudge i { font-style: normal; font-size: 13px; line-height: 1.5; color: var(--muted-2); }
.bk-nudge span { font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }
.bk-nudge strong { font-weight: 650; color: var(--text-2); }

.bk-problem { display: flex; gap: 11px; padding: 13px 15px; border-radius: var(--radius); background: var(--rollup-soft); border: 1px solid #FFC9B4; margin-bottom: 20px; }
.bk-problem i { font-style: normal; color: var(--rollup-ink); font-size: 14px; line-height: 1.4; }
.bk-problem b { display: block; font-size: 14px; font-weight: 700; color: var(--rollup-ink); }
.bk-problem span { display: block; font-size: 13px; line-height: 1.55; color: #8A2F14; margin-top: 3px; }

.bk-empty { text-align: center; }
.bk-empty-icon { width: 44px; height: 44px; border-radius: 50%; background: var(--card-2); border: 1px solid var(--border-soft); color: var(--text-faint); display: grid; place-items: center; font-size: 19px; margin: 0 auto 14px; }
.bk-empty h2 { margin: 0 0 6px; font-family: var(--pf-font-head, inherit); font-size: 19px; font-weight: var(--pf-head-weight, 700); letter-spacing: -0.01em; color: var(--text); }
.bk-empty p { margin: 0 0 18px; font-size: 14px; line-height: 1.6; color: var(--text-muted); }
.bk-empty p strong { font-weight: 650; color: var(--text); }
.bk-empty-tail { margin: 14px 0 0; font-size: 13px; color: var(--text-faint); }
.bk-empty-tail a { font-weight: 650; color: var(--pf-accent-link, var(--accent-ink)); }

/* ── Summary panel ─────────────────────────────────────────────────────────────── */

/* With no lead line under it (no message of the owner's and no email to name), the summary
   sits under the heading, not on it. */
.bk-h1 + .bk-summary { margin-top: 18px; }
.bk-summary { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; border-radius: var(--radius); background: var(--card-2); border: 1px solid var(--border-soft); margin-bottom: 24px; }
.bk-summary-row { display: flex; align-items: baseline; gap: 10px; }
.bk-summary-k { width: 74px; flex-shrink: 0; font-size: 12px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.bk-summary-v { flex: 1; font-size: 14px; font-weight: 600; color: var(--text); }
.bk-summary-a { font-size: 12.5px; font-weight: 600; color: var(--pf-accent-link, var(--accent-ink)); }

/* ── Fields and buttons ────────────────────────────────────────────────────────── */

.bk-field { margin-bottom: 18px; }
.bk-field > label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px; color: var(--text); }
.bk-req { color: var(--rollup-ink); }
.bk-field input, .bk-field textarea, .bk-field select {
    width: 100%; box-sizing: border-box; padding: 10px 12px; font: inherit; font-size: 14px;
    border: 1px solid var(--border-2); border-radius: var(--radius); background: var(--card); color: var(--text);
}
.bk-field textarea { min-height: 62px; resize: vertical; }
.bk-field input::placeholder, .bk-field textarea::placeholder { color: var(--ink-300); }
.bk-field input:focus, .bk-field textarea:focus, .bk-field select:focus {
    outline: none; border-color: var(--pf-accent-deep, var(--accent-ink)); box-shadow: 0 0 0 3px var(--pf-accent-tint, var(--accent-tint));
}
.bk-checks { display: flex; flex-wrap: wrap; gap: 10px; }
.bk-checks label, .bk-check { display: flex; gap: 8px; align-items: center; font-size: 14px; font-weight: 400; }
.bk-checks input, .bk-check input { width: auto; }

/* .bk-go's own declaration is gone: it IS .bk-btn, and is folded into that selector at the
   foot of this file. Two definitions of one button is how a vocabulary drifts. */
.bk-foot { margin: 12px 0 0; font-size: 12px; line-height: 1.55; color: var(--text-faint); text-align: center; }

.bk-secondary { display: flex; gap: 8px; flex-wrap: wrap; }
/* The booked screen's row of things to do with the booking: each button takes an equal share of
   its line, so on a phone that wraps two-and-one the third fills its line instead of sitting
   alone at the left. */
.bk-secondary--booked > .bk-btn2 { flex: 1 1 auto; }
/* inline-flex and centred, like .bk-btn: as an inline-block with a 44px floor on a touch
   screen the label sat at the top of its box, and where a row stretched the two buttons
   ("Add to calendar", "Google Calendar") they read as empty boxes with a caption. */
.bk-btn2 { padding: 10px 16px; border-radius: var(--radius); border: 1px solid var(--border-2); background: var(--card); font-size: 13.5px; font-weight: 650; line-height: 1.3; color: var(--text); text-decoration: none; display: inline-flex; align-items: center; justify-content: center; text-align: center; box-sizing: border-box; }
.bk-btn2:hover { background: var(--card-2); }
.bk-btn2--danger { border-color: #FFC9B4; color: var(--rollup-ink); }

/* ── Confirmation ──────────────────────────────────────────────────────────────── */

.bk-check-circle { width: 52px; height: 52px; border-radius: 50%; background: var(--pf-accent-tint, var(--accent-tint)); color: var(--pf-accent-link, var(--accent-ink)); display: grid; place-items: center; font-size: 26px; font-weight: 700; margin-bottom: 16px; }

/* ── Classes ───────────────────────────────────────────────────────────────────── */

.bk-sessions { display: flex; flex-direction: column; gap: 8px; }
.bk-session { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; padding: 12px 14px; border: 1.5px solid transparent; box-shadow: inset 0 0 0 1px var(--border); border-radius: var(--radius); background: var(--card); font: inherit; cursor: pointer; }
.bk-session[aria-pressed="true"] { border-color: var(--pf-accent, var(--accent-deep)); box-shadow: none; background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-session:focus-visible { outline: 2px solid var(--pf-accent-deep, var(--accent-ink)); outline-offset: 2px; }
.bk-session-bar { width: 3px; align-self: stretch; min-height: 30px; border-radius: 2px; background: var(--mint); flex-shrink: 0; }
.bk-session-body { flex: 1; min-width: 0; }
.bk-session-name { display: block; font-size: 14px; font-weight: 650; color: var(--text); }
.bk-session-when { display: block; font-size: 12.5px; color: var(--muted-2); margin-top: 2px; }
.bk-session-seats { font-size: 12.5px; font-weight: 600; color: var(--formula-ink); flex-shrink: 0; text-align: right; }
.bk-session-cap { display: block; font-size: 11px; font-weight: 500; color: var(--text-faint); margin-top: 2px; }
.bk-session--scarce .bk-session-seats { color: var(--rollup-ink); font-weight: 700; }
/* Full stays VISIBLE and disabled — "Tuesday is full" is information; a class that vanished
   from the list reads as a class that does not exist. */
.bk-session[disabled] { cursor: not-allowed; background: var(--border-soft); box-shadow: inset 0 0 0 1px var(--border-2); }
.bk-session[disabled] .bk-session-bar { background: var(--ink-300); }
.bk-session[disabled] .bk-session-name, .bk-session[disabled] .bk-session-when, .bk-session[disabled] .bk-session-seats { color: var(--text-faint); }

.bk-none { color: var(--text-faint); font-size: 14px; }

/* ── The timetable ─────────────────────────────────────────────────────────────
   A week of columns with each session drawn where it sits and as tall as it lasts. Geometry is
   emitted in MINUTES (--top, --mins) and converted to pixels exactly once, here, so the server
   never has to guess a row height — the same contract the calendar's hour grid uses.

   Five tones cycle by series (--tone), so one class is one colour all week. The palette is
   tokens.css's own soft/ink pairs; nothing here invents a colour. */

.bk-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 18px; }
.bk-chip { padding: 6px 12px; border-radius: 999px; background: var(--card); border: 1px solid var(--border); color: var(--text-2); font-size: 12.5px; font-weight: 600; text-decoration: none; }
.bk-chip--on { background: var(--pf-accent, var(--accent-deep)); border-color: var(--pf-accent, var(--accent-deep)); color: var(--pf-on-accent, #14182E); font-weight: 650; }

.bk-cal { --px-per-min: 0.767; margin-bottom: 18px; }   /* 46px per hour */
.bk-cal-head, .bk-cal-body { display: grid; grid-template-columns: 46px repeat(7, 1fr); }
.bk-cal-head { border-top: 1px solid var(--border-soft); border-left: 1px solid var(--border-soft); border-radius: var(--radius) var(--radius) 0 0; overflow: hidden; }
.bk-cal-gutter-head { border-right: 1px solid var(--border-soft); border-bottom: 1px solid var(--border); background: var(--card-2); }
.bk-cal-day { padding: 9px 4px; border-right: 1px solid var(--border-soft); border-bottom: 1px solid var(--border); background: var(--card-2); text-align: center; }
.bk-cal-dow { display: block; font-size: 10.5px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }
.bk-cal-date { display: block; font-size: 14px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }

.bk-cal-body {
    position: relative;
    height: calc(var(--hours) * 46px);
    border-left: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
    border-radius: 0 0 var(--radius) var(--radius);
    /* Hour banding is a BACKGROUND, not rows — so the number of hours can change with the data
       without the grid needing a row per hour. */
    background: linear-gradient(180deg, var(--card) 0, var(--card) 46px, var(--card-2) 46px, var(--card-2) 92px);
    background-size: 100% 92px;
}
.bk-cal-gutter { grid-column: 1; display: flex; flex-direction: column; border-right: 1px solid var(--border-soft); }
.bk-cal-hour { height: 46px; font-size: 10.5px; font-weight: 600; color: var(--text-faint); text-align: right; padding: 1px 6px 0 0; box-sizing: border-box; font-variant-numeric: tabular-nums; }
.bk-cal-rule { grid-column: var(--col); grid-row: 1; border-right: 1px solid var(--border-soft); }
.bk-cal-rule:last-of-type { border-right: 0; }

.bk-cal-slot {
    grid-column: var(--col); grid-row: 1;
    position: relative;
    align-self: start;
    margin-top: calc(var(--top) * var(--px-per-min) * 1px);
    height: calc(var(--mins) * var(--px-per-min) * 1px);
    padding: 1px 3px;
    box-sizing: border-box;
    min-height: 20px;
    /* Overlapping sessions share the column: --lanes of them side by side, this one in lane
       --lane. Two drawn at the same coordinates means the second is invisible and the first
       eats its clicks — a session nobody can see or book. */
    width: calc(100% / var(--lanes, 1));
    margin-left: calc(var(--lane, 0) * 100% / var(--lanes, 1));
}
.bk-cal-block {
    display: block; width: 100%; height: 100%; overflow: hidden; text-align: left;
    padding: 4px 6px; border: 0; border-left: 3px solid var(--tone-ink); border-radius: 8px;
    background: var(--tone-soft); font: inherit; cursor: pointer; box-sizing: border-box;
}
.bk-cal-block .bk-session-bar, .bk-cal-block .bk-session-when, .bk-cal-block .bk-session-cap { display: none; }
.bk-cal-block .bk-session-time { display: block; font-size: 11px; font-weight: 700; color: var(--tone-ink); font-variant-numeric: tabular-nums; }
.bk-cal-block .bk-session-name { display: block; font-size: 11.5px; font-weight: 650; color: var(--text); line-height: 1.25; }
.bk-cal-block .bk-session-seats { display: block; font-size: 10.5px; font-weight: 600; color: var(--tone-ink); }
.bk-cal-block[aria-pressed="true"] { outline: 2px solid var(--pf-accent-deep, var(--accent-ink)); outline-offset: -2px; }
.bk-cal-block.is-scarce .bk-session-seats { color: var(--rollup-ink); font-weight: 700; }
/* Full stays in the grid, greyed — a session missing from the timetable reads as one that
   does not exist. */
.bk-cal-block.is-full { background: var(--border-soft); border-left-color: var(--ink-300); cursor: not-allowed; }
.bk-cal-block.is-full .bk-session-time,
.bk-cal-block.is-full .bk-session-name,
.bk-cal-block.is-full .bk-session-seats { color: var(--text-faint); }

/* The five tones. Soft is the block, ink is its rule and its text. */
.bk-cal-block, .bk-session { --tone-soft: var(--formula-soft); --tone-ink: var(--formula-ink); }
[style*="--tone:1"] { --tone-soft: var(--relation-soft); --tone-ink: var(--relation-ink); }
[style*="--tone:2"] { --tone-soft: var(--lookup-soft); --tone-ink: var(--lookup-ink); }
[style*="--tone:3"] { --tone-soft: var(--amber-soft); --tone-ink: #8A5A0B; }
[style*="--tone:4"] { --tone-soft: var(--rose-soft); --tone-ink: #B02465; }

.bk-session .bk-session-time { display: none; }
.bk-session .bk-session-bar { background: var(--tone-ink); }
.bk-session .bk-session-seats { color: var(--tone-ink); }
.bk-session.is-scarce .bk-session-seats { color: var(--rollup-ink); font-weight: 700; }

/* Seven columns do not fit a phone, so the timetable gives way to the list. Both are drawn
   from the same partial, so they cannot disagree about a session. */
.bk-cal-list { display: none; }
@media (max-width: 820px) {
    .bk-cal { display: none; }
    .bk-cal-list { display: flex; }
}

/* ── Manage page ───────────────────────────────────────────────────────────────── */

.bk-pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; background: var(--pf-accent-tint, var(--accent-tint)); color: var(--pf-accent-link, var(--accent-ink)); font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; margin-bottom: 14px; }
.bk-pill i { width: 6px; height: 6px; border-radius: 50%; background: var(--pf-accent, var(--accent-deep)); font-style: normal; }
.bk-pill--soon { background: var(--amber-soft); color: #8A5A0B; }
.bk-detail { display: flex; flex-direction: column; gap: 9px; padding: 15px 16px; border-radius: var(--radius-lg); background: var(--card-2); border: 1px solid var(--border-soft); margin-bottom: 20px; }
.bk-detail-row { display: flex; align-items: baseline; gap: 12px; }
.bk-detail-k { width: 64px; flex-shrink: 0; font-size: 11.5px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }
/* min-width and anywhere: an email address is one unbreakable word, and a long one ran out
   of the box on a phone. */
.bk-detail-v { min-width: 0; overflow-wrap: anywhere; font-size: 14px; font-weight: 600; color: var(--text); }
.bk-actions { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.bk-actions > * { flex: 1; text-align: center; white-space: nowrap; }
/* On a phone three buttons in a row is a third each, which wrapped "Add to calendar" onto two
   lines. The main action takes the first line to itself and the other two share the second. */
@media (max-width: 520px) {
    .bk-actions > .bk-btn:not(.bk-btn--quiet) { flex-basis: 100%; }
}
.bk-notice { padding: 12px 14px; border-radius: var(--radius); background: var(--card-2); border: 1px solid var(--border-soft); font-size: 13.5px; line-height: 1.6; color: var(--text-2); }

/* Honeypot: off-screen rather than display:none, which some bots skip. */
.bk-hp { position: absolute; left: -9999px; }

/* ── Mobile guards — the same two publicform.css carries, and for the same reason:
      an input under 16px makes iOS zoom on focus, and a target under 44px is a miss. ── */

@media (max-width: 820px) {
    .bk-stage { padding: 24px 12px 40px; }
    .bk-card { padding: 22px 20px; }
    .bk-h1 { font-size: 22px; line-height: 1.18; }
    .bk-lead { font-size: 14px; }
    .bk-days { grid-template-columns: repeat(7, 1fr); gap: 4px; }
    .bk-day-num { font-size: 15px; }
    .bk-day-free { font-size: 9.5px; }
    .bk-times { grid-template-columns: 1fr 1fr; }
    .bk-slot { padding: 15px 0; font-size: 16px; }
    .bk-field input, .bk-field textarea, .bk-field select { font-size: 16px; min-height: 44px; }
    .bk-go { padding: 15px 18px; font-size: 15.5px; min-height: 44px; }
    .bk-summary-k { width: 62px; }
}

@media (pointer: coarse) {
    .bk-slot, .bk-day, .bk-arrow, .bk-btn2, .bk-session { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
    .bk * { animation: none !important; transition: none !important; }
}

/* ══ Stays: the nights picker ══════════════════════════════════════════════════
   Two months side by side, because a holiday let's commonest range crosses a month
   boundary and a one-month calendar makes that a click, a repaint and a guess.
   Below 760px the second month stacks under the first rather than shrinking: seven
   columns at half width is a 20px tap target, and the mobile guard at the foot of
   this file exists precisely to stop that.
   ── */

.bk-units { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin: 0 0 22px; }
.bk-unit { display: flex; flex-direction: column; gap: 3px; padding: 12px 14px; border: 1.5px solid transparent; box-shadow: inset 0 0 0 1px var(--border); border-radius: var(--radius); background: var(--card); text-decoration: none; color: inherit; }
.bk-unit--on { border-color: var(--pf-accent, var(--accent-deep)); box-shadow: none; background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-unit-photo { width: 100%; height: 84px; object-fit: cover; border-radius: calc(var(--radius) - 4px); margin-bottom: 4px; }
.bk-unit-name { font-weight: 600; font-size: 14.5px; }
.bk-unit-sub { font-size: 12.5px; color: var(--text-2); line-height: 1.4; }
/* A withheld unit stays at full ink. Dimming it would say "disabled" about a listing that
   is real and bookable by phone — the message beneath it is the disclosure, not the
   opacity. Same call the dashboard's out-of-scope tiles make. */
.bk-unit--off { cursor: default; box-shadow: inset 0 0 0 1px var(--border-soft); background: var(--card-2); }
.bk-unit--off .bk-unit-sub { color: var(--text-faint); }

.bk-cal { margin: 0 0 20px; }
.bk-cal-nav { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.bk-cal-title { font-weight: 650; font-size: 15px; }
/* The second month's name shows only where both grids are drawn; the per-grid headings
   below carry it otherwise, so the two never disagree about what is on screen. */
.bk-cal-title2 { display: none; }
.bk-cal-arrow { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; text-decoration: none; color: var(--text-2); border: 1px solid var(--border); background: var(--card); font-size: 18px; line-height: 1; }
.bk-cal-arrow:hover { background: var(--card-2); }
.bk-cal-arrow--off { opacity: .35; }

.bk-cal-months { display: grid; grid-template-columns: 1fr; gap: 22px; }
.bk-cal-mname { font-size: 13px; font-weight: 650; color: var(--text-2); margin-bottom: 8px; text-align: center; }
.bk-cal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 4px; }
.bk-cal-dow span { text-align: center; font-size: 10.5px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
.bk-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.bk-cal-pad { aspect-ratio: 1; }

.bk-cal-day { aspect-ratio: 1; min-height: 38px; display: grid; place-items: center; border: 0; background: transparent; border-radius: 9px; font: inherit; font-size: 14px; color: var(--text); cursor: pointer; padding: 0; }
.bk-cal-day:hover { background: var(--card-2); }
.bk-cal-day:focus-visible { outline: 2px solid var(--pf-accent, var(--accent-deep)); outline-offset: 1px; }
/* Unavailable: struck through rather than merely pale. Colour alone is the one channel a
   low-contrast or colour-blind reader may not get, and this is the distinction the whole
   control turns on. */
.bk-cal-day--off { color: var(--text-faint); cursor: default; text-decoration: line-through; text-decoration-thickness: 1px; }
.bk-cal-day--off:hover { background: transparent; }
/* Reachable in principle, but not from the arrival now selected — a different fact from
   "sold", and drawn differently so the two are not confused. */
.bk-cal-day--unreachable { opacity: .3; }
.bk-cal-day--in, .bk-cal-day--out { background: var(--pf-accent, var(--accent-deep)); color: var(--pf-on-accent, #fff); font-weight: 650; }
.bk-cal-day--mid { background: var(--pf-accent-tint, var(--accent-tint)); border-radius: 0; }
.bk-cal-hint { margin: 12px 0 0; font-size: 12.5px; color: var(--text-2); }

.bk-stay-sum { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; margin: 0 0 22px; background: var(--card-2); display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.bk-stay-sum[hidden] { display: none; }
.bk-stay-dates { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.bk-stay-leg { display: flex; flex-direction: column; gap: 1px; }
.bk-stay-k { font-size: 10.5px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }
.bk-stay-v { font-size: 14.5px; font-weight: 600; }
.bk-stay-t { font-size: 11.5px; color: var(--text-2); }
.bk-stay-arrow { color: var(--text-faint); }
.bk-stay-total { text-align: right; }
.bk-stay-nights { display: block; font-size: 12.5px; color: var(--text-2); }
.bk-stay-price { font-size: 19px; font-weight: var(--pf-head-weight, 680); font-family: var(--pf-font-head, var(--font-display)); font-variant-numeric: lining-nums; }

@media (min-width: 760px) {
    .bk-cal-months { grid-template-columns: 1fr 1fr; gap: 26px; }
    .bk-cal-title2 { display: inline; }
    .bk-cal-title2::before { content: " – "; }
    /* Each grid keeps its own heading on wide screens too: the nav's combined title says
       which two months, and the headings say which is which. */
}

@media (max-width: 820px) {
    .bk-cal-day { min-height: 42px; font-size: 15px; }
    .bk-stay-price { font-size: 17px; }
}

/* ══ The 2026-09 design pass ═══════════════════════════════════════════════════
   Boards 01–03, 09, 12 and 13 of the Claude Design handoff. Everything here is a
   component on the existing vocabulary — no new colour, no library, no framework —
   and every brand-carrying value comes from the --pf-* seam the page injects.
   ── */

/* ── The soonest ───────────────────────────────────────────────────────────────
   A .bk-slot worn differently, so the form's existing delegated handler selects it
   with no new script. Dark rather than accent-filled: it is the one row on the page
   that ends the funnel, and a pale brand accent has no way to say that. --text is a
   neutral from tokens.css and is the same on everybody's page, which is the point —
   this is a control's weight, not a brand's colour, and the accent sits INSIDE it. ── */

.bk-soonest {
    display: flex; align-items: center; gap: 12px; width: 100%;
    padding: 13px 15px; margin-bottom: 14px; border: none; box-shadow: none;
    border-radius: var(--radius); background: var(--text); text-align: left; cursor: pointer;
}
.bk-soonest-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.bk-soonest-k { font-size: 10.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--pf-accent, var(--accent-deep)); }
.bk-soonest-v { font-size: 15px; font-weight: 650; color: #fff; font-variant-numeric: tabular-nums; }
.bk-soonest-go { flex-shrink: 0; font-size: 13px; font-weight: 650; color: var(--pf-accent, var(--accent-deep)); }
.bk-soonest[aria-pressed="true"] { outline: 2px solid var(--pf-accent, var(--accent-deep)); outline-offset: 2px; background: var(--text); }
.bk-soonest:focus-visible { outline: 2px solid var(--pf-accent, var(--accent-deep)); outline-offset: 2px; }

/* ── Time bands ────────────────────────────────────────────────────────────────
   Morning / Afternoon / Evening. Three bands make a person read one; a flat grid of
   eleven times makes them read eleven. ── */

.bk-band { font-size: 10.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); margin: 0 0 8px; }
.bk-band + .bk-times { margin-bottom: 16px; }
.bk-band--rows { margin: 16px 0 8px; }
.bk-band--rows:first-child { margin-top: 0; }

/* ── Whose clock ───────────────────────────────────────────────────────────────
   Quiet by default, because on the common case everybody is local and this is a
   confirmation rather than a warning. .bk-tz--differs is added by script only when
   the offsets genuinely differ for the instant on screen — and it is the one place
   on this page that goes amber, so it cannot be mistaken for decoration.

   The amber is --rollup-* from tokens.css: an existing, contrast-audited pair, not a
   twelfth accent invented here. ── */

.bk-tz { margin-top: 18px; padding: 11px 13px; border-radius: var(--radius); background: var(--card-2); border: 1px solid var(--border-soft); }
.bk-tz-line { display: block; font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }
.bk-tz-line strong { font-weight: 650; color: var(--text-2); }
.bk-tz--differs { background: var(--rollup-soft); border-color: #E5A12B; }
.bk-tz--differs .bk-tz-line { font-size: 13px; color: var(--text); }
.bk-tz--differs .bk-tz-line strong { color: var(--text); }

/* ── Who: the next-free column, and a long roster ──────────────────────────────
   The row grows a right-hand column carrying the one fact the choice turns on. It
   wraps under the name below 380px rather than squeezing the name — a truncated
   person's name is worse than a stacked row. ── */

.bk-row-when { flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right; }
.bk-row-when-k { font-size: 9.5px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); }
.bk-row-when-v { font-size: 12.5px; font-weight: 650; color: var(--text-2); font-variant-numeric: tabular-nums; }
.bk-row-when--none { font-weight: 500; color: var(--text-faint); }
.bk-row[aria-current="true"] .bk-row-when-k { color: var(--muted-2); }
.bk-row[aria-current="true"] .bk-row-when-v { color: var(--text-2); }

.bk-row--any .bk-row-name { font-weight: 680; }

.bk-find { display: flex; align-items: center; gap: 8px; padding: 0 12px; margin-bottom: 10px; border-radius: var(--radius); background: var(--card); box-shadow: inset 0 0 0 1px var(--border-2); }
.bk-find-icon { flex-shrink: 0; font-size: 15px; color: var(--text-faint); }
.bk-find input { flex: 1; min-width: 0; border: 0; background: none; outline: none; padding: 12px 0; font: inherit; font-size: 16px; color: var(--text); min-height: 44px; }
.bk-find:focus-within { box-shadow: inset 0 0 0 1.5px var(--pf-accent-deep, var(--accent-ink)); }
.bk-find-none { margin: 12px 0 0; font-size: 13px; color: var(--text-faint); }

/* ── The check that did not run ────────────────────────────────────────────────
   A .bk-nudge in amber. It is not an error and must not read as one: the times
   below it are real and bookable, and the sentence is about our confidence in
   them, not about a failure the visitor can act on. Same --rollup-* pair the
   timezone strip uses, so the page has exactly one "pay attention" colour. ── */

.bk-nudge--warn { background: var(--rollup-soft); border-color: #E5A12B; margin-top: 0; margin-bottom: 18px; }
.bk-nudge--warn i { color: var(--rollup-ink); }
.bk-nudge--warn span { color: var(--text); }

/* ── Stays: sold, unreachable, and the changeover ──────────────────────────────
   Three states, drawn three ways, and NOT by colour alone — colour is the one
   channel a colour-blind or low-contrast reader may not receive, and this control
   turns entirely on the distinction.

     sold        a line through the number: somebody has it
     unreachable dimmed only: free, but cannot START a stay from here
     changeover  a diagonal split: they leave in the morning, you arrive that
                 afternoon — the [in, out) rule made visible instead of left to a
                 policy sentence nobody reads ── */

.bk-cal-day--sold { text-decoration: line-through; text-decoration-thickness: 1.5px; }
.bk-cal-day--changeover {
    background: linear-gradient(
        to bottom right,
        var(--card-2) 0 calc(50% - 1px),
        var(--border-2) calc(50% - 1px) calc(50% + 1px),
        var(--card) calc(50% + 1px) 100%);
}

/* The key beneath the months, so the split square is read rather than puzzled over. */
.bk-cal-key { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 12px; }
.bk-cal-key span { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-muted); }
.bk-cal-key i { width: 15px; height: 15px; border-radius: 4px; box-shadow: inset 0 0 0 1px var(--border-2); font-style: normal; flex-shrink: 0; }
.bk-cal-key .k-changeover { background: linear-gradient(to bottom right, var(--card-2) 0 calc(50% - 1px), var(--border-2) calc(50% - 1px) calc(50% + 1px), var(--card) calc(50% + 1px) 100%); }
.bk-cal-key .k-sold { background: var(--card-2); position: relative; }
.bk-cal-key .k-sold::after { content: ""; position: absolute; inset: 50% 2px auto; height: 1.5px; background: var(--text-faint); }

/* ── The deposit ───────────────────────────────────────────────────────────────
   Two numbers and a reason. Not a basket: no line items, no subtotal, no VAT row,
   no "order" — that is what keeps this a booking that happens to take money. ── */


/* ── The waitlist ──────────────────────────────────────────────────────────────
   Attached to the full class it is about, and quiet: it is an offer, not a
   control competing with the sessions that CAN be booked. ── */

.bk-wait { margin: -2px 0 10px; padding: 12px 14px; border-radius: var(--radius); background: var(--card-2); border: 1px solid var(--border-soft); }
.bk-wait-p { margin: 0 0 9px; font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }
.bk-wait-p strong { font-weight: 650; color: var(--text-2); }
.bk-wait-row { display: flex; gap: 8px; }
.bk-wait-row input { flex: 1; min-width: 0; font-size: 16px; min-height: 44px; padding: 10px 12px; border-radius: var(--radius); border: 1px solid var(--border-2); background: var(--card); color: var(--text); }
.bk-wait-row input:focus { outline: none; border-color: var(--pf-accent-deep, var(--accent-ink)); box-shadow: 0 0 0 3px var(--pf-accent-tint, var(--accent-tint)); }
.bk-wait-row .bk-btn2 { flex-shrink: 0; min-height: 44px; }
.bk-wait-done { font-size: 13px; line-height: 1.55; color: var(--text-2); }
.bk-wait-done strong { font-weight: 680; color: var(--text); }

/* ── The rescue ────────────────────────────────────────────────────────────────
   Three real times, offered when the slot went while somebody was typing. Rows
   rather than a grid, because each carries a sentence about how it differs from
   what they asked for — which is the thing that makes the choice one read. ── */

.bk-rescue { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.bk-rescue-row { display: flex; align-items: center; gap: 12px; width: 100%; padding: 13px 15px; text-align: left; }
.bk-rescue-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.bk-rescue-when { font-size: 15px; font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; }
.bk-rescue-how { font-size: 12.5px; font-weight: 500; color: var(--text-2); }
.bk-rescue-take { flex-shrink: 0; font-size: 13px; font-weight: 650; color: var(--pf-accent-link, var(--accent-ink)); }
.bk-rescue-row[aria-pressed="true"] .bk-rescue-take { color: var(--text); }

/* ── The second thought, and the move that answers it ─────────────────────────
   Board 10. The change block is tinted and sits below the reference: the
   obligation is created above it, and this is an afterthought by design. ── */

.bk-change { margin-top: 20px; padding: 15px 17px; border-radius: var(--radius); background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-change-h { font-size: 14px; font-weight: 680; color: var(--text); margin-bottom: 5px; }
.bk-change-p { margin: 0 0 12px; font-size: 13px; line-height: 1.55; color: var(--text-2); }
.bk-change-p strong { font-weight: 650; color: var(--text); }
.bk-change-actions { display: flex; gap: 8px; }
.bk-change-actions .bk-btn2 { flex: 1; text-align: center; }

/* Old → new, both drawn. A bare "that's your new time" cannot answer "was that
   always when it was?", and somebody who moved twice cannot tell which took. */
.bk-moved { padding: 14px 16px; margin-bottom: 18px; border-radius: var(--radius); background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-moved-pair { display: flex; align-items: baseline; flex-wrap: wrap; gap: 9px; margin-bottom: 7px; }
.bk-moved-was { font-size: 14px; font-weight: 600; color: var(--muted-2); text-decoration: line-through; font-variant-numeric: tabular-nums; }
.bk-moved-arrow { color: var(--muted-2); }
.bk-moved-now { font-size: 16px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.bk-moved-note { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--text-2); }

/* YOUR own time in the re-pick calendar. Marked, never drawn as taken: a visitor
   who sees their own appointment as unavailable concludes the page is broken. */
.bk-slot--yours { background: var(--pf-accent-tint, var(--accent-tint)); box-shadow: inset 0 0 0 1.5px var(--pf-accent, var(--accent-deep)); }
.bk-slot-tag { display: block; margin-top: 2px; font-size: 9.5px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-2); }

/* ── The masthead ──────────────────────────────────────────────────────────────
   What replaces the service picker on a page selling one thing. Tinted, because it
   is a statement rather than a control and must not read as something to tap. The
   three facts are the ones a client of a one-room practice actually asks. ── */

.bk-masthead { padding: 15px 17px; margin-bottom: 22px; border-radius: var(--radius); background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-masthead-name { font-family: var(--pf-font-head, var(--font-display)); font-size: 19px; font-weight: var(--pf-head-weight, 600); letter-spacing: -0.01em; color: var(--text); margin-bottom: 10px; }
.bk-masthead-facts { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.bk-masthead-fact { display: flex; flex-direction: column; gap: 2px; font-size: 14px; font-weight: 600; color: var(--text); }
.bk-masthead-k { font-size: 10px; font-weight: 650; letter-spacing: .07em; text-transform: uppercase; color: var(--muted-2); }

/* ── Where: the location step ──────────────────────────────────────────────────
   A .bk-row with two sub-lines and its own soonest, so a site is chosen on the
   thing the choice actually turns on. No map: tiles come from a third-party host
   and this page may not hand a visitor's IP to one on our customer's behalf. ── */

.bk-row--site { align-items: flex-start; }
/* A FIXED frame, so no image an owner uploads can break the card's contrast or its
   height. That constraint is the whole reason this one picture is allowed where a
   hero image behind the funnel is not. */
.bk-site-photo { width: 56px; height: 56px; flex-shrink: 0; object-fit: cover; border-radius: 9px; background: var(--card-2); }
.bk-site-photo--wide { display: block; width: 100%; height: 128px; margin-top: 8px; border-radius: var(--radius); }
.bk-row--site .bk-radio { margin-top: 3px; }
.bk-row--site .bk-row-when { margin-top: 1px; }
.bk-row-sub--soft { color: var(--text-faint); }
.bk-summary-note { display: block; margin-top: 3px; font-size: 12.5px; font-weight: 400; color: var(--text-muted); }
/* The booking's video call, on the confirmation and the manage page. Accent TEXT reads
   --pf-accent-link, never the deep shade (see the rule on the booking page's accent). */
.bk-call { color: var(--pf-accent-link); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }

/* ── The empty states ──────────────────────────────────────────────────────────
   One shape, five headlines. The primary action is a real date or a phone number —
   .bk-go--inline is the same button as the confirm, sized to its words, because the
   way on out of an empty page deserves the weight of the way on through it. ── */

/* .bk-go--inline is declared with .bk-btn at the foot of this file. */
.bk-empty .bk-go--inline + .bk-empty-tail { margin-top: 16px; }

/* ── The commit, and the thumb ─────────────────────────────────────────────────
   Board 13. Below 560px the confirm sticks to the bottom of the viewport carrying
   the answer, so the thing you tap next is always in the bottom third and "which
   time did I pick?" costs no scroll.

   ONE button, made sticky — never a second one in a bar. The summary above it is
   filled by script from the chosen control itself, so it cannot come to disagree
   with what was actually clicked.

   The stage gets bottom padding to match, or the bar covers the badge and the last
   field on a page scrolled to its end. ── */

.bk-commit-sum {
    font-size: 13px; font-weight: 650; color: var(--text-2); text-align: center;
    margin-bottom: 10px; font-variant-numeric: tabular-nums;
}

@media (max-width: 560px) {
    .bk-commit {
        position: sticky; bottom: 0; z-index: 5;
        margin: 0 -20px -22px; padding: 12px 20px 16px;
        background: var(--card);
        border-top: 1px solid var(--border-soft);
        box-shadow: 0 -10px 24px -18px rgba(0, 0, 0, .45);
    }
    .bk-commit .bk-go { margin-top: 0; }
    .bk-commit .bk-foot { margin-bottom: 0; }
    .bk-stage { padding-bottom: 96px; }
}

/* ── Narrow: 320px, standing up, one thumb ─────────────────────────────────────
   At 320 a two-column time grid gives cells whose tap target drifts to the edge of
   the screen where thumbs miss. One column, full width — slower to scan, impossible
   to mis-tap. The 16px and 44px guards above already cover the rest. ── */

/* ⚠ The DAY strip is the one control that cannot meet the 44px rule, and it is left
   alone deliberately. Seven columns inside a 320px screen have 256px to share once the
   stage's 12px and the card's 20px are taken from each side, which is 33px a column and
   no padding will rescue it — 12px card padding buys 35px. The two ways out are both
   worse: a horizontal scroller nests a touch scroll region inside a page scroll, which
   is the precise failure class this product has already spent a fortnight on (see the
   embed.js note in CLAUDE.md), and fewer than seven columns stops being a week. The
   cells are 71px TALL, so the target misses on one axis rather than on both, and every
   day is also reachable from the times below it. Measured at 320px on 2026-09-06. */

@media (max-width: 400px) {
    .bk-times { grid-template-columns: 1fr; }
    .bk-slot { padding: 14px 0; }
    /* The LABEL goes, never the time. "Thu 10:15 AM" is the fact this row exists to
       carry and the whole reason the list is ordered the way it is; "Next free" above
       it is a caption the column shape already gives away. */
    .bk-row-when-k { display: none; }
    .bk-row-when-v { font-size: 12px; }
}

@media (max-width: 380px) {
    .bk-soonest { gap: 8px; padding: 12px; }
    .bk-soonest-v { font-size: 14px; }
    .bk-soonest-go { font-size: 12px; }
}


/* The .bk-plan block that lived here is retired: .bk-money below is the same component with
   the deposit and the ladder unified under one modifier, which is what the two always were. */


/* ==================================================================================== */
/*  THE MESSAGE REGISTER, THE MONEY BLOCK AND THE BUTTON VOCABULARY
    Design pass, 7 September 2026. Replaces .bk-nudge, .bk-nudge--warn, .bk-problem,
    .bk-empty, the unreachable-calendar note and the preview banner with one anatomy.  */
/* ==================================================================================== */

/* .bk-say lives in tokens.css. It is deliberately the SAME five registers on all three
   surfaces of this module — the funnel, the manage page and /Booking/Payouts — and the third
   is an app page that must not load this file: booking.css is the PUBLIC booking stylesheet,
   and an admin screen borrowing it is the category error SelfHostedFontsTests catches. The
   fold moved there for the identical reason. */

/* ── .bk-money — what happens now, and what happens later ─────────────────────────
   The deposit and the instalment ladder are ONE component. Both make the same
   statement and both already separate the two halves with a rule; the deposit is the
   two-rung case where neither rung needs a date, so it lies flat, and the ladder is the
   n-rung case where the rungs are ordered in time, so it stands up. --flat and --ladder
   choose the axis. The 2px accent rule on the now/later boundary is the component's
   spine and its only use of the brand.

   Below the rungs: NOTHING. No subtotal, no VAT row, no basket, no line items, and the
   sum of the rungs is never printed — printing it invites a discount row next to it.
   The deposit percentage is a sentence in the reason line, never a third figure. This
   is a booking that happens to take money, not a shop.

   Figures are --font-sans with tabular-nums, NOT --font-mono. Mono is this module's
   register for things that get read out and typed back in, which is why .bk-ref is
   right to use it. A price is read, not transcribed, and a mono ladder reads as a
   receipt. */

.bk-money {
    background: var(--card-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

/* The owner's own sentence from the setup page's Why field, or the element is absent.
   Never generated copy — a reason nobody wrote is worse than no reason. */
.bk-money-reason {
    display: block;
    padding: 11px 14px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
}

.bk-money-rule { height: 2px; background: var(--pf-accent, var(--accent)); }

/* ── Flat · a deposit. Two numbers, side by side ─────────────────────────────────
   They stay side by side at 412px. Stacking them fits, and loses the comparison that is
   the entire point of drawing two numbers. */
.bk-money--flat .bk-money-rungs { display: flex; align-items: stretch; }
.bk-money--flat .bk-money-rungs > .bk-money-rule { width: 2px; height: auto; flex: none; }

.bk-money-cell        { flex: 1; padding: 12px 14px 14px; }
.bk-money-cell--now   { background: var(--card); flex: 1.35; }
.bk-money-cell label  { display: block; font: var(--t-micro); letter-spacing: var(--t-micro-track); text-transform: uppercase; color: var(--text-muted); }
.bk-money-cell b      { display: block; margin-top: 3px; font-size: 26px; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.bk-money-cell--later b { font-weight: 650; color: var(--text-2); }

/* ── Ladder · one row per rung ───────────────────────────────────────────────────
   Reading order is label · date · amount, amount right in a fixed column. Past four
   rungs the LABEL truncates first; the date never truncates, because a date you cannot
   read is worse than a label you can guess. */
.bk-money-rung {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: 11px 14px;
    border-bottom: 1px solid var(--border-soft);
}
.bk-money-rung:last-child      { border-bottom: none; }
.bk-money-rung--now            { background: var(--card); padding: 12px 14px; }

.bk-money-index                { flex: none; width: 52px; font-size: 12.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.bk-money-rung--now .bk-money-index { font: var(--t-micro); letter-spacing: var(--t-micro-track); text-transform: uppercase; color: var(--text-muted); }

.bk-money-label                { flex: 1; min-width: 0; font-size: 14px; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bk-money-rung--now .bk-money-label { font-weight: 650; color: var(--text); }

.bk-money-date                 { flex: none; font-size: 13px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* A rung whose date is not known yet says so IN THE SAME SLOT, at the same width. An
   absent element makes a ladder with two dates and a gap, which reads as a bug — and
   "On the day" is literally true: the rung is dated relative to the booking, and the
   booking has no time yet. No server change. */
.bk-money-date--none           { color: var(--text-faint); }

.bk-money-amount               { flex: none; width: 78px; text-align: right; font-size: 15px; font-weight: 650; color: var(--text-2); font-variant-numeric: tabular-nums; }
.bk-money-rung--now .bk-money-amount { font-size: 19px; font-weight: 700; color: var(--text); }

/* ── --charging · the same block on the Pay screen ───────────────────────────────
   Not byte-identical to the You step, on purpose. On You the block is a DISCLOSURE and
   the useful figure is the whole shape. Here it is the thing being AUTHORISED, so the
   now-rung's amount is the biggest number on screen and the later rungs recede — on
   this screen they are no longer a decision, they are a reassurance that the rest is
   not being taken.

   Keep the duplication between the two screens. A payment plan discovered on a payment
   screen was told in the wrong order. */
.bk-money--charging .bk-money-cell--now,
.bk-money--charging .bk-money-rung--now       { background: var(--pf-accent-tint, var(--accent-tint)); }
.bk-money--charging .bk-money-cell--now b,
.bk-money--charging .bk-money-rung--now .bk-money-amount { font-size: 30px; letter-spacing: -0.015em; }
.bk-money--charging .bk-money-cell--later b   { font-size: 19px; }
.bk-money--charging .bk-money-rung           { padding: 9px 14px; }
.bk-money--charging .bk-money-rung .bk-money-amount { font-size: 14px; }

/* The cancellation consequence, under the Pay button. The one piece of new copy on this
   surface: a deposit changes what cancelling costs, and /b/manage is weeks away when
   they read this. */
.bk-money-terms { display: block; margin-top: var(--s-3); font-size: 12.5px; line-height: 1.55; color: var(--text-faint); text-align: center; }

/* ── .bk-btn — the module's whole button vocabulary ───────────────────────────────
   Three. A fourth means a screen has two primary actions and the SCREEN is wrong, not
   the vocabulary.

   This exists because /b/manage carries 13 inline style= attributes, most of them making
   an <a> look like a button by hand, several of them overriding .bk-go. The app has 55
   button-ish classes and none of them reaches these two pages.

   The handoff mapped .bk-go onto --quiet. That is inverted, and it was checked against the
   markup before it went in: .bk-go is today accent-FILLED, full-width and weight 700, and it
   carries "Confirm booking", "Pay X and confirm", "Move this" and "Move it instead" — every
   primary action on both public surfaces. Aliasing it to --quiet would demote all of them.
   .bk-go IS .bk-btn; .bk-btn2, which already exists as --card on 1px --border-2, IS
   --quiet. Only --plain is genuinely new.

   44px minimum on all three: these are pressed by strangers on phones. */

.bk-btn,
.bk-go {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 13px 18px;
    box-sizing: border-box;
    border-radius: var(--r-ctl);
    border: 1px solid var(--pf-accent-deep, var(--accent-deep));
    background: var(--pf-accent, var(--accent));
    color: var(--pf-on-accent, #fff);
    font-size: 14.5px;
    font-weight: 700;
    font-family: inherit;
    text-decoration: none;
    cursor: pointer;
}
.bk-btn:focus-visible,
.bk-go:focus-visible  { outline: 2px solid var(--pf-accent-deep, var(--accent-ink)); outline-offset: 2px; }
.bk-btn[disabled],
.bk-go[disabled],
.bk-btn--off          { background: var(--paper-2); border-color: var(--border-2); color: var(--text-faint); cursor: default; }

/* Everything secondary. .bk-btn2 is this, and always was. */
.bk-btn--quiet,
.bk-btn2              { background: var(--card); border-color: var(--border-2); color: var(--text-2); font-weight: 650; }
.bk-btn--quiet:hover,
.bk-btn2:hover        { background: var(--card-2); }
.bk-btn2--danger      { border-color: var(--rollup-line); color: var(--rollup-ink); }

/* "Yes, cancel it" IS this style, and it needs to be a real one rather than a button
   with its button-ness removed by hand. Full width, reachable, plain, no red, no
   dimming, no second confirmation and no reason field: making leaving hard is how a
   business earns a bad review instead of a rebooking. */
.bk-btn--plain {
    width: 100%;
    padding: 14px 0;
    background: none;
    border: none;
    color: var(--text-2);
    font-weight: 650;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.bk-btn--plain:hover { color: var(--text); }
/* A <button> does not inherit the page's face or size, so without these "Yes, cancel it" was
   drawn in the browser's default font beside everything else in the brand's. */
.bk-btn--plain { font-family: inherit; font-size: 14.5px; cursor: pointer; }

/* A link with no class of its own ("Keep it as it is", "Never mind", a suggested service in a
   notice) had no rule at all on these pages and was drawn in the browser's blue. Inside the
   card it takes the accent's link ink, which is the one checked against white; in a back or
   foot line it stays the line's own quiet ink, underlined, because those are ways out rather
   than the next step. */
.bk-card a:not([class]) { color: var(--pf-accent-link, var(--accent-ink)); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.bk-back a:not([class]),
.bk-foot a:not([class]) { color: var(--text-2); font-weight: 600; text-decoration-color: var(--border-2); }
.bk-back a:not([class]):hover,
.bk-foot a:not([class]):hover { color: var(--text); text-decoration-color: currentColor; }
/* Big enough to hit on a phone: the back line is the one way out of the cancel screen. */
.bk-back a:not([class]) { display: inline-block; padding: 10px 0; }

/* A committing button while its form is on its way (book-busy.js). It keeps its own colour:
   greyed out it would read as refused. The mark turns; with reduced motion it only stands. */
.bk-go.is-busy,
.bk-btn.is-busy,
.bk-btn--plain.is-busy { cursor: progress; }
.bk-go.is-busy,
.bk-btn.is-busy { opacity: .92; }
.bk-spin {
    display: inline-block;
    width: 14px;
    height: 14px;
    margin-right: 9px;
    vertical-align: -2px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: bkSpin .7s linear infinite;
    flex-shrink: 0;
}
@keyframes bkSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .bk-spin { animation: none; border-right-color: currentColor; opacity: .55; } }

.bk-btn--block { width: 100%; }

/* .bk-go is the primary, and stays block-level because every one of its call sites is a
   full-width commit. It keeps its own selector rather than being swapped for .bk-btn in the
   markup: one alias here retires the inline overrides on /b/manage without touching sixteen
   call sites, and the two can be unified whenever somebody is in those files anyway. */
.bk-go                { display: block; width: 100%; margin-top: 24px; text-align: center; }
.bk-go--inline        { display: inline-flex; width: auto; margin-top: 0; }


/* ── The classes /b/manage's 13 inline style= attributes were standing in for ──────────
   Most of them existed to make an <a> look like a button by hand, several by overriding
   .bk-go. With the three-button vocabulary above those go; what is left is genuinely
   page-level layout, and it belongs here rather than in an attribute. */
.bk-foot--left      { text-align: left; }
.bk-foot--flush     { margin: 0; }
.bk-back            { margin: 6px 0 0; font-size: 13.5px; text-align: left; color: var(--text-faint); }
/* The nudge says "moving it keeps your place" about the button under it, so the two read as
   one block, with room between them. */
.bk-nudge + .bk-btn--block { margin-top: 12px; }
.bk-cancel-form     { margin: 10px 0 0; }
.bk-btn--grow       { flex: 1; }
.bk-strong          { font-weight: 650; color: var(--text-2); }


/* ── The wide-screen funnel ───────────────────────────────────────────────────────────
   Two panes above 1080px: what has been settled on the left, what is being asked on the
   right. Below that it is one column and .bk-recap is not rendered at all, so the phone
   layout — which is correct and was never the problem — is untouched.

   The left pane earns its place beyond wayfinding: it is the only point at which a visitor
   sees the WHOLE booking before committing, and it is the same anatomy the confirmation and
   the manage page draw, through one partial. */
.bk-two { display: block; }

@media (min-width: 1080px) {
    /* The column is 600px because the funnel is phone-first and correct at that width. Two
       panes will not fit in it — measured before this rule existed, the step card came out at
       224px — so the container widens for exactly the layout that needs it, and for nothing
       else. .bk-col stays 600px everywhere the recap is not drawn, including the confirmation,
       the pay screen and the closed page, which are all still one column. */
    /* BOTH rules are gated on the recap actually being in the DOM, and that is the whole
       fix rather than a tidy-up. `bk-col--wide` is emitted by Book.cshtml on every step
       that is not Closed/Booked/Pay, while the recap renders only once
       `Model.Answered.Count > 0` — so on step ONE the column widened to 1000px, the grid
       reserved its 352px first track for a recap that was not there, and the step CARD
       fell into it. Measured: a 352px funnel with 624px of dead space beside it.
       The card is meant to be the 1fr track; the 352px one belongs to the recap, which is
       why the Razor emits the recap FIRST. With no recap there is no second pane, so there
       is no two-column layout to draw and the column should stay at its phone-first 600px
       — which is exactly what falling back to plain `.bk-col` gives.
       `:has()` degrades safely: an engine without it drops these rules and every step
       renders as the single 600px column, i.e. the pre-two-pane layout. */
    .bk-col--wide:has(.bk-recap) { max-width: 1000px; }

    .bk-two:has(.bk-recap) {
        display: grid;
        /* ⚠ THE LOAD-BEARING LINE. .bk-col is `display: flex; flex-direction: column;
           align-items: center`, and on a column flex container align-items governs the
           CROSS axis — so without an explicit width every child shrinks to its own
           content instead of filling the 1000px the rule above just granted. Measured
           before this line existed: .bk-col--wide was a correct 1000px while .bk-two
           inside it was 376px, the grid resolved to `352px 0px`, and the recap track was
           therefore ZERO on every screen — a whole pane that never rendered anywhere.
           The funnel itself stayed at 352px, i.e. phone width, on every desktop.

           The comment above records measuring the card at 224px and widening the
           CONTAINER to fix it. That was the right diagnosis applied one element too high:
           widening an `align-items: center` parent hands its children nothing. */
        width: 100%;
        grid-template-columns: 352px minmax(0, 1fr);
        gap: 24px;
        align-items: start;
    }
    .bk-recap {
        position: sticky; top: 16px;
        background: var(--card); border: 1px solid var(--border);
        border-radius: var(--radius-xl); padding: 8px 4px;
    }
}

/* Not rendered below 1080px, so this is belt and braces rather than the mechanism. */
@media (max-width: 1079px) { .bk-recap { display: none; } }

/* The summary rows themselves are shared with the confirmation and the manage page. Only the
   Change link is new, and it appears only where the reader can actually change something. */
.bk-summary-sub    { display: block; margin-top: 2px; font-size: 12.5px; color: var(--text-faint); }
.bk-summary-change {
    flex: none; align-self: center; margin-left: auto; padding: 6px 10px;
    font-size: 12.5px; font-weight: 650; color: var(--pf-accent-link, var(--accent-ink));
    text-decoration: none; border-radius: var(--radius-sm);
}
.bk-summary-change:hover { background: var(--card-2); }


/* An answered step is drawn ONCE. Above 1080px the recap on the left holds it, with a Change
   link back; below that the recap is not rendered and the step stays exactly where it was, so
   the phone funnel is unchanged. Without this the two-pane layout put the same two answers on
   screen twice and left the card 2,377px long — the very thing it was built to fix. */
@media (min-width: 1080px) {
    .bk-sec--done { display: none; }
}
