/* ==========================================================================
   Expy Med — the layer on top of the Neu kit.
   Loaded AFTER neu.css. Two jobs:
     1. Retune the palette so status and label colours clear WCAG AA.
     2. Add the components the kit doesn't have — field errors, slide-over,
        desktop shell, data rows, queue cards, empty states.
   neu.css itself stays unmodified so the kit remains upgradeable.
   ========================================================================== */

/* ---------- 1. Palette ----------------------------------------------------
   Two tiers, because on a light surface you cannot have both bright and
   legible in one value — at 4.5:1 the colour is necessarily dark. That is
   physics, not tuning, so the system splits the job:

     --x-fill   the bright hue. Fills, strokes, bars, switches — anything
                that is a shape rather than a word. This is what the eye
                reads as "the colour".
     --x        a text-safe variant of the same hue, 4.5:1 on the surface.
                Only where colour meets small text.
     --x-tint   a pale wash of the hue for pill and badge backgrounds. The
                text variant clears 4.9:1 on it, so a pill reads as vividly
                coloured and still passes.

   The kit paints both fills and text with --accent, so --accent keeps the
   text-safe value and section 2 re-points the eight rules that are fills.   */

:root{
  /* The kit's ground is #e8ecf3, a distinctly blue-grey. This lifts it most
     of the way to white while keeping enough of the hue that the surface
     still reads as a material rather than as paper — pure white leaves no
     headroom above the surface for a highlight, at which point the whole
     raised/pressed language collapses into flat cards with drop shadows.

     Where it sits is a three-way compromise, and worth stating because the
     next person will want to move it. A paler ground looks cleaner; a darker
     one gives white more room to light an edge with, which is the whole
     raised/pressed language; and the colours have to keep clearing contrast
     against whichever is chosen. #f1f3f8 was tried first and read washed out
     — white lifted only 1.110 above it. This lifts 1.161, near the kit's own
     1.185, and stays clearly lighter than the kit's #e8ecf3.

     The three text-safe colours below are re-solved for it rather than the
     ground being compromised for them. The bright fills — the coral on a
     button, the red on a toast — are untouched, so nothing the eye reads as
     "the colour" got duller. */
  --bg:            #ebeef5;
  --surface:       #ebeef5;

  /* Softer shadow pair than the kit's. The kit is a showcase and pushes the
     effect; a clinical tool is looked at all day, and #c2c9d6 against this
     surface made every element shout for the same attention.

     Re-solved for the lighter ground rather than carried over: depth here is
     the contrast between surface and shadow, so the kit's #ced5e1 would read
     deeper against a paler surface than it did against its own.

     Deepened again to the "firm" step: white is a ceiling and this ground
     leaves it only 1.16 of lift, so the material has to get the rest of its
     definition from the dark half. 1.24 -> 1.41 of depth. The asymmetric
     pair in section 6c is the other half of that decision. */
  --sh-light:      #ffffff;
  --sh-dark:       #bcc7da;
  --sh-light-soft: rgba(255,255,255,.72);

  /* Elevation, one notch shallower across the board. */
  --d2: 5px;  --b2: 13px;
  --d3: 9px;  --b3: 20px;

  /* Warm coral — the Neu kit's original accent hue, retuned for this surface.
     Distinct from --danger, which stays a sharper alarm red.

     BOTH TIERS SIT BELOW AA ON THIS SURFACE, and that is a decision rather
     than an oversight: the coral is the product's identity, and holding
     4.5:1 turns it into a brick. The ratios are recorded as floors in
     tests/design.test.mjs, which still fails if either gets worse than what
     was accepted. What it costs: --accent also paints small text
     (.filter-chip.is-on, .side-item, .dp-day.is-today), and that text is
     below AA. Fixing that without moving the brand means a separate, darker
     --accent-text for those components, leaving display type and fills here. */
  --accent:      #e25046;   /* 3.31:1 on surface — waived */
  --accent-fill: #F4685E;   /* 2.59:1 on surface, white on it 3.00:1 — waived */
  --accent-tint: #fff1ef;
  --accent-2:    #FF8A7A;
  --accent-ink:  #ffffff;

  /* The sweep runs light -> fill -> deep across 135deg. The bright end is
     LIGHTER than the flat fill, so white ink reads worst at the button's
     top-left corner (2.63:1) rather than on the fill (3.00:1). Setting
     --accent-light to the fill itself would even that out, at the cost of a
     flatter gradient — kept as is because the sweep is the effect. */
  --accent-light: #FF7568;   /* white: 2.63:1 — weakest point of the sweep */
  --accent-deep:  #E04E43;   /* white: 3.93:1 */
  --accent-gradient: linear-gradient(135deg,
    var(--accent-light) 0%, var(--accent-fill) 50%, var(--accent-deep) 100%);

  /* Moved off the old mint (hue 164), which was picked to sit beside a
     teal-ish accent and read as a different palette entirely next to the
     coral that replaced it. Hue 136 partners the warm accent instead.
     One value serves both tiers, so the fill is as light as the text —
     waived on the same terms as the coral, and tolerable only because green
     is never the only signal: every dot and pill carries a label too. */
  --green:       #29c352;   /* 2.00:1 on surface — waived */
  --green-fill:  #29c352;   /* 2.00:1 on surface — waived */
  --green-tint:  #f0faf3;

  --amber:       #e49827;   /* 2.05:1 on surface — waived */
  --amber-fill:  #ffb648;
  --amber-tint:  #fff9f0;

  --blue:        #005be6;   /* 4.87:1 */
  --blue-fill:   #2b7fff;
  --blue-tint:   #f0f6ff;

  --muted:  #5e6b87;        /* 4.51:1, was #97a1b6 */

  --danger:      #CE2013;   /* 4.71:1 on surface */
  --danger-fill: #EE352A;   /* white on it: 4.06:1 */
  --danger-tint: #fef2f1;
  /* Pure alarm red — sharper and cooler than the coral accent so errors do
     not read as primary actions. White holds 4.06:1 at the bright end. */
  --danger-gradient: linear-gradient(135deg, #EE352A 0%, #E62D22 50%, #DE2A1F 100%);
}

body[data-accent="blue"]{  --accent:#005be6; --accent-fill:#2b7fff; --accent-tint:#f0f6ff; --accent-2:#5ea0ff; --accent-light:#2b7fff; --accent-deep:#0047c7; }
body[data-accent="coral"]{ --accent:#C23B32; --accent-fill:#F4685E; --accent-tint:#fff1ef; --accent-2:#FF8A7A; --accent-light:#FF7568; --accent-deep:#E04E43; }
body[data-accent="ink"]{   --accent:#33405c; --accent-fill:#3c4a68; --accent-tint:#f1f3f7; --accent-2:#54658a; }
body[data-accent="green"]{ --accent:#007a59; --accent-fill:#2fc39b; --accent-tint:#f3fcf9; --accent-2:#65d9ba; }

/* Dark mode inverts the problem — the bright hues already pass on a dark
   ground, so fill and text converge and the palette gets its vividness back.
   The shadow pair is also widened, but only a little: the kit's default
   highlight sits 1.19:1 above the ground, which makes raised and pressed
   nearly indistinguishable, while a first pass at 1.49:1 read as harsh.     */
[data-theme="dark"]{
  --bg:            #272b34;
  --surface:       #272b34;
  --sh-light:      #323842;   /* Was #353b46 at 1.26:1. Dark needs more separation
                                 than light does to read at all, so the calm pass
                                 takes less off here than it does above — enough
                                 to stop the panels glowing, not so much that
                                 raised and pressed collapse into each other. */
  --sh-dark:       #1c1f26;
  --sh-light-soft: #2e333c;

  --text:   #edf1f8;
  --text-2: #bcc6d8;
  --muted:  #98a3b8;
  --line:   rgba(255,255,255,.09);

  --accent:      #FF9A8F;   /* 6.12:1 on the dark surface */
  --accent-ink:  #ffffff;
  --accent-fill: #F4685E;   /* same fill in both themes — one coral is one
                               brand, whichever surface it sits on. */
  --accent-tint: #3a2523;
  --accent-light: #FF7568;
  --accent-deep:  #E04E43;
  --green:       #4FD070;   /* 7.15:1 on the dark surface */
  --green-fill:  #22A043;
  --green-tint:  #16301f;
  --amber:       #ffc165;
  --amber-fill:  #ffb648;
  --amber-tint:  #362a15;
  --blue:        #7fb0ff;
  --blue-fill:   #5ea0ff;
  --blue-tint:   #1e2a3f;
  --danger:      #ff8578;
  --danger-fill: #EE352A;
  --danger-tint: #3a2724;
  /* A touch deeper than the light theme's so it does not glare off a dark
     ground; white runs 4.4 / 5.0 / 5.6:1 across it. */
  --danger-gradient: linear-gradient(135deg, #E33328 0%, #D62B20 50%, #C9241A 100%);
}

/* The kit lightens raised cards with a gradient; match it to the new ground. */
[data-theme="dark"] .neu,
[data-theme="dark"] .neu-sm,
[data-theme="dark"] .neu-lg{ background:linear-gradient(145deg,#2b303a,#23272f); }
[data-theme="dark"] .lcd{ background:#20242b; }

/* ---------- 2. Fills use the bright hue ----------------------------------
   Left alone these would render in the text-safe value and the whole UI
   would look muddy.                                                        */

::selection{ background:var(--accent-fill); color:#fff; }
/* .btn-accent's actual background is set in section 11e, as a gradient across
   --accent-light/--accent-fill/--accent-deep — this is the fallback if that
   rule is ever removed, so it stays the bright fill and not the muddy
   text-safe one. */
.btn-accent{ background:var(--accent-fill); }
.switch input:checked ~ .track{ background:var(--accent-gradient); }
/* Not .check — the kit's checkbox is a physical toggle: the box is carved into
   the surface when off and pops out when on, with the tick appearing in colour.
   Filling the box with accent throws that away and turns it into a generic
   checkbox. Only the tick and the radio dot take the bright hue; at 3.29:1 on
   the surface they clear the 3:1 a graphical object needs. */
.check input:checked ~ .box{ color:var(--accent-fill); }
.check.radio .box::after{ background:var(--accent-fill); }

/* The kit's icon stroke is 1.8, which is right for a 19px nav glyph and thin
   for a 15px tick that is the entire signal of a consent. CSS stroke-width
   overrides the attribute, so this thickens the tick without touching the
   shared icon set. */
.check .box svg{ stroke-width:2.8; width:16px; height:16px; }

/* ---------- 2b. The off switch --------------------------------------------
   The kit paints an off track in --surface, so a switch that is off is the
   same colour as the page it sits on. Carved, but easy to miss at a glance —
   and on a settings list of six, "which of these are on?" should be one look.
   The trough now sits slightly darker than the surface in light, slightly
   lighter in dark, so it reads as a real recess. The knob keeps the surface
   colour and so gains 1.18:1 against its own trough.                        */

.switch .track{ background:var(--switch-off, #d3dbe9); }
[data-theme="dark"]{ --switch-off:#333945; }   /* a control fill, not a
   highlight — it stays at 1.22:1 so an off switch is still findable */
[data-theme="contrast"]{ --switch-off:#dfe3ea; }
.slider-wrap .slider-fill{ background:var(--accent-fill); }
.ring .bar{ stroke:var(--accent-fill); }
.bars .bar i{ background:var(--accent-fill); }

/* A swatch is a fill, so it is a real element inside a well — never a colour
   painted as the background of an inset-shadowed box. An inset shadow paints
   *over* its own background and hazes the edges, which is the kit's own rule
   and exactly why the first pass looked blurry at the sides. Same
   construction as .slider-fill and .bars .bar i.                            */

.swatch{
  border-radius:var(--r-sm);
  box-shadow:inset 3px 3px 7px var(--sh-dark), inset -3px -3px 7px var(--sh-light);
  padding:6px;
  display:block;
}

.swatch > i{
  display:block;
  min-height:38px;
  border-radius:9px;
  background:var(--sw, var(--accent-fill));
  box-shadow:0 1px 2px rgba(20,26,40,.2);   /* one tight shadow — a soft pair on a
                                               coloured fill just reads as haze */
}

/* ---------- 2c. The off switch says so ------------------------------------
   Colour alone was carrying the state, and off is the one that has to survive
   a glance across a room. The word sits in the empty half of the track and
   disappears under the knob when it slides over.                            */

.switch .track::after{
  content:'OFF';
  position:absolute; top:0; bottom:0; right:9px;
  display:flex; align-items:center;
  font-size:9px; font-weight:800; letter-spacing:.1em;
  color:var(--muted);
  transition:opacity var(--dur) var(--ease);
}

.switch input:checked ~ .track::after{ opacity:0; }

/* ---------- 3. High-contrast theme ---------------------------------------
   Not a different design — the same one, firmed up. The kit's soft shadows
   stay, so it still reads as Expy Med; they gain a hairline edge so a control
   boundary does not depend on perceiving a gradient. Text sits near 12:1
   rather than the light theme's 8.6:1.

   An earlier pass stripped the shadows entirely for hard black borders on
   pure white. That passed every check and looked like a different product. */

[data-theme="contrast"]{
  --bg:            #f7f8fa;
  --surface:       #f7f8fa;
  --sh-light:      #ffffff;
  --sh-dark:       #c4ccd9;   /* this theme leans on the hairline, not the shadow */
  --sh-light-soft: #ffffff;

  --text:   #141b26;
  --text-2: #303c4d;
  --muted:  #4a5668;
  --line:   rgba(20,27,38,.20);

  /* --danger below stays a true red on purpose — it is the separate error
     semantic, not the brand color, and should read as alarming rather than
     warm regardless of what the accent does. */
  /* Same hue as the other themes so the brand does not change colour here,
     re-solved at this theme's stricter levels rather than brightened to
     match — that is the whole point of it. */
  --accent:       #9B2E26;   /* 7.08:1 on the contrast surface */
  --accent-fill:  #D14A40;   /* white on it: 4.94:1 */
  --accent-tint:  #fdeeed;
  --accent-light: #E0554A;   /* white on it: 4.18:1 */
  --accent-deep:  #B83830;   /* white on it: 5.82:1 */
  --green:       #146B31;   /* 6.22:1 */
  --green-fill:  #1C8C3A;   /* 4.06:1 */
  --green-tint:  #ecf8ef;
  --amber:       #7a4900;
  --amber-fill:  #d99400;
  --amber-tint:  #fff5e6;
  --blue:        #0047c7;
  --blue-fill:   #1667e8;
  --blue-tint:   #eaf1ff;
  --danger:      #b8140a;

  color-scheme: light;
}

/* One hairline on top of the existing shadow — an edge you can see without
   relying on the gradient, without flattening the material. */
[data-theme="contrast"] .neu,
[data-theme="contrast"] .neu-sm,
[data-theme="contrast"] .neu-lg,
[data-theme="contrast"] .neu-flat,
[data-theme="contrast"] .neu-in,
[data-theme="contrast"] .neu-in-deep,
[data-theme="contrast"] .field,
[data-theme="contrast"] .segmented,
[data-theme="contrast"] .tabbar,
[data-theme="contrast"] .side-item,
[data-theme="contrast"] .row-card,
[data-theme="contrast"] .pill,
[data-theme="contrast"] .btn,
[data-theme="contrast"] .icon-btn{
  border:1px solid var(--line);
}

[data-theme="contrast"] .segmented .thumb{ border:1px solid var(--line); }
[data-theme="contrast"] .switch .track{ border:1px solid var(--line); }
[data-theme="contrast"] .check .box{ border-width:2px; border-color:var(--text-2); }
[data-theme="contrast"] .ring .track{ stroke:#d3dae4; filter:none; }
[data-theme="contrast"] .btn-accent{ border-color:var(--accent); }

/* ---------- 4. Field states ----------------------------------------------
   The kit has no invalid state. Intake forms are mostly validation, so this is
   the single most-needed addition. Colour is never the only cue: the message
   text and the aria wiring carry it too.                                     */

.field.is-invalid{
  box-shadow:
    inset 4px 4px 9px var(--sh-dark),
    inset -4px -4px 9px var(--sh-light),
    0 0 0 2px var(--danger);
}

.field.is-invalid:focus-within{
  box-shadow:
    inset 4px 4px 9px var(--sh-dark),
    inset -4px -4px 9px var(--sh-light),
    0 0 0 2.5px var(--danger);
}

.field-label{
  display:block;
  font-size:13px;
  font-weight:600;
  color:var(--text-2);
  margin-bottom:7px;
}

.field-label .req{ color:var(--danger); margin-left:2px; }

.field-note{ font-size:12.5px; color:var(--muted); margin-top:7px; }

.field-error{
  display:flex;
  align-items:flex-start;
  gap:6px;
  font-size:12.5px;
  font-weight:600;
  color:var(--danger);
  margin-top:7px;
}

.field-error svg{ width:14px; height:14px; flex:0 0 auto; margin-top:2px; }

.field textarea{
  width:100%; border:0; outline:0; background:transparent;
  resize:vertical; min-height:76px; font:inherit; color:inherit;
}

/* Safari on iOS zooms the page whenever a focused input computes below 16px,
   and there is no way to decline it other than meeting the threshold. Scoped
   to coarse pointers so the desktop type scale is untouched. */
@media (pointer: coarse){
  .field input,
  .field select,
  .field textarea,
  input, select, textarea{ font-size:16px; }
}

/* ---------- 5. Slide-over ------------------------------------------------
   Kit has toasts and nothing else. The practice board needs a detail surface
   that doesn't lose the queue behind it.                                     */

.sheet-scrim{
  position:fixed; inset:0; z-index:60;
  background:color-mix(in srgb, #1b2130 46%, transparent);
  opacity:0; transition:opacity var(--dur) var(--ease);
}

.sheet-scrim.show{ opacity:1; }

.sheet{
  position:fixed; z-index:61; background:var(--bg);
  display:flex; flex-direction:column;
  transition:transform var(--dur) var(--ease);
  /* A panel edge can't be a shadow here — it sits over a scrim, not the sheet. */
  box-shadow:-18px 0 46px rgba(27,33,48,.22);
}

/* Desktop: slides in from the right. */
.sheet{
  top:0; right:0; bottom:0;
  width:min(460px, 100%);
  transform:translateX(100%);
  padding:24px 26px calc(24px + env(safe-area-inset-bottom));
}

.sheet.show{ transform:translateX(0); }

/* Mobile: comes up from the bottom with a grab handle, which is what a thumb
   expects — and avoids a 460px panel on a 375px screen. */
@media (max-width:680px){
  .sheet{
    top:auto; left:0; right:0; bottom:0;
    width:auto; max-height:88dvh;
    border-radius:var(--r-xl) var(--r-xl) 0 0;
    transform:translateY(100%);
    padding:12px 20px calc(20px + env(safe-area-inset-bottom));
    box-shadow:0 -18px 46px rgba(27,33,48,.22);
  }
  .sheet.show{ transform:translateY(0); }
}

.sheet-grip{
  width:42px; height:5px; border-radius:3px;
  background:var(--sh-dark); margin:0 auto 14px;
  flex:0 0 auto;
}

@media (min-width:681px){ .sheet-grip{ display:none; } }

.sheet-head{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:14px; flex:0 0 auto; margin-bottom:16px;
}

.sheet-body{
  flex:1; min-height:0; overflow-y:auto; scrollbar-width:none;
  display:flex; flex-direction:column; gap:20px;
  /* overflow-x:visible cannot coexist with a scrolling y axis. Widen the
     viewport into the sheet's gutters, then restore the content alignment
     with padding so card shadows have room on every side. */
  margin:-16px -20px; padding:16px 20px;
}
.sheet-body > *{flex-shrink:0;}
.sheet-body::-webkit-scrollbar{ display:none; }
.sheet-foot{ flex:0 0 auto; padding-top:16px; display:flex; flex-direction:column; gap:10px; }
/* Nothing left to do on this record: no footer, and no padding for one. */
.sheet-foot:empty{ display:none; }

/* ---------- 6. Desktop shell ---------------------------------------------
   The kit is phone-frame only. The practice portal is a desk tool.           */

.shell{ display:flex; min-height:100dvh; }

/* The topbar is sticky at the top, so the sidebar has to start below it and be
   short by the same amount. At top:0 / 100dvh the nav slid up underneath the
   topbar and only locked once it had lost 66px off the top. */
:root{ --topbar-h:66px; }
.topbar{
  min-height:var(--topbar-h);
  overflow:visible;
}

/* The dot's neumorphic shadow paints outside its box. Without breathing room
   the topbar's padding edge (and body overflow-x) slice it flat — neu.css
   already warns never to put overflow on .brand itself for this reason. */
.topbar .brand{
  overflow:visible;
  padding:6px 14px 6px 6px;
  margin:-6px 0 -6px -6px;
  border-radius:16px;
}

.topbar .brand .dot{ flex-shrink:0; }

/* .pressable would sink the whole wordmark into an inset well; only the badge
   beside the name is shaped like a key and should press. */
.topbar .brand.is-pressed{ box-shadow:none; }

.topbar .brand.is-pressed .dot,
.topbar .brand:active .dot{
  box-shadow:inset 3px 3px 7px var(--sh-dark), inset -3px -3px 7px var(--sh-light);
}

.topbar .brand:active{ box-shadow:none; }

.sidebar{
  flex:0 0 224px;
  padding:14px 12px;
  display:flex; flex-direction:column; gap:10px;
  position:sticky; top:var(--topbar-h); align-self:flex-start;
  height:calc(100dvh - var(--topbar-h));
  overflow-y:auto; scrollbar-width:none;
  /* Navigation sits directly on the page; each key supplies its own depth. */
  border-radius:0 24px 24px 0;
  box-shadow:none;
}

.sidebar::-webkit-scrollbar{ display:none; }

/* Same raised-key physics as .tabbar button: inactive keys lift gently,
   the current one presses in and picks up the accent. */
.side-item{
  position:relative;
  display:flex; align-items:center; gap:12px;
  padding:11px 14px;
  border-radius:16px;
  color:var(--muted);
  font-size:14.5px; font-weight:600;
  background:var(--surface);
  box-shadow:4px 4px 9px var(--sh-dark), -4px -4px 9px var(--sh-light);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:box-shadow var(--dur) var(--ease), color var(--dur) var(--ease);
}

.side-item svg{ width:19px; height:19px; flex:0 0 auto; }

.side-item[aria-current="page"]{
  color:var(--accent);
  background:var(--surface);
  box-shadow:inset 4px 4px 8px var(--sh-dark), inset -4px -4px 8px var(--sh-light);
}

.side-item.is-pressed{
  box-shadow:inset 4px 4px 8px var(--sh-dark), inset -4px -4px 8px var(--sh-light);
}

.side-item:not([aria-current="page"]):hover{ background:var(--surface); }

.side-item:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

.side-badge{
  margin-left:auto;
  min-width:22px; height:22px; padding:0 6px;
  border-radius:11px;
  background:var(--accent-fill); color:#fff;
  font-size:11px; font-weight:800;
  display:grid; place-items:center;
  font-variant-numeric:tabular-nums;
}

.work{ flex:1; min-width:0; padding:22px 26px 80px; }
.work-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:22px; }

/* Below 900px the sidebar becomes the kit's own bottom tab bar. */
@media (max-width:900px){
  .shell{ display:block; }
  /* Opaque, with the fade lifted out into a strip above it. The kit's own
     dock fades from transparent because it sits inside a phone screen that
     scrolls behind it by design; a fixed app bar with a transparent top band
     just shows you the text sliding underneath. */
  .sidebar{
    position:fixed; left:0; right:0; bottom:0; top:auto; z-index:30;
    /* 4px was fine between flat rows; raised keys need room for their own
       shadows or they smear into each other. The patient bar uses 10px. */
    flex-direction:row; height:auto; gap:9px;
    padding:11px 12px calc(11px + env(safe-area-inset-bottom));
    background:var(--bg);
    border-radius:0;
    box-shadow:none;
  }

  .sidebar::before{
    content:''; position:absolute; left:0; right:0; bottom:100%; height:20px;
    background:linear-gradient(180deg, transparent, var(--bg));
    pointer-events:none;
  }

  /* Layout only — the raised-key look lives on .side-item above. */
  .side-item{
    flex:1;
    flex-direction:column; gap:5px;
    padding:9px 4px;
    font-size:10.5px; font-weight:600;
  }

  .side-item svg{ width:19px; height:19px; }

  /* Absolute, so without position:relative above it resolves against the
     fixed bar and lands on whichever tab happens to be in the middle. */
  .side-badge{ position:absolute; top:1px; right:calc(50% - 21px); }

  .work{ padding:16px 16px 120px; }
}

/* The patient app has the same bar and the same problem: the kit fades its
   dock from fully transparent, which on a real phone means the list scrolls
   visibly through the top half of it. */
@media (max-width:680px){
  .screen .dock{ background:var(--bg); }

  .screen .dock::before{
    content:''; position:absolute; left:0; right:0; bottom:100%; height:22px;
    background:linear-gradient(180deg, transparent, var(--bg));
    pointer-events:none;
  }
}

/* ---------- 6c. An asymmetric pair ----------------------------------------
   A tight, spread white against a wider, softer dark.

   The reason is the ceiling: the highlight is #ffffff and cannot go brighter,
   so on a pale ground it has barely any luminance to spend. Concentrating it
   — less blur, a positive spread, pulled in closer — buys apparent brightness
   that the colour itself cannot supply, while the dark half spreads wider and
   softer to carry the depth. The two halves stop mirroring each other and
   start behaving like a light source and a cast shadow, which is what the
   material is meant to be imitating.

   EVERY SELECTOR HERE GUARDS ITS OWN STATES WITH :not(), and that is the
   whole point of how it is written. An earlier attempt at this shipped as
   `:root:not([data-theme="contrast"]) .btn` — specificity (0,0,3,0), which
   outranks the kit's `.btn.is-pressed` at (0,0,2,0) — and silently killed
   the press on every button in the app. A :not() guard means these rules do
   not match a pressed, active or selected element at all, so the kit's state
   rules win no matter how the specificity falls. That is a structural
   guarantee rather than a specificity race, which is the only kind worth
   relying on in a stylesheet that sits on top of someone else's.

   Insets are deliberately untouched. The asymmetry models a light source
   falling on a raised object; a carved well reads fine with a matched pair,
   and .neu-in doubles as the pressed state for half these components. */

.neu:not(.is-pressed){
  box-shadow:8px 9px 17px var(--sh-dark), -6px -6px 10px 3px var(--sh-light);
}

.neu-sm:not(.is-pressed){
  box-shadow:6px 6px 12px var(--sh-dark), -4px -4px 8px 3px var(--sh-light);
}

.neu-lg:not(.is-pressed){
  box-shadow:11px 12px 23px var(--sh-dark), -8px -8px 14px 4px var(--sh-light);
}

.btn:not(.is-pressed):not(:active){
  box-shadow:6px 6px 12px var(--sh-dark), -4px -4px 8px 3px var(--sh-light);
}

.icon-btn:not(.is-pressed):not(:active):not(.is-on){
  box-shadow:6px 6px 12px var(--sh-dark), -4px -4px 8px 3px var(--sh-light);
}

.tabbar button:not(.is-pressed):not([aria-selected="true"]),
.side-item:not(.is-pressed):not([aria-current="page"]),
.brand .dot:not(.is-pressed){
  box-shadow:5px 5px 10px var(--sh-dark), -4px -4px 7px 2px var(--sh-light);
}

.chip:not(.is-pressed):not(.is-on),
.avatar:not(.is-pressed),
.thumb-icon:not(.is-pressed){
  box-shadow:4px 4px 9px var(--sh-dark), -3px -3px 6px 2px var(--sh-light);
}

/* ---------- 7. Data rows -------------------------------------------------
   Replaces the practice portal's 7-column table. A row is a card in a well,
   so it reflows instead of scrolling sideways.                               */

.rows{
  border-radius:var(--r-lg);
  box-shadow:inset 6px 6px 14px var(--sh-dark), inset -6px -6px 14px var(--sh-light);
  padding:12px;
  display:flex; flex-direction:column; gap:10px;
}

.row-card{
  display:grid;
  grid-template-columns:auto 1fr auto;
  align-items:center; gap:14px;
  padding:14px 16px;
  border-radius:var(--r);
  background:var(--surface);
  box-shadow:var(--d1) var(--d1) var(--b1) var(--sh-dark), calc(var(--d1) * -1) calc(var(--d1) * -1) var(--b1) var(--sh-light);
  text-align:left; width:100%;
  transition:box-shadow var(--dur) var(--ease);
}

.row-card .rc-main{ min-width:0; display:flex; flex-direction:column; gap:3px; }
.row-card .rc-title{ font-size:15px; font-weight:700; letter-spacing:-.01em; }
.row-card .rc-meta{ font-size:12.5px; color:var(--muted); }
.row-card .rc-end{ display:flex; align-items:center; gap:10px; flex:0 0 auto; }

@media (max-width:560px){
  .row-card{ grid-template-columns:auto 1fr; }
  .row-card .rc-end{ grid-column:1 / -1; justify-content:flex-end; }
}

/* Status pill — always a word, never colour alone. */
.pill{
  display:inline-flex; align-items:center; gap:6px;
  font-size:10.5px; font-weight:800;
  letter-spacing:.07em; text-transform:uppercase;
  padding:5px 10px; border-radius:999px;
  background:var(--tint, var(--surface));
  color:var(--ink, var(--muted));
  box-shadow:1px 1px 3px var(--sh-dark), -1px -1px 3px var(--sh-light);
  white-space:nowrap;
}

/* The dot is the bright hue, the label the text-safe one — so the pill reads
   as colour at a glance and still passes as small text. */
.pill i{ width:7px; height:7px; border-radius:50%; flex:0 0 auto; background:var(--dot, currentColor); }

.pill-wait  { --tint:var(--surface);     --ink:var(--muted); --dot:var(--muted); }
.pill-active{ --tint:var(--blue-tint);   --ink:var(--blue);  --dot:var(--blue-fill); }
.pill-ready { --tint:var(--amber-tint);  --ink:var(--amber); --dot:var(--amber-fill); }
.pill-done  { --tint:var(--green-tint);  --ink:var(--green); --dot:var(--green-fill); }
.pill-alert { --tint:var(--danger-tint); --ink:var(--danger);--dot:var(--danger-fill); }

/* ---------- 8. Stat tiles ------------------------------------------------ */

.stat-row{ display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:14px; }

.stat-tile{
  padding:16px 18px;
  border-radius:var(--r);
  display:flex; align-items:center; gap:14px;
}

.stat-tile .st-n{ font-size:24px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
.stat-tile .st-l{ font-size:11px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--muted); margin-top:4px; }

/* ---------- 9. Empty + loading ------------------------------------------- */

.empty-state{
  padding:44px 24px; text-align:center;
  display:flex; flex-direction:column; align-items:center; gap:12px;
}

.empty-state .thumb-icon{ width:52px; height:52px; border-radius:16px; }
.empty-state .thumb-icon svg{ width:22px; height:22px; }

.spinner{
  width:15px; height:15px; flex:0 0 auto;
  border:2px solid currentColor; border-right-color:transparent;
  border-radius:50%; animation:mt-spin .6s linear infinite;
}

@keyframes mt-spin{ to{ transform:rotate(360deg); } }

.loading-row{ display:flex; align-items:center; justify-content:center; gap:10px; padding:34px; color:var(--muted); font-size:14px; }

/* ---------- 10. Stepper ---------------------------------------------------
   The patient check-in runs as one sequence. The kit's refund demo has a bar
   of segments; this is that, made reusable and given a live label.           */

.stepper{ display:flex; flex-direction:column; gap:8px; }
.stepper-track{ display:flex; align-items:center; gap:6px; }

.stepper-track i{
  height:5px; flex:1; border-radius:3px;
  background:var(--sh-dark); opacity:.55;
  transition:background .4s var(--ease), opacity .4s var(--ease);
}

/* The long-packet form: one continuous bar instead of pips too thin to read.
   Both selectors are needed to outrank `.stepper-track i` above, which would
   otherwise impose that pip's own background and .55 opacity on the fill. */
.stepper-track.is-dense{
  display:block; height:5px; border-radius:3px; overflow:hidden;
  background:color-mix(in srgb, var(--sh-dark) 55%, transparent);
}

.stepper-track.is-dense i.stepper-fill{
  display:block; height:100%; min-width:3px;
  border-radius:3px; opacity:1;
  background:var(--accent-fill);
  transition:width .4s var(--ease);
}

.stepper-track i.done{ background:var(--accent-fill); opacity:1; }
.stepper-track i.now{ background:var(--accent-fill); opacity:1; box-shadow:0 0 0 2px color-mix(in srgb, var(--accent-fill) 30%, transparent); }
.stepper-label{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; }

/* ---------- 11. Review rows ---------------------------------------------- */

.kv{ display:flex; justify-content:space-between; gap:18px; padding:9px 0; border-bottom:1px solid var(--line); }
.kv:last-child{ border-bottom:0; }
.kv dt{ color:var(--muted); font-size:13.5px; flex:0 0 auto; max-width:48%; }
.kv dd{ font-size:13.5px; font-weight:600; text-align:right; margin:0; min-width:0; overflow-wrap:anywhere; }

/* ---------- 11b. Small button --------------------------------------------
   The kit ships .btn and .btn-lg. A data row needs a third size: shallower
   shadow to match the smaller footprint, so it still reads as one material. */

.btn-sm{
  min-height:34px; padding:0 14px;
  border-radius:11px; font-size:12.5px;
  box-shadow:3px 3px 8px var(--sh-dark), -3px -3px 8px var(--sh-light);
}

.btn-sm:active{ box-shadow:inset 3px 3px 7px var(--sh-dark), inset -3px -3px 7px var(--sh-light); }
.btn-sm.btn-accent{ box-shadow:0 3px 8px -2px rgba(0,0,0,.28); }
.btn-sm.btn-accent:active{ box-shadow:inset 0 2px 5px rgba(0,0,0,.3); }
.btn-sm svg{ width:14px; height:14px; }

/* ---------- 11c. Icon size fallback --------------------------------------
   The painter turns <i data-icon> into a bare <svg>, and an SVG with no width
   rule around it renders at its 300x150 intrinsic size and wrecks the layout
   it sits in. That has now bitten twice — an oversized back chevron, and a
   note whose icon pushed the page sideways. `:where()` carries no specificity,
   so every component rule still wins; this only catches the ones nobody wrote
   a rule for. Scoped to the icon viewBox, so rings, bars and LCDs are
   untouched. */

:where(svg[viewBox="0 0 24 24"]){ width:18px; height:18px; }

/* ---------- 11d. The calm pass -------------------------------------------
   One rule behind all of this: **a shadow means you can touch it.**

   The kit gives everything the same treatment, which is right for a showcase
   and wrong for a screen a receptionist reads all day. A single queue row was
   stacking five shadowed surfaces — a raised card, inside a carved tray, with
   a raised avatar, a shadowed pill and a raised icon plate inside it — and
   nothing in that pile told you which part you could press.

   So controls keep their depth. Everything that is only information becomes a
   flat plate: a slight tint off the surface, no shadow, no competition. The
   material is unchanged; there is just far less of it.                      */

:root{
  /* A surface that reads as an object without pretending to be a control. */
  --plate:      color-mix(in srgb, var(--sh-dark) 30%, var(--surface));
  --plate-line: color-mix(in srgb, var(--sh-dark) 55%, var(--surface));
}

[data-theme="dark"]{
  --plate:      color-mix(in srgb, var(--sh-light) 55%, var(--surface));
  --plate-line: color-mix(in srgb, var(--sh-light) 90%, var(--surface));
}

/* Decorative glyphs sit on their parent, without a grey backing square.
   Avatars retain the original light material and their small raised edge. */
.thumb-icon:not(.is-pressed){ background:transparent; box-shadow:none; }
.avatar{ background:var(--surface); }

/* The pill was a raised badge with a tint and a dot doing the same job three
   times over. The tint and the dot are enough. */
.pill{ box-shadow:none; }
.pill-wait{ box-shadow:inset 0 0 0 1px var(--plate-line); }

/* Lists group rows through spacing, without a second carved container. */
.rows{
  box-shadow:none;
  padding:10px;
}

.segmented{ box-shadow:inset 3px 3px 7px var(--sh-dark), inset -3px -3px 7px var(--sh-light); }
.field{ box-shadow:inset 3px 3px 7px var(--sh-dark), inset -3px -3px 7px var(--sh-light); }
.field:focus-within{
  box-shadow:inset 3px 3px 7px var(--sh-dark), inset -3px -3px 7px var(--sh-light),
             0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* A row inside a tray only needs to lift off it, not off the page. */
.row-card{ box-shadow:2px 2px 6px var(--sh-dark), -2px -2px 6px var(--sh-light); }

/* The ring's track was carrying a drop-shadow per stroke — a lot of rendering
   for a 42px graphic. Flat track, coloured bar. It has to be the line colour,
   not the plate: a stroke this thin disappears at plate contrast. */
.ring .track{ stroke:var(--plate-line); filter:none; }

/* ── Controls: these keep their depth, because they answer to a finger ──── */

.btn{ box-shadow:4px 4px 10px var(--sh-dark), -4px -4px 10px var(--sh-light); }
.icon-btn{ box-shadow:4px 4px 9px var(--sh-dark), -4px -4px 9px var(--sh-light); }

/* ---------- 11e. Keys look like keys --------------------------------------
   The calm pass flattened decoration, which left controls and containers
   wearing the same soft shadow — you could not tell a card from a button
   without trying it.

   So the two get different physics. A control is domed and lit from above: a
   gradient down the face and a hairline of light along its top edge, the way a
   real key catches the light. A container is flat: a shadow that says it sits
   on the page, and no highlight at all. Pressing still inverts, as the kit
   intends — this only makes the resting state legible.                       */

.btn,
.icon-btn,
.chip,
.filter-chip,
.palette-chip,
.vt-form,
.pick,
.who-item,
.visit-chip{
  background:linear-gradient(180deg,
    color-mix(in srgb, var(--sh-light) 60%, var(--surface)) 0%,
    var(--surface) 62%);
}

.btn{
  box-shadow:4px 4px 10px var(--sh-dark), -4px -4px 10px var(--sh-light),
             inset 0 1px 0 color-mix(in srgb, var(--sh-light) 85%, transparent);
}

.icon-btn{
  box-shadow:4px 4px 9px var(--sh-dark), -4px -4px 9px var(--sh-light),
             inset 0 1px 0 color-mix(in srgb, var(--sh-light) 85%, transparent);
}

/* This was `background:var(--accent)` — the darker, text-safe variant meant
   for small text on the surface, not for painting the entire face of the
   primary button. Section 2's own comment warns about exactly this: "left
   alone these would render in the text-safe value and the whole UI would
   look muddy." The gradient runs --accent-light through the fill to
   --accent-deep, which a solid fill can't be. */
.btn-accent{
  background:var(--accent-gradient);
  box-shadow:0 4px 11px -3px rgba(0,0,0,.32),
             inset 0 1px 0 rgba(255,255,255,.28);
}

.btn-accent:active,
.btn-accent.is-pressed{ box-shadow:inset 0 2px 6px rgba(0,0,0,.32); }

/* Containers: flat, and never wearing a control's highlight. */
.card,
.q-card,
.pulse-card,
.vt-card,
.stat-tile,
.auth-card,
.checkin-card,
.bblock,
.step-card{
  background:var(--surface);
  box-shadow:var(--d2) var(--d2) var(--b2) var(--sh-dark),
             calc(var(--d2) * -1) calc(var(--d2) * -1) var(--b2) var(--sh-light);
}

/* A row that opens something is a control, so it gets the face — but a plain
   row of data does not. */
button.row-card{
  background:linear-gradient(180deg,
    color-mix(in srgb, var(--sh-light) 55%, var(--surface)) 0%,
    var(--surface) 62%);
  box-shadow:2px 2px 6px var(--sh-dark), -2px -2px 6px var(--sh-light),
             inset 0 1px 0 color-mix(in srgb, var(--sh-light) 70%, transparent);
}

/* ---------- 11f. Colour beyond the accent ---------------------------------
   Every chart drawn in the same red made four different measurements look like
   one. Each now carries its own hue — --hue-1..4 stay flat, single colours,
   for the line chart's strokes/dots/legend swatches, where a gradient on a
   2px stroke would just look noisy. A ring or a bar is a large enough surface
   to carry real depth, so those get a second, gradient-bearing token built the
   same way as --accent-gradient: the fill's own lightness and saturation,
   swept from a paler top to the theme's darker text-safe variant. No text
   ever sits on a bar or a ring, so unlike the button gradient this one is
   free to move in brightness, not just hue.                                 */

:root{
  --hue-1:var(--accent-fill);
  --hue-2:var(--blue-fill);
  --hue-3:var(--green-fill);
  --hue-4:var(--amber-fill);

  --hue-1-gradient: linear-gradient(180deg, var(--accent-light) 0%, var(--accent-fill) 55%, var(--accent-deep) 100%);
  --hue-2-gradient: linear-gradient(180deg, color-mix(in srgb, var(--blue-fill) 65%, white 35%) 0%, var(--blue-fill) 55%, var(--blue) 100%);
  --hue-3-gradient: linear-gradient(180deg, color-mix(in srgb, var(--green-fill) 65%, white 35%) 0%, var(--green-fill) 55%, var(--green) 100%);
  --hue-4-gradient: linear-gradient(180deg, color-mix(in srgb, var(--amber-fill) 65%, white 35%) 0%, var(--amber-fill) 55%, var(--amber) 100%);
}

.hue-1{ --chart-ink:var(--hue-1); --chart-fill:var(--hue-1-gradient); }
.hue-2{ --chart-ink:var(--hue-2); --chart-fill:var(--hue-2-gradient); }
.hue-3{ --chart-ink:var(--hue-3); --chart-fill:var(--hue-3-gradient); }
.hue-4{ --chart-ink:var(--hue-4); --chart-fill:var(--hue-4-gradient); }

/* `stroke` cannot take a CSS gradient — SVG paint only accepts a colour or a
   url() reference to a <linearGradient>. The four defs it points at are a
   one-time hidden sprite in the page (see practice.html), one per hue, built
   from the exact same tokens as the CSS gradient above via stop-color:var()
   so a theme switch re-paints both at once. */
.ring.hue-1 .bar{ stroke:url(#ring-grad-1); }
.ring.hue-2 .bar{ stroke:url(#ring-grad-2); }
.ring.hue-3 .bar{ stroke:url(#ring-grad-3); }
.ring.hue-4 .bar{ stroke:url(#ring-grad-4); }

.bars.hue-1 .bar i,
.bars.hue-2 .bar i,
.bars.hue-3 .bar i,
.bars.hue-4 .bar i{ background:var(--chart-fill); }

/* ---------- 12. Utilities the kit is missing ----------------------------- */

.stack{ display:flex; flex-direction:column; }
.g18{ gap:18px; } .g28{ gap:28px; }
.mt28{ margin-top:28px; } .mt36{ margin-top:36px; }
.text-center{ text-align:center; }
.nowrap{ white-space:nowrap; }
.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;
}

/* A raised element inside a scroll container gets its shadow sliced at the
   clip edge. The kit solves this with .rail-room; this is the vertical case. */
.scroll-room{ padding:14px 12px; margin:-14px -12px; }


/* ---------- 13. Progress ring --------------------------------------------
   The kit sizes the circle as r = (size - stroke) / 2, which puts the stroke's
   outer edge exactly on the SVG viewport. The track then carries a paired
   drop-shadow that has nowhere to render, so it is sliced flat at the sides —
   the ring looks like its container is cut off.

   An SVG clips to its viewport by default, and a filter clips to its own
   region on top of that. Both need opening up.                              */

.ring svg{ overflow:visible; }
.ring .track{ filter:drop-shadow(2px 2px 3px var(--sh-dark)) drop-shadow(-2px -2px 3px var(--sh-light)); }

/* ---------- 14. Bars ------------------------------------------------------
   The kit insets the fill by 3px on three sides but sizes it `height:100%` of
   the whole track, so a full bar runs 3px past the top and `overflow:hidden`
   slices its rounded corners flat — it reads as overflowing the well rather
   than filling it.

   Rebuilt on the vertical dimmer's proportions: a deeper well, a generous
   inset, and a fill whose height is computed against the space actually
   available. Nothing overflows, so nothing needs clipping — which also means
   the fill's shadow is never sliced, per the kit's own rule.               */

.bars{ gap:12px; }

.bars .bar{
  --bar-pad:6px;
  border-radius:22px;
  overflow:visible;                 /* the calc keeps the fill inside */
  /* The dimmer's 5px/12px well suits a 280px-tall control; on a 150px bar it
     reads as a trench. This sits between that and the kit's original 3px/7px. */
  box-shadow:inset 4px 4px 9px var(--sh-dark), inset -4px -4px 9px var(--sh-light);
}

.bars .bar i{
  left:var(--bar-pad); right:var(--bar-pad); bottom:var(--bar-pad);
  border-radius:16px;
  background:var(--accent-fill);
  /* The dimmer's fill is grey and carries the soft paired shadow. On a
     coloured fill a white inner shadow reads as haze, so this takes one tight
     shadow instead — the kit's rule for accent fills. */
  box-shadow:0 2px 6px rgba(20,26,40,.26);
  height:calc((100% - var(--bar-pad) * 2) * var(--v, 0));
  transition:height .7s var(--ease);
}

/* Optional value inside the fill, the way the dimmer shows its number. */
.bars .bar i b{
  position:absolute; left:0; right:0; bottom:8px;
  text-align:center; font-size:11px; font-weight:800;
  color:#fff; opacity:.92;
}

/* ---------- 15. Dropdown menu -------------------------------------------- */

/* The kit's menu button is already flex; it now carries a check on the right
   so the current choice is marked by a glyph and not only by an inset shadow. */
.dropdown .menu button{ justify-content:space-between; gap:10px; }
.dropdown .menu button svg{ flex:0 0 auto; color:var(--accent); }

/* ---------- 16. Date picker ----------------------------------------------
   `<input type="date">` renders the browser's own picker, which ignores every
   token in this file and looks like a different application. It is also poor
   for the field we need it for most: a date of birth is decades back, and a
   month-by-month calendar makes that dozens of taps.

   So the trigger is a normal .field, and the panel is ours — with the month
   and year as separate controls, either of which opens a grid.               */

.datefield{ position:relative; }

.datefield-trigger{
  width:100%;
  text-align:left;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

.datefield-trigger .placeholder{ color:var(--muted); }

.datepanel{
  position:absolute;
  z-index:40;
  top:calc(100% + 10px);
  left:0;
  width:min(330px, calc(100vw - 32px));
  padding:16px;
  border-radius:var(--r-lg);
  background:var(--bg);
  box-shadow:
    var(--d2) var(--d2) var(--b2) var(--sh-dark),
    calc(var(--d2) * -1) calc(var(--d2) * -1) var(--b2) var(--sh-light),
    0 18px 40px rgba(27,33,48,.18);
  display:flex;
  flex-direction:column;
  gap:12px;
}

/* On a phone the panel would run off the bottom of a scrolled field, so it
   becomes a sheet anchored to the viewport instead. */
@media (max-width:560px){
  .datepanel{
    position:fixed;
    top:auto; left:0; right:0; bottom:0;
    width:auto;
    border-radius:var(--r-xl) var(--r-xl) 0 0;
    padding:14px 18px calc(18px + env(safe-area-inset-bottom));
    box-shadow:0 -16px 40px rgba(27,33,48,.24);
  }
}

.datepanel-scrim{ position:fixed; inset:0; z-index:39; }

.datepanel-head{ display:flex; align-items:center; gap:8px; }

.datepanel-title{
  flex:1;
  display:flex;
  align-items:center;
  gap:6px;
  justify-content:center;
}

/* Month and year are separate buttons — tapping the year is the whole point
   for a date of birth. */
.dp-jump{
  padding:7px 12px;
  border-radius:999px;
  font-size:14.5px;
  font-weight:700;
  letter-spacing:-.01em;
  background:var(--surface);
  box-shadow:2px 2px 5px var(--sh-dark), -2px -2px 5px var(--sh-light);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

.dp-jump[aria-expanded="true"]{
  color:var(--accent);
  box-shadow:inset 3px 3px 6px var(--sh-dark), inset -3px -3px 6px var(--sh-light);
}

.dp-week{
  display:grid;
  grid-template-columns:repeat(7,1fr);
  gap:2px;
}

.dp-week span{
  text-align:center;
  font-size:10.5px;
  font-weight:700;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--muted);
  padding-bottom:2px;
}

.dp-grid{
  display:grid;
  grid-template-columns:repeat(7,1fr);
  gap:4px;
}

.dp-day{
  aspect-ratio:1;
  display:grid;
  place-items:center;
  border-radius:50%;
  font-size:13.5px;
  font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--text-2);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:box-shadow var(--dur) var(--ease), color var(--dur) var(--ease);
}

.dp-day:hover:not(:disabled):not([aria-selected="true"]){
  box-shadow:2px 2px 5px var(--sh-dark), -2px -2px 5px var(--sh-light);
}

.dp-day:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }

.dp-day.is-outside{ color:var(--muted); opacity:.5; }

.dp-day:disabled{ opacity:.28; cursor:default; }

/* Today is marked with a dot, not just a colour — one more non-colour signal. */
.dp-day.is-today{ color:var(--accent); }
.dp-day.is-today::after{
  content:"";
  position:absolute;
  transform:translateY(13px);
  width:4px; height:4px;
  border-radius:50%;
  background:var(--accent-fill);
}
.dp-day{ position:relative; }

.dp-day[aria-selected="true"]{
  color:#fff;
  background:var(--accent-fill);
  box-shadow:0 2px 6px rgba(20,26,40,.28);
}

.dp-day[aria-selected="true"]::after{ background:#fff; }

/* Month and year grids reuse the same well, so switching modes does not
   change the panel's height and make it jump. */
.dp-list{
  display:grid;
  gap:6px;
  max-height:236px;
  overflow-y:auto;
  scrollbar-width:none;
  padding:4px;
}

.dp-list::-webkit-scrollbar{ display:none; }
.dp-list.months{ grid-template-columns:repeat(3,1fr); }
.dp-list.years{ grid-template-columns:repeat(4,1fr); }

.dp-list button{
  padding:10px 4px;
  border-radius:var(--r-sm);
  font-size:13.5px;
  font-weight:600;
  font-variant-numeric:tabular-nums;
  color:var(--text-2);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

.dp-list button:hover{ box-shadow:2px 2px 5px var(--sh-dark), -2px -2px 5px var(--sh-light); }
.dp-list button:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

.dp-list button[aria-selected="true"]{
  color:#fff;
  background:var(--accent-fill);
  box-shadow:0 2px 6px rgba(20,26,40,.28);
}

.datepanel-foot{
  display:flex;
  align-items:center;
  gap:8px;
  padding-top:4px;
  border-top:1px solid var(--line);
}

/* ---------- 15. Line chart ------------------------------------------------
   The kit has a ring, a bar column and a segment display — nothing that shows
   a shape over time. This is that, in the same material: a soft area wash
   under a drawn line, a hairline grid, and a raised dot on the last reading
   so the eye lands on "now".                                                 */

.chart{ display:flex; flex-direction:column; gap:10px; }

/* A fixed height with preserveAspectRatio="none" on the svg: the plot stretches
   to the card's width while keeping the height it was designed at. `height:auto`
   held the viewBox ratio instead and grew to 446px in a wide card. */
.chart-plot{ position:relative; width:100%; }
.chart-plot svg{ display:block; width:100%; height:var(--chart-h, 150px); overflow:visible; }

.chart-grid line{ stroke:var(--plate-line); stroke-width:1; vector-effect:non-scaling-stroke; }

/* The axis is HTML, not SVG text: the plot is drawn under a non-uniform scale
   and would stretch its own labels out of shape. */
.chart-x{ display:flex; }
.chart-x span{ flex:1; text-align:center; font-size:10.5px; font-weight:600; color:var(--muted); }
.chart-x span:first-child{ text-align:left; }
.chart-x span:last-child{ text-align:right; }

.chart-line{ fill:none; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
.chart-area{ stroke:none; opacity:.16; }

/* The last reading is the one being asked about, so it is the one that lifts. */
/* An HTML element, not an SVG shape — see practice.html's lineChart() for why:
   this plot scales non-uniformly, and only something positioned by plain
   percentages, outside that coordinate system, can stay circular under it. */
.chart-dot{
  position:absolute; z-index:1;
  width:9px; height:9px; margin:-4.5px 0 0 -4.5px;
  border-radius:50%;
  box-shadow:0 0 0 2.5px var(--surface);
  pointer-events:none;
}

.chart-legend{ display:flex; flex-wrap:wrap; gap:14px; }
.chart-key{ display:inline-flex; align-items:center; gap:6px; font-size:11.5px; font-weight:600; color:var(--text-2); }
.chart-key i{ width:9px; height:9px; border-radius:3px; flex:0 0 auto; }
.chart-key b{ font-variant-numeric:tabular-nums; color:var(--text); }

.chart-empty{ padding:26px 12px; text-align:center; color:var(--muted); font-size:13px; }

/* ---------- Surface hierarchy -------------------------------------------
   Keep the existing spacing and radii. Depth belongs to the main card and
   its controls; supporting content sits quietly on the same material. */
.surface-plain{
  background:transparent;
  border-radius:var(--r);
  box-shadow:none;
}

/* The kit also carves the dock at its mobile breakpoint. */
.screen .dock .tabbar{ box-shadow:none; }

/* Option lists share their parent material. Only the small add/check key
   carries depth, like the reference's switches inside an otherwise calm row.
   The entire row remains the hit target and retains its keyboard outline. */
.choice-row,
.choice-row:active,
.choice-row.is-pressed{
  background:transparent;
  box-shadow:none;
}

.choice-mark{
  flex:0 0 auto;
  display:grid; place-items:center;
  width:24px; height:24px; border-radius:8px;
  background:var(--surface); color:var(--muted);
  box-shadow:3px 3px 7px var(--sh-dark), -3px -3px 6px var(--sh-light);
  transition:box-shadow var(--dur) var(--ease), color var(--dur) var(--ease);
}

.choice-mark svg{ width:15px; height:15px; }
.choice-row[aria-pressed="true"] .choice-mark,
.choice-row:active .choice-mark,
.choice-row.is-pressed .choice-mark{
  color:var(--accent);
  box-shadow:inset 2px 2px 5px var(--sh-dark), inset -2px -2px 5px var(--sh-light);
}

.choice-row:disabled{ cursor:default; opacity:.55; }
[data-theme="contrast"] .choice-mark{ border:1px solid var(--line); }
