/* EcoLinguist Booking — student pages */
/* ═══════════════════════════════════════════════════════════════════════════
   COLOUR CONTRACT (2026-07-29) — ONE meaning per colour, in every calendar
   this app renders: the public booking flow, the variable-price week grid and
   the learner portal's own month/week views.

   Before this, the same yellow meant "free, click me" in one calendar and
   "taken by another student" in another, and the portal used a dark green
   nobody else used. A learner who books through two doors met two languages.

     teal SOLID      → your lesson (confirmed, yours, in your calendar)
     teal SOFT+ring  → a free time you can take    ← click target
     teal SOLID+gold → the free time you just picked
     amber           → taken (by another learner, or held)
     grey hatch      → not offered at all
     grey dashed     → cancelled (kept visible, struck through)
     red             → join now / live

   --green* keep their names because ~400 rules across five files reference
   them; only their VALUES moved onto Norbert's brand teal (#009688 and its
   shades). Do not reintroduce #0f5956.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --green: #00786c;          /* brand teal, darkened for white text (5:1) */
  --green-mid: #009688;      /* the brand teal itself */
  --green-soft: #e3f4f2;
  --teal: #009688;
  --teal-deep: #00584f;
  --teal-line: #a7d9d4;
  --cream: #f7f9fb;
  --paper: #fffdf7;
  --line: #dfe7ed;
  --ink: #243a2e;
  --muted: #6f6a58;
  --accent: #f5d160;         /* brand yellow — selection ring, highlights */
  --stripe-surface: #f5d160;
  --danger: #b3402f;
  --join: #f44336;           /* join-the-call red (course player + portal) */
  --radius: 14px;

  /* Calendar slot states — the contract above, as tokens. */
  --slot-free-bg: #e3f4f2;
  --slot-free-line: #009688;
  --slot-free-ink: #00584f;
  --slot-pick-bg: #009688;
  --slot-pick-ink: #ffffff;
  --slot-taken-bg: #fbe3b0;
  --slot-taken-line: #e0a92a;
  --slot-taken-ink: #6b4e05;
  --slot-off-bg: #f4f3ef;
  --slot-done-bg: #e4eceb;
  --slot-done-ink: #55635f;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  background: var(--cream);
  color: var(--ink);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
.bk-wrap { max-width: 1240px; margin: 0 auto; padding: 20px 16px 60px; }
/* Embed mode (index.html?embed=1 inside an iframe): drop the page chrome so the
   booking flow sits cleanly on the host page; the host frames it. */
.bk-embed { background: transparent; }
.bk-embed .bk-wrap { padding: 4px 4px 12px; max-width: 960px; }
.bk-embed .bk-header h1 { display: none; }
.bk-embed #lang-toggle { display: none; }
.bk-embed .bk-header { margin-bottom: 10px; }
/* h1 and #lang-toggle are unconditionally display:none above, so #embed-home
   is the only child of .bk-header that can ever be visible in embed mode.
   When it also carries .bk-hide (every view except view-types, or when the
   portal never passed embed_home) the header has nothing left to show —
   collapse its margin so it leaves no gap above the progress bar. */
.bk-embed .bk-header:has(> #embed-home.bk-hide) { margin: 0; }
/* Panel mode (widget.js mounts the flow under the pricing card the learner clicked):
   the host section owns the width, so don't box it in a second time. */
/* `max-width:none` meant the host section owned the width — but nothing then
   owned the CONTENT's width, so on a wide laptop portal the header, price
   bar and step chips stretched the full column while the calendar inside
   capped and centred, which reads as a flow shoved to one side. Cap the
   whole flow instead and centre it as one block. */
.bk-panel .bk-wrap { max-width: 1300px; margin: 0 auto; padding: 0 0 8px; }
.bk-panel .bk-header { margin-bottom: 8px; }
.bk-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.bk-header h1 { font-size: 22px; color: var(--green); margin: 0; }
.bk-lang { display: inline-grid; grid-template-columns: 1fr 1fr; gap: 2px; align-items: center; border: 1px solid var(--line); background: #fff; border-radius: 999px; padding: 3px; cursor: pointer; font-size: 12px; color: var(--muted); }
.bk-lang .lang-option { min-width: 32px; border-radius: 999px; padding: 3px 8px; text-align: center; font-weight: 700; }
.bk-lang .lang-option.on { background: var(--green); color: #fff; }
/* Booking progress. Two renderings of the same steps — see renderProgress():
   the chip row when there is room, a "Step 2 of 5 + bar" when there is not.
   Exactly one is displayed, so the other is out of the accessibility tree too. */
.bk-progress { margin: 0 0 16px; }
.bk-progress-now { display: none; }
.bk-progress-steps { display: flex; gap: 6px; margin: 0; padding: 0 0 2px; list-style: none; overflow-x: auto; }
.bk-step { flex: 1; min-width: 104px; border: 1px solid var(--line); background: rgba(255,253,247,.72); color: var(--muted); border-radius: 8px; padding: 7px 9px; font-size: 12px; font-weight: 700; text-align: center; white-space: nowrap; }
.bk-step button, .bk-step span { display: block; width: 100%; border: 0; padding: 0; background: transparent; color: inherit; font: inherit; text-align: inherit; white-space: inherit; }
.bk-step button { cursor: pointer; }
.bk-step button:hover, .bk-step button:focus { text-decoration: underline; text-underline-offset: 2px; outline: none; }
.bk-step.done { background: var(--green-soft); border-color: var(--teal-line); color: var(--green); }
.bk-step.active { background: var(--green); border-color: var(--green); color: #fff; }
.bk-card { background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); padding: 22px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,120,108,.06); }
.bk-card h2 { margin: 0 0 6px; font-size: 18px; color: var(--green); }
.bk-sub { color: var(--muted); font-size: 14px; margin: 0 0 14px; }
.bk-currency { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; color: var(--muted); font-size: 13px; }
.bk-currency label { font-weight: 600; color: var(--green); }
.bk-currency select { border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; font: inherit; background: #fff; color: var(--ink); }
.bk-hold-recovery { display: flex; justify-content: space-between; align-items: center; gap: 14px; margin: 0 0 16px; padding: 13px 14px; border: 1px solid #e5c76d; border-left: 5px solid var(--gold); border-radius: 10px; background: #fffbea; color: #604b00; }
.bk-hold-recovery strong { display: block; color: #604b00; font-size: 14px; }
.bk-hold-recovery span { display: block; margin-top: 3px; color: var(--ink); font-size: 13px; font-weight: 650; }
.bk-hold-recovery p { margin: 4px 0 0; color: var(--muted); font-size: 13px; }
.bk-hold-recovery-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.bk-hold-recovery .bk-btn { width: auto; padding: 9px 12px; white-space: nowrap; }

.bk-types { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.bk-types.bk-pricing-block-grid { grid-template-columns: repeat(var(--bk-page-cols, auto-fit), minmax(min(100%, 270px), 1fr)); align-items: stretch; }
@media (max-width: 760px) { .bk-types.bk-pricing-block-grid { grid-template-columns: 1fr; } }
.bk-type { border: 2px solid var(--line); border-radius: 12px; padding: 16px; cursor: pointer; background: #fff; transition: border-color .15s, transform .1s; text-align: left; font: inherit; }
.bk-type:hover { border-color: var(--green-mid); transform: translateY(-1px); }
.bk-type .t-title { font-weight: 700; color: var(--green); margin-bottom: 4px; }
.bk-type .t-meta { font-size: 13px; color: var(--muted); }
.bk-type .t-price { margin-top: 8px; font-weight: 600; }
.bk-type.bk-product-option { padding: 0; overflow: hidden; display: flex; min-height: 100%; }
.bk-type.bk-product-option:hover { box-shadow: 0 10px 26px rgba(0,120,108,.10); }
.bk-product-card { display: flex; flex-direction: column; width: 100%; min-width: 0; background: #fff; }
.bk-product-card.is-featured { box-shadow: inset 0 4px 0 var(--green-mid); }
.bk-product-head { padding: 17px 19px 14px; background: #eef4f3; }
.bk-product-card.is-featured .bk-product-head { background: var(--green); }
.bk-product-card.is-featured .t-title { color: #fff; }
.bk-product-card.is-featured .t-meta { color: rgba(255,255,255,.86); }
.bk-product-badge { display: inline-block; margin: 0 0 8px; border-radius: 999px; padding: 3px 10px; background: #fff7e6; color: #8a6100; font-size: 11px; font-weight: 800; text-transform: uppercase; }
.bk-product-body { display: flex; flex-direction: column; flex: 1; padding: 16px 19px 18px; }
.bk-product-body .t-price { margin: 0; color: #d9563f; font-size: 24px; line-height: 1.05; font-weight: 800; }
.bk-product-tagline { margin: 10px 0 14px; color: var(--ink); font-size: 14px; line-height: 1.5; }
.bk-product-includes { margin: 14px 0 16px; padding-top: 13px; border-top: 1px solid var(--line); }
.bk-product-includes p { margin: 0 0 8px; color: var(--muted); font-size: 13px; }
.bk-product-includes ul { list-style: none; margin: 0; padding: 0; }
.bk-product-includes li { display: flex; gap: 9px; align-items: flex-start; margin: 0 0 9px; color: var(--ink); font-size: 13.5px; line-height: 1.4; }
.bk-product-includes svg { width: 16px; height: 16px; flex: none; margin-top: 2px; color: var(--green); }
.bk-inc-marker { width: 16px; flex: none; margin-top: 1px; text-align: center; font-size: 14px; line-height: 1.2; }
.bk-inc-lead { font-weight: 800; }
.bk-product-validity { display: flex; gap: 8px; align-items: flex-start; margin: 0 0 16px; border: 1px solid #ead89a; border-left: 5px solid #f5d160; border-radius: 8px; background: #fffdf3; padding: 9px 11px; color: #6b5300; font-size: 13.5px; font-weight: 750; line-height: 1.4; }
.bk-product-validity svg { width: 16px; height: 16px; flex: none; margin-top: 1px; }
.bk-product-bonus { margin: 0 0 16px; border: 1px solid #f0d27b; border-left: 5px solid var(--accent); border-radius: 8px; background: #fffbea; padding: 9px 11px; color: #6b5300; font-size: 13.5px; line-height: 1.45; }
.bk-product-bonus p { margin: 0; }
.bk-product-bonus p + p { margin-top: 7px; }
.bk-product-cta { margin-top: auto; display: block; width: 100%; border-radius: 8px; background: var(--green); color: #fff; padding: 10px 12px; text-align: center; font-weight: 750; }

.bk-pkg { border-top: 3px solid var(--accent); }
.bk-badge { display: inline-block; background: var(--green-soft); color: var(--green); border-radius: 999px; padding: 2px 10px; font-size: 12px; font-weight: 600; }
.bk-badge.warn { background: #fff7e6; color: var(--green); }

.bk-back { background: none; border: none; color: var(--green-mid); cursor: pointer; font-size: 14px; padding: 0; margin-bottom: 10px; }
.bk-back:before { content: "← "; }

/* Calendar */
.bk-caltop { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.bk-caltop .m-label { font-weight: 700; color: var(--green); }
.bk-caltop button { border: 1px solid var(--line); background: #fff; border-radius: 8px; width: 34px; height: 34px; cursor: pointer; font-size: 16px; color: var(--green); }
.bk-caltop button:disabled { opacity: .3; cursor: default; }
/* The three columns cap at 400/520/320 rather than 360/420/260 (2026-09-11).
   Inside the learner portal the flow is a full-width panel, so the old caps
   stopped growing at ~1076px and left a wide unused margin beside a cramped
   month grid and a one-column slot list — a laptop showed a small booking
   widget floating in a big empty card. The extra room goes where it is
   actually useful: a readable month grid and a slot list that can run two
   columns. The cap still exists, and the grid still centres, so an extremely
   wide host does not stretch a date picker across a metre of screen. */
.bk-calendar-shell { display: grid; grid-template-columns: minmax(300px, 400px) minmax(360px, 520px) minmax(220px, 320px); justify-content: center; gap: 18px; align-items: stretch; transition: opacity .12s ease; }
.bk-calendar-shell.is-loading { opacity: .62; }
.bk-calendar-shell.week-mode { grid-template-columns: minmax(0, 1fr); }
.bk-calendar-shell.week-mode .bk-cal-side { display: flex; justify-content: flex-end; }
.bk-calendar-shell.week-mode .bk-grid { display: none; }
.bk-calendar-shell.week-mode .bk-service-info { display: block; width: min(100%, 760px); justify-self: center; min-height: 0; }
.bk-calendar-shell.week-mode .bk-week-table { min-width: 0; }
.bk-calendar-shell.month-mode .bk-week-grid { border: none; background: transparent; overflow: visible; justify-self: stretch; width: 100%; }
.bk-calendar-shell.month-mode .bk-mini-month-title { display: none; }
.bk-calendar-shell.month-mode .bk-service-info,
.bk-calendar-shell.month-mode .bk-day-slot-panel { min-height: 480px; }
.bk-cal-side { min-width: 0; }
.bk-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.bk-mini-month-title { grid-column: 1 / -1; color: var(--ink); font-size: 16px; font-weight: 800; margin: 0 0 6px; }
.bk-grid .dow { text-align: center; font-size: 11px; color: var(--muted); text-transform: uppercase; padding: 4px 0; }
.bk-day { aspect-ratio: 1; border: 1px solid #e1dac8; border-radius: 999px; background: #fffdf7; font: inherit; font-size: 14px; color: #8b8678; display: flex; align-items: center; justify-content: center; }
.bk-week-day { aspect-ratio: auto; min-height: 58px; flex-direction: column; gap: 2px; }
.bk-week-day span { font-size: 11px; text-transform: uppercase; }
.bk-week-day strong { font-size: 13px; color: inherit; }
.bk-day:disabled { cursor: default; }
.bk-day.blank { background: transparent; border-color: transparent; }
.bk-day.in-week { border-color: #bfc6bb; color: var(--ink); }
/* was pale-yellow "unavailable"; now the contract's off-state grey */
.bk-day.unavailable { background: var(--slot-off-bg); border-color: #e6dfcf; color: #9e9788; }
/* past/completed → contract's done tokens */
.bk-day.past { background: var(--slot-done-bg); color: var(--slot-done-ink); border-color: #d8d8d2; }
/* a day with no openings left because everything on it is taken → contract's taken tokens */
.bk-day.busyday:not(.open) { background: var(--slot-taken-bg); border-color: var(--slot-taken-line); color: var(--slot-taken-ink); }
.bk-day.open { background: var(--slot-free-bg); border-color: var(--slot-free-line); color: var(--slot-free-ink); font-weight: 700; cursor: pointer; }
/* hover keeps the free colours; the accent ring is the only hover signal now */
.bk-day.open:hover { outline: 2px solid var(--accent); outline-offset: 1px; }
.bk-day.sel { background: var(--slot-pick-bg); border-color: var(--slot-pick-bg); color: var(--slot-pick-ink); box-shadow: 0 0 0 2px var(--accent); }
/* .bk-tz is a <label> wrapping the tz picker elsewhere in this app; this
   file's calendar side panel uses it on a plain <div> (see index.html), but
   the class is shared, and a vertical margin on an inline element (the
   <label> case) does nothing — the timezone row sat jammed against the panel
   below it (2026-09-11). flex fixes both: it gives the margin somewhere to
   act, and keeps the label/span + select on one line, wrapping only once the
   width genuinely runs out. */
.bk-tz { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 14px 0 16px; font-size: 13px; color: var(--muted); }
.bk-tz select { border: 1px solid var(--line); border-radius: 8px; padding: 4px 8px; font-size: 13px; background: #fff; color: var(--ink); max-width: 100%; }

.bk-week-grid {
  /* Single source of truth for one hour's pixel height in the week grid.
     Defined here (not on .bk-week-table) because .bk-week-scroller — an
     ANCESTOR of .bk-week-table — also needs to read it for its max-height,
     and CSS custom properties only inherit downward. Change this one value
     to resize every hour row, the half-rows, and the scroller cap together. */
  --bk-week-hour: 48px;
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: #fff;
}
.bk-week-scroller {
  overflow: auto;
  /* ~8.5 hours of rows + the sticky day-header row (measured ~48px: font
     11px/13px two-line label + 7px top/bottom padding, floored by the
     .bk-week-head min-height below). Adjust the trailing addend if the
     header's rendered height ever changes. */
  max-height: calc(var(--bk-week-hour, 48px) * 8.5 + 48px);
  overscroll-behavior: contain;
}
.bk-week-table { display: grid; min-width: 610px; grid-template-columns: 70px repeat(7, minmax(72px, 1fr)); }
.bk-week-head, .bk-week-time, .bk-week-hour-cell {
  border-right: 1px solid #e7e1d0;
  border-bottom: 1px solid #e7e1d0;
}
.bk-week-head { min-height: 48px; position: sticky; top: 0; z-index: 6; background: #fbfaf5; color: var(--muted); font-size: 11px; font-weight: 700; text-align: center; padding: 7px 4px; text-transform: uppercase; }
.bk-week-head strong { display: block; color: var(--ink); font-size: 13px; text-transform: none; }
.bk-week-time, .bk-week-hour-cell { min-height: var(--bk-week-hour, 48px); }
.bk-week-time { display: flex; align-items: center; justify-content: center; background: #fbfaf5; color: var(--muted); font-size: 12px; font-weight: 700; }
.bk-week-table > .bk-week-head:first-child,
.bk-week-time { position: sticky; left: 0; z-index: 5; }
.bk-week-table > .bk-week-head:first-child { background: #fbfaf5; top: 0; z-index: 7; }
.bk-week-time, .bk-week-table > .bk-week-head:first-child { box-shadow: 1px 0 0 var(--line); }
.bk-week-hour-cell { display: grid; grid-template-rows: 1fr 1fr; background: #fff; overflow: visible; }
.bk-week-half { height: calc(var(--bk-week-hour, 48px) / 2); min-height: 0; border: 0; }
.bk-week-half + .bk-week-half { border-top: 1px solid #e7e1d0; }
/* not offered at all → contract's grey hatch, colours only */
.bk-week-empty { background: repeating-linear-gradient(45deg, #f4f3ef, #f4f3ef 5px, #e7e5df 5px, #e7e5df 7px); }
/* free/bookable time in the variable-price week grid — this used to be the old
   apple-green "available" colour; amber never means free now, this is the same
   teal-soft used everywhere else free is shown.
   min-height/overflow added: this block's height is set inline by JS as a
   percentage (see booking.js) to represent lesson duration on the half-hour
   grid; without a clip, long price labels forced the box taller than that
   inline height and the overflow spilled into the next cell's own label. */
.bk-week-slot { position: relative; z-index: 2; display: flex; align-items: center; justify-content: center; align-self: start; background: var(--slot-free-bg); border-color: var(--slot-free-line); color: var(--slot-free-ink); cursor: pointer; font: inherit; padding: 2px 5px; text-align: center; box-shadow: inset 0 0 0 1px rgba(255,255,255,.42); min-height: 0; overflow: hidden; }
/* hover keeps the free colours; the accent ring is the only hover signal now */
.bk-week-slot:hover, .bk-week-slot:focus { z-index: 8; outline: 2px solid var(--accent); outline-offset: 1px; }
.bk-week-slot .s-time { display: none; }
.bk-week-slot .s-price { display: block; font-size: 10.5px; color: var(--slot-free-ink); line-height: 1; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* hovering a multi-half-hour session previews its full span; still the free state */
.bk-week-empty.session-hover,
.bk-week-slot.session-hover { background: var(--slot-free-bg); box-shadow: inset 0 0 0 2px rgba(0,120,108,.28); }
.bk-week-slot:hover::after,
.bk-week-slot:focus::after { content: attr(aria-label); position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translateX(-50%); z-index: 6; min-width: 138px; max-width: 220px; padding: 7px 9px; border-radius: 7px; background: rgba(36,58,46,.96); color: #fff; box-shadow: 0 4px 14px rgba(0,120,108,.18); text-align: center; font-size: 12px; font-weight: 800; line-height: 1.25; pointer-events: none; }
/* "unavailable-because-busy" → contract's taken/amber tokens, both the plain
   external-busy block and the more specific "booked by another student" one */
.bk-week-busy { position: relative; background: var(--slot-taken-bg); color: var(--slot-taken-ink); padding: 3px 5px; font-size: 11px; line-height: 1.2; }
.bk-week-busy.booked { background: var(--slot-taken-bg); color: var(--slot-taken-ink); font-weight: 700; }
.bk-week-busy .busy-mark { display: block; width: 100%; height: 100%; min-height: 20px; border-radius: 5px; background: repeating-linear-gradient(45deg, rgba(0,120,108,.11), rgba(0,120,108,.11) 4px, transparent 4px, transparent 8px); }
.bk-week-busy.booked .busy-mark { background: linear-gradient(90deg, rgba(245,209,96,.7), rgba(245,209,96,.28)); }
.bk-week-busy:hover::after { content: attr(aria-label); position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translateX(-50%); z-index: 5; min-width: 112px; max-width: 190px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 7px; background: #fff; color: var(--ink); box-shadow: 0 3px 10px rgba(0,120,108,.12); text-align: center; font-size: 11px; line-height: 1.25; pointer-events: none; }
.bk-week-beyond { display: grid; justify-items: center; gap: 8px; padding: 34px 18px; text-align: center; }
/* Inline SVG, not an emoji glyph — house style is icons only. */
.bk-week-beyond-icon { color: var(--green-mid); opacity: .8; }
.bk-week-beyond-icon svg { width: 30px; height: 30px; }
.bk-week-beyond-title { margin: 0; font-weight: 800; color: var(--green); font-size: 15.5px; }
.bk-week-beyond-note { margin: 0; max-width: 460px; color: var(--muted); font-size: 13.5px; }
.bk-week-beyond-back { margin-top: 4px; }
.bk-week-head.out-of-window { color: #a9a293; }
.bk-week-head-note { display: block; font-style: normal; font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: #a9a293; }
/* outside working hours → contract's off-state hatch, colours only */
.bk-week-outside { background: repeating-linear-gradient(45deg, #f4f3ef, #f4f3ef 5px, #e7e5df 5px, #e7e5df 7px); opacity: .75; }
.bk-mobile-day { display: none; }
.bk-week-note { margin: 0; padding: 16px; }
.bk-day-slot-panel { background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: 14px; min-height: 260px; }
.bk-day-slot-panel h3 { margin: 0 0 12px; color: var(--green); font-size: 15px; }
/* Two columns as soon as the panel is wide enough: a 10-slot day used to be
   a single tall scrolling strip next to an otherwise empty card. */
.bk-day-slot-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(122px, 1fr)); gap: 7px; max-height: 420px; overflow: auto; padding-right: 3px; justify-items: stretch; }
/* a pickable day-panel time button → contract's free tokens */
.bk-list-slot { display: inline-flex; justify-content: center; align-items: center; width: 100%; min-height: 38px; border: 1.25px solid var(--slot-free-line); border-radius: 8px; background: var(--slot-free-bg); color: var(--slot-free-ink); padding: 8px 12px; font: inherit; cursor: pointer; }
/* hover keeps the free colours; the accent ring is the only hover signal now */
.bk-list-slot:hover { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The time this learner has already chosen (and is holding). It is open to
   them and to nobody else, so it must not look like any other free slot. */
.bk-slot-mine { position: relative; border-color: var(--green) !important; background: var(--green-soft) !important; box-shadow: inset 0 0 0 1px var(--green); }
.bk-slot-mine-tick { margin-right: 5px; color: var(--green); font-weight: 900; }
.bk-list-slot.bk-slot-mine { gap: 0; }

.bk-list-slot strong { font-size: 14px; }
.bk-list-slot span { color: var(--muted); font-size: 12px; }

.bk-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; margin-top: 14px; }
/* a pickable time button (reschedule / ask-another-time flows) → contract's free tokens */
.bk-slot { border: 2px solid var(--slot-free-line); background: var(--slot-free-bg); border-radius: 10px; padding: 10px 8px; cursor: pointer; font: inherit; text-align: center; transition: border-color .12s; }
/* hover keeps the free colours; the accent ring is the only hover signal now */
.bk-slot:hover { outline: 2px solid var(--accent); outline-offset: 1px; }
.bk-slot .s-time { font-weight: 700; color: var(--slot-free-ink); }
.bk-slot .s-price { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.bk-slot .s-tier { font-size: 11px; color: var(--accent); font-weight: 600; }
/* Reschedule requests (manage page) */
.bk-slot.wide { grid-column: 1 / -1; text-align: left; padding: 12px 14px; }
/* the one candidate time the learner has actively picked → contract's pick tokens */
.bk-slot.picked { border-color: var(--slot-pick-bg); background: var(--slot-pick-bg); box-shadow: 0 0 0 2px var(--accent); }
.bk-slot.picked::after { content: ' ✔'; color: var(--slot-pick-ink); font-weight: 700; }
/* legibility: the inner labels set their own colour above and would otherwise
   stay dark-on-dark once the block behind them turns solid teal */
.bk-slot.picked .s-time, .bk-slot.picked .s-price, .bk-slot.picked .s-sub { color: var(--slot-pick-ink); }
/* a candidate time that's already taken → contract's taken tokens */
.bk-slot.taken { background: var(--slot-taken-bg); border-color: var(--slot-taken-line); color: var(--slot-taken-ink); opacity: .5; cursor: default; border-style: dashed; }
.bk-slot.taken:hover { border-color: var(--slot-taken-line); }
.bk-slot .s-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.bk-quote { border-left: 3px solid var(--green-mid); background: #fff; border-radius: 0 8px 8px 0; padding: 10px 12px; margin: 0 0 12px; color: var(--ink); font-size: 13.5px; white-space: pre-wrap; }
.bk-chosen { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 4px; }
.bk-chip { border: 1px solid var(--green); background: var(--green-soft); color: var(--green); border-radius: 999px; padding: 6px 13px; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.bk-chip:hover { background: var(--green); color: #fff; }
.bk-rr-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 16px; }

/* Form */
.bk-summary { background: var(--green-soft); border-radius: 10px; padding: 12px 16px; margin-bottom: 16px; font-size: 14.5px; }
.bk-summary strong { color: var(--green); }
.bk-selected-slot { display: grid; grid-template-columns: 92px 1fr; gap: 10px; align-items: stretch; }
.bk-selected-date { background: #fff; border: 1px solid var(--teal-line); border-radius: 8px; text-align: center; padding: 8px 4px; color: var(--green); font-weight: 800; }
.bk-selected-date span { display: block; color: var(--muted); font-size: 11px; font-weight: 700; text-transform: uppercase; }
/* The time you just picked. Was a lime #9ad45d — a survivor of the palette the
   2026-07-29 colour contract replaced, and the loudest thing on a form whose
   job is to be filled in quietly. The payment step had ALREADY overridden it to
   this teal treatment, which is the tell: that override is now the rule and the
   override itself is gone. */
.bk-selected-time { background: var(--green-soft); color: var(--green); border: 1px solid var(--teal-line); border-radius: 8px; padding: 9px 12px; }
.bk-selected-time strong { display: block; color: var(--green); font-size: 17px; }
.bk-selected-time span { display: block; color: var(--green); font-size: 12px; opacity: .85; }
.bk-next { background: #f8f4e9; border: 1px solid #eadfca; border-radius: 8px; color: var(--ink); font-size: 13.5px; margin: 0 0 14px; padding: 10px 12px; }
.bk-next.subtle { background: #fffaf0; color: var(--muted); }
.bk-schedule-choice { border: 0; margin: 0 0 20px; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
/* The weekly-clash decision. Amber, not red: nothing has gone wrong and nothing
   has been lost — there is simply a choice to make, and the panel's whole job is
   to make the three answers pressable instead of describing them in a sentence. */
.bk-conflict { margin: 0 0 18px; padding: 15px 16px; border: 1px solid #ead89a; border-left: 5px solid #f5d160; border-radius: 10px; background: #fffdf3; color: #4d3c00; }
.bk-conflict h4 { margin: 0 0 6px; font-size: 15px; color: #6b5200; }
.bk-conflict p { margin: 0 0 8px; font-size: 13.5px; }
.bk-conflict-dates { margin: 0 0 10px; padding-left: 20px; font-size: 13.5px; font-weight: 700; }
.bk-conflict-dates li { margin-bottom: 2px; }
.bk-conflict-count { font-weight: 700; }
.bk-conflict-options { display: grid; gap: 9px; }
.bk-conflict-option { display: grid; gap: 3px; text-align: left; border: 1.5px solid var(--line); border-radius: 10px; background: #fff; padding: 12px 14px; font: inherit; color: var(--ink); cursor: pointer; }
.bk-conflict-option strong { font-size: 14px; color: var(--green); }
.bk-conflict-option span { font-size: 12.5px; color: var(--muted); }
.bk-conflict-option:hover { border-color: var(--green); }
.bk-conflict-option.lead { border-color: var(--green); box-shadow: inset 0 0 0 1px var(--green); background: var(--green-soft); }
.bk-conflict-option.lead span { color: #3d5a52; }
.bk-schedule-choice legend { grid-column: 1 / -1; color: var(--green); font-size: 14px; font-weight: 800; margin: 0 0 8px; padding: 0; }
.bk-schedule-choice label { min-width: 0; display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 9px; align-items: start; border: 1px solid var(--line); border-radius: 8px; padding: 13px; background: #fff; cursor: pointer; }
.bk-schedule-choice label:has(input:checked) { border-color: var(--green-mid); background: var(--green-soft); box-shadow: 0 0 0 1px var(--green-mid) inset; }
.bk-schedule-choice input { margin: 3px 0 0; accent-color: var(--green); }
.bk-schedule-choice strong, .bk-schedule-choice small { display: block; }
.bk-schedule-choice strong { color: var(--green); font-size: 14px; }
.bk-schedule-choice small { color: var(--muted); font-size: 12.5px; line-height: 1.4; margin-top: 3px; }
.bk-details-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 30px; align-items: start; }
.bk-details-fields .bk-field { margin-bottom: 0; }
.bk-email-stack { display: grid; gap: 14px; padding-bottom: 6px; }
#email-confirm-wrap { margin-bottom: 10px; }
.bk-field { margin-bottom: 12px; }
.bk-field label { display: block; font-size: 13px; font-weight: 600; color: var(--green); margin-bottom: 4px; }
.bk-field input, .bk-field textarea { width: 100%; border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; font: inherit; background: #fff; color: var(--ink); }
.bk-field input.locked { background: #f6f3e8; color: var(--muted); }
.bk-field input:focus, .bk-field textarea:focus { outline: 2px solid var(--green-mid); border-color: transparent; }
.bk-field.invalid input { border-color: var(--danger); background: #fff8f7; }

/* The one unmet requirement, pointed at. Continue is no longer disabled when a
   box is unticked (a greyed-out button explains nothing) — it names what is
   missing and this marks where.
   The CHECKBOX carries the loudest signal, not the panel around it: the
   optional "I need an invoice" box sits directly above in an identical
   .bk-reminders panel, so tinting panels alone leaves the learner comparing
   two similar boxes to work out which one the message meant.
   Border + outline only, no layout shift, so nothing moves under the cursor. */
.bk-needs-attention { border-color: var(--danger); background: #fff8f7; }
.bk-needs-attention > label { font-weight: 700; color: var(--danger); }
.bk-needs-attention input[type="checkbox"] {
  accent-color: var(--danger);
  outline: 2px solid var(--danger);
  outline-offset: 2px;
  border-radius: 3px;
}
@media (prefers-reduced-motion: no-preference) {
  .bk-needs-attention { transition: border-color .15s ease, background-color .15s ease; }
  /* Three pulses, then it stays put — a signal that repeats forever stops
     being a signal and starts being noise. */
  .bk-needs-attention input[type="checkbox"] { animation: bk-attention 0.9s ease-in-out 3; }
}
@keyframes bk-attention {
  0%, 100% { box-shadow: 0 0 0 0 rgba(179, 64, 47, 0); }
  50%      { box-shadow: 0 0 0 5px rgba(179, 64, 47, .28); }
}
.bk-field-error { color: var(--danger); font-size: 12.5px; font-weight: 700; margin: 6px 0 0; }
.bk-field.valid input { border-color: #32a66a; background: #f1fbf5; box-shadow: 0 0 0 1px rgba(50,166,106,.16) inset; padding-right: 40px; }
.bk-field.valid { position: relative; }
.bk-field.valid::after { content: "✓"; position: absolute; right: 13px; top: 31px; width: 18px; height: 18px; border-radius: 999px; background: #32a66a; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 900; }
.bk-paychoice { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 12px; margin: 16px 0; align-items: stretch; }
.bk-paychoice button { min-width: 0; border: 2px solid var(--line); background: #fff; border-radius: 12px; padding: 16px 17px; cursor: pointer; font: inherit; text-align: left; display: grid; grid-template-columns: 22px minmax(0,1fr); grid-template-rows: auto auto 1fr; column-gap: 10px; row-gap: 4px; align-content: start; min-height: 134px; }
.bk-paychoice button.sel, .bk-paychoice button:hover { border-color: var(--green-mid); box-shadow: 0 8px 22px rgba(0,120,108,.09); }
.bk-paychoice button.sel { background: #fffef8; }
.bk-paychoice .p-title { display: contents; font-weight: 700; color: var(--green); }
.bk-paychoice .p-title svg { width: 18px; height: 18px; flex: 0 0 auto; }
.bk-paychoice .p-title svg { grid-column: 1; grid-row: 1; margin-top: 2px; color: var(--green); }
.bk-paychoice .p-title span { grid-column: 2; grid-row: 1; font-weight: 800; color: var(--green); line-height: 1.25; }
.bk-paychoice .p-sub { grid-column: 2; grid-row: 2; font-size: 12.5px; color: var(--muted); line-height: 1.35; }
.bk-paychoice .bk-note { grid-column: 1 / -1; grid-row: 3; align-self: end; margin: 11px 0 0; padding-top: 10px; border-top: 1px solid var(--line); color: var(--muted); font-size: 12.5px; line-height: 1.4; text-align: left; }

.bk-btn { display: inline-block; background: var(--green); color: #fff; border: none; border-radius: 10px; padding: 12px 26px; font: inherit; font-weight: 700; font-size: 15px; cursor: pointer; }
.bk-btn + .bk-btn { margin-left: 8px; }
/* --teal-deep, not the pre-teal #16402a this used to hard-code: a primary
   button that went bottle-green on hover was the one place the old palette
   survived the 2026-07-29 colour-contract remap. */
.bk-btn:hover { background: var(--teal-deep); }
.bk-btn:disabled { opacity: .5; cursor: default; }
.bk-btn.ghost { background: transparent; color: var(--green); border: 2px solid var(--green); }
.bk-error { color: var(--danger); font-size: 14px; margin-top: 10px; }
.bk-note { color: var(--muted); font-size: 13px; }
/* Payment hold countdown: quiet by default, amber only once the hold lapses. */
.bk-note-warn { color: #8a5a12; background: #fdf5e3; border: 1px solid #efdcb4; border-radius: 8px; padding: 9px 11px; }
/* Calendar freshness bar — shown when the student returns to a stale tab. */
.bk-stale { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; padding: 9px 12px; border: 1px solid var(--teal-line); border-radius: 10px; background: var(--green-soft); color: var(--green); font-size: 13px; }
.bk-stale button { border: 1px solid var(--green); background: #fff; color: var(--green); border-radius: 999px; padding: 5px 14px; font: inherit; font-weight: 700; font-size: 12.5px; cursor: pointer; }
.bk-stale button:hover { background: var(--green); color: #fff; }
.bk-service-info { color: var(--ink); font-size: 13.5px; border: 1px solid #e4edf0; border-radius: 12px; background: rgba(255,255,255,.64); padding: 20px 18px; box-shadow: 0 1px 2px rgba(0,120,108,.035); }
.bk-service-info.compact { border: 1px solid var(--line); border-radius: 10px; background: #fff; padding: 12px; margin: 0 0 14px; }
.bk-teacher { display: grid; justify-items: center; gap: 6px; margin-bottom: 12px; text-align: center; }
.bk-teacher img { width: 64px; height: 64px; border-radius: 999px; object-fit: cover; border: 3px solid var(--green-soft); background: var(--green-soft); }
.bk-teacher strong { display: block; color: var(--green); font-size: 14px; }
.bk-service-info h3 { margin: 0 0 6px; color: var(--green); font-size: 16px; line-height: 1.25; }
.bk-service-info .bk-duration { color: var(--muted); font-weight: 700; margin: 0 0 12px; }
/* Admin-authored markdown (instructor bio, terms) can contain a long unbroken
   URL or word; without this it forces the card wider than the ~700px embed
   viewport instead of wrapping. */
.bk-md { color: var(--ink); overflow-wrap: anywhere; }
.bk-md p { margin: 0 0 10px; }
.bk-md ul { margin: 0 0 12px 18px; padding: 0; }
.bk-md li { margin: 0 0 5px; }
.bk-md strong { color: var(--ink); }
.bk-md a { color: var(--green-mid); font-weight: 700; }
.bk-reminders { border: 1px solid var(--line); border-radius: 10px; background: #fff; padding: 12px; margin: 0 0 14px; }
.bk-details-fields + .bk-reminders { margin-top: 10px; }
.bk-reminders strong { display: block; color: var(--green); margin-bottom: 7px; }
.bk-reminders > summary { display:flex; align-items:center; gap:9px; list-style:none; cursor:pointer; color:var(--green); font-weight:800; }
.bk-reminders > summary::-webkit-details-marker { display:none; }
.bk-reminders > summary::before { content:'›'; display:inline-grid; place-items:center; width:20px; height:20px; flex:0 0 auto; border-radius:50%; background:var(--green-soft); font-size:20px; line-height:1; transition:transform .15s ease; }
.bk-reminders[open] > summary::before { transform:rotate(90deg); }
.bk-reminders > summary strong { display:inline; margin:0; }
/* The checkboxes are inside a COLLAPSED details, so the summary has to say
   what is ticked — otherwise two pre-selected reminders are invisible. */
.bk-rem-state { font-size:12.5px; font-weight:700; color:var(--green); }
.bk-rem-state.off { color:var(--muted); font-weight:600; }
.bk-reminders[open] > summary { margin-bottom:9px; }
/* A checkbox in here is a flex ITEM, so it inherited flex-shrink:1 and a long
   label squeezed it into a sliver — which is what "the checkbox is buggy"
   looked like (Norbert, 2026-09-05). Worst on the two terms boxes, whose
   labels are the longest, and inside the narrow portal iframe where they wrap.
   All seven booking checkboxes share this rule, which is why it looked like
   more than one bug. Pin the size, stop it shrinking, and align it to the
   FIRST LINE so a wrapped label does not leave it floating mid-paragraph.
   accent-color stays on the LOWER-specificity `.bk-reminders input` rule on
   purpose: `.bk-needs-attention input[type="checkbox"]` must still be able to
   turn it red when Continue names it. */
/* The whole ROW is the hit target, not the 16px square (Norbert, 2026-09-05:
   "the check box needs to be easy to click on for users"). The <input> is
   already inside the <label>, so the browser gives us that for free — what was
   missing was making the row big enough and making it LOOK clickable, so a
   learner does not aim at a tiny box on a phone. Padding + hover + pointer. */
.bk-reminders label { display: flex; align-items: flex-start; gap: 10px; color: var(--ink); font-size: 13.5px; font-weight: 500; margin: 2px -8px; padding: 9px 8px; border-radius: 8px; cursor: pointer; min-height: 40px; align-content: center; transition: background-color .12s ease;
  /* Norbert, 2026-09-12: "When I click on the I have read conditions button
     it doesn't check - it just highlights the text." A click that lands on
     the label's TEXT can be taken for the start of a selection drag, and a
     selection gesture does not run the label's activation behaviour, so the
     checkbox never toggles. The text in these rows is a control, not prose:
     make it unselectable so a click can only ever be a click. The input keeps
     its own selection behaviour (it has none) and the terms COPY above stays
     selectable — only the row label is locked. */
  -webkit-user-select: none; user-select: none; }
.bk-reminders label:hover { background: var(--green-soft); }
.bk-reminders label:focus-within { background: var(--green-soft); }
.bk-reminders input { width: auto; accent-color: var(--green); }
.bk-reminders input[type="checkbox"] { flex: 0 0 auto; width: 18px; height: 18px; margin: 1px 0 0; cursor: pointer; }
.bk-reminders label > span { min-width: 0; }
.bk-terms-summary p:last-child, .bk-terms-full p:last-child { margin-bottom:0; }
/* The terms are the one block a learner must be able to skim before paying.
   Headings, a highlighted policy block and real emphasis — not a grey wall. */
.bk-md .bk-md-h { margin: 14px 0 6px; font-size: 14px; font-weight: 800; color: var(--green); letter-spacing: .01em; }
.bk-md .bk-md-h:first-child { margin-top: 0; }
/* `display:inline` is LOAD-BEARING, not redundant. Both terms panels reuse the
   `.bk-reminders` card class as a generic box, so `.bk-reminders strong`
   (display:block + margin-bottom) applied to every inline `**bold**` inside the
   rendered terms markdown. A single authored sentence — "…online up to
   **24 hours** before the lesson." — was torn into three stacked blocks, while
   paragraphs containing no bold flowed normally, which is why it read as random
   rather than as one rule. */
.bk-md strong { display: inline; margin: 0; font-weight: 800; color: var(--ink); }
/* A heading authored as `### **Title**` must still look like the heading.
   Otherwise it renders in body ink next to its green peers — which is exactly
   why "Package Terms & Lifetime" looked weaker than the section above it. */
.bk-md .bk-md-h strong { color: inherit; font-weight: inherit; }
.bk-terms-summary { padding: 12px 14px; border-radius: 10px; background: var(--green-soft); border: 1px solid var(--teal-line); }
.bk-terms-summary .bk-md-h { margin-top: 0; }
.bk-terms-full { margin-top:10px; padding:14px 16px; border-left:3px solid var(--teal-line); background:#f8fbfa; border-radius:0 8px 8px 0; line-height: 1.6; }
.bk-terms-full p { margin: 0 0 11px; }
.bk-terms-full ul { margin: 0 0 12px 18px; }
/* Package context in the calendar side panel — what is being bought, not just
   which lesson type is being scheduled. */
.bk-pkg-context { margin: 0 0 14px; padding: 12px 14px; border-radius: 10px; background: var(--green-soft); border: 1px solid var(--teal-line); }
.bk-pkg-context-title { margin: 0 0 8px; font-weight: 800; color: var(--green); font-size: 14px; }
.bk-pkg-context dl { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; margin: 0; font-size: 13px; }
.bk-pkg-context dt { color: var(--muted); font-weight: 600; }
.bk-pkg-context dd { margin: 0; font-weight: 800; color: var(--ink); text-align: right; }
.bk-pkg-context-note { margin: 9px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.bk-terms-more { margin:8px 0 12px; }
.bk-terms-more > summary { width:fit-content; cursor:pointer; color:var(--green); font-weight:800; text-decoration:underline; text-underline-offset:3px; }
/* Clicking the disclosure left a persistent focus rectangle around the link,
   which read as a stray box in the middle of the terms. Keyboard focus keeps
   its ring. */
.bk-terms-more > summary:focus:not(:focus-visible) { outline: none; }
/* The accept checkbox sat flush against the terms it refers to. */
.bk-md + label { margin-top: 11px; }
@media (max-width: 700px) {
  .bk-details-fields { grid-template-columns: 1fr; }
}
.bk-inline-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
/* manage.html #actions: "Pick a new time" / "Ask your teacher to move it" /
   "Cancel booking" — one row when it fits, a clean full-width stack when it
   doesn't (holy albatross), matching portal-embed.html .pc-actions. */
#actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; --pc-row-min: 440px; }
#actions > .bk-btn { flex: 1 1 calc((var(--pc-row-min) - 100%) * 999); min-width: fit-content; white-space: nowrap; margin-left: 0; }
@media (max-width: 380px) {
  #actions > .bk-btn { white-space: normal; }
}
.bk-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 10px; margin-top: 12px; }
.bk-stat { border: 1px solid var(--line); border-radius: 10px; background: #fff; padding: 12px; }
.bk-stat strong { display: block; color: var(--green); font-size: 18px; }
.bk-portal-list { display: grid; gap: 10px; margin-top: 12px; }
.bk-portal-item { border: 1px solid var(--line); border-radius: 10px; background: #fff; padding: 12px; }
.bk-portal-item h3 { margin: 0 0 4px; color: var(--green); font-size: 15px; }
.bk-spin { display: inline-block; width: 16px; height: 16px; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; border-radius: 50%; animation: bkspin .7s linear infinite; vertical-align: -3px; margin-right: 6px; }
@keyframes bkspin { to { transform: rotate(360deg); } }

.bk-code { display: flex; gap: 10px; align-items: center; }
.bk-code input { font-size: 24px; letter-spacing: 8px; text-align: center; width: 190px; }

.bk-confirm { text-align: center; padding: 30px 10px; }
/* Confirmation-as-overlay (2026-09-11). The booked calendar stays on screen
   underneath and the confirmation sits over it as a dismissible card, so a
   learner sees WHAT happened next to WHERE it happened instead of being moved
   to a separate success page they then have to navigate back out of.
   position:ABSOLUTE, never fixed: this flow runs inside an auto-height iframe
   in the learner portal, where a fixed element is positioned against the wrong
   box and can end up off screen entirely. */
.bk-wrap { position: relative; }
#view-confirm.bk-as-overlay { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 40; display: block; padding: 0; background: rgba(15, 51, 43, .38); backdrop-filter: blur(1.5px); }
#view-confirm.bk-as-overlay .bk-confirm { position: sticky; top: 16px; width: min(100%, 520px); margin: 16px auto; padding: 26px 22px 22px; box-shadow: 0 18px 46px rgba(0, 45, 40, .28); border-color: var(--green); }
.bk-confirm-close { position: absolute; top: 8px; right: 10px; width: 32px; height: 32px; display: grid; place-items: center; border: 0; border-radius: 50%; background: transparent; color: var(--muted); font-size: 24px; line-height: 1; cursor: pointer; }
.bk-confirm-close:hover { background: var(--green-soft); color: var(--green); }
.bk-confirm { position: relative; }
.bk-confirm-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
/* #confirm-bank-note is a SIBLING of .bk-confirm, not a child (see index.html) —
   the popup itself must read as the short green "done" message, never as the
   bank details screen (Norbert, 2026-09-11: "I refered to a Green bar saying
   Gotowe! Rather than the Banking details"). In overlay mode it renders in
   normal flow directly under the sticky card, in the SAME scrollable column,
   so it is still reachable by scrolling once the card is closed or scrolled
   past — position:relative, never fixed, for the same auto-height-iframe
   reason the card above already documents. Outside overlay mode (the plain,
   no-calendar-behind confirmation) it needs no special rule; it already sits
   in normal flow under the card. */
/* 2026-09-12: moved INSIDE .bk-confirm (Norbert: the details looked
   disconnected outside the popup). The comment above describes the old
   sibling placement and is kept only as history. */
#confirm-bank-note { margin: 4px 0 16px; text-align: left; }
#confirm-bank-note .bk-bank-details { margin-top: 0; }
body.bk-overlay-open #view-calendar { pointer-events: none; }
@media (max-width: 560px) {
  #view-confirm.bk-as-overlay .bk-confirm { width: 100%; margin: 10px 0; padding: 22px 16px 18px; }
  .bk-confirm-actions .bk-btn { flex: 1 1 100%; }
}
.bk-bank-details { max-width: 520px; margin: 14px auto 0; text-align: left; border: 1px solid #ead89a; border-left: 5px solid #f5d160; border-radius: 10px; background: #fffdf3; padding: 13px 15px; color: #4d3c00; }
.bk-bank-details p { margin: 0 0 10px; }
.bk-bank-details .bk-md p { margin: 6px 0; }
/* Inline SVG success mark, not a 🎉 emoji — house style is icons only. */
.bk-confirm .big { color: var(--green-mid); line-height: 0; }
.bk-confirm .big svg { width: 42px; height: 42px; }
.bk-confirm h2 { color: var(--green); }
.bk-checkout-layout { display: grid; grid-template-columns: minmax(240px, 330px) minmax(320px, 1fr); gap: 18px; align-items: start; margin-top: 14px; }
.bk-payment-service { position: sticky; top: 16px; }
.bk-payment-service h3 { text-align: center; margin-bottom: 10px; }
.bk-payment-service .bk-terms-heading { text-align: left; margin-top: 16px; }
.bk-payment-service .bk-duration,
.bk-payment-when { text-align: center; }
.bk-payment-when { margin: 0; color: var(--ink); font-weight: 700; font-size: 13px; }
.bk-payment-service .bk-teacher img { width: 82px; height: 82px; }
.bk-payment-service .bk-teacher strong { font-size: 15px; }
.bk-payment-slot { margin-top: 14px; }
/* (the old .bk-payment-slot override lived here — it is the default now) */
.bk-stripe-panel { border: 1px solid rgba(245,209,96,.72); border-radius: 12px; background: var(--stripe-surface); padding: 14px 14px 24px; box-shadow: 0 1px 3px rgba(0,120,108,.06); overflow: hidden; }
#stripe-box { min-height: 540px; padding-bottom: 12px; }

.bk-hide { display: none !important; }
@media (max-width: 980px) {
  /* The `.bk-wrap` width cap that used to live here (860 CSS pixels) is gone
     (Norbert, 2026-09-11): it capped the WHOLE flow on any screen narrower
     than 980px for no layout reason still standing, and inside the learner
     portal — where standalone/direct-link mode has no embed/panel override —
     that left the payment step stacked in a single narrow column with an
     empty right-hand third of the laptop screen, narrower than the "Twoja
     cena" bar above it that already used the portal's full width. */
  /* The three-column shell needs 300+360+220 plus two 18px gaps = 916px of
     track before it can shrink at all, so anywhere narrower it overflowed its
     own container instead of reflowing. That is what the booking flow looked
     like inside the learner portal iframe (~740px): the lesson-info column
     clipped off the left edge and the times column off the right.

     But 980px was too high a bar. Inside the v3 dashboard the booking column
     is the viewport minus a ~380px side rail, which on a laptop lands around
     890px — just under the threshold — so the lesson-info card jumped ABOVE
     the calendar and the whole flow read as squeezed (Norbert, 2026-09-05).
     Between 800 and 980 the three columns are kept and simply given smaller
     minimums (240+300+190 + two 14px gaps = 758px), which is the layout that
     width can actually hold. Only below 800px is stacking the honest answer. */
  .bk-calendar-shell {
    grid-template-columns: minmax(240px, 320px) minmax(300px, 380px) minmax(190px, 230px);
    gap: 14px;
  }
}
/* The checkout is only TWO columns — an aside capped at minmax(260px,330px)
   and a Stripe column at minmax(0,1fr) — not the three-column calendar shell
   above, and Stripe's own embedded checkout needs roughly 320px to lay out
   its fields without wrapping. That holds down to about 700px of CONTAINER
   width, not 980px: at 980px the flow was already wide enough for two
   columns wherever it actually ran (a laptop inside the learner portal's
   main column), so it stacked early and left the right-hand third of the
   screen empty next to a narrow single-column payment card (Norbert,
   2026-09-11 — "the width of the booking flow is equal to the Twoja cena
   bar's above" was the tell: the calendar step never had this problem, only
   payment did, because only payment collapsed at the wrong breakpoint). */
/* 2026-09-12. Two changes, measured in a browser rather than reasoned about.
   Norbert: the payment step "stacks the elements up and creates an empty space
   to the right" on a laptop, where the learner portal's main column hands this
   flow well under 800px.

   719 was not an arbitrary number — it was the width below which the OLD track
   list starved Stripe. With `minmax(260px, 330px) minmax(0, 1fr)` the aside
   greedily takes its 330px maximum at every width, so at a 700px viewport the
   Stripe column was left 306px, under the ~320px its embedded checkout needs.
   Dropping the breakpoint alone would therefore have made 600-719px WORSE.

   So the track list changed first: `minmax(320px, 1fr)` makes Stripe's minimum
   a real floor, and the aside's 240px floor lets it give way to it. Measured
   (Chrome, this flow, in an iframe): 760 -> 330/366 (unchanged), 700 -> 316/320,
   660 -> 276/320, 620 -> 240/320, and 600 overflows — 240 + 320 + 18 gap + 46
   padding = 624. Hence 619 as the last width where stacking is the honest
   answer, and NOT one pixel lower. Re-measure if any of those numbers move. */
@media (max-width: 619px) {
  .bk-checkout-layout { grid-template-columns: 1fr; }
  .bk-payment-service { position: static; order: 0; }
  .bk-stripe-panel { order: 1; }
}
@media (max-width: 799px) {
  .bk-calendar-shell { grid-template-columns: minmax(0, 1fr) minmax(200px, 250px); }
  .bk-calendar-shell .bk-service-info { grid-column: 1 / -1; }
  .bk-calendar-shell.month-mode .bk-service-info { min-height: 0; }
}
@media (max-width: 700px) {
  .bk-calendar-shell { grid-template-columns: minmax(0, 1fr); }
  .bk-calendar-shell.month-mode .bk-day-slot-panel { min-height: 0; }
}
@media (max-width: 560px) {
  .bk-schedule-choice { grid-template-columns: 1fr; }
  .bk-card { padding: 16px; }
  .bk-step { min-width: 88px; font-size: 11px; padding: 7px 8px; }
  .bk-calendar-shell { display: flex; flex-direction: column; }
  .bk-service-info { order: 1; margin: 0 0 14px; padding: 12px; font-size: 13px; }
  .bk-service-info .bk-teacher { grid-template-columns: 42px 1fr; justify-items: start; text-align: left; gap: 8px; margin-bottom: 8px; }
  .bk-service-info .bk-teacher img { width: 42px; height: 42px; }
  .bk-service-info h3 { font-size: 14px; margin-bottom: 3px; }
  .bk-service-info .bk-duration { margin-bottom: 8px; }
  .bk-md p { margin-bottom: 7px; }
  .bk-cal-main { order: 2; }
  .bk-week-grid { order: 3; }
  .bk-calendar-shell.month-mode .bk-service-info,
  .bk-calendar-shell.month-mode .bk-day-slot-panel { min-height: 0; }
  .bk-calendar-shell.week-mode .bk-cal-side { display: block; }
  .bk-calendar-shell.week-mode .bk-grid { display: none; }
  .bk-week-grid { margin-top: 14px; }
  .bk-calendar-shell.month-mode .bk-week-grid { margin-top: 14px; }
  .bk-day-slot-panel { min-height: 0; padding: 12px; }
  .bk-day-slot-list { max-height: none; }
  .bk-week-scroller { display: none; }
  .bk-calendar-shell.week-mode .bk-week-scroller { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .bk-calendar-shell.week-mode .bk-week-table { min-width: 640px; grid-template-columns: 54px repeat(7, minmax(82px, 1fr)); }
  .bk-calendar-shell.week-mode .bk-mobile-day { display: none; }
  .bk-mobile-day { display: grid; gap: 7px; padding: 10px; }
  .bk-mobile-day h3 { margin: 0 0 2px; font-size: 14px; color: var(--green); }
  .bk-mobile-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--line); border-radius: 8px; padding: 9px 10px; background: #fff; font: inherit; text-align: left; }
  /* free/bookable row → contract's free tokens */
  .bk-mobile-row.open { background: var(--slot-free-bg); border-color: var(--slot-free-line); color: var(--slot-free-ink); font-weight: 700; }
  /* "unavailable-because-busy" → contract's taken tokens, plain busy and booked-by-another-student alike */
  .bk-mobile-row.busy { background: var(--slot-taken-bg); border-color: var(--slot-taken-line); color: var(--slot-taken-ink); cursor: pointer; }
  .bk-mobile-row.busy.booked { background: var(--slot-taken-bg); border-color: var(--slot-taken-line); color: var(--slot-taken-ink); }
  .bk-mobile-row.busy .busy-label { display: none; font-size: 12px; font-weight: 700; text-align: right; }
  .bk-mobile-row.busy.reveal .busy-label { display: inline; }
  /* Stacking order lives with the collapse itself (the 619px query above);
     between 620 and 700 the payment step is still TWO columns and must keep
     its sticky aside. Only the aside's internal typography belongs here. */
  .bk-payment-service .bk-teacher { display: grid; grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .bk-payment-service h3 { text-align: center; }
  .bk-payment-service .bk-terms-heading { text-align: left; }
  .bk-stripe-panel { padding: 10px 10px 22px; border-radius: 10px; }
  #stripe-box { min-height: 575px; padding-bottom: 12px; }
  .bk-slots { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
  .bk-selected-slot { grid-template-columns: 1fr; }
}

/* Below 620px, keep the pre-existing week-grid behaviour: horizontal-only
   scroll (handled above via .bk-week-scroller { display / overflow-x }),
   no capped inner vertical scrollbox and no sticky-top header — the grid
   just runs its full 24h length and the page scrolls it, same as before
   this change. Left-edge sticky (.bk-week-time / corner cell) is untouched. */
@media (max-width: 619px) {
  .bk-week-scroller { max-height: none; overflow-y: visible; }
  .bk-week-head { top: auto; }
  .bk-week-table > .bk-week-head:first-child { top: auto; }
}

/* ─── Narrow screens, and narrower still when nested ──────────────────────
   The flow is embedded twice over inside the learner portal — v3 player →
   portal-embed → this document — and every level used to spend some of the
   remaining width on its own padding. At 390px the flow alone measures fine;
   nested it does not, and the first thing to suffer is Stripe's embedded
   checkout, which draws its own currency chooser and conversion note inside
   OUR box and simply overflows when we hand it too little (2026-08-16).
   So below 480px the flow stops decorating and gives the width to content. */
@media (max-width: 480px) {
  .bk-wrap { padding: 12px 10px 40px; }
  .bk-card { padding: 14px 12px; border-radius: 12px; }
  .bk-card h2 { font-size: 17px; }
  .bk-reminders { padding: 10px; }
  .bk-hold-recovery { align-items: stretch; flex-direction: column; }
  .bk-hold-recovery-actions { justify-content: stretch; }
  .bk-hold-recovery .bk-btn { width: 100%; }

  /* Stripe's checkout gets the full width of the card it sits in — its own
     layout is responsive, it just needs room. Negative margins pull it back
     out over the card's remaining padding rather than shrinking the card. */
  #stripe-box { margin-left: -12px; margin-right: -12px; }


  /* "Email reminders for this lesson" and its "— on: 24 h before, 1 h before"
     state were two flex items on one row, so both wrapped to three or four
     words per line. The state belongs on its own line at this width. */
  .bk-reminders > summary { flex-wrap: wrap; }
  .bk-reminders > summary .bk-rem-state { flex: 1 1 100%; margin-left: 0; }

  /* A long product line ("Great for: …") was being clipped rather than wrapped. */
  .bk-service-info, .bk-service-info * { min-width: 0; overflow-wrap: anywhere; }
}


/* ─── Progress on a phone ────────────────────────────────────────────────
   Five labelled chips need ~520px. Below that they were squeezed to ~55px
   each and "Sessions added" wrapped to two lines inside a 26px-tall box.
   Shrinking type further is not a design. Show WHERE THE LEARNER IS instead:
   the step count, the current step's name, and how far along the bar is —
   the same thing a desktop stepper communicates, in the space a phone has. */
@media (max-width: 560px) {
  .bk-progress-steps { display: none; }
  .bk-progress-now { display: block; }
  .bk-progress-now-text { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
  .bk-progress-count { flex: none; font-size: 11.5px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .02em; }
  .bk-progress-label { min-width: 0; flex: 1 1 auto; font-size: 14px; font-weight: 800; color: var(--green); overflow-wrap: anywhere; }
  .bk-progress-track { height: 4px; border-radius: 999px; background: var(--line); overflow: hidden; }
  .bk-progress-track > span { display: block; height: 100%; border-radius: inherit; background: var(--green); }
}
@media (max-width: 560px) and (prefers-reduced-motion: no-preference) {
  .bk-progress-track > span { transition: width .25s ease; }
}

/* A step being repositioned. Not `visibility:hidden` and not `display:none`:
   the element must keep its box so the offset we are scrolling to is the real
   one. One frame, or 400ms at the very worst — long enough to hide the jump,
   far too short to read as a delay. Anyone who prefers less motion gets the
   step immediately and simply sees the reposition. */
.bk-step-settling { opacity: 0; }
/* NOT lifted under prefers-reduced-motion. This hold is not motion — it is the
   absence of it. Someone who asked for less movement is exactly who should not
   watch a screen appear in one place and then slide to another. */
