/* ============================================================
   slotly — app-specific components.
   Loaded after app.css. Uses the tokens defined there, and overrides
   the accent ones (section 0) to slotly's own teal.
   Solid fills only: no gradients, no blur, no glow.
   0 accent · 1 layout bits · 2 queue · 3 calendar · 4 slot picker
   5 staff + hours · 6 templates · 7 drawer detail · 8 booking page
   9 responsive
   ============================================================ */

/* == 0. accent ==
   slotly is teal, not the kit's yellow. The token NAMES stay --amber-* so
   nothing else in the app or the kit has to change; only the values move.
   The structure is the kit's, unchanged: --amber-fill is the FILL, the two
   --deep/--darker shades are the readable text-on-white pair, --soft/--line
   are the tint and hairline for selected states, --on-amber is what sits on
   the fill.

   The one thing that does change is the polarity. #EAC81C is a light ground
   that carries ink; #0B7D74 is a dark ground that carries white, so every
   place the kit paired ink with the fill is flipped below and in 7b/7e/7f.

   Contrast, measured not guessed (WCAG 2.1 relative luminance):
     #FFFFFF on #0B7D74 ............ 5.00:1   (text on the fill)
     rgba(255,255,255,.94) on fill .. 4.61:1   (quiet text on the fill)
     #0A6156 on #FFFFFF ............ 7.35:1   (--amber-deep, text on white)
     #0A6156 on #D8F1ED ............ 6.20:1   (--amber-deep on the tint)
     #06463E on #FFFFFF ............ 10.75:1  (--amber-darker)
     #FFFFFF on #096A63 ............ 6.45:1   (the hover fill)
     fill vs #FFFFFF page .......... 5.00:1   (the fill's own edge, light)
     fill vs #191A1D surface ....... 3.48:1   (the fill's own edge, dark)
   Dark theme:
     #45D9D0 on #191A1D ............ 10.02:1  (--amber-deep)
     #45D9D0 on #0E2B28 ............ 8.67:1   (--amber-deep on the tint)
     #85E8E0 on #191A1D ............ 12.11:1  (--amber-darker)

   Quiet text on the fill was white at 88% when the accent was rose (4.80:1);
   teal is a lighter ground than that rose, so 88% falls to 4.24:1 and every
   one of those rules moves to 94% below. Hairlines and hovers keep their old
   alphas — they carry no type and only owe 3:1.

   The hue sits at 175°, clear of --bad (#B3261E, 8°) and of --info (211°).
   It is 26° off --ok (#1E7A4B, 149°), which is the one neighbour close enough
   to matter: a "done" pill and a selected slot are told apart by lightness
   rather than hue — the pill is a pale tint with dark green type, the slot is
   the solid fill with white on it — and section 3 pulls the calendar's staff
   bars apart for the same reason. */
:root{
  --amber:#0B7D74;       --amber-deep:#0A6156;   --amber-darker:#06463E;
  --amber-fill:#0B7D74;  --amber-fill-hi:#096A63;
  --amber-soft:#D8F1ED;  --amber-line:#93D2C9;   --on-amber:#FFFFFF;
  /* the kit's hover tint is the accent at its faintest, so it moves too */
  --hover:#EFF9F7;
}
[data-theme="dark"]{
  --amber-soft:#0E2B28;  --amber-line:#20514A;   --amber-deep:#45D9D0;  --amber-darker:#85E8E0;
  --hover:#16302C;
}
/* the kit sets ink on the fill here because yellow is light; teal is not */
::selection{background:var(--amber-fill);color:#FFFFFF}

/* == 1. layout bits == */
button.pill{cursor:pointer;font-family:var(--mono)}
button.pill:hover{border-color:var(--amber)}
.toolbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:16px}
.toolbar .search{max-width:280px}
.seg{display:inline-flex;border:1px solid var(--line-2);border-radius:var(--r);overflow:hidden}
.seg button{background:var(--surface);border:0;border-right:1px solid var(--line);padding:7px 12px;font-size:13px;font-weight:600;color:var(--muted);cursor:pointer}
.seg button:last-child{border-right:0}
.seg button:hover{background:var(--hover)}
.seg button.is-on{background:var(--amber-soft);color:var(--amber-deep)}
.datenav{display:inline-flex;align-items:center;gap:6px}
.datenav__label{font-family:var(--mono);font-size:12.5px;font-weight:600;min-width:190px;text-align:center}
.fieldrow{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
.fieldrow>.field+.field{margin-top:0}
.legend{display:flex;gap:14px;flex-wrap:wrap;align-items:center;font-size:12px;color:var(--muted)}
.legend b{display:inline-block;width:11px;height:11px;border-radius:var(--r-xs);border:1px solid var(--line-2);vertical-align:-1px;margin-right:5px}
.legend .is-free{background:var(--surface)}
.legend .is-full{background:var(--surface-2)}
.legend .is-today{background:var(--amber-soft);border-color:var(--amber-line)}
.kv{display:grid;grid-template-columns:120px minmax(0,1fr);gap:6px 12px;font-size:13.5px}
.kv dt{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);padding-top:3px}
.kv dd{color:var(--ink-2)}
.tokenbig{font-family:var(--mono);font-size:34px;font-weight:600;letter-spacing:-.02em;line-height:1}

/* == 2. today queue == */
.queue{display:flex;flex-direction:column;gap:10px}
.qrow{display:flex;align-items:center;gap:14px;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);padding:12px 14px}
.qrow--now{border-color:var(--amber-line);background:var(--amber-soft)}
.qrow--done{background:var(--surface-2)}
.qrow--miss{border-color:var(--bad-line)}
.qtok{width:78px;flex:none;text-align:center;border-right:1px solid var(--line);padding-right:12px}
.qtok__n{font-family:var(--mono);font-size:18px;font-weight:600;letter-spacing:-.02em}
.qtok__t{font-family:var(--mono);font-size:10.5px;color:var(--faint);white-space:nowrap}
.qmain{flex:1;min-width:0}
.qmain__name{font-weight:600;font-size:14.5px}
.qmain__sub{font-size:12.5px;color:var(--muted);margin-top:2px}
.qacts{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.nowcard{border:1px solid var(--amber-line);background:var(--amber-soft);border-radius:var(--r-lg);padding:16px 18px;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.nowcard__body{flex:1;min-width:180px}

/* == 3. calendar == */
.calwrap{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);overflow:hidden}
.calscroll{overflow-x:auto}
.cal{display:grid;grid-template-columns:74px repeat(7,minmax(112px,1fr));min-width:860px}
.cal__hcell{position:sticky;top:0;z-index:2;background:var(--surface-2);border-bottom:1px solid var(--line);border-left:1px solid var(--line);padding:9px 10px;text-align:center}
.cal__hcell:first-child{border-left:0}
.cal__hcell--today{background:var(--amber-soft)}
.cal__hd{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.cal__hn{font-weight:700;font-size:14px;margin-top:2px}
.cal__time{border-bottom:1px solid var(--line);padding:6px 10px;font-family:var(--mono);font-size:11px;color:var(--faint);text-align:right;background:var(--surface)}
.cal__cell{border-left:1px solid var(--line);border-bottom:1px solid var(--line);min-height:46px;padding:4px;display:flex;flex-direction:column;gap:3px;text-align:left;background:var(--surface);width:100%;font:inherit;color:inherit;cursor:pointer}
.cal__cell:hover{background:var(--hover)}
.cal__cell--shut{background:var(--surface-2);cursor:not-allowed}
.cal__cell--full{background:var(--surface-2);cursor:not-allowed}
.cal__cell--past{cursor:not-allowed}
.cal__cell--past:hover{background:var(--surface)}
.cal__free{font-family:var(--mono);font-size:10.5px;color:var(--amber-deep);font-weight:600}
.cal__shut{font-family:var(--mono);font-size:10px;color:var(--faint)}
.cal__ev{border:1px solid var(--line-2);border-left:3px solid var(--night);border-radius:var(--r-xs);background:var(--surface-2);padding:3px 6px;font-size:11px;line-height:1.35;overflow:hidden}
.cal__ev b{display:block;font-family:var(--mono);font-size:10px;font-weight:600;color:var(--faint)}
/* Three staff, three hues that have to survive being 3px wide. The accent was
   rose when the third bar took --amber-deep, 183° off --ok; teal lands 26°
   away from it, and two dark green-blue hairlines side by side are one bar.
   The third bar moves to --warn instead. Held time is --warn too, but that
   event is dashed, tinted and labelled "Held", so the pair never reads as the
   same thing. Every bar also prints its owner's initials — colour is a second
   signal here, never the only one. */
.cal__ev--st-rehana{border-left-color:var(--info)}
.cal__ev--st-anwar{border-left-color:var(--ok)}
.cal__ev--st-nithya{border-left-color:var(--warn)}
.cal__ev--off{opacity:.55;border-left-color:var(--line-2)}
/* time held back for a staff member: not a booking, but the slot is gone */
.cal__ev--held{border-style:dashed;border-left:3px solid var(--warn);background:var(--warn-soft);color:var(--warn)}
.cal__ev--held b{color:var(--warn)}

/* == 4. slot picker == */
.slotgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:7px;max-height:190px;overflow-y:auto;padding:2px}
.slotbtn{border:1px solid var(--line-2);background:var(--surface);border-radius:var(--r-sm);padding:7px 4px;font-family:var(--mono);font-size:12px;cursor:pointer;text-align:center}
.slotbtn:hover{border-color:var(--amber);color:var(--amber-deep)}
.slotbtn.is-on{background:var(--amber-fill);border-color:var(--amber-fill);color:var(--on-amber);font-weight:600}
.slotbtn small{display:block;font-size:9.5px;color:var(--faint);letter-spacing:.04em}
.slotbtn.is-on small{color:var(--on-amber)}

/* == 5. staff, hours, services == */
/* The kit hides a switch's checkbox with position:absolute. With no positioned
   ancestor it resolves against the initial containing block, escaping the
   horizontally scrolled Services table and widening the whole document. */
.switch{position:relative}
/* min() rather than a bare 320px: below a 320px track the minimum would win
   over the 1fr and the card would stand wider than the page it sits in, which
   is a sideways scroll on a 320px phone. */
.staffgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(320px,100%),1fr));gap:var(--gutter)}
.staffcard__top{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.staffcard__top .avatar{width:38px;height:38px;font-size:13px}
.daypicker{display:flex;gap:5px;flex-wrap:wrap}
.daypicker button{border:1px solid var(--line-2);background:var(--surface);border-radius:var(--r-sm);width:38px;height:30px;font-family:var(--mono);font-size:11px;cursor:pointer;color:var(--muted)}
.daypicker button.is-on{background:var(--amber-fill);border-color:var(--amber-fill);color:var(--on-amber);font-weight:600}
.skillist{display:flex;flex-direction:column;gap:8px}
.skillrow{display:flex;align-items:center;gap:9px;font-size:13.5px}
.skillrow input{width:16px;height:16px;accent-color:var(--amber);flex:none}
.loadbar{display:flex;align-items:center;gap:10px}
.loadbar .meter{flex:1}

/* == 6. templates == */
.tplgrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,320px);gap:var(--gutter);align-items:start}
.msgprev{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);padding:16px}
.bubble{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:11px 13px;font-size:13.5px;line-height:1.6;color:var(--ink-2)}
.bubble+.bubble{margin-top:10px}
.bubble__from{font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:5px}
.tokchips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.tokchip{font-family:var(--mono);font-size:11px;border:1px solid var(--line-2);background:var(--surface-2);border-radius:var(--r-xs);padding:2px 6px;cursor:pointer;color:var(--muted)}
.tokchip:hover{border-color:var(--amber);color:var(--amber-deep)}

/* == 7. drawer detail == */
.hist{border-left:1px solid var(--line);padding-left:16px}
.hist__i{position:relative;padding-bottom:12px;font-size:13px}
.hist__i::before{content:"";position:absolute;left:-20px;top:6px;width:7px;height:7px;border-radius:50%;background:var(--amber);border:2px solid var(--surface)}
.hist__t{font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.stripe{display:flex;gap:3px;flex-wrap:wrap}
.stripe i{width:12px;height:18px;border-radius:2px;background:var(--line-2);display:block}
.stripe i.ok{background:var(--ok)}
.stripe i.miss{background:var(--bad)}
.stripe i.cut{background:var(--line-2)}

/* == 7a. about modal ==
   Short lines with a solid square marker, so the four blocks stay scannable
   inside the modal at phone width. */
.aboutlist{display:flex;flex-direction:column;gap:7px;margin-top:5px}
.aboutlist li{position:relative;padding-left:16px;font-size:12.5px;line-height:1.55;color:var(--muted)}
.aboutlist li::before{content:"";position:absolute;left:0;top:6px;width:6px;height:6px;border-radius:var(--r-xs);background:var(--amber-fill)}
.aboutlist b{color:var(--ink-2);font-weight:600}
/* a repository URL is long and unbreakable — let it wrap rather than push the
   modal wider than the 390px viewport */
.aboutlink{font-size:11.5px;overflow-wrap:anywhere}

/* == 7b. the teal sidebar ==
   The kit's tone mode was drawn for a light yellow fill carrying ink text.
   Teal is a dark ground, so the polarity of the whole subtree flips: white
   type, white hairlines, white-tinted hovers. Everything below restates a
   rule app.css writes with ink in it — same selectors, same structure, only
   the side of the fill the colour comes from.

   White on #0B7D74 is 5.00:1 and the quiet text (white at 94%) is 4.61:1.
   The active row and every control keep a white ground with ink or teal type
   on them, which is the same move the yellow sidebar made — a white pill on
   the fill — and it survives the flip unchanged. These rules carry no theme
   selector because the fill is the same colour in both themes. */
.side[data-tone="amber"]{--line:rgba(255,255,255,.24);--line-2:rgba(255,255,255,.34);--hover:rgba(255,255,255,.14)}
.side[data-tone="amber"] .side__brand,
.side[data-tone="amber"] .side__foot,
.side[data-tone="amber"] .navgroup+.navgroup{border-color:rgba(255,255,255,.24)}
/* the brand square inverts: a white tile with the teal glyph, because ink on
   teal is only 3.4:1 and a 12px mono letter cannot live there */
.side[data-tone="amber"] .mark{background:#FFFFFF;color:var(--amber-deep)}
.side[data-tone="amber"] .side__name{color:#FFFFFF}
.side[data-tone="amber"] .navlink{color:#FFFFFF}
/* the glyph is not type and owes 3:1, so it keeps the lighter alpha */
.side[data-tone="amber"] .navlink svg{color:rgba(255,255,255,.88)}
/* quiet text on the fill: white at 94% (4.61:1). --amber-darker was the
   yellow sidebar's answer here and it is a dark shade — unusable on teal. */
.side[data-tone="amber"] .side__tag,
.side[data-tone="amber"] .navgroup__label,
.side[data-tone="amber"] .navlink__count,
.side[data-tone="amber"] .side__sub{color:rgba(255,255,255,.94)}
.side[data-tone="amber"] .side__sub .linkish{border-bottom-color:rgba(255,255,255,.6)}
.side[data-tone="amber"] .navlink:hover{background:rgba(255,255,255,.14)}
.side[data-tone="amber"] .navlink.is-active{background:#FFFFFF;color:#17181A}
.side[data-tone="amber"] .navlink.is-active svg{color:var(--amber-deep)}
/* the active row is a white pill on teal; a hover tint over it only muddies
   the state */
.side[data-tone="amber"] .navlink.is-active:hover{background:#FFFFFF}
/* the kit rings focus with --ink here because ink reads on yellow; on teal
   the ring has to be the white one */
.side[data-tone="amber"] :focus-visible{outline-color:#FFFFFF}

/* == 7b2. sidebar footer: links + install ==
   Everything in the footer is read against #0B7D74 first and against white
   second: the controls are white grounds with ink on them, and the one dark
   link keeps its inverted ground. */

/* The footer is About on its own, then two paired rows. Everything inside a
   pair is a control rather than a nav row, so it carries a hairline — except
   the one dark link, which keeps its inverted ground. The rows supply their
   own vertical rhythm, so no member carries a margin of its own. */
.side__foot .side__pair{margin-top:6px}
.side__pair .navlink:not(.sitelink){border:1px solid var(--line-2)}
.side__pair .navlink:not(.sitelink):hover{background:var(--hover);border-color:var(--amber-line)}
/* on the fill the controls stand as near-white tiles with ink type: white at
   92% over teal is 16.0:1 for ink, and the tile itself is 4.5:1 off the fill */
.side[data-tone="amber"] .btn,
.side[data-tone="amber"] .side__brandbtns .btn,
.side[data-tone="amber"] .side__pair .navlink:not(.sitelink){background:rgba(255,255,255,.92);border-color:rgba(23,24,26,.2);color:#17181A}
.side[data-tone="amber"] .btn:hover,
.side[data-tone="amber"] .side__brandbtns .btn:hover,
.side[data-tone="amber"] .side__pair .navlink:not(.sitelink):hover{background:#FFFFFF;border-color:rgba(23,24,26,.3)}
.side[data-tone="amber"] .btn svg,
.side[data-tone="amber"] .side__brandbtns .btn svg,
.side[data-tone="amber"] .side__pair .navlink:not(.sitelink) svg{color:#2E3033}
/* a paired label truncates rather than pushing its neighbour out of the row */
.side__pair .navlink{min-width:0;gap:7px;padding:8px}
.side__pair .navlink span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* the one dark element in the sidebar. Ink ground, white type — 16:1 either way,
   and it reads as deliberate against the teal rather than as a hole in it. */
.sitelink{background:var(--night);color:#FFFFFF}
.sitelink svg{color:#FFFFFF}
.sitelink:hover{background:var(--night-2);color:#FFFFFF}
/* the kit's tone rules repaint every .navlink, so the dark one opts back out */
.side[data-tone="amber"] .sitelink{background:var(--night);color:#FFFFFF}
.side[data-tone="amber"] .sitelink svg{color:#FFFFFF}
.side[data-tone="amber"] .sitelink:hover{background:var(--night-2)}

/* the source link is an outline control, not a second inverted one — the
   nasvih.in link stays the only dark element in the footer */
.srclink svg{color:var(--ink-2)}

/* == 7c. topbar: demo/about button and the chrome controls ==
   Title, then everything else. The title is the only thing allowed to shrink,
   so the controls never push past the right edge at 390px. */
.topbar__id{min-width:0;flex:0 1 auto;overflow:hidden}
.topbar__title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.topbar__tools{display:flex;align-items:center;gap:2px;flex:none}
/* the title is the only thing in the bar allowed to give up width */
.topbar>.btn,.topbar__tools .btn{flex:none}
.aboutpill{flex:none;text-transform:none;letter-spacing:.01em;font-size:11px;padding:5px 10px;white-space:nowrap}
.devseg{display:inline-flex;align-items:center;gap:2px}
.devseg .btn.is-on{background:var(--amber-soft);border-color:var(--amber-line);color:var(--amber-deep)}

/* == 7d. notifications ==
   Derived from the live bookings every time the panel opens; only the read
   marks are stored. Unread is a dot AND the word, never colour alone. */
.bellwrap{position:relative;display:inline-flex}
.bellbtn{position:relative}
.bellbtn__count{position:absolute;top:0;right:0;min-width:16px;height:16px;padding:0 4px;border-radius:var(--r-pill);background:var(--bad);color:#fff;font-size:9.5px;font-weight:700;line-height:16px;text-align:center}
.notepanel{position:absolute;top:calc(100% + 8px);right:0;width:360px;max-height:min(70vh,520px);overflow-y:auto;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-3);z-index:40}
.notepanel__head{position:sticky;top:0;z-index:1;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;border-bottom:1px solid var(--line);background:var(--surface)}
.notelist{display:flex;flex-direction:column}
.noterow{display:flex;gap:10px;align-items:flex-start;width:100%;text-align:left;background:none;border:0;border-bottom:1px solid var(--line);padding:11px 14px;cursor:pointer;color:inherit;font:inherit}
.noterow:hover{background:var(--hover)}
.noterow.is-new{background:var(--amber-soft)}
.noterow__dot{width:8px;height:8px;border-radius:50%;margin-top:5px;flex:none;background:var(--faint)}
.noterow__dot--bad{background:var(--bad)}
.noterow__dot--warn{background:var(--warn)}
.noterow__dot--info{background:var(--info)}
.noterow__dot--muted{background:var(--line-2)}
.noterow__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.noterow__title{font-weight:600;font-size:13px}
.noterow__new{font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--amber-deep)}
.noterow__text{font-size:12px;color:var(--muted);line-height:1.45}
.noterow__when{font-size:10.5px;color:var(--faint);flex:none;padding-top:2px}
.notepanel__empty{padding:28px 20px;text-align:center}
.notepanel__foot{padding:10px 14px;border-top:1px solid var(--line);line-height:1.5}

/* == 7e. the token slip ==
   Plain teal ground, white type, nothing else on it. White on #0B7D74 is
   5.00:1, so every colour here is fixed against the fill rather than against
   the page — which is why it reads the same in dark mode.
   The scrim scrolls, so the slip is centred with auto margins rather than with
   place-items: centring a grid item taller than its scroll container puts its
   top above the scroll origin, and the head of the slip becomes unreachable on
   a short viewport (a phone held sideways). Auto margins collapse instead. */
.tokscrim{position:fixed;inset:0;z-index:78;background:rgba(23,24,26,.55);display:flex;padding:18px;overflow-y:auto}
.tokenpop{width:min(420px,100%);margin:auto;background:var(--amber-fill);color:var(--on-amber);border:1px solid var(--on-amber);border-radius:var(--r-lg);box-shadow:var(--shadow-3);padding:16px 20px 18px;text-align:center}
.tokenpop__head{display:flex;align-items:center;justify-content:space-between;gap:10px}
.tokenpop__eyebrow{font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.tokenpop__x{width:30px;height:30px;flex:none;border-radius:var(--r-sm);border:1px solid rgba(255,255,255,.55);background:transparent;color:var(--on-amber);display:grid;place-items:center;cursor:pointer;padding:0}
.tokenpop__x svg{width:16px;height:16px}
.tokenpop__x:hover{background:rgba(255,255,255,.14)}
.tokenpop__label{font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;margin-top:14px;color:rgba(255,255,255,.94)}
.tokenpop__num{font-family:var(--mono);font-size:clamp(58px,17vw,86px);font-weight:700;line-height:1.02;letter-spacing:-.03em;margin-top:4px}
.tokenpop__ref{font-family:var(--mono);font-size:14px;font-weight:600;letter-spacing:.1em;margin-top:4px}
.tokenpop__kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:7px 14px;text-align:left;margin-top:16px;padding-top:14px;border-top:1px solid rgba(255,255,255,.35)}
.tokenpop__kv dt{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.94);padding-top:2px}
.tokenpop__kv dd{font-size:13.5px;font-weight:600;overflow-wrap:anywhere}
.tokenpop__note{text-align:left;font-size:12px;line-height:1.5;margin-top:14px;color:rgba(255,255,255,.94)}
.tokenpop__foot{display:flex;gap:8px;margin-top:16px}
.tokenpop__foot .btn{flex:1}
/* the primary is the solid one: a white slab with teal type (7.35:1, and
   6.20:1 on its hover tint); the secondary is an outline: white type straight
   on the fill (5.00:1), and its hover tint stops at 14% so the type still
   clears AA */
/* literal shades, not --amber-deep: the slip is fixed against the fill, and
   --amber-deep is a light cyan in the dark palette that would vanish here */
.tokenpop__dl{background:#FFFFFF;border-color:#FFFFFF;color:#0A6156}
.tokenpop__dl:hover{background:#D8F1ED;border-color:#D8F1ED;color:#0A6156}
.tokenpop__dl svg{color:#0A6156}
.tokenpop__done{background:transparent;border-color:rgba(255,255,255,.55);color:var(--on-amber)}
.tokenpop__done:hover{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.8)}
.tokenpop :focus-visible{outline-color:var(--on-amber)}

/* == 7f. device preview ==
   A real iframe at 390×844 inside a bezel, so the app's own breakpoints fire
   for the honest reason. Teal surround, white text — the usual pairing. */
.devstage{position:fixed;inset:0;z-index:90;background:var(--amber-fill);color:var(--on-amber);display:flex;flex-direction:column;align-items:center;padding:16px 16px 0;overflow:hidden}
.devstage__head{width:100%;max-width:560px;display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:14px}
.devstage__name{font-size:18px;font-weight:700;letter-spacing:-.02em}
.devstage__sub{font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.94);margin-top:2px}
.devstage__body{flex:1;width:100%;display:flex;justify-content:center;align-items:flex-start;min-height:0}
.devbezel{width:414px;height:868px;flex:none;padding:12px;border-radius:46px;background:#17181A;box-shadow:var(--shadow-3);transform-origin:top center}
.devframe{width:390px;height:844px;border:0;border-radius:34px;background:var(--bg);display:block}
.devstage .btn{background:transparent;border-color:rgba(255,255,255,.55);color:var(--on-amber)}
.devstage .btn:hover{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.8)}
.devstage :focus-visible{outline-color:var(--on-amber)}

/* == 7g. worked examples in the About modal == */
.exlist{display:flex;flex-direction:column;gap:12px;margin-top:8px}
.exrow{border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;background:var(--surface-2)}
.exrow__t{font-weight:600;font-size:13px}
.exrow__in{display:block;font-family:var(--mono);font-size:11.5px;margin-top:5px;padding:5px 8px;border:1px solid var(--line-2);border-radius:var(--r-xs);background:var(--surface);overflow-wrap:anywhere}
.exrow__out{margin-top:6px;line-height:1.5}

/* == 7h. held time (staff card) == */
.heldlist{display:flex;flex-direction:column;gap:8px}
.heldrow{display:flex;align-items:center;gap:10px;justify-content:space-between;border:1px solid var(--line-2);border-radius:var(--r-sm);padding:8px 10px}

/* == 7i. dark mode corrections ==
   app.css ships the palette; these are the places where this app's own markup
   would otherwise inherit the wrong side of it.

   The kit re-pins the whole light palette inside the tone sidebar in dark
   mode, because #EAC81C is a light surface that the dark theme's near-white
   ink would land on at 1.9:1. #0B7D74 is a dark surface, so that reasoning
   inverts: the teal sidebar wants the same white type it has in light mode,
   which the theme-free rules in 7b already give it. All that is left to undo
   is the accent pair the kit re-pinned to its yellow shades — they are read
   on the white active pill and the white footer tiles, so they have to be
   the teal text shades, not the yellow ones. */
[data-theme="dark"] .side[data-tone="amber"]{
  --amber-deep:#0A6156; --amber-darker:#06463E;
  border-right-color:var(--amber-fill-hi);
}
/* the inverted link needs a ground that still reads as deliberate on teal */
[data-theme="dark"] .sitelink{background:#101113}
/* switch knob: the unchecked one sits on a dark track, the checked one is a
   white dot on the rose fill */
[data-theme="dark"] .switch__track::after{background:var(--surface)}
[data-theme="dark"] .switch input:checked+.switch__track::after{background:var(--on-amber)}
[data-theme="dark"] .slotbtn.is-on small{color:var(--on-amber)}
/* the calendar's colour bars are per staff and must stay apart on a dark grid */
[data-theme="dark"] .cal__ev{background:var(--surface)}
[data-theme="dark"] .stripe i{background:var(--line-2)}

/* == 8. the booking page ==
   The customer's side of the same product. Same tokens, same type scale, same
   radii and hairlines as the desk — and none of its density: no sidebar, no
   table, no counts, one column that is never wider than a comfortable line.
   Written phone-first, because that is where a clinic gets booked from: every
   control here is at least 44px tall before any media query touches it.

   The desk's chrome is hidden rather than unmounted while this page is up, so
   the two shells never fight over the same fixed corner. */
body.is-public .assist-fab,
body.is-public .assist{display:none}

.pubshell{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh;background:var(--bg)}
.pubbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:10px;
  min-height:var(--bar);padding:8px 16px;padding-top:calc(8px + env(safe-area-inset-top));
  background:var(--surface);border-bottom:1px solid var(--line)}
.pubbar__brand{display:flex;align-items:center;gap:10px;min-width:0}
.pubbar__id{min-width:0}
.pubbar__name{font-weight:700;letter-spacing:-.02em;font-size:15px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pubbar__tag{font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the way into the desk: an outline control, not a primary one — booking is
   the only thing this page is asking anybody to do */
.pubbar__staff{flex:none;min-height:40px}
/* The mirror of .pubbar__staff, on the desk's own bar. */
.topbar__public{flex:none;min-height:40px}
@media (max-width:640px){
  /* Six controls do not fit a 320px bar with their labels on. This one and
     "New booking" are the two that read from their glyph alone. */
  .topbar__public span{display:none}
  .topbar__public{width:40px;height:40px;padding:0;justify-content:center}
}
/* the kit sizes an icon button for a mouse (34px) and only lifts it under
   900px; this page is thumbed at every width, so its two controls are 40px
   from 320 to 1920 */
/* flex:none as well: at 320 the row is full and a shrinking square lands at
   39.97px, which is under the size the rest of the page holds to */
.pubbar .btn--icon{width:40px;height:40px;padding:7px;flex:none}
.pubmain{flex:1}
.pubfoot{border-top:1px solid var(--line);background:var(--surface-2);
  padding:20px 16px calc(24px + env(safe-area-inset-bottom))}
.pubfoot p{max-width:640px;margin:0 auto;line-height:1.6}
.pubfoot__acts{max-width:640px;margin:12px auto 0;display:flex;gap:8px;flex-wrap:wrap}
.pubfoot__acts .btn{min-height:44px}

/* the permanent live region: read out, never drawn */
.pub__sr{position:absolute;width:1px;height:1px;padding:0;margin:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

.pub__col{max-width:640px;margin:0 auto;padding:24px 16px 48px}
.pub__intro{margin-bottom:28px}
.pub__intro h1{font-size:26px}
.pub__lede{color:var(--muted);margin-top:8px;line-height:1.6}
/* Each question is a heading, not a legend inside a box: the page is a list of
   five plain questions and nothing else. */
.pub__q{display:flex;align-items:baseline;gap:8px;font-size:16px;margin:28px 0 10px}
.pub__opt{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}

/* --- service --- */
.pub__opts{display:flex;flex-direction:column;gap:8px}
.pubopt{display:flex;align-items:center;gap:12px;width:100%;min-height:60px;text-align:left;
  border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);
  padding:12px 14px;cursor:pointer;color:inherit;font:inherit}
.pubopt:hover{border-color:var(--amber);background:var(--hover)}
.pubopt__main{flex:1;min-width:0}
.pubopt__name{display:block;font-weight:600;font-size:14.5px}
.pubopt__meta{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}
.pubopt__price{flex:none;font-size:14px;font-weight:600}
.pubopt.is-on{background:var(--amber-fill);border-color:var(--amber-fill);color:var(--on-amber)}
.pubopt.is-on .pubopt__meta{color:rgba(255,255,255,.94)}
.pubopt.is-on:hover{background:var(--amber-fill-hi);border-color:var(--amber-fill-hi)}

/* --- day --- */
.pub__days{display:grid;grid-template-columns:repeat(auto-fill,minmax(72px,1fr));gap:8px}
.pubday{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  min-height:72px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);
  cursor:pointer;color:inherit;font:inherit;padding:8px 4px}
.pubday:hover{border-color:var(--amber);background:var(--hover)}
.pubday__dow{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.pubday__n{font-family:var(--mono);font-size:19px;font-weight:600;line-height:1.1}
.pubday__free{font-size:10.5px;color:var(--muted)}
.pubday.is-on{background:var(--amber-fill);border-color:var(--amber-fill);color:var(--on-amber)}
.pubday.is-on .pubday__dow,.pubday.is-on .pubday__free{color:rgba(255,255,255,.94)}
/* a closed or full day is still on the strip, because "why can I not pick
   Sunday" is a worse question than a greyed square that says Closed */
.pubday.is-off{background:var(--surface-2);color:var(--faint);cursor:not-allowed;opacity:.75}
.pubday.is-off:hover{border-color:var(--line-2);background:var(--surface-2)}
/* The day already chosen can fall out from under the reader when the desk
   takes the last slot on it. It keeps the selected hairline so the choice is
   still visible, but the type goes back to ink: the white-on-fill pair above
   would be white on a pale grey square, which is nothing at all. */
.pubday.is-on.is-off{border-color:var(--amber-line);color:var(--faint)}
.pubday.is-on.is-off .pubday__dow,
.pubday.is-on.is-off .pubday__free{color:var(--faint)}

/* --- time --- */
.pub__times{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:8px}
.pubtime{min-height:48px;border:1px solid var(--line-2);border-radius:var(--r);background:var(--surface);
  font-family:var(--mono);font-size:13px;font-weight:600;cursor:pointer;color:inherit;padding:8px 4px}
.pubtime:hover{border-color:var(--amber);background:var(--hover);color:var(--amber-deep)}
.pubtime.is-on{background:var(--amber-fill);border-color:var(--amber-fill);color:var(--on-amber)}
.pubtime.is-on:hover{background:var(--amber-fill-hi);border-color:var(--amber-fill-hi);color:var(--on-amber)}

/* --- who --- */
.pub__who{display:flex;flex-wrap:wrap;gap:8px}
.pubwho{flex:1 1 160px;min-height:52px;text-align:left;border:1px solid var(--line-2);border-radius:var(--r);
  background:var(--surface);padding:9px 12px;cursor:pointer;color:inherit;font:inherit}
.pubwho:hover{border-color:var(--amber);background:var(--hover)}
.pubwho__n{display:block;font-weight:600;font-size:13.5px}
.pubwho__r{display:block;font-size:12px;color:var(--muted);margin-top:1px}
.pubwho.is-on{background:var(--amber-fill);border-color:var(--amber-fill);color:var(--on-amber)}
.pubwho.is-on .pubwho__r{color:rgba(255,255,255,.94)}

/* --- states --- */
.pub__notice{border:1px solid var(--amber-line);background:var(--amber-soft);border-radius:var(--r);
  padding:11px 14px;font-size:13px;color:var(--ink-2);margin-bottom:12px;line-height:1.5}
.pub__empty{padding:24px 18px}
.pub__emptyact{margin-top:14px}
.pub__emptyact .btn{min-height:44px}

/* --- details + confirm --- */
.pub__form{margin-top:4px}
.pub__fields{display:flex;flex-direction:column;gap:16px}
.pub__fields .field+.field{margin-top:0}
.pub__form .input{min-height:46px}
.pub__sum{margin-top:20px;font-size:13.5px;color:var(--muted);line-height:1.55}
/* The live region is in the page from the first paint and empty until it has
   something to say, so a later change to it is a change assistive tech reports
   rather than a node appearing from nowhere. */
.pub__live{margin-top:12px;font-size:13.5px;line-height:1.55;color:var(--ink-2)}
.pub__live.is-on{border:1px solid var(--line-2);border-radius:var(--r);padding:10px 12px;background:var(--surface-2)}
.pub__live.is-bad{border-color:var(--bad-line);background:var(--bad-soft);color:var(--bad)}
.pub__go{width:100%;min-height:50px;margin-top:16px;font-size:15px}

/* --- after it is booked --- */
.pubdone{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);padding:24px 20px;text-align:center}
.pubdone__tick{width:44px;height:44px;margin:0 auto 14px;border-radius:50%;
  background:var(--ok-soft);border:1px solid var(--ok-line);color:var(--ok);display:grid;place-items:center}
.pubdone__tick svg{width:22px;height:22px;stroke-width:2}
.pubdone__h{font-size:24px}
.pubdone__sub{color:var(--muted);margin-top:8px;line-height:1.6}
/* The token is repeated here as a tint rather than the solid fill: the slip
   over it is the solid one, and two of those on screen at once is one too
   many. */
.pubdone__tok{margin-top:20px;border:1px solid var(--amber-line);background:var(--amber-soft);border-radius:var(--r);padding:14px}
.pubdone__toklabel{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--amber-deep)}
.pubdone__tokn{font-size:44px;font-weight:700;letter-spacing:-.03em;line-height:1.1;margin-top:2px;color:var(--amber-darker)}
.pubdone__ref{font-size:12.5px;font-weight:600;letter-spacing:.1em;color:var(--amber-deep)}
.pubdone__kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:9px 16px;text-align:left;
  margin-top:20px;padding-top:18px;border-top:1px solid var(--line)}
.pubdone__kv dt{font-family:var(--mono);font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);padding-top:3px}
.pubdone__kv dd{font-size:13.5px;font-weight:600;overflow-wrap:anywhere}
.pubdone__acts{display:flex;gap:8px;flex-wrap:wrap;margin-top:22px}
.pubdone__acts .btn{flex:1 1 140px;min-height:46px}

/* == 9. responsive == */
@media (max-width:480px){
  /* Under 480px the bar is the desk's name, the theme switch and the way into
     the desk, and the name is what matters. The staff control drops to its
     glyph — the same move the desk's topbar makes with "New booking" — which
     buys the name back about 70px and stops it truncating at 320. */
  .pubbar__staff span{display:none}
  .pubbar__staff{width:40px;height:40px;padding:0;justify-content:center}
}
@media (min-width:641px){
  .pub__col{padding:36px 24px 64px}
  .pub__intro h1{font-size:30px}
  .pubbar{padding:8px 24px;padding-top:calc(8px + env(safe-area-inset-top))}
  .pubfoot{padding:24px}
  .pub__go{width:auto;min-width:240px}
}
@media (max-width:1080px){
  .tplgrid{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:900px){
  /* rail mode is a desktop concern: under 900px the sidebar is already an
     off-canvas drawer, so the collapse control is not offered at all */
  .side__brandbtns [data-act="rail"]{display:none}

  /* Below 900px the sidebar is a drawer, which is the app admitting the pointer
     is a thumb. The kit's controls are sized for a mouse — a 34px icon button,
     a 30px small button, a 38px nav row — so everything that gets pressed is
     lifted here. Order matters: .btn goes first, then the variants restate the
     boxes they own, or the wider padding would burst their fixed squares. */
  .btn{padding:9px 14px}
  .btn--sm{padding:8px 11px}
  .btn--icon{padding:7px;width:40px;height:40px}
  .side__brandbtns .btn{width:36px;height:36px;padding:0}
  .navlink{padding:10px}
  .side__pair .navlink{padding:10px 8px}
  .chip{min-height:36px;padding:6px 11px}
  .seg button{min-height:40px}
  .tab{min-height:40px}
  .daypicker button{width:40px;height:36px}
  .tokenpop__x{width:36px;height:36px}
  .aboutpill{min-height:34px}
}
@media (max-width:640px){
  .fieldrow{grid-template-columns:minmax(0,1fr)}
  .qrow{flex-wrap:wrap}
  .qacts{justify-content:flex-start;width:100%}
  .qtok{border-right:0;padding-right:0;width:auto;text-align:left}
  /* Prev · week label · next · "This week" is 324px of controls that will not
     compress, so at phone width the row wraps instead of pushing the page
     sideways. The label claims enough basis that "This week" is the member that
     drops to the second line, and the date range keeps its single line. */
  .datenav{display:flex;flex-wrap:wrap}
  .datenav__label{flex:1 1 160px;min-width:160px;font-size:11.5px}
  /* app.css already widens .search to a full row at this width */
  .kv{grid-template-columns:minmax(0,1fr)}
  .kv dd{margin-bottom:6px}
  /* The topbar carries five controls at phone width and none of them may be
     squeezed, so the section title stands down — every screen already prints
     its own heading directly underneath. The primary button drops to its icon
     and the notification panel becomes a sheet.
     Even so the row did not fit: at 360px the "New booking" button sat 2px past
     the edge and at 320px it was 42px out, so the whole page scrolled sideways
     and the primary action could not be reached. The device preview is what
     goes: it opens a 390×844 phone bezel, which is nothing a phone-width
     viewport needs — the app already withholds it inside that frame for the
     same reason. Dropping it frees 70px and the row fits at 320px with room to
     spare, which is what pays for the 40px tap targets below. */
  .topbar{gap:6px;padding:0 10px}
  .topbar__id{display:none}
  .topbar__tools{gap:0}
  .topbar .btn--primary span{display:none}
  .devseg{display:none}
  /* the label is gone, so the button becomes the same 40px square as its
     neighbours rather than a narrower slab */
  .topbar .btn--primary{width:40px;height:40px;padding:0}
  /* last-resort give: the pill yields width before the row can overflow again */
  .aboutpill{font-size:10px;padding:5px 8px;letter-spacing:0;flex:0 1 auto;min-width:0;overflow:hidden}
  .notepanel{position:fixed;top:calc(var(--bar) + 6px);left:12px;right:12px;width:auto;max-height:calc(100vh - var(--bar) - 24px)}
  .devstage{padding:12px 12px 0}

  /* A card table has no room for wide headers, but these tables scroll inside
     their wrapper, so wrapping a header only squeezes the column and doubles
     the row height. Let them keep their line and scroll instead. */
  .tablewrap--scroll table.data th{white-space:nowrap}
  .tablewrap--scroll table.data .linkish{white-space:nowrap}

  /* The assistant launcher is fixed 22px off the bottom and is 52px across, so
     the foot of a view would otherwise sit under it. */
  .view--pad{padding:16px 16px 88px}
}
