/* ============================================================
   Scheduling module — Aurora SaaS look.
   Only classes introduced by this module. Reuses tokens from
   variables.css and canonical components (.btn/.card/.field/.table/
   .modal/.badge/.chip/.empty/.skeleton) from components.css.
   RTL-first: logical properties everywhere, no left/right.
   ============================================================ */

/* small inline button rows (kept for markup compatibility) */
.pmu-row{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.pmu-row--between{justify-content:space-between}

/* compact inputs (time pickers etc.) */
.input--sm{padding:8px 12px;font-size:var(--fz-sm);width:auto;min-width:118px}

/* ============ SERVICE STATUS DOT / PILL ============ */
.svc-dot{display:inline-block;width:12px;height:12px;border-radius:var(--r-full);flex:0 0 auto;
  box-shadow:0 0 0 3px color-mix(in srgb,currentColor 14%,transparent)}

/* ============ SERVICES LIST (dashboard) ============ */
.svc-list{display:flex;flex-direction:column;gap:var(--s-2)}
.svc-item{display:flex;align-items:center;gap:var(--s-4);padding:var(--s-4);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);
  box-shadow:var(--shadow-xs),inset 0 1px 0 var(--glass-border);
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease),opacity var(--dur) var(--ease)}
.svc-item:hover{border-color:var(--line-strong);box-shadow:var(--shadow),inset 0 1px 0 var(--glass-border)}
.svc-item__swatch{width:44px;height:44px;border-radius:var(--r);display:grid;place-items:center;flex:0 0 auto;
  color:var(--on-brand);box-shadow:var(--shadow-sm),inset 0 1px 0 rgba(255,255,255,.2)}
.svc-item__swatch .icon{width:22px;height:22px}
.svc-meta{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}
.svc-meta__name{display:flex;align-items:center;gap:var(--s-2);font-weight:700;color:var(--text)}
.svc-meta__facts{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap;font-size:var(--fz-xs);color:var(--muted)}
.svc-meta__fact{display:inline-flex;align-items:center;gap:5px}
.svc-meta__fact .icon{width:14px;height:14px}
.svc-item__actions{display:flex;align-items:center;gap:var(--s-1);margin-inline-start:auto}

/* ============ AVAILABILITY EDITOR ============ */
.avail-grid{display:flex;flex-direction:column;gap:var(--s-2)}
.avail-day{display:grid;grid-template-columns:120px 1fr auto;align-items:start;gap:var(--s-4);
  padding:var(--s-4) 0;border-bottom:1px solid var(--line)}
.avail-day:last-of-type{border-bottom:0}
.avail-day__name{display:flex;align-items:center;gap:var(--s-2);font-weight:700;color:var(--text);padding-block-start:8px}
.avail-day__name .icon{width:16px;height:16px;color:var(--brand-fg)}
.avail-day__rows{display:flex;flex-direction:column;gap:var(--s-2);min-width:0}
.avail-day__rows:empty::after{content:attr(data-empty);color:var(--muted);font-size:var(--fz-sm);padding-block:8px}
.avail-row{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.avail-row .sep{color:var(--muted);font-weight:600}
.avail-day__add{align-self:start}
@media (max-width:640px){
  .avail-day{grid-template-columns:1fr;gap:var(--s-2)}
  .avail-day__add{justify-self:start}
}

/* ============ WORKING-HOURS SUMMARY (single availability model) ============ */
.wh-summary{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4);flex-wrap:wrap;
  padding:var(--s-4);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.wh-summary__days{display:flex;gap:6px;flex-wrap:wrap}
.wh-day{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  min-inline-size:46px;block-size:38px;padding-inline:14px;
  border-radius:var(--r-full);border:1px solid var(--line);background:var(--bg-3);color:var(--text-2);
  font-size:var(--fz-sm);font-weight:600}
.wh-day.is-on{background:linear-gradient(180deg,var(--blue-500),var(--brand-2));border-color:var(--brand-line);color:var(--on-brand);box-shadow:var(--glass-hi-soft),var(--shadow-xs)}
.wh-summary__hours{display:inline-flex;align-items:center;gap:6px;font-weight:700;color:var(--text);
  font-variant-numeric:tabular-nums}
.wh-summary__hours .icon{width:16px;height:16px;color:var(--brand-fg)}

/* meeting types summary (dashboard index) */
.mt-summary{list-style:none;margin:var(--s-3) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.mt-summary__item{display:flex;align-items:center;gap:var(--s-3);padding:10px var(--s-3);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.mt-summary__label{font-weight:600;color:var(--text);flex:1;min-width:0}

/* ============ SERVICE EDITOR — colour picker row ============ */
.color-field{display:flex;align-items:center;gap:var(--s-3)}
.color-field input[type="color"]{-webkit-appearance:none;appearance:none;width:48px;height:40px;padding:0;
  border:1px solid var(--line-strong);border-radius:var(--r-sm);background:var(--bg-2);cursor:pointer}
.color-field input[type="color"]::-webkit-color-swatch-wrapper{padding:4px}
.color-field input[type="color"]::-webkit-color-swatch{border:none;border-radius:var(--r-xs)}
.color-swatches{display:flex;gap:var(--s-2);flex-wrap:wrap}
.color-swatches button{width:26px;height:26px;border-radius:var(--r-full);border:2px solid transparent;
  box-shadow:var(--shadow-xs);cursor:pointer;padding:0;
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.color-swatches button:hover{box-shadow:var(--shadow-sm),0 0 0 2px color-mix(in srgb,var(--text) 40%,transparent)}
.color-swatches button[aria-pressed="true"]{border-color:var(--text);box-shadow:var(--focus-ring)}

/* ============ APPOINTMENTS CARD — tabbed header (segmented) ============ */
/* The Upcoming / History switch is the shared .segmented pill, living inside
   the flush card's titlebar so it reads as ONE control joined to the table
   below (the card__head bottom border is the seam). initTabs toggles .active
   on the buttons + panels — the same contract .segmented already styles. */
.sched-appts__head{flex-wrap:wrap;gap:var(--s-3) var(--s-4)}
.sched-appts__seg{flex-wrap:wrap}
/* The per-segment count badge is now the shared `.seg-count` (components.css):
   the same 1.7em circle the Posts/Inbox filter bars use, instead of the 20px
   --r-full pill with a --muted 22% fill and weight 700 this file used to
   declare. Button layout, gap, icon size, hover, focus, active and the active
   badge tint all come from the shared component too — the only thing left here
   is the wrap, because this control lives in a card titlebar. */
/* Panels sit flush under the titlebar: the first table's sticky header butts
   straight up against the card__head seam (no rounded corner, no double gap). */
.sched-appts .tab-panel > .table-wrap{border:0;border-radius:0;box-shadow:none}
/* Narrow screens: no local rule any more. The family has ONE overflow strategy
   (scroll the track at 720px, components.css); the 560px equal-split this file
   used to declare was a third, conflicting one. */
.sched-appts__seg-label{min-width:0}

/* ============ APPOINTMENTS LIST — actions cell ============ */
/* Inline "Confirmed" state that replaces the Confirm button once confirmed. */
.appt-confirmed-flag{display:inline-flex;align-items:center;gap:6px;font-size:var(--fz-sm);font-weight:600;
  color:var(--success-fg);padding:6px 8px;white-space:nowrap}
.appt-confirmed-flag .icon{width:16px;height:16px}

/* "The client will be notified" hint inside the action modals. */
.appt-notify-hint{display:flex;align-items:center;gap:8px;margin:var(--s-4) 0 0;padding:10px var(--s-3);
  border:1px dashed var(--line-strong);border-radius:var(--r-lg);background:var(--surface-2);
  color:var(--text-2);font-size:var(--fz-sm)}
.appt-notify-hint .icon{width:16px;height:16px;color:var(--brand-fg);flex:0 0 auto}

/* In-place row highlight after a successful patch (reduced-motion: none). */
@keyframes apptRowFlash{0%{background:color-mix(in srgb,var(--brand-2) 16%,transparent)}100%{background:transparent}}
.appt-row--flash > td{animation:apptRowFlash .9s var(--ease)}
@media (prefers-reduced-motion:reduce){.appt-row--flash > td{animation:none}}

/* The "More actions" popover is PORTALED to <body> (scheduling.js) so the
   appointments card (.card--flush{overflow:hidden}) and the scrolling
   .table-wrap can never clip it. It reuses the canonical .menu look but is
   positioned fixed against its trigger, RTL-safe + viewport-clamped in JS.
   z-index sits above the sticky table header + card, below modals (1300). */
.appt-menu{position:fixed;inset:auto;z-index:1250;min-width:220px;
  background:var(--surface-solid);border:1px solid var(--line-strong);border-radius:var(--r-lg);
  padding:6px;box-shadow:var(--shadow-lg),var(--glass-hi-soft);
  opacity:0;visibility:hidden;transform:translateY(-8px) scale(.98);pointer-events:none;transform-origin:top;
  transition:opacity var(--dur) var(--ease),visibility var(--dur) var(--ease),transform var(--dur) var(--ease-out-expo)}
.appt-menu.open{opacity:1;visibility:visible;transform:none;pointer-events:auto}
.appt-menu .menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:9px 12px;border-radius:var(--r-sm);
  color:var(--text-2);font-size:var(--fz-sm);text-align:start;cursor:pointer;background:none;border:0}
.appt-menu .menu__item:hover,.appt-menu .menu__item:focus-visible{background:var(--tint);color:var(--text);outline:none}
.appt-menu .menu__item .icon{width:18px;height:18px;color:var(--muted);flex:0 0 auto}
.appt-menu .menu__item--danger{color:var(--danger-fg)}
.appt-menu .menu__item--danger:hover,.appt-menu .menu__item--danger:focus-visible{background:color-mix(in srgb,var(--accent-red) 12%,transparent);color:var(--danger-fg)}
.appt-menu .menu__item--danger .icon{color:var(--danger-fg)}
.appt-menu .menu__sep{height:1px;background:var(--line);margin:6px 4px}
@media (prefers-reduced-motion:reduce){.appt-menu{transition:none}}

/* ============ CANCEL MODAL — segmented mode toggle ============ */
/* Full-width variant of the shared track. The buttons are plain `.seg button`s
   now — the local copy re-declared padding 9px 12px, border:0, a --r-full
   radius and an opaque --surface-solid + --shadow-sm active state that dropped
   the family's brand colour and border entirely. All of that is shared. */
.seg--full{display:flex;width:100%;margin-bottom:var(--s-4)}
.seg--full .seg__btn{flex:1}
/* The only genuinely local bit: "cancel outright" is a destructive choice. */
.appt-cancel-mode .seg__btn.is-active[data-appt-cancel-mode="hard"]{color:var(--danger-fg)}

/* ============ QUICK-SWITCH — searchable client picker ============ */
.client-picker{list-style:none;margin:var(--s-3) 0 0;padding:6px;display:flex;flex-direction:column;gap:2px;
  max-height:260px;overflow-y:auto;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.client-picker__item{display:flex;align-items:center;gap:var(--s-3);padding:8px 10px;border-radius:var(--r-sm);
  cursor:pointer;color:var(--text);transition:background var(--dur) var(--ease)}
.client-picker__item:hover,.client-picker__item:focus-visible{background:var(--tint);outline:none}
.client-picker__item[aria-selected="true"]{background:color-mix(in srgb,var(--brand-2) 14%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--brand-2) 40%,transparent)}
.client-picker__item--current{opacity:.6;cursor:not-allowed}
.client-picker__avatar{width:32px;height:32px;border-radius:var(--r-full);flex:0 0 auto;display:grid;place-items:center;
  background:var(--brand-grad);color:var(--on-brand);font-weight:700;font-size:var(--fz-sm)}
.client-picker__meta{display:flex;flex-direction:column;min-width:0;flex:1}
.client-picker__name{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.client-picker__sub{font-size:var(--fz-xs);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;unicode-bidi:plaintext}
.client-picker__current{font-size:var(--fz-xs);color:var(--muted);margin-inline-start:auto}
.client-picker__empty{padding:14px 10px;text-align:center;color:var(--muted);font-size:var(--fz-sm)}
.appt-reassign-chosen{display:flex;align-items:center;gap:var(--s-2);margin-top:var(--s-3);flex-wrap:wrap}

/* ============ PUBLIC BOOKING SHELL ============ */
/* offset for the fixed public top nav (var(--nav-h)) */
.book-wrap{max-width:1080px;margin-inline:auto;
  padding:calc(var(--nav-h) + var(--s-10)) var(--s-6) var(--s-16)}
.book-head{margin-bottom:var(--s-8)}
.book-back{display:inline-flex;align-items:center;gap:6px;font-size:var(--fz-sm);font-weight:600;color:var(--text-2);
  margin-bottom:var(--s-4);transition:color var(--dur) var(--ease)}
.book-back:hover{color:var(--brand-fg)}
.book-back .icon{width:16px;height:16px}
/* flip the back chevron per direction */
[dir="rtl"] .book-back .icon{transform:scaleX(-1)}

/* stepper */
.stepper{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;margin-bottom:var(--s-8);font-size:var(--fz-sm)}
.stepper__step{display:inline-flex;align-items:center;gap:8px;color:var(--muted);font-weight:600;
  transition:color var(--dur) var(--ease)}
.stepper__step.is-active{color:var(--text)}
.stepper__step.is-done{color:var(--success-fg)}
/* GEOMETRY IS DELIBERATELY UNCHANGED from the light reference design: a 26px
   round bubble. Only the two COLOUR ROLES moved onto theme-resolved tokens, and
   both resolve to their previous light values verbatim
   (--success-fg == --accent-green == #0E9F6E), so light is untouched:
     .is-done  --accent-green -> --success-fg. As a FOREGROUND on the dark
               washes the fixed green measured 4.27:1; --success-fg lifts it to
               #4ADE9A / 8.40:1.
     .is-active keeps background:var(--brand-grad) — a brand FILL is correct on
               dark, and --on-brand on the gradient's mid stop is 6.53:1, so this
               marker needs no dark treatment at all. */
.stepper__dot{width:26px;height:26px;border-radius:var(--r-full);display:grid;place-items:center;
  border:1px solid var(--line-strong);background:var(--bg-2);font-size:var(--fz-xs);color:var(--muted);
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.stepper__step.is-active .stepper__dot{background:var(--brand-grad);color:var(--on-brand);border-color:transparent;box-shadow:var(--glow)}
.stepper__step.is-done .stepper__dot{background:color-mix(in srgb,var(--success-fg) 14%,transparent);color:var(--success-fg);border-color:color-mix(in srgb,var(--success-fg) 38%,transparent)}
.stepper__sep{flex:0 0 32px;height:1px;background:var(--line-strong)}
@media (max-width:560px){.stepper__sep{flex-basis:14px}}

/* ============ PUBLIC SERVICE CARDS ============ */
.svc-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:var(--s-6)}
.svc-card{display:flex;flex-direction:column;gap:var(--s-3);text-decoration:none;color:inherit;position:relative;overflow:hidden;
  box-shadow:var(--shadow-sm),inset 0 1px 0 var(--glass-border);
  transition:box-shadow var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.svc-card:hover{border-color:var(--line-strong);box-shadow:var(--shadow-lg),inset 0 1px 0 var(--glass-border)}
.svc-card__accent{height:5px;border-radius:var(--r-full);width:52px;margin-bottom:var(--s-1);box-shadow:var(--glow)}
.svc-card__desc{color:var(--text-2);font-size:var(--fz-sm);line-height:1.6;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.svc-card__meta{display:flex;gap:var(--s-2);flex-wrap:wrap;margin-top:auto;padding-top:var(--s-3)}
.svc-card__go{display:inline-flex;align-items:center;gap:6px;font-weight:700;color:var(--brand-fg);font-size:var(--fz-sm);
  margin-top:var(--s-2);transition:gap var(--dur) var(--ease)}
.svc-card__go .icon{width:16px;height:16px}
[dir="rtl"] .svc-card__go .icon{transform:scaleX(-1)}
.svc-card:hover .svc-card__go{gap:12px}
.svc-card__price{font-size:var(--fz-lg);font-weight:800;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:var(--tracking-display)}

/* ============ BOOKING SERVICE SUMMARY ============ */
.book-summary{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap}
.book-summary__badge{width:52px;height:52px;border-radius:var(--r-lg);display:grid;place-items:center;color:var(--on-brand);
  flex:0 0 auto;box-shadow:var(--shadow-sm),inset 0 1px 0 rgba(255,255,255,.2)}
.book-summary__badge .icon{width:26px;height:26px}
.book-summary__facts{display:flex;gap:var(--s-3);flex-wrap:wrap;margin-top:var(--s-1)}

/* ============================================================
   THE STANDARD TIME PICKER — .pmu-timegrid / .pmu-timeslot
   ------------------------------------------------------------
   This is the CMS-wide hour picker. Any screen that asks a human to choose a
   time out of a set of candidates should reuse THESE classes rather than roll
   its own grid of buttons. It is deliberately generic (no booking wording, no
   scheduling-only tokens) so it can be lifted into another module verbatim.

   MARKUP CONTRACT
     <div class="pmu-timegrid-wrap">                       <- optional shell
       <div class="pmu-timegrid__head">                    <- label + count
         <span class="label">Available times</span>
         <span class="pmu-timegrid__count">12 available</span>
       </div>
       <p class="slots__summary">…</p>                     <- OPTIONAL totals
       <p class="slots__legend">…</p>                      <- OPTIONAL key
       <div class="pmu-timegrid" role="listbox|group">     <- the scroller
         <button class="pmu-timeslot pmu-timeslot--limited"
                 aria-pressed="false" data-tooltip="2 spots left">
           <span class="pmu-timeslot__time">09:00</span>
           <span class="pmu-timeslot__rem">2 spots</span>  <- OPTIONAL
           <span class="pmu-timeslot__lvl">…svg…</span>    <- OPTIONAL level glyph
           <span class="pmu-timeslot__check">…svg…</span>
         </button>
       </div>
     </div>

   RULES
     * Selection is exposed with aria-pressed (never colour alone: the selected
       chip is filled AND carries a check mark AND reports its state).
     * Availability level (--free / --limited / --full) is likewise never colour
       alone — the level class always ships with the shaped .pmu-timeslot__lvl
       glyph, wording in the accessible name and a [data-tooltip] with the exact
       number of remaining spots. Only add a level class when it says something.
     * The secondary line (.pmu-timeslot__rem) is only rendered when the offering
       has real multi-capacity. A 1-on-1 slot shows the time and nothing else.
     * Arrow keys + Home/End move between chips, Enter/Space picks (booking.js).
     * The grid scrolls inside itself with a stable scrollbar gutter so adding or
       removing rows never shifts the surrounding layout.
   ============================================================ */

.pmu-timegrid-wrap,.slots{margin-top:var(--s-4)}
.pmu-timegrid__head,.slots__head{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-3);margin-bottom:var(--s-3);flex-wrap:wrap}
.pmu-timegrid__count,.slots__count{font-size:var(--fz-xs);color:var(--muted);font-weight:700;
  font-variant-numeric:tabular-nums}

/* Component tokens — ONE definition, both themes.
   The accent used to be re-declared in two dark blocks (an explicit
   [data-theme] copy plus a prefers-color-scheme twin) because raw --brand-2 is
   the same deep navy in both themes and disappeared against the dark surfaces.
   It now derives from the theme-resolved foreground tokens, so the dark blocks
   are gone and the two themes cannot drift apart.
     --ts-accent -> --brand-fg   was 2.48:1 on the dark card, now 8.35:1
     --ts-sel-bg  bottom stop -> --brand-fill, so light resolves to the identical
       #2E6BE6 -> #1E50C8 gradient while dark becomes a flat, readable #2E6BE6
       (3.50:1 vs the card; --on-brand on it 4.55:1). */
.pmu-timegrid,.pmu-timegrid-wrap{
  --ts-accent:var(--brand-fg);
  --ts-sel-bg:linear-gradient(180deg,var(--blue-500),var(--brand-fill));
  --ts-sel-fg:var(--on-brand);
  --ts-sel-shadow:0 2px 10px rgba(30,80,200,.30);
}

.pmu-timegrid,.slot-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:var(--s-2);
  max-block-size:min(46vh,340px);overflow-y:auto;overscroll-behavior:contain;
  scrollbar-gutter:stable;padding-block:2px;padding-inline-end:2px;
}

/* ---- AVAILABILITY LEVEL ---------------------------------------------------
   A chip may declare how full it is: --free (plenty left), --limited (almost
   gone), --full (nothing left). Each level resolves TWO tokens — --ts-lvl (the
   hue used for the border/wash/glow) and --ts-lvl-fg (a text-safe foreground) —
   both theme-resolved, so light and dark are served by one declaration.

   COLOUR IS NEVER THE ONLY SIGNAL. A levelled chip also renders a distinctly
   SHAPED glyph (.pmu-timeslot__lvl — a tick, a warning triangle, a blocked
   ring), states its availability in the accessible name, and exposes the exact
   number of remaining spots through the canonical [data-tooltip]. The grid
   therefore still reads in greyscale and under any colour-vision deficiency.

   The host only ADDS a level class when the level carries information (a real
   multi-seat service, or a slot with nothing left). A 1-on-1 grid where every
   chip is trivially free stays neutral instead of becoming a wall of green. */
.pmu-timeslot--free{--ts-lvl:var(--accent-green);--ts-lvl-fg:var(--success-fg)}
.pmu-timeslot--limited{--ts-lvl:var(--accent-amber);--ts-lvl-fg:var(--warn-fg)}
.pmu-timeslot--full{--ts-lvl:var(--muted);--ts-lvl-fg:var(--muted)}

.pmu-timeslot,.slot-btn{
  position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  min-block-size:48px;box-sizing:border-box;
  /* BOTH gutters are always reserved — the check mark (inline-start) and the
     availability glyph (inline-end) — so no state change ever reflows the time. */
  padding-block:9px;padding-inline:26px 24px;
  border:1px solid var(--line-strong);border-radius:var(--r-full);background:var(--bg-2);
  color:var(--text);font:inherit;font-size:var(--fz-sm);font-weight:700;
  font-variant-numeric:tabular-nums;cursor:pointer;text-align:center;
  isolation:isolate;
  transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),
             color var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
/* The label rides ABOVE the two paint layers declared below. */
.pmu-timeslot > *,.slot-btn > *{position:relative;z-index:2}

/* LAYER 1 (::after) — the SELECTED fill. `background-image` does not interpolate,
   so per the motion standard the gradient lives on its own layer and only its
   OPACITY animates: the fill fades IN on select and OUT on deselect.
   motion-standard.css ALREADY reserves `html .slot-btn.slot-btn::after` for
   exactly this job (with --brand-grad). Rather than fight it on a second layer,
   this component keeps that layer, its z-index and its clock and only swaps in
   its own softer diagonal wash — the doubled class matches motion-standard's
   specificity and scheduling.css is linked after it (see the layout <head>),
   which is what makes the override land. `isolation:isolate` on the base rule is
   what keeps the z-index:-1 layer INSIDE the chip instead of sinking behind the
   card; without it that layer never painted at all. */
html .pmu-timeslot.pmu-timeslot::after,
html .slot-btn.slot-btn::after{
  content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;opacity:0;
  background:linear-gradient(135deg,
    color-mix(in srgb,var(--brand-2) 92%,#fff 8%) 0%,
    var(--brand-2) 46%,
    color-mix(in srgb,var(--brand-2) 84%,#000 16%) 100%);
  transition:opacity var(--dur) var(--ease);
}
html .pmu-timeslot.pmu-timeslot[aria-pressed="true"]::after,
html .pmu-timeslot.pmu-timeslot.is-selected::after,
html .slot-btn.slot-btn.is-selected::after{opacity:1}

/* LAYER 2 (::before) — a hover/focus sheen. At z-index 0 it paints ABOVE the
   selected fill (negative-z layers paint first) and BELOW the label, so it works
   on the neutral chip AND on the filled one. This is what keeps a SELECTED hour
   alive under the pointer instead of going inert the moment it is chosen. */
.pmu-timeslot::before,.slot-btn::before{
  content:"";position:absolute;inset:0;z-index:0;border-radius:inherit;pointer-events:none;opacity:0;
  background:linear-gradient(180deg,rgba(255,255,255,.26),rgba(255,255,255,0) 64%);
  transition:opacity var(--dur) var(--ease);
}
.pmu-timeslot:hover:not([disabled])::before,
.pmu-timeslot:focus-visible::before,
.slot-btn:hover:not([disabled])::before{opacity:1}

.pmu-timeslot__time{line-height:1.15;unicode-bidi:isolate}
.pmu-timeslot__rem,.slot-btn__rem{font-size:10px;font-weight:600;letter-spacing:.2px;
  color:var(--ts-lvl-fg,var(--muted));line-height:1.1;
  transition:color var(--dur) var(--ease)}

/* The level glyph — SHAPE, not just hue, is what separates the three states. */
.pmu-timeslot__lvl{
  position:absolute;inset-inline-end:7px;inset-block-start:50%;translate:0 -50%;z-index:2;
  inline-size:14px;block-size:14px;display:inline-flex;
  color:var(--ts-lvl-fg,var(--muted));
  transition:color var(--dur) var(--ease),opacity var(--dur) var(--ease);
}
.pmu-timeslot__lvl svg{inline-size:100%;block-size:100%}

/* Level wash on an UNSELECTED chip. Mixed against --bg-2 so it stays a whisper
   in light and a legible tint in dark, never a saturated block in either. */
.pmu-timeslot--free:not(.is-selected):not([disabled]),
.pmu-timeslot--limited:not(.is-selected):not([disabled]){
  border-color:color-mix(in srgb,var(--ts-lvl) 40%,var(--line-strong));
  background:color-mix(in srgb,var(--ts-lvl) 9%,var(--bg-2));
}
.pmu-timeslot--full{
  border-color:color-mix(in srgb,var(--ts-lvl) 32%,var(--line));
  background:color-mix(in srgb,var(--ts-lvl) 7%,var(--bg-2));
  color:var(--muted);
}

/* Hover tracks the chip's OWN level (green stays green, amber stays amber) and
   falls back to the brand blue on an unlevelled chip. */
.pmu-timeslot:hover:not([disabled]),.slot-btn:hover:not([disabled]){
  border-color:color-mix(in srgb,var(--ts-lvl,var(--brand-line)) 62%,var(--line-strong));
  background:color-mix(in srgb,var(--ts-lvl,var(--brand-2)) 14%,var(--surface));
  color:var(--text);
  box-shadow:0 2px 10px -6px color-mix(in srgb,var(--ts-lvl,var(--brand-2)) 62%,transparent);
}
.pmu-timeslot:focus-visible,.slot-btn:focus-visible{
  outline:2px solid var(--ts-accent);outline-offset:2px}

/* SELECTED — filled + a check mark, so the state survives greyscale.
   The check is NOT crammed into the corner: the pill always reserves a fixed
   inline-start gutter for it, so selecting a slot never reflows the time (no
   jump), and the mark reads as part of the chip instead of a sticker on its
   edge. `background` here is a flat COLOUR (it interpolates); the diagonal
   glass wash is the ::before layer above. */
.pmu-timeslot[aria-pressed="true"],.pmu-timeslot.is-selected,.slot-btn.is-selected{
  background:var(--brand-2);
  color:var(--ts-sel-fg);
  border-color:color-mix(in srgb,var(--brand-line) 70%,#fff 30%);
  box-shadow:
    0 1px 0 rgba(255,255,255,.28) inset,
    0 6px 18px -6px color-mix(in srgb,var(--brand-2) 65%,transparent),
    0 2px 6px -2px rgba(8,20,50,.35);
}
.pmu-timeslot[aria-pressed="true"] .pmu-timeslot__rem,
.pmu-timeslot.is-selected .pmu-timeslot__rem,
.slot-btn.is-selected .slot-btn__rem{color:rgba(255,255,255,.92)}
.pmu-timeslot[aria-pressed="true"] .pmu-timeslot__lvl,
.pmu-timeslot.is-selected .pmu-timeslot__lvl{color:rgba(255,255,255,.92)}

/* HOVER / FOCUS AFTER SELECTION. The chosen hour must not go dead under the
   pointer, but it must also stay visually authoritative over its neighbours:
   the edge brightens, the glow deepens and a brand halo appears — a level ABOVE
   the unselected hover, never a fallback to it. All of these properties sit on
   the base rule's transition list, so the state fades in AND out symmetrically. */
.pmu-timeslot[aria-pressed="true"]:hover:not([disabled]),
.pmu-timeslot.is-selected:hover:not([disabled]),
.pmu-timeslot[aria-pressed="true"]:focus-visible,
.pmu-timeslot.is-selected:focus-visible,
.slot-btn.is-selected:hover:not([disabled]){
  background:color-mix(in srgb,var(--brand-2) 88%,#fff 12%);
  border-color:color-mix(in srgb,#fff 52%,var(--brand-line));
  box-shadow:
    0 1px 0 rgba(255,255,255,.44) inset,
    0 8px 22px -6px color-mix(in srgb,var(--brand-2) 78%,transparent),
    0 0 0 3px color-mix(in srgb,var(--brand-2) 26%,transparent);
}

/* Centred in the reserved gutter — vertically middled, never overlapping text. */
.pmu-timeslot__check{
  position:absolute;inset-inline-start:9px;inset-block-start:50%;
  translate:0 -50%;z-index:2;
  inline-size:15px;block-size:15px;opacity:0;color:currentColor;
  transition:opacity var(--dur) var(--ease);
}
.pmu-timeslot[aria-pressed="true"] .pmu-timeslot__check,
.pmu-timeslot.is-selected .pmu-timeslot__check{opacity:1}

/* UNAVAILABLE. Two spellings are honoured: the hard `disabled` attribute, and
   aria-disabled — which the booking grid uses so the chip still receives pointer
   and keyboard focus and can therefore still show the tooltip explaining WHY the
   hour is gone. Neither may light up under the pointer, so the hover wash from
   the rule above is explicitly cancelled (same specificity, declared later). */
.pmu-timeslot[disabled],.slot-btn[disabled],
.pmu-timeslot[aria-disabled="true"]{opacity:.62;cursor:not-allowed}
.pmu-timeslot[disabled]::before,.slot-btn[disabled]::before,
.pmu-timeslot[aria-disabled="true"]::before,
.pmu-timeslot[aria-disabled="true"]:hover::before,
.pmu-timeslot[aria-disabled="true"]:focus-visible::before{opacity:0}
.pmu-timeslot[aria-disabled="true"]:hover{
  border-color:color-mix(in srgb,var(--ts-lvl,var(--muted)) 32%,var(--line));
  background:color-mix(in srgb,var(--ts-lvl,var(--muted)) 7%,var(--bg-2));
  color:var(--muted);
  box-shadow:none;
}
.pmu-timeslot[aria-disabled="true"]:focus-visible{outline:2px solid var(--muted);outline-offset:2px}

/* Loading skeleton — identical geometry to a real chip so nothing jumps. */
.pmu-timegrid--skeleton,.slots-skeleton{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:var(--s-2)}
.pmu-timegrid--skeleton .skeleton,.slots-skeleton .skeleton{height:48px;border-radius:var(--r-full)}

/* ---- Availability summary + legend under the picker header ----------------
   "N times available · M hours bookable" for the chosen date. The numbers come
   from the slots endpoint's meta when it supplies them and are derived from the
   slot list otherwise, so the line is correct either way. */
.slots__summary{display:flex;align-items:center;gap:var(--s-2) var(--s-4);flex-wrap:wrap;
  margin:0 0 var(--s-3);font-size:var(--fz-xs);color:var(--text-2)}
.slots__summary-item{display:inline-flex;align-items:center;gap:6px;font-weight:600;
  font-variant-numeric:tabular-nums}
.slots__summary-item svg{inline-size:14px;block-size:14px;flex:0 0 auto;color:var(--brand-fg)}

/* Printed only when more than one level is actually on screen, so the legend
   explains the colouring instead of decorating a uniform grid. */
.slots__legend{display:flex;align-items:center;gap:var(--s-2) var(--s-3);flex-wrap:wrap;
  margin:0 0 var(--s-3);padding-block-start:var(--s-2);border-block-start:1px solid var(--line);
  font-size:11px;color:var(--muted)}
.slots__legend-item{display:inline-flex;align-items:center;gap:5px}
.slots__legend-ico{inline-size:12px;block-size:12px;display:inline-flex;flex:0 0 auto;
  color:var(--_l,var(--muted))}
.slots__legend-ico svg{inline-size:100%;block-size:100%}
.slots__legend-item--free{--_l:var(--success-fg)}
.slots__legend-item--limited{--_l:var(--warn-fg)}
.slots__legend-item--full{--_l:var(--muted)}

@media (prefers-reduced-motion:reduce){
  html .pmu-timeslot,html .slot-btn,.pmu-timeslot__check,.pmu-timeslot__lvl,
  html .pmu-timeslot.pmu-timeslot::before,html .pmu-timeslot.pmu-timeslot::after,
  html .slot-btn.slot-btn::before,html .slot-btn.slot-btn::after{transition:none}
}

/* ============================================================
   MEETING TYPE — accessible radio group styled as segmented cards
   (.pmu-mt). Real <input type=radio> under the hood: arrow keys, name
   grouping and form semantics come from the platform, the card is only paint.
   ============================================================ */
.pmu-mt{border:0;padding:0;margin:0 0 var(--s-5);min-inline-size:0}
.pmu-mt__group{display:flex;flex-wrap:wrap;gap:var(--s-2)}
.pmu-mt__opt{position:relative;flex:1 1 158px;min-inline-size:0;cursor:pointer;display:block}
/* Focusable but invisible (opacity, not display:none — display:none kills focus). */
.pmu-mt__input{position:absolute;inset-block-start:50%;inset-inline-start:12px;
  inline-size:1px;block-size:1px;opacity:0;margin:0;pointer-events:none}
.pmu-mt__card{display:flex;align-items:center;gap:8px;inline-size:100%;box-sizing:border-box;
  min-block-size:46px;padding:10px 12px;border:1px solid var(--line-strong);border-radius:var(--r-lg);
  background:var(--bg-2);color:var(--text-2);font-size:var(--fz-sm);font-weight:600;
  transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),
             color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.pmu-mt__ico{inline-size:18px;block-size:18px;flex:0 0 auto;color:var(--muted);
  transition:color var(--dur) var(--ease)}
.pmu-mt__label{min-inline-size:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pmu-mt__check{margin-inline-start:auto;flex:0 0 auto;display:inline-flex;opacity:0;
  color:var(--ts-accent);transition:opacity var(--dur) var(--ease)}
.pmu-mt__check .icon{inline-size:16px;block-size:16px}
.pmu-mt__opt:hover .pmu-mt__card{border-color:var(--line-brand);background:var(--tint);color:var(--text)}
.pmu-mt__input:focus-visible + .pmu-mt__card{outline:2px solid var(--focus-line);outline-offset:2px}.pmu-mt__input:checked + .pmu-mt__card{
  border-color:var(--line-brand);background:var(--tint-strong,var(--tint));color:var(--text);
  box-shadow:inset 0 0 0 1px var(--line-brand)}
.pmu-mt__input:checked + .pmu-mt__card .pmu-mt__ico{color:var(--brand-fg)}.pmu-mt__input:checked + .pmu-mt__card .pmu-mt__check{opacity:1}@media (prefers-reduced-motion:reduce){
  .pmu-mt__card,.pmu-mt__ico,.pmu-mt__check{transition:none}
}

/* ============================================================
   WHATSAPP PREFERRED-CONTACT OPT-IN (.pmu-wa)
   ------------------------------------------------------------
   A REAL <input type="checkbox"> drives everything — keyboard, form value,
   assistive-tech state. The visible control is a custom switch painted from that
   input's :checked state. The <label> itself also carries `.is-on` (toggled by
   booking.js) because no sibling selector can reach an ancestor, plus `.is-flash`
   for the single, non-looping attention cue fired when it is switched ON.

   DELIBERATELY OUT-OF-SYSTEM: WhatsApp's own brand greens. A third-party brand
   mark keeps its own colour, so these must NOT be folded onto --success-fg —
   but they are still TOKENS, not raw hex, and they live in variables.css:
     --wa       the brand green. A PIGMENT: identical in both themes (fills, the
                switch track, every color-mix below).
     --wa-ink   the brand's darker green — borders that sit ON the fill.
     --wa-fg    the state wording, a foreground: it forks per theme.
     --wa-mark  the standalone mark GLYPH, also a foreground and so also forked:
                WhatsApp's teal #128C7E on light (4.14:1 on the card), the bright
                green #25D366 on dark (8.49:1). Both values clear the 3:1 icon
                floor in BOTH themes — the teal on dark would still have passed,
                at 4.07:1 — so this fork exists for brand vibrancy, not for
                compliance. It reproduces the old [data-theme="dark"] patch's
                intent exactly, without the patch.

   MOTION: every value that changes is declared on the BASE rule with the full
   property list, so enabling and disabling fade symmetrically. The green
   highlight is a colour MIX (it interpolates); the softer aura is a gradient and
   therefore rides its own opacity layer. The pulse runs exactly once and is
   dropped entirely under prefers-reduced-motion.
   ============================================================ */
.pmu-wa{position:relative;display:flex;align-items:center;gap:10px;margin-block-start:var(--s-3);
  padding:10px 12px;border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--surface-2);cursor:pointer;isolation:isolate;
  transition:border-color var(--dur) var(--ease),background var(--dur) var(--ease),
             box-shadow var(--dur) var(--ease)}
/* Soft green aura — a gradient, so it cross-fades on its own layer. */
.pmu-wa::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;
  pointer-events:none;opacity:0;
  background:radial-gradient(125% 165% at 100% 0%,
    color-mix(in srgb,var(--wa) 24%,transparent) 0%,
    color-mix(in srgb,var(--wa) 6%,transparent) 46%,
    transparent 78%);
  transition:opacity var(--dur) var(--ease)}
.pmu-wa.is-on::before{opacity:1}
.pmu-wa.is-on{border-color:color-mix(in srgb,var(--wa) 58%,var(--line));
  background:color-mix(in srgb,var(--wa) 9%,var(--surface-2));
  box-shadow:0 2px 14px -8px color-mix(in srgb,var(--wa) 70%,transparent)}
/* One-shot attention cue on enable. Never loops. */
@keyframes pmuWaFlash{
  0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--wa) 55%,transparent)}
  60%{box-shadow:0 0 0 9px color-mix(in srgb,var(--wa) 0%,transparent)}
  100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--wa) 0%,transparent)}
}
.pmu-wa.is-flash{animation:pmuWaFlash .72s var(--ease-out-expo) 1}

.pmu-wa__input{position:absolute;inline-size:1px;block-size:1px;opacity:0;margin:0;pointer-events:none}
.pmu-wa__mark{flex:0 0 auto;display:inline-flex;color:var(--muted);
  transition:color var(--dur) var(--ease)}
.pmu-wa.is-on .pmu-wa__mark{color:var(--wa-mark)}
.pmu-wa__ico{inline-size:20px;block-size:20px}
.pmu-wa__text{display:flex;flex-direction:column;gap:2px;min-inline-size:0;flex:1}
.pmu-wa__title{font-size:var(--fz-sm);font-weight:700;color:var(--text)}
.pmu-wa__hint{font-size:var(--fz-xs);color:var(--muted);line-height:1.4;
  transition:color var(--dur) var(--ease)}
.pmu-wa.is-on .pmu-wa__hint{color:var(--text-2)}
/* The on/off state is ALSO spelled out in words, so it never rests on green
   alone — and the wording is what a screen reader's label already announces. */
.pmu-wa__state{font-size:var(--fz-xs);font-weight:700;color:var(--muted);
  transition:color var(--dur) var(--ease)}
.pmu-wa.is-on .pmu-wa__state{color:var(--wa-fg)}

/* ---- The switch --------------------------------------------------------- */
.pmu-wa__switch{position:relative;flex:0 0 auto;align-self:center;margin-inline-start:auto;
  inline-size:46px;block-size:26px;box-sizing:border-box;border-radius:var(--r-full);
  background:color-mix(in srgb,var(--muted) 26%,var(--bg-2));
  border:1px solid var(--line-strong);
  box-shadow:inset 0 1px 2px rgba(11,27,58,.10);
  transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),
             box-shadow var(--dur) var(--ease)}
.pmu-wa__input:checked ~ .pmu-wa__switch{background:var(--wa);border-color:var(--wa-ink);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.18)}
.pmu-wa:hover .pmu-wa__switch{border-color:color-mix(in srgb,var(--wa) 55%,var(--line-strong))}
.pmu-wa.is-on:hover .pmu-wa__switch{box-shadow:inset 0 1px 2px rgba(0,0,0,.18),
  0 0 0 3px color-mix(in srgb,var(--wa) 24%,transparent)}
.pmu-wa__input:focus-visible ~ .pmu-wa__switch{outline:2px solid var(--focus-line);outline-offset:2px}
.pmu-wa__knob{position:absolute;inset-block-start:2px;inset-inline-start:2px;
  inline-size:20px;block-size:20px;border-radius:var(--r-full);background:#fff;
  box-shadow:0 1px 3px rgba(8,20,50,.34);
  transition:translate var(--dur) var(--ease-out-expo),box-shadow var(--dur) var(--ease)}
/* The knob carries a tick when ON — the switch survives greyscale. */
.pmu-wa__knob::after{content:"";position:absolute;inset-block-start:5px;inset-inline-start:7px;
  inline-size:4px;block-size:8px;border:solid var(--wa-ink);border-width:0 2px 2px 0;
  transform:rotate(45deg);opacity:0;transition:opacity var(--dur) var(--ease)}
.pmu-wa__input:checked ~ .pmu-wa__switch .pmu-wa__knob{translate:20px 0;
  box-shadow:0 1px 4px rgba(8,20,50,.42)}
.pmu-wa__input:checked ~ .pmu-wa__switch .pmu-wa__knob::after{opacity:1}
/* `translate` has no logical form — mirror the knob's travel for RTL. */
[dir="rtl"] .pmu-wa__input:checked ~ .pmu-wa__switch .pmu-wa__knob{translate:-20px 0}

@media (prefers-reduced-motion:reduce){
  .pmu-wa,.pmu-wa::before,.pmu-wa__mark,.pmu-wa__hint,.pmu-wa__state,
  .pmu-wa__switch,.pmu-wa__knob,.pmu-wa__knob::after{transition:none}
  .pmu-wa.is-flash{animation:none}
}

/* Required-field marker used by the booking form labels. */
.req{color:var(--danger-fg);font-weight:800;margin-inline-start:2px}

/* chosen-slot recap chip */
.chosen{display:flex;align-items:center;gap:8px;min-height:22px;font-size:var(--fz-sm);color:var(--text-2);font-weight:600}
.chosen .icon{width:16px;height:16px;color:var(--success-fg)}
.chosen:empty{display:none}

/* success confirmation panel (replaces the form after booking) */
.book-done{text-align:center;padding:var(--s-6) var(--s-2)}
.book-done__icon{width:64px;height:64px;border-radius:var(--r-full);display:grid;place-items:center;margin:0 auto var(--s-4);
  background:color-mix(in srgb,var(--accent-green) 14%,transparent);color:var(--success-fg);border:1px solid color-mix(in srgb,var(--accent-green) 32%,transparent)}
.book-done__icon .icon{width:32px;height:32px}

/* ============================================================
   INTERACTIVE CALENDAR — Month · Week · Day
   Ground-up rebuild. RTL-first (logical properties, no left/right).
   Reuses tokens from variables.css and the canonical
   .card/.btn/.badge/.modal/.field components. Both light + dark via
   design tokens. The appointment block keeps the soft status-tinted
   surface + status-coloured inline-start rail language.
   ============================================================ */

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* Page shell — a clean vertical rhythm between header, bar and stage. */
.cal-shell{display:flex;flex-direction:column;gap:var(--s-4)}

/* ---- Page header ---- */
.cal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-4);flex-wrap:wrap}
.cal-head__intro{min-width:0}
.cal-head__sub{margin:var(--s-1) 0 0;max-width:60ch}
.cal-head__actions{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.cal-head__actions .btn .icon{opacity:.85}

/* ---- Command bar: nav · view switch · filters ---- */
.cal-bar{display:grid;grid-template-columns:minmax(max-content,1fr) auto 1fr;align-items:center;gap:var(--s-3) var(--s-4);
  padding:var(--s-3) var(--s-4)}
.cal-bar__group{display:flex;align-items:center;gap:var(--s-2);min-width:0}
.cal-bar__nav{justify-self:start}
.cal-bar__center{justify-self:center}
.cal-bar__tools{justify-self:end;flex-wrap:wrap;justify-content:flex-end}
.cal-nav{display:inline-flex;align-items:center;gap:var(--s-1)}
.cal-nav__today{font-weight:700}
.cal-bar__range{margin:0 var(--s-1);font-size:var(--fz-lg);font-weight:800;color:var(--text);
  letter-spacing:var(--tracking-display);min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-bar__divider{inline-size:1px;block-size:26px;background:var(--line);flex:0 0 auto;margin-inline:2px}
.cal-bar__select.is-active{color:var(--brand-fg);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--brand-2) 42%,transparent);background:var(--tint)}

/* Segmented Month/Week/Day switch — the shared track (markup: "seg cal-switch").
   It used to be a second copy with the SAME square-in-a-circle defect as Inbox
   (--r-sm 10px buttons inside an --r-lg 18px track), a --surface-2 fill, a
   colour-only hover, a hard `outline:2px solid` focus and a --surface-solid /
   --shadow-sm active state without the brand colour. All shared now. */
.cal-switch__btn.is-active .icon{opacity:1}

/* Filters */
.cal-filter{min-width:0}
.cal-filter .input--sm{min-width:150px}
.cal-filter__clear{color:var(--muted);transition:color var(--dur) var(--ease)}
.cal-filter__clear:hover{color:var(--danger-fg)}

/* ---- Bulk-actions bar (select mode) ---- */
.cal-bulkbar{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  flex-wrap:wrap;padding:var(--s-2) var(--s-3);
  background:linear-gradient(180deg,color-mix(in srgb,var(--brand-2) 8%,var(--surface)),var(--surface))}
.cal-bulkbar__count{font-weight:800;color:var(--text)}
.cal-bulkbar__actions{display:flex;gap:var(--s-2);flex-wrap:wrap}

/* ---- Two-column layout: trainee rail + stage ---- */
.cal-layout{display:grid;grid-template-columns:264px 1fr;gap:var(--s-4);align-items:start}

/* ---- Trainee rail ---- */
.cal-rail{position:sticky;inset-block-start:var(--s-4);display:flex;flex-direction:column;gap:var(--s-3);
  max-height:calc(100vh - 120px)}
.cal-rail__head{display:flex;align-items:center;justify-content:space-between}
.cal-rail__title{display:flex;align-items:center;gap:7px;margin:0;font-size:var(--fz-md);font-weight:800;color:var(--text)}
.cal-rail__title .icon{width:16px;height:16px;color:var(--brand-fg)}
.cal-rail__hint{display:flex;align-items:flex-start;gap:6px;font-size:var(--fz-xs);margin:0}
.cal-rail__hint .icon{width:14px;height:14px;flex:0 0 auto;margin-block-start:2px;color:var(--brand-fg)}
.cal-rail__search{margin:0}
.cal-trainees{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;overflow-y:auto;min-height:0;flex:1}
.cal-trainees__empty{padding:var(--s-4);text-align:center}
.cal-trainee{display:flex;align-items:center;gap:var(--s-3);inline-size:100%;padding:8px 10px;
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);cursor:grab;
  text-align:start;color:var(--text);font:inherit;
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.cal-trainee:hover{border-color:var(--line-strong);box-shadow:var(--shadow-xs)}
.cal-trainee:focus-visible{outline:2px solid var(--focus-line);outline-offset:2px}
.cal-trainee[aria-selected="true"]{border-color:var(--brand-line);box-shadow:0 0 0 3px color-mix(in srgb,var(--brand-2) 22%,transparent)}
.cal-trainee.is-dragging{opacity:.5;cursor:grabbing}
.cal-trainee__avatar{width:30px;height:30px;border-radius:var(--r-full);flex:0 0 auto;display:grid;place-items:center;
  font-weight:700;font-size:var(--fz-sm);color:var(--on-brand);background:linear-gradient(135deg,var(--brand-3),var(--brand-2))}
.cal-trainee__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.cal-trainee__grip{color:var(--muted);flex:0 0 auto;display:inline-flex}
.cal-trainee__grip .icon{width:14px;height:14px}
.cal-rail__selected{display:flex;align-items:center;gap:var(--s-2);padding-block-start:var(--s-2);
  border-block-start:1px solid var(--line);flex-wrap:wrap}
.cal-rail__selected-lbl{font-size:var(--fz-xs)}

/* ---- Calendar stage ---- */
.cal-stage{position:relative;overflow:hidden}
.cal-viewport{position:relative;overflow:auto;max-height:calc(100vh - 210px)}
.cal-loading{display:flex;align-items:center;justify-content:center;gap:var(--s-2);padding:var(--s-10)}
.cal-empty{text-align:center;padding:var(--s-10) var(--s-5)}
.cal-empty .empty__icon{display:inline-grid;place-items:center;margin-block-end:var(--s-3)}

/* ============================================================
   MONTH VIEW
   ============================================================ */
.cal-month{display:flex;flex-direction:column;min-width:520px}
.cal-month__head{display:grid;grid-template-columns:repeat(7,1fr);position:sticky;inset-block-start:0;z-index:3;
  background:var(--surface);border-block-end:1px solid var(--line)}
.cal-month__dow{padding:10px var(--s-2);text-align:center;font-size:var(--fz-xs);font-weight:700;
  text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.cal-month__dow.is-off{color:color-mix(in srgb,var(--muted) 62%,transparent)}
.cal-month__grid{display:grid;grid-template-columns:repeat(7,1fr);grid-auto-rows:minmax(104px,1fr)}
.cal-mday{position:relative;display:flex;flex-direction:column;gap:4px;padding:6px;min-width:0;
  border-inline-end:1px solid var(--line);border-block-end:1px solid var(--line);
  transition:background var(--dur) var(--ease)}
.cal-mday:nth-child(7n){border-inline-end:0}
.cal-mday__num{align-self:flex-end;display:grid;place-items:center;min-inline-size:26px;block-size:26px;
  padding-inline:6px;font-size:var(--fz-sm);font-weight:700;color:var(--text-2);border-radius:var(--r-full)}
.cal-mday.is-out{background:color-mix(in srgb,var(--muted) 4%,transparent)}
.cal-mday.is-out .cal-mday__num{color:color-mix(in srgb,var(--muted) 70%,transparent)}
.cal-mday.is-today .cal-mday__num{background:var(--brand-grad);color:var(--on-brand);box-shadow:var(--glow)}
.cal-mday--closed{background:repeating-linear-gradient(45deg,transparent,transparent 7px,
  color-mix(in srgb,var(--muted) 7%,transparent) 7px,color-mix(in srgb,var(--muted) 7%,transparent) 14px)}
.cal-mday--past{background:color-mix(in srgb,var(--muted) 5%,transparent)}
.cal-mday--free{cursor:pointer}
.cal-mday--free:hover{background:color-mix(in srgb,var(--brand-2) 7%,transparent)}
.cal-mday--free:focus-visible{outline:2px solid var(--focus-line);outline-offset:-2px}
.cal-mday.is-dropzone{background:color-mix(in srgb,var(--brand-2) 16%,transparent);
  box-shadow:inset 0 0 0 2px var(--brand-2)}
.cal-mday__list{display:flex;flex-direction:column;gap:3px;min-width:0}
.cal-mday__more{align-self:flex-start;appearance:none;border:0;background:transparent;cursor:pointer;
  font:inherit;font-size:var(--fz-xs);font-weight:700;color:var(--brand-fg);padding:1px 4px;border-radius:var(--r-xs);
  transition:background var(--dur) var(--ease)}
.cal-mday__more:hover{background:var(--tint)}
.cal-mday__more:focus-visible{outline:2px solid var(--focus-line);outline-offset:1px}

/* Month appointment pill — same status-tint language as the timeline chip. */
.cal-mchip{position:relative;display:flex;align-items:center;gap:6px;inline-size:100%;padding:3px 8px 3px 12px;
  border-radius:var(--r-sm);cursor:grab;text-align:start;font:inherit;font-size:var(--fz-xs);line-height:1.25;
  color:var(--text);overflow:hidden;
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--_c,var(--brand-2)) 16%,var(--surface)),
    color-mix(in srgb,var(--_c,var(--brand-2)) 9%,var(--surface)));
  border:1px solid color-mix(in srgb,var(--_c,var(--brand-line)) 28%,var(--line));
  transition:box-shadow var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.cal-mchip::before{content:"";position:absolute;inset-block:0;inset-inline-start:0;inline-size:3px;background:var(--_c,var(--brand-2))}
.cal-mchip:hover{border-color:color-mix(in srgb,var(--_c,var(--brand-line)) 46%,var(--line));
  box-shadow:0 3px 10px color-mix(in srgb,var(--_c,var(--brand-2)) 20%,transparent)}
.cal-mchip:focus-visible{outline:2px solid var(--_c,var(--focus-line));outline-offset:1px}
.cal-mchip.is-dragging{opacity:.55;cursor:grabbing}
.cal-mchip.is-optimistic{opacity:.7}
.cal-mchip__time{font-weight:700;font-variant-numeric:tabular-nums;flex:0 0 auto;unicode-bidi:isolate}
.cal-mchip__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-2)}
.cal-mchip--booked{--_c:var(--accent-amber)}
.cal-mchip--confirmed{--_c:var(--brand-2)}
.cal-mchip--completed{--_c:var(--accent-green)}
.cal-mchip--no_show{--_c:var(--accent-red)}
.cal-mchip--cancelled{--_c:var(--muted)}
.cal-mchip--requested{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent-amber) 55%,transparent)}
.cal-mchip.is-selectable{cursor:pointer}
.cal-mchip.is-selected{outline:2px solid var(--focus-line);outline-offset:1px;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand-2) 26%,transparent)}

/* ============================================================
   WEEK / DAY TIMELINE
   ============================================================ */
.cal-week{position:relative;display:grid;
  grid-template-columns:64px repeat(var(--cal-cols,7),minmax(96px,1fr));min-width:max-content;--cal-row-h:44px}
.cal-week--day{grid-template-columns:64px minmax(260px,1fr)}

.cal-colhead{position:sticky;inset-block-start:0;z-index:3;background:var(--surface);
  border-block-end:1px solid var(--line);padding:var(--s-2);text-align:center;font-size:var(--fz-xs)}
.cal-colhead--gutter{background:var(--surface);border-inline-end:1px solid var(--line)}
.cal-colhead__dow{font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.cal-colhead__date{font-weight:800;font-size:var(--fz-md);color:var(--text);font-variant-numeric:tabular-nums}
.cal-colhead.is-today .cal-colhead__date{color:var(--brand-fg)}
.cal-colhead.is-today{box-shadow:inset 0 -2px 0 var(--brand-2)}

.cal-time{position:sticky;inset-inline-start:0;z-index:2;background:var(--surface);
  border-block-end:1px solid var(--line);border-inline-end:1px solid var(--line);padding:2px 6px;
  font-size:var(--fz-xs);color:var(--muted);text-align:end;min-height:44px;
  display:flex;align-items:flex-start;justify-content:flex-end;font-variant-numeric:tabular-nums}
.cal-time span{transform:translateY(-6px)}

.cal-slot{border-block-end:1px solid var(--line);border-inline-end:1px solid var(--line);min-height:44px;
  position:relative;padding:2px;transition:background var(--dur) var(--ease)}
.cal-slot--free{cursor:pointer}
.cal-slot--free:hover,.cal-slot--free:focus-visible{background:color-mix(in srgb,var(--brand-2) 8%,transparent);outline:none}
.cal-slot--free:focus-visible{box-shadow:inset 0 0 0 2px var(--brand-2)}
.cal-slot--closed{background:repeating-linear-gradient(45deg,transparent,transparent 6px,
  color-mix(in srgb,var(--muted) 8%,transparent) 6px,color-mix(in srgb,var(--muted) 8%,transparent) 12px)}
.cal-slot--past{background:color-mix(in srgb,var(--muted) 6%,transparent);cursor:not-allowed}
.cal-slot.is-dropzone{background:color-mix(in srgb,var(--brand-2) 18%,transparent);box-shadow:inset 0 0 0 2px var(--brand-2)}

/* Appointment chip — soft status-tinted surface + status-coloured inline-start
   rail. Height is proportional to duration via --_span (durationMin/stepMinutes).
   Text uses theme --text/--text-2 for high contrast in light + dark. */
.cal-appt{position:absolute;inset-inline:3px;inset-block-start:2px;z-index:1;border-radius:var(--r);
  block-size:calc(var(--_span,1) * var(--cal-row-h,44px) - 4px);min-block-size:22px;
  padding-block:5px;padding-inline:12px 10px;font-size:var(--fz-xs);line-height:1.3;color:var(--text);
  overflow:hidden;cursor:grab;
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--_c,var(--brand-2)) 16%,var(--surface)),
    color-mix(in srgb,var(--_c,var(--brand-2)) 8%,var(--surface)));
  border:1px solid color-mix(in srgb,var(--_c,var(--brand-line)) 30%,var(--line));
  box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:1px;
  transition:box-shadow var(--dur) var(--ease),border-color var(--dur) var(--ease)}
.cal-appt::before{content:"";position:absolute;inset-block:0;inset-inline-start:0;inline-size:3px;background:var(--_c,var(--brand-2))}
.cal-appt__range{font-variant-numeric:tabular-nums;unicode-bidi:isolate}
.cal-appt:hover{
  box-shadow:var(--shadow-sm),0 4px 12px color-mix(in srgb,var(--_c,var(--brand-2)) 22%,transparent);
  border-color:color-mix(in srgb,var(--_c,var(--brand-line)) 46%,var(--line))}
.cal-appt:focus-visible{outline:2px solid var(--_c,var(--focus-line));outline-offset:2px}
.cal-appt.is-dragging{opacity:.55;cursor:grabbing}
.cal-appt.is-optimistic{opacity:.7}
.cal-appt__name{font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-appt__meta{color:var(--text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-appt--booked{--_c:var(--accent-amber)}
.cal-appt--confirmed{--_c:var(--brand-2)}
.cal-appt--completed{--_c:var(--accent-green)}
.cal-appt--no_show{--_c:var(--accent-red)}
.cal-appt--cancelled{--_c:var(--muted)}

/* SHORT appointment (a squeezed 15–30 min slot): a compact single-line chip that
   expands to full height on hover / focus, overlapping the rows beneath it. */
.cal-appt--short{flex-direction:row;align-items:center;gap:6px;padding-block:0;min-block-size:16px;line-height:1.15}
.cal-appt--short .cal-appt__name{flex:0 1 auto;font-size:11px}
.cal-appt--short .cal-appt__meta{flex:1 1 auto;font-size:10px;color:var(--muted)}
.cal-appt--short:hover,.cal-appt--short:focus-visible,.cal-appt--short:focus-within{
  z-index:6;block-size:auto;min-block-size:calc(var(--cal-row-h,44px) - 4px);
  flex-direction:column;align-items:stretch;gap:1px;padding-block:5px}
.cal-appt--short:hover .cal-appt__name,.cal-appt--short:focus-visible .cal-appt__name,.cal-appt--short:focus-within .cal-appt__name{font-size:var(--fz-xs)}
.cal-appt--short:hover .cal-appt__meta,.cal-appt--short:focus-visible .cal-appt__meta,.cal-appt--short:focus-within .cal-appt__meta{font-size:var(--fz-xs);white-space:normal}

/* Selectable / selected chips (bulk mode) + cancellation-request flag. */
.cal-appt.is-selectable{cursor:pointer}
.cal-appt.is-selected{outline:2px solid var(--focus-line);outline-offset:1px;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand-2) 26%,transparent)}
.cal-appt--requested{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent-amber) 60%,transparent)}
.cal-appt__flag{position:absolute;inset-block-start:2px;inset-inline-end:2px;width:16px;height:16px;
  border-radius:var(--r-full);display:grid;place-items:center;font-size:11px;font-weight:800;line-height:1;
  color:#fff;background:var(--accent-amber)}

/* Current-time indicator: a thin line across TODAY's column + a leading dot. */
.cal-nowline{position:absolute;height:0;border-top:2px solid var(--accent-red);z-index:2;pointer-events:none}
.cal-nowline__dot{position:absolute;inset-inline-start:0;inset-block-start:-4px;inline-size:8px;block-size:8px;
  border-radius:var(--r-full);background:var(--accent-red);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent-red) 24%,transparent)}

/* Detail-modal cancellation request banner (reuses .alert--warn). */
.cal-appt-request{margin-block-end:var(--s-4)}

/* ============================================================
   WORKING-HOURS DAY PICKER (settings + break editor)
   ============================================================ */
.cal-daypick{display:flex;flex-wrap:wrap;gap:6px}
.cal-daypick__opt{cursor:pointer}
.cal-daypick__opt input{position:absolute;opacity:0;width:1px;height:1px}
.cal-daypick__chip{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  min-inline-size:46px;block-size:38px;padding-inline:14px;border:1px solid var(--line);border-radius:var(--r-full);
  background:var(--bg-3);font-weight:600;font-size:var(--fz-sm);color:var(--text-2);
  transition:background var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
.cal-daypick__opt:hover .cal-daypick__chip{border-color:var(--line-brand);background:var(--tint);color:var(--text)}
.cal-daypick__opt input:checked + .cal-daypick__chip{background:linear-gradient(180deg,var(--blue-500),var(--brand-2));
  border-color:var(--brand-line);color:var(--on-brand);box-shadow:var(--glass-hi-soft),var(--shadow-xs)}
.cal-daypick__opt input:focus-visible + .cal-daypick__chip{outline:2px solid var(--focus-line);outline-offset:2px}
.field--span2{grid-column:1 / -1}

/* ============================================================
   MODALS (sizes + calendar-specific bits)
   ============================================================ */
.modal__panel--sm{max-width:420px;text-align:center}
.modal__panel--wide{max-width:660px}
.cal-appt-sub{margin-block-start:0}
.cal-appt-foot__cancel{margin-inline-end:auto}

/* Searchable client picker inside the appointment modal (in-flow, never clipped). */
.cal-clientpick{position:relative}
.cal-clientpick__list{margin:4px 0 0;padding:6px;list-style:none;max-height:220px;overflow:auto;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-xs)}
.cal-clientpick__list[hidden]{display:none}
.cal-clientpick__item{display:flex;align-items:center;gap:var(--s-2);padding:8px;border-radius:var(--r);cursor:pointer;
  transition:background var(--dur) var(--ease)}
.cal-clientpick__item:hover,.cal-clientpick__item:focus-visible{background:var(--tint);outline:none}
.cal-clientpick__avatar{inline-size:28px;block-size:28px;border-radius:var(--r-full);flex:0 0 auto;display:grid;place-items:center;
  font-weight:700;font-size:var(--fz-xs);color:var(--on-brand);background:linear-gradient(135deg,var(--brand-3),var(--brand-2))}
.cal-clientpick__meta{display:flex;flex-direction:column;min-width:0}
.cal-clientpick__name{font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-clientpick__sub{font-size:var(--fz-xs);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-clientpick__empty{padding:10px 8px;color:var(--muted);font-size:var(--fz-sm);line-height:1.4}

/* Past-slot illustration */
.cal-past-illo{width:64px;height:64px;border-radius:var(--r-full);display:grid;place-items:center;margin:0 auto var(--s-4);
  background:color-mix(in srgb,var(--accent-amber) 16%,transparent);color:var(--warn-fg);
  border:1px solid color-mix(in srgb,var(--accent-amber) 34%,transparent)}
.cal-past-illo .icon{width:30px;height:30px}

/* Meeting-types editor list */
.mt-list{list-style:none;margin:0 0 var(--s-3);padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.mt-row{display:grid;grid-template-columns:1fr 160px auto;align-items:center;gap:var(--s-2);
  padding:var(--s-2);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.mt-row__name{min-width:0}
.mt-row__kind{min-width:0}
.mt-row__controls{display:flex;align-items:center;gap:var(--s-1);flex-wrap:wrap;justify-content:flex-end}
.mt-row__default{font-size:var(--fz-xs);white-space:nowrap}
.mt-row__default.is-default{color:var(--brand-fg);font-weight:700;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--brand-2) 40%,transparent)}
.mt-row__rm{color:var(--danger-fg)}
.mt-add{margin-block-end:var(--s-3)}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:1024px){
  .cal-bar{grid-template-columns:auto 1fr;row-gap:var(--s-3)}
  .cal-bar__nav{justify-self:start}
  .cal-bar__tools{justify-self:end}
  .cal-bar__center{grid-column:1 / -1;justify-self:stretch;order:3}
  /* The full-width equal-split that used to live here is GONE. It was the last
     surviving per-module narrow-screen strategy for a member of the segmented
     family, and it contradicted both the family and itself: the shared bars all
     scroll their track and keep the buttons at intrinsic width, and below 720px
     the family's own `:is(.segmented,.seg) button{flex:0 0 auto}` (specificity
     0,1,1) out-ranked `.cal-switch__btn` (0,1,0) anyway — so the buttons
     stretched between 721 and 1024px and then snapped back. The track now
     shrink-wraps and start-aligns in the stretched cell, exactly like
     .sched-appts__seg and .an-range. */
}
@media (max-width:900px){
  .cal-layout{grid-template-columns:1fr}
  .cal-rail{position:static;max-height:none}
  .cal-trainees{max-height:280px}
}
@media (max-width:640px){
  .cal-head__actlabel{display:none}
  .cal-bar{grid-template-columns:1fr}
  .cal-bar__nav,.cal-bar__tools{justify-self:stretch;justify-content:space-between;width:100%}
  .cal-bar__range{font-size:var(--fz-md)}
  .cal-filter .input--sm{min-width:0;flex:1}
  .mt-row{grid-template-columns:1fr;gap:var(--s-2)}
  .mt-row__controls{justify-content:flex-start}
}

/* ---- Trainee "my appointments" panel (public booking page) ---- */
.my-appts{margin-block:var(--s-5) var(--s-6);padding:var(--s-5)}
.my-appts__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-3);
  flex-wrap:wrap;margin-block-end:var(--s-4)}
.my-appts__actions{display:flex;gap:var(--s-2);flex-wrap:wrap}
.my-appts__list{display:flex;flex-direction:column;gap:var(--s-5)}
.my-appts__section-title{font-size:var(--fz-sm);color:var(--muted);text-transform:uppercase;
  letter-spacing:.04em;margin:0 0 var(--s-2)}
.my-appts__rows{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.my-appt{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);
  border-inline-start:3px solid var(--_c,var(--brand-line))}
.my-appt__body{flex:1;min-width:0}
.my-appt__title{font-weight:700;color:var(--text)}
.my-appt__meta{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;margin-block-start:2px}
.my-appt__when{font-size:var(--fz-sm);color:var(--muted)}
.my-appt__mt{font-size:var(--fz-xs)}
.my-appt__check{inline-size:18px;block-size:18px;flex:none}
.my-appt__cancel{flex:none}
.my-appts__empty,.my-appts__loading{color:var(--muted);padding:var(--s-3) 0}

/* ============================================================
   AVAILABILITY UI — duration hint, buffer explanation, breaks builder.
   Lives in the working-hours settings modal. RTL-first (logical props).
   ============================================================ */

/* Plain-language hint under a field (duration / buffer). */
.cal-hint{display:flex;align-items:flex-start;gap:6px;font-size:var(--fz-xs);
  margin:var(--s-2) 0 0;line-height:1.45}
.cal-hint .icon{width:14px;height:14px;flex:0 0 auto;margin-block-start:2px;color:var(--brand-fg)}

/* Live "N clients an hour" recap chip for the default meeting length. */
.cal-rate{display:inline-flex;align-items:center;gap:6px;margin:var(--s-2) 0 0;
  padding:4px 10px;border-radius:var(--r-full);font-size:var(--fz-xs);font-weight:700;
  color:var(--brand-fg);background:color-mix(in srgb,var(--brand-2) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--brand-line) 26%,transparent);width:max-content}
.cal-rate[hidden]{display:none}

/* Breaks / blocked-ranges section (inside the settings modal). */
.cal-breaks__list{list-style:none;margin:var(--s-3) 0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.cal-breaks__list:empty{margin:0}
.cal-breaks__empty{font-size:var(--fz-sm);margin:var(--s-2) 0}
.cal-breaks__add{margin-block-start:var(--s-1)}

.cal-break-item{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-2) var(--s-3);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.cal-break-item__body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.cal-break-item__label{font-weight:700;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-break-item__meta{font-size:var(--fz-xs);color:var(--muted);font-variant-numeric:tabular-nums}
.cal-break-item__controls{display:flex;align-items:center;gap:var(--s-1);flex:0 0 auto}
.cal-break-item__rm{color:var(--danger-fg)}

/* Segmented recurring / one-off toggle — the shared track (markup: "seg cal-seg").
   Its old .is-active filled with var(--brand-grad) + var(--glow): the loudest
   active state in the family, and it SNAPPED, because background-image cannot
   interpolate and this control was never registered in motion-standard.css's
   gradient cross-fade layer. It now uses the family's recessed brand pill,
   which fades. Nothing local left to declare. */

/* "Every day" / "All day" checkbox rows in the break editor. */
.cal-break-everyday{display:flex;align-items:center;gap:var(--s-2);margin:var(--s-2) 0;
  font-size:var(--fz-sm);color:var(--text-2);cursor:pointer}

/* Disabled day chips when "Every day" is on. */
.cal-daypick.is-disabled{opacity:.5;pointer-events:none}

/* A break window rendered on the calendar grid: a distinct closed cell that
   also shows the break label, so the trainer sees WHY the slot is not bookable. */
.cal-slot--break{background:repeating-linear-gradient(45deg,
    color-mix(in srgb,var(--accent-amber) 12%,transparent),
    color-mix(in srgb,var(--accent-amber) 12%,transparent) 6px,
    color-mix(in srgb,var(--accent-amber) 20%,transparent) 6px,
    color-mix(in srgb,var(--accent-amber) 20%,transparent) 12px)}
.cal-slot__break{position:absolute;inset:2px;display:flex;align-items:center;justify-content:center;
  font-size:10px;font-weight:700;letter-spacing:.02em;text-align:center;line-height:1.1;
  color:color-mix(in srgb,var(--warn-fg) 82%,var(--text));
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 4px;pointer-events:none}

/* Uses the canonical .spinner from components.css (do not redefine).
   Only relax calendar-specific transitions under reduced motion. */
/* .cal-seg__opt was dropped from this list: it is a shared segment button now,
   and the family keeps its colour fade under reduced motion (just shorter) —
   killing the transition outright was against the house rule. */
@media (prefers-reduced-motion:reduce){
  .cal-appt,.cal-trainee,.svc-item{transition:none}
}

/* ==========================================================================
   BOOKING — RICHER HOVER / FOCUS AFFORDANCES
   --------------------------------------------------------------------------
   The booking controls each had a single flat border swap on hover, which read
   as "nothing happened" next to the rest of the CMS. These add a consistent,
   legible affordance to every interactive surface on the flow.

   Deliberately NO transform: components.css establishes that nothing MOVES on
   hover anywhere in the app — colour, border and a soft glow are the entire
   signal. Timing/easing is owned by motion-standard.css (base-rule transitions),
   so these rules only change VALUES and therefore fade in and out symmetrically.
   ========================================================================== */

/* ---- Meeting-type cards ("איך ניפגש?") ---------------------------------- */
.pmu-mt__opt:hover .pmu-mt__card{
  border-color:color-mix(in srgb,var(--brand-line) 58%,var(--line-strong));
  background:color-mix(in srgb,var(--brand-2) 9%,var(--surface));
  box-shadow:0 2px 12px -6px color-mix(in srgb,var(--brand-2) 62%,transparent);
}
.pmu-mt__opt:hover .pmu-mt__ico{color:var(--brand-fg)}
/* The chosen option stays authoritative under the pointer. */
.pmu-mt__input:checked + .pmu-mt__card,
.pmu-mt__opt:hover .pmu-mt__input:checked + .pmu-mt__card{
  box-shadow:
    0 1px 0 rgba(255,255,255,.22) inset,
    0 4px 16px -6px color-mix(in srgb,var(--brand-2) 60%,transparent);
}

/* ---- WhatsApp opt-in ----------------------------------------------------- */
/* Keeps WhatsApp's own green identity rather than borrowing the brand blue.
   The OFF row gets a light green invitation; the ON row (.is-on, above) is
   already highlighted, so hover only deepens what is there. Both directions
   fade because the values live on .pmu-wa's base transition list. */
.pmu-wa:hover{
  border-color:color-mix(in srgb,var(--wa) 46%,var(--line));
  background:color-mix(in srgb,var(--wa) 6%,var(--surface-2));
  box-shadow:0 2px 12px -6px color-mix(in srgb,var(--wa) 50%,transparent);
}
.pmu-wa:hover .pmu-wa__mark{color:var(--wa-mark)}
.pmu-wa.is-on:hover{
  border-color:color-mix(in srgb,var(--wa) 76%,var(--line));
  background:color-mix(in srgb,var(--wa) 14%,var(--surface-2));
  box-shadow:0 4px 20px -8px color-mix(in srgb,var(--wa) 82%,transparent);
}
/* :focus-within lands on the label because the real checkbox lives inside it.
   Kept to a TIGHT ring — the switch already draws its own focus outline. */
.pmu-wa:focus-within{
  border-color:color-mix(in srgb,var(--wa) 68%,var(--line));
  box-shadow:0 0 0 3px color-mix(in srgb,var(--wa) 20%,transparent);
}

/* ---- TEXT FIELDS ON THE BOOKING FLOW -------------------------------------
   The focused field used to stack TWO halos: the global --focus-ring (a 4px,
   18%-alpha wash on the .input itself) plus a second 3px shadow drawn on the
   .input-group WRAPPER — which is an unrounded, borderless flex box, so that
   second ring rendered as a big square smear around a rounded input. It read as
   a thick, washed-out blur.

   The replacement is one crisp 1px brand edge + one tight, low-spread ring, on
   the INPUT itself (the element that actually has the border-radius). The
   wrapper draws nothing; it only tints its leading glyph so the whole group
   still reacts. Identical expression in light and dark: --focus-line and
   --brand-2 are both theme-resolved. */
.book-wrap .input,
.book-wrap .textarea,
.book-wrap select.input,
.book-wrap .pmu-dp__trigger{
  transition:border-color var(--dur) var(--ease),box-shadow var(--dur) var(--ease),
             background var(--dur) var(--ease);
}
.book-wrap .input:hover,
.book-wrap .textarea:hover,
.book-wrap select.input:hover,
.book-wrap .pmu-dp__trigger:hover{
  border-color:color-mix(in srgb,var(--brand-line) 52%,var(--line-control));
}
.book-wrap .input:focus,
.book-wrap .input:focus-visible,
.book-wrap .textarea:focus,
.book-wrap select.input:focus,
.book-wrap .pmu-dp__trigger:focus-visible{
  outline:none;
  border-color:var(--focus-line);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand-2) 18%,transparent);
}
/* The wrapper itself paints NOTHING — no second halo, ever. */
.book-wrap .input-group{border:0;box-shadow:none}
.book-wrap .input-group:focus-within{box-shadow:none}
.book-wrap .input-group .icon{transition:color var(--dur) var(--ease)}
.book-wrap .input-group:hover .icon{color:var(--text-2)}
.book-wrap .input-group:focus-within .icon{color:var(--brand-fg)}
/* An invalid field keeps the same geometry, only the hue changes. */
.book-wrap .field--error .input:focus,
.book-wrap .field--error .textarea:focus{
  border-color:var(--danger-fg);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--danger-fg) 18%,transparent);
}
@media (prefers-reduced-motion:reduce){
  .book-wrap .input,.book-wrap .textarea,.book-wrap select.input,
  .book-wrap .pmu-dp__trigger,.book-wrap .input-group .icon{transition:none}
}

/* ---- Service cards on the picker step ------------------------------------ */
.svc-card:hover .svc-card__go{
  background:color-mix(in srgb,var(--brand-2) 12%,transparent);
  border-color:color-mix(in srgb,var(--brand-line) 45%,transparent);
}

/* ============================================================
   BOOKING REVIEW MODAL (.bkc)
   ------------------------------------------------------------
   The last gate before an appointment is created. Pressing "Confirm booking"
   no longer POSTs — it opens THIS dialog with a read-back of everything the
   visitor chose, and only its own confirm button books.

   IT IS NOT A NEW DIALOG PRIMITIVE. The markup is the canonical .modal family
   (.modal > .modal__backdrop + .modal__panel), so core/components.js keeps
   owning the backdrop, the Tab trap, Escape, focus restore to the trigger and
   the page scroll lock. Everything below is PAINT on top of that: a hero for
   the date/time, grouped read-back rows, and the WhatsApp identity chip.

   MOTION: the staggered entrance rides ONE utility (.bkc__fx) whose transition
   is declared on the BASE rule with the full property list, so the rows fade
   and settle IN when .is-ready lands and fade back OUT symmetrically when it is
   removed — never a pop, and nothing animates from display:none. The per-row
   delay is a custom property (--i) set by booking.js, and the whole effect is
   dropped under prefers-reduced-motion.
   ============================================================ */
.bkc__panel{max-width:620px;padding:var(--s-8) var(--s-6) var(--s-6)}

/* ---- Head: seal + title. Reserve the inline-end gutter for .modal__close. -- */
.bkc__head{display:flex;align-items:flex-start;gap:var(--s-4);
  margin-bottom:var(--s-5);padding-inline-end:var(--s-8)}
.bkc__seal{flex:0 0 auto;inline-size:44px;block-size:44px;border-radius:var(--r-full);
  display:grid;place-items:center;background:var(--brand-grad);color:var(--on-brand);
  box-shadow:var(--glow)}
.bkc__seal .icon{inline-size:24px;block-size:24px}
.bkc__head-txt{min-inline-size:0}
.bkc__title{margin:0 0 3px;font-size:var(--fz-lg);letter-spacing:var(--tracking-display)}
.bkc__sub{margin:0;font-size:var(--fz-sm);color:var(--muted);line-height:1.5}

/* ---- Hero: the date + time, the single most important fact here ----------- */
.bkc__hero{position:relative;isolation:isolate;overflow:hidden;
  display:flex;align-items:center;gap:var(--s-4);
  padding:var(--s-4) var(--s-5);border-radius:var(--r-lg);
  border:1px solid var(--line-strong);background:var(--surface-2)}
/* A gradient wash cannot interpolate, so it rides its own static layer. */
.bkc__hero::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:var(--brand-grad-soft)}
.bkc__cal{flex:0 0 auto;inline-size:58px;padding:7px 0 9px;text-align:center;
  border-radius:var(--r-lg);background:var(--surface-solid);
  border:1px solid var(--line-strong);box-shadow:var(--shadow-sm)}
/* Hebrew has no abbreviated month names, so Intl returns the full word here
   ("ספטמבר"). Clamp it to the tile instead of letting it wrap the day number. */
.bkc__cal-mon{display:block;padding-inline:5px;font-size:10px;font-weight:800;line-height:1.2;
  letter-spacing:.06em;text-transform:uppercase;color:var(--brand-fg);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bkc__cal-day{display:block;font-size:26px;font-weight:800;line-height:1.15;
  color:var(--text);font-variant-numeric:tabular-nums}
.bkc__hero-txt{display:flex;flex-direction:column;gap:2px;min-inline-size:0}
.bkc__hero-date{font-size:var(--fz-sm);font-weight:700;color:var(--text-2)}
.bkc__hero-time{font-size:var(--fz-xl);font-weight:800;line-height:1.15;color:var(--text);
  letter-spacing:var(--tracking-display);font-variant-numeric:tabular-nums}
.bkc__hero-sub{font-size:var(--fz-xs);color:var(--muted);line-height:1.45}

/* ---- Grouped read-back rows ---------------------------------------------- */
.bkc__group{margin-top:var(--s-5)}
.bkc__group-title{margin:0 0 var(--s-2);font-size:var(--fz-xs);font-weight:800;
  letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}
.bkc__rows{margin:0;display:flex;flex-direction:column;gap:3px}
.bkc__row{display:flex;align-items:flex-start;gap:var(--s-3);
  padding:8px var(--s-3);border-radius:12px;background:var(--surface-2);
  border:1px solid var(--line)}
/* The icon sits INSIDE the label, on the same line as its text. */
.bkc__label{flex:0 0 auto;min-inline-size:138px;margin:0;
  display:inline-flex;align-items:center;gap:8px;
  font-size:var(--fz-xs);font-weight:700;line-height:1.5;color:var(--muted)}
.bkc__ico{flex:0 0 auto;display:inline-flex;color:var(--brand-fg)}
.bkc__ico svg{inline-size:16px;block-size:16px}
.bkc__value{flex:1 1 190px;min-inline-size:0;margin:0;
  font-size:var(--fz-sm);font-weight:600;line-height:1.5;color:var(--text);
  overflow-wrap:anywhere}
.bkc__value--notes{white-space:pre-wrap;font-weight:500;color:var(--text-2)}
/* Phone numbers, e-mail addresses, URLs and clock ranges are LTR runs. They are
   wrapped in <bdi dir="ltr">, which isolates them WITHOUT flipping the row's own
   alignment — the label/value pair stays right-aligned on a Hebrew page. */
.bkc__value bdi,.bkc__hero-time bdi{unicode-bidi:isolate}
@media (max-width:520px){
  .bkc__row{flex-direction:column;gap:3px}
  .bkc__label{min-inline-size:0}
  .bkc__hero{gap:var(--s-3);padding:var(--s-4)}
}

/* ---- WhatsApp identity chip ---------------------------------------------- */
/* Same out-of-system brand pigments as .pmu-wa (see the block above): the mark
   keeps WhatsApp's own green, and it is never the ONLY signal — the state is
   also spelled out in the chip's words. */
.bkc__wa{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;
  border-radius:var(--r-full);font-size:var(--fz-xs);font-weight:800;
  border:1px solid var(--line-strong);background:var(--bg-3);color:var(--text-2)}
.bkc__wa svg{inline-size:15px;block-size:15px;flex:0 0 auto}
.bkc__wa.is-on{border-color:color-mix(in srgb,var(--wa) 55%,var(--line));
  background:color-mix(in srgb,var(--wa) 12%,var(--surface-2));color:var(--wa-fg);
  box-shadow:0 2px 12px -8px color-mix(in srgb,var(--wa) 75%,transparent)}
.bkc__wa.is-on svg{color:var(--wa-mark)}

/* ---- Foot ---------------------------------------------------------------- */
.bkc__error{margin-top:var(--s-4);margin-bottom:0}
.bkc__foot{margin-top:var(--s-6);flex-wrap:wrap}
.bkc__foot .btn{flex:1 1 170px}
/* Busy state: aria-disabled (not :disabled) so the spinner keeps full contrast
   instead of inheriting the .btn:disabled 50% wash. booking.js also guards the
   handler, so a keyboard Enter cannot double-submit either. */
.bkc__btn[aria-disabled="true"]{pointer-events:none}
/* .modal__close is not a .btn, so it needs its own inert treatment while the
   POST is in flight (booking.js disables it for the duration). */
.bkc .modal__close:disabled{opacity:.5;pointer-events:none}
/* The "go back" chevron points at the form, so it mirrors with the writing mode. */
[dir="rtl"] .bkc__btn--back .icon{transform:scaleX(-1)}
.bkc__btn--go[aria-busy="true"]{opacity:1}
.bkc__note{margin:var(--s-3) 0 0;display:flex;align-items:center;justify-content:center;
  gap:6px;text-align:center;font-size:var(--fz-xs);color:var(--muted)}
.bkc__note .icon{inline-size:14px;block-size:14px;flex:0 0 auto}

/* ---- Staggered entrance --------------------------------------------------- */
.bkc__fx{opacity:0;transform:translateY(6px);
  transition:opacity var(--dur-slow) var(--ease),transform var(--dur-slow) var(--ease);
  transition-delay:calc(var(--i,0) * 34ms)}
.bkc.is-ready .bkc__fx{opacity:1;transform:none}

@media (prefers-reduced-motion:reduce){
  .bkc__fx{opacity:1;transform:none;transition:none;transition-delay:0s}
  .bkc__wa,.bkc__row{transition:none}
}

/* ============================================================
   CANCEL OR MOVE — the dedicated surface (#cal-cancel-modal, the appointments
   list's #appt-cancel-modal / #appt-reschedule-modal, and the trainee's
   #my-cancel-modal all share these three pieces).

   Deliberately SMALL. Everything structural is already shared and is reused
   verbatim, never forked:
     * the two-mode toggle is .seg / .seg__btn / .seg--full from components.css;
     * the hour grid is .pmu-timegrid / .pmu-timeslot documented above;
     * the fades belong to core/overlay-fx.js.
   What is genuinely new is only: the "this is the hour you are moving away
   from" line, the chip variant that marks that hour inside the grid, and the
   server-driven "what the trainee will get" block.

   Tokens only (no theme hex, no [data-theme] patch), logical properties so RTL
   mirrors for free, and no decorative hover movement anywhere.
   ============================================================ */

/* The current time of the session being changed. Not interactive — it is
   context, so it reads as quiet metadata rather than a control. */
.cal-cx__current{margin:0 0 var(--s-4);font-size:var(--fz-sm);color:var(--text-2);
  font-variant-numeric:tabular-nums}
.cal-cx__current:empty{display:none}

/* THE APPOINTMENT'S OWN HOUR inside the grid. The server excludes the row from
   its own collision set, so this hour comes back as bookable — but taking it is
   a no-op move. It is therefore SHOWN (hiding it would read as a missing hour)
   and marked as "this session": dashed edge + a muted, un-takeable look, with
   the reason in the tooltip and the accessible name (never colour alone). */
.pmu-timeslot--current{
  border-style:dashed;border-color:var(--brand-fg);
  background:color-mix(in srgb,var(--brand-fill) 8%,transparent);
  color:var(--text-2);
}
.pmu-timeslot--current[aria-disabled="true"]{opacity:1}

/* WHAT THE TRAINEE WILL GET. Populated from SchedulingService::
   cancellationChannels() — only channels that will really fire — so this block
   never states a promise the server did not make. When nothing automatic can
   reach the person it degrades to the manual tel:/WhatsApp link, which is why
   list items may contain anchors. */
.cal-cx__notify{margin:var(--s-4) 0 var(--s-3);padding:var(--s-3) var(--s-4);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2)}
.cal-cx__notify .label{display:flex;align-items:center;gap:6px}
.cal-cx__notify .icon{color:var(--brand-fg)}
.cal-cx__notify-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.cal-cx__notify-list li{position:relative;padding-inline-start:14px;
  font-size:var(--fz-sm);color:var(--text-2)}
/* A small logical-inset marker, so the bullet sits on the correct side in RTL. */
.cal-cx__notify-list li::before{content:"";position:absolute;inset-block-start:.62em;
  inset-inline-start:0;inline-size:5px;block-size:5px;border-radius:var(--r-full);
  background:var(--brand-fg)}
.cal-cx__notify-list a{color:var(--brand-fg);text-decoration:underline;text-underline-offset:2px}

/* ============================================================
   CALENDAR LANDING (0290) — two small pieces, nothing structural.

   1) .cal-landnote  the inline "why am I not looking at today" note that sits
      beside the range label in the command bar. It only ever toggles [hidden]:
      it never fades IN, so there is nothing for overlay-fx to fade OUT and no new
      open/close logic exists for it.
   2) .cal-landing   the personal preference block, LAST in the working-hours
      modal under a divider. The control itself is the shared .seg / .seg__btn /
      .seg--full family from components.css, used verbatim — nothing about the
      segmented control is redefined here.

   Tokens only, logical properties throughout (so RTL mirrors for free), and hover
   is a background/colour/shadow fade with NO movement.
   ============================================================ */

/* The note. Quiet metadata, not an alert: it explains a deliberate, harmless jump
   and must not shout.
   IT OWNS A ROW of the .cal-bar grid (grid-column:1/-1) rather than sitting inside
   the nav group. Inline, its text sized the grid's first column — measured at 995px
   against a 1500px viewport, which starved the third column to 0px and left the
   tools group painted over the Month/Week/Day switcher. A full row cannot squeeze a
   sibling, whatever the message length or the locale. */
.cal-landnote{grid-column:1 / -1;justify-self:start;
  display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  margin:0;min-inline-size:0;font-size:var(--fz-xs);color:var(--text-2);line-height:1.45}
.cal-landnote[hidden]{display:none}
.cal-landnote .icon{inline-size:14px;block-size:14px;flex:0 0 auto;color:var(--brand-fg)}
/* The "Change" affordance. Colour + background fade only — no transform, no shift. */
.cal-landnote__act{font-size:var(--fz-xs);padding-block:2px;padding-inline:var(--s-2)}
.cal-landnote__act:hover{color:var(--brand-fg);background:var(--tint)}

/* PERSONAL, and visibly separate from the tenant-wide settings above it: the
   divider is the whole point of the placement decision (business settings first,
   a personal display choice last, clearly fenced off). */
.cal-landing{margin:var(--s-5) 0 0;padding:var(--s-4) 0 0;border:0;
  border-block-start:1px solid var(--line)}
.cal-landing__legend{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;padding:0}
/* "Only affects you" — a plain badge, deliberately not success/danger coloured:
   it states a scope, not a state. */
.cal-landing__badge{padding-block:3px;padding-inline:9px;font-size:10px;font-weight:700;
  text-transform:none;letter-spacing:.02em}
.cal-landing__seg{margin-block:var(--s-3) 0}
