/* Components — The Dentist Is In
   Requires tokens.css. Class names map 1:1 to ViewComponents. */
*{box-sizing:border-box}
body{margin:0;font-family:var(--font);font-size:var(--text-body);color:var(--ink);
     background:var(--surface-sunken);-webkit-font-smoothing:antialiased;line-height:1.5}
h1,h2,h3,h4{margin:0;font-weight:650;letter-spacing:-.01em}
p{margin:0 0 var(--s3)}
.num,td.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}

/* ============================================================
   COMPONENT · Icon — 16px stroke, inherits currentColor
   ============================================================ */
.icon{width:16px;height:16px;flex:0 0 16px;stroke:currentColor;fill:none;
      stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
.icon--lg{width:20px;height:20px;flex-basis:20px}
.icon--xl{width:28px;height:28px;flex-basis:28px;stroke-width:1.5}

/* ============================================================
   COMPONENT · StatusPill — dot + label. Colour from a central map.
   NEVER accepts a colour argument.
   ============================================================ */
.pill{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 10px;
      border-radius:var(--radius-pill);font-size:var(--text-caption);font-weight:600;
      white-space:nowrap;border:1px solid transparent}
.pill::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:0 0 6px}
.pill--danger {background:var(--danger-subtle); color:var(--danger); border-color:var(--danger-border)}
.pill--success{background:var(--success-subtle);color:var(--success);border-color:var(--success-border)}
.pill--warning{background:var(--warning-subtle);color:var(--warning);border-color:var(--warning-border)}
.pill--neutral{background:var(--neutral-subtle);color:var(--neutral);border-color:var(--neutral-border)}
.pill--accent {background:var(--accent-subtle); color:var(--accent); border-color:var(--accent-border)}
.pill--bare::before{display:none}

/* ============================================================
   COMPONENT · Stepper — the lab case lifecycle, made visible.
   A case moving through 5 stages is inherently visual; a text
   label wastes that. This is the product's signature element.
   ============================================================ */
.stepper{display:flex;align-items:center;gap:0}
.stepper__node{display:flex;flex-direction:column;align-items:center;gap:6px;position:relative}
.stepper__dot{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;
              background:var(--surface);border:2px solid var(--rule-strong);color:var(--ink-muted)}
.stepper__dot .icon{width:11px;height:11px;flex-basis:11px;stroke-width:2.5}
.stepper__label{font-size:var(--text-micro);color:var(--ink-muted);white-space:nowrap}
.stepper__bar{height:2px;width:44px;background:var(--rule-strong);margin:0 2px;margin-bottom:20px}
.stepper__node.is-done .stepper__dot{background:var(--success);border-color:var(--success);color:#fff}
.stepper__node.is-done .stepper__label{color:var(--ink-secondary)}
.stepper__bar.is-done{background:var(--success)}
.stepper__node.is-current .stepper__dot{background:var(--accent);border-color:var(--accent);color:#fff;
     box-shadow:0 0 0 4px var(--accent-subtle)}
.stepper__node.is-current .stepper__label{color:var(--accent);font-weight:600}
.stepper__node.is-late .stepper__dot{background:var(--danger);border-color:var(--danger);color:#fff;
     box-shadow:0 0 0 4px var(--danger-subtle)}
.stepper__node.is-late .stepper__label{color:var(--danger);font-weight:600}

/* Compact — for table rows. Five dots, no labels. */
.stepper--mini{gap:3px}
.stepper--mini .stepper__dot{width:9px;height:9px;border-width:1.5px}
.stepper--mini .stepper__dot .icon{display:none}
.stepper--mini .stepper__bar{width:10px;height:1.5px;margin-bottom:0}
.stepper--mini .stepper__label{display:none}
.stepper--mini .stepper__node{gap:0}

/* ============================================================
   COMPONENT · Avatar — patient initials. Humanises dense tables.
   ============================================================ */
.avatar{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;
        background:var(--accent-subtle);color:var(--accent);font-size:var(--text-micro);
        font-weight:700;letter-spacing:.02em;flex:0 0 28px}
.avatar--lg{width:36px;height:36px;flex-basis:36px;font-size:var(--text-dense)}
.avatar--muted{background:var(--neutral-subtle);color:var(--neutral)}
.who{display:flex;align-items:center;gap:10px;min-width:0}
/* min-width:0 on .who__name (a flex item of .who) and on .who itself (a flex item wherever
   it's nested, e.g. inside Phase 7 slice 5's new phone-card .table tbody td) — without it, a
   flex item's default min-width:auto lets it grow past its container instead of ever
   shrinking far enough for text-overflow:ellipsis to engage; the exact bug class this
   codebase has already hit and fixed twice (.today-grid, .app__main), applied here
   pre-emptively rather than waiting to find it live a third time. */
.who__name{font-weight:550;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.who__sub{font-size:var(--text-caption);color:var(--ink-muted)}
/* ClickUp 86d42wtm3: every other .who__name usage (patients/lab_cases/invoices/prescriptions
   index rows) wraps the *entire* .who row in a link with its own inline
   text-decoration:none;color:inherit, so the browser default link styling never applies.
   AgendaRowComponent's Today row is the one place a real <a> lives *inside* .who__name
   itself (only the name is clickable there, not the whole row) — it fell through to the
   browser's default blue-underlined link. Scoped to `a` so it can't affect any other page. */
.who__name a{color:var(--ink);font-weight:600;text-decoration:none}
.who__name a:hover{text-decoration:underline}

/* ============================================================
   COMPONENT · Button
   ============================================================ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:38px;
     padding:0 14px;border-radius:var(--radius-sm);font-family:inherit;font-size:var(--text-body);
     font-weight:550;border:1px solid transparent;cursor:pointer;white-space:nowrap;
     text-decoration:none;transition:background .12s,border-color .12s,box-shadow .12s}
.btn--primary{background:var(--accent);color:#fff;box-shadow:var(--shadow-sm)}
.btn--primary:hover{background:var(--accent-hover)}
.btn--secondary{background:var(--surface);color:var(--ink);border-color:var(--rule-strong);box-shadow:var(--shadow-sm)}
.btn--secondary:hover{background:var(--surface-hover)}
.btn--destructive{background:var(--surface);color:var(--danger);border-color:var(--danger-border)}
.btn--destructive:hover{background:var(--danger-subtle)}
.btn--ghost{background:transparent;color:var(--ink-secondary)}
.btn--ghost:hover{background:var(--surface-hover);color:var(--ink)}
.btn--chase{background:var(--danger);color:#fff;box-shadow:var(--shadow-sm)}
.btn--chase:hover{background:#A81E1E}
.btn--sm{height:30px;padding:0 10px;font-size:var(--text-dense);gap:5px}
.btn--sm .icon{width:14px;height:14px;flex-basis:14px}
.btn--lab{height:var(--tap-min)}
.btn--clinical{height:var(--tap-clinical);font-size:var(--text-large)}
.btn:disabled{opacity:.4;cursor:not-allowed;box-shadow:none}

/* ============================================================
   COMPONENT · Card
   ============================================================ */
.card{background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
      box-shadow:var(--shadow-sm)}
.card__header{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
              padding:var(--s3) var(--s4);border-bottom:1px solid var(--rule);
              font-size:var(--text-section);font-weight:650}
.card__body{padding:var(--s4)}
.card__body--flush{padding:0}

/* ============================================================
   COMPONENT · Stat — headline numbers. Hierarchy, not decoration.
   ============================================================ */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s3)}

/* Marketing Library — a grid of PhotoPairComponent cards (Phase 4 slice 5) */
.marketing-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(420px,1fr));gap:var(--s4)}
.stat{background:var(--surface);border:1px solid var(--rule);border-left:3px solid var(--rule-strong);
      border-radius:var(--radius);padding:var(--s3) var(--s4);box-shadow:var(--shadow-sm)}
.stat--danger{border-left-color:var(--danger);background:linear-gradient(90deg,var(--danger-subtle) 0%,var(--surface) 60%)}
.stat--warning{border-left-color:var(--warning);background:linear-gradient(90deg,var(--warning-subtle) 0%,var(--surface) 60%)}
.stat--success{border-left-color:var(--success)}
.stat__val{font-size:26px;font-weight:700;line-height:1.1;font-variant-numeric:tabular-nums}
.stat--danger .stat__val{color:var(--danger)}
.stat--warning .stat__val{color:var(--warning)}
.stat__label{display:flex;align-items:center;gap:6px;margin-top:var(--s1);
             font-size:var(--text-caption);color:var(--ink-secondary);font-weight:500}

/* ============================================================
   COMPONENT · FormField
   ============================================================ */
.field{margin-bottom:var(--s4)}
.field__label{display:block;margin-bottom:6px;font-size:var(--text-caption);
              font-weight:600;color:var(--ink-secondary);letter-spacing:.01em}
.field__input,.field__select,.field__textarea{
  width:100%;height:38px;padding:0 var(--s3);font-family:inherit;font-size:var(--text-body);
  color:var(--ink);background:var(--surface);border:1px solid var(--rule-strong);
  border-radius:var(--radius-sm);outline:none;transition:border-color .12s,box-shadow .12s}
.field__textarea{height:auto;padding:10px var(--s3);resize:vertical;line-height:1.55}
.field__input:focus,.field__select:focus,.field__textarea:focus{
  border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-subtle)}
.field__hint{display:flex;gap:5px;margin-top:6px;font-size:var(--text-caption);color:var(--ink-muted)}
.field__error{display:flex;gap:5px;margin-top:6px;font-size:var(--text-caption);color:var(--danger)}
.field--invalid .field__input{border-color:var(--danger)}
.field--clinical .field__input{height:var(--tap-clinical);font-size:var(--text-large)}
/* UAT feedback: enlarged date pickers (Appointment Date, Patient DoB) — same tap-target
   sizing as field--clinical, kept as its own modifier since "large" and "holds clinical
   content" are unrelated concerns that happen to share one CSS rule today. */
.field--lg .field__input{height:var(--tap-clinical);font-size:var(--text-large)}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3)}

/* Duration step-slider (UAT feedback, Item 3) — the "Custom" fallback behind the duration
   preset chips, 5-90 minutes in 5-minute steps. Cross-browser range styling: WebKit/Firefox
   thumb+track rules can't be combined into one selector without invalidating the whole
   rule in some engines, so they're kept as separate prefixed blocks deliberately. */
.field__range{flex:1;height:38px;padding:0;background:transparent;-webkit-appearance:none;appearance:none}
.field__range::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:var(--rule-strong)}
.field__range::-moz-range-track{height:4px;border-radius:2px;background:var(--rule-strong)}
.field__range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:20px;height:20px;
  margin-top:-8px;border-radius:50%;background:var(--accent);border:3px solid var(--surface);
  box-shadow:0 0 0 1px var(--accent);cursor:pointer}
.field__range::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:var(--accent);
  border:3px solid var(--surface);box-shadow:0 0 0 1px var(--accent);cursor:pointer}
.field__range:focus{outline:none}
.field__range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px var(--accent),0 0 0 4px var(--accent-subtle)}
.field__range:focus::-moz-range-thumb{box-shadow:0 0 0 1px var(--accent),0 0 0 4px var(--accent-subtle)}

/* Inline calendar (UAT feedback, ClickUp 86d42wthq) — appointment booking date, replacing
   the native <input type="date">. Entirely CSS-grid for the day cells; JS only ever
   populates/re-populates .cal__grid's children, never restyles them inline. */
.cal{max-width:320px;padding:var(--s3);border:1px solid var(--rule-strong);border-radius:var(--radius)}
.cal__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--s2)}
.cal__title{font-weight:600;font-size:var(--text-body)}
.cal__weekdays{display:grid;grid-template-columns:repeat(7,1fr);text-align:center;
  font-size:var(--text-micro);font-weight:700;color:var(--ink-muted);text-transform:uppercase;
  letter-spacing:.03em;margin-bottom:4px}
.cal__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.cal__day{aspect-ratio:1;display:flex;align-items:center;justify-content:center;
  font-size:var(--text-dense);border:none;border-radius:var(--radius-sm);background:transparent;
  color:var(--ink);cursor:pointer;transition:background-color .12s}
.cal__day:hover{background:var(--surface-hover)}
.cal__day--blank{visibility:hidden;cursor:default}
.cal__day--today{font-weight:700;color:var(--accent)}
.cal__day--selected{background:var(--accent);color:#fff;font-weight:600}
.cal__day--selected:hover{background:var(--accent-hover)}
.cal__selected{margin-top:var(--s2);font-size:var(--text-caption);color:var(--ink-secondary)}
.inputgroup{position:relative;display:flex;align-items:center}
.inputgroup .icon{position:absolute;left:11px;color:var(--ink-muted);pointer-events:none}
.inputgroup .field__input{padding-left:34px}

/* ============================================================
   COMPONENT · Table — rows carry their own primary action
   ============================================================ */
.table{width:100%;border-collapse:collapse;font-size:var(--text-dense)}
.table thead th{padding:10px var(--s3);text-align:left;background:var(--surface-sunken);
                font-size:var(--text-micro);font-weight:700;color:var(--ink-secondary);
                text-transform:uppercase;letter-spacing:.05em;
                border-bottom:1px solid var(--rule);white-space:nowrap}
.table tbody td{padding:0 var(--s3);height:52px;border-bottom:1px solid var(--rule);vertical-align:middle}
.table tbody tr{transition:background .1s}
.table tbody tr:hover{background:var(--surface-hover)}
.table tbody tr.is-overdue{box-shadow:inset 3px 0 0 var(--danger)}
.table tbody tr.is-overdue td:first-child{padding-left:15px}
.table .right{text-align:right}
.table .actions{display:flex;gap:6px;justify-content:flex-end;opacity:.35;transition:opacity .12s}
.table tbody tr:hover .actions{opacity:1}
.table tbody tr.is-overdue .actions{opacity:1}
.late{color:var(--danger);font-weight:650}
.due-soon{color:var(--warning);font-weight:600}

/* Phase 7 slice 5 (ClickUp 86d3upap9): "Patient / lab lists | Tables → cards." CSS-only
   reflow of the existing <table> markup — no duplicate mobile-only markup to keep in sync.
   thead hides; each <tr> becomes a bordered card; each <td> becomes a labelled row inside it
   via a `data-label` attribute (added only on the views' own secondary cells — the first,
   identity cell needs no label, it reads as the card's own heading, and the last, action
   cell becomes a full-width button at the card's foot instead of a label+value row).
   .actions' hover-reveal fade (line 211) is meaningless on a touch device with no hover
   state, so it's forced fully opaque here — same override this file already gives
   .is-overdue rows for the identical reason. */
@media (max-width:640px){
  .table thead{display:none}
  .table, .table tbody, .table tr{display:block;width:100%}
  .table tbody tr{border:1px solid var(--rule);border-radius:var(--radius);
                  margin-bottom:var(--s3);padding:var(--s3) var(--s3) var(--s2);background:var(--surface);
                  box-shadow:none}
  .table tbody tr:last-child{margin-bottom:0}
  .table tbody tr.is-overdue{border-left:3px solid var(--danger)}
  .table tbody tr.is-overdue td:first-child{padding-left:0}
  .table tbody td{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
                  height:auto;padding:8px 0;border-bottom:1px solid var(--rule)}
  .table tbody td:first-child{padding-top:0}
  .table tbody td[data-label]::before{content:attr(data-label);font-weight:600;
                                       color:var(--ink-muted);font-size:var(--text-caption);flex:0 0 auto}
  .table tbody td:last-child{display:block;padding-bottom:0;border-bottom:none}
  .table tbody td.right .btn,.table tbody td:last-child .actions .btn{width:100%}
  .table .actions{opacity:1}
}

/* ============================================================
   COMPONENT · EmptyState — teaches, never a bare panel
   ============================================================ */
.empty{padding:var(--s6) var(--s4);text-align:center}
.empty__icon{display:grid;place-items:center;width:44px;height:44px;margin:0 auto var(--s3);
             border-radius:50%;background:var(--surface-sunken);color:var(--ink-muted)}
.empty__title{font-weight:650;margin-bottom:var(--s1)}
.empty__msg{margin-bottom:var(--s4);color:var(--ink-secondary);font-size:var(--text-dense);
            max-width:36ch;margin-left:auto;margin-right:auto}

/* ============================================================
   COMPONENT · UndoToast — replaces confirmation dialogs
   ============================================================ */
.toast{display:inline-flex;align-items:center;gap:var(--s4);padding:12px var(--s4);
       background:var(--ink);color:#fff;border-radius:var(--radius);font-size:var(--text-dense);
       box-shadow:var(--shadow-lg);position:relative;overflow:hidden}
.toast__action{color:#93C5FD;font-weight:650;background:none;border:0;cursor:pointer;
               font-family:inherit;font-size:var(--text-dense)}
.toast__progress{position:absolute;left:0;bottom:0;height:2px;background:#93C5FD;width:60%}

/* ============================================================
   COMPONENT · Chips / Banner
   ============================================================ */
.chips{display:flex;gap:6px}
.chip{height:32px;padding:0 12px;display:inline-flex;align-items:center;gap:6px;
      background:var(--surface);border:1px solid var(--rule-strong);border-radius:var(--radius-pill);
      font-size:var(--text-dense);font-weight:500;color:var(--ink-secondary);cursor:pointer;
      text-decoration:none}
.chip:hover{background:var(--surface-hover)}
.chip[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:#fff}
.chip__count{font-size:var(--text-micro);opacity:.7;font-variant-numeric:tabular-nums}

.banner{display:flex;gap:10px;padding:12px var(--s3);border-radius:var(--radius-sm);
        font-size:var(--text-dense);line-height:1.55;align-items:flex-start}
.banner .icon{margin-top:1px}
.banner--warning{background:var(--warning-subtle);color:#8A4A08;border:1px solid var(--warning-border)}
.banner--danger{background:var(--danger-subtle);color:#8F1D1D;border:1px solid var(--danger-border)}
.banner--accent{background:var(--accent-subtle);color:#1B3FA8;border:1px solid var(--accent-border)}

/* ============================================================
   COMPONENT · ToothPicker (FDI)
   ============================================================ */
.teeth{display:inline-block;padding:var(--s3);background:var(--surface-raised);
       border:1px solid var(--rule);border-radius:var(--radius)}
.teeth__arch{display:flex;gap:3px;justify-content:center}
.teeth__arch+.teeth__arch{margin-top:6px}
.teeth__gap{width:14px}
.tooth{width:30px;height:36px;display:grid;place-items:center;background:var(--surface);
       border:1px solid var(--rule-strong);border-radius:5px;font-size:var(--text-micro);
       font-variant-numeric:tabular-nums;color:var(--ink-secondary);cursor:pointer;
       font-family:inherit;transition:all .1s;text-decoration:none}
.tooth:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px)}
.tooth[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:#fff;
       font-weight:700;box-shadow:var(--shadow-sm)}
.teeth__legend{margin-top:10px;font-size:var(--text-micro);color:var(--ink-muted);text-align:center}

/* Clinical tooth chart — states, not selection (Phase 5 slice 2). Same .teeth/.tooth
   markup as the picker above, scoped larger and state-coloured under .chart. */
/* Phase 7 slice 4 (ClickUp 86d3upap9): "Tooth chart | Keep the anatomical arrangement,
   scale to width. Teeth stay ≥44px. Never reflow into a grid." .chart .tooth is already
   exactly 44px — the floor, not a larger desktop-only size — so there's no room to shrink
   it further on a phone without breaking that floor; a full arch at 44px teeth is wider
   than a phone viewport (confirmed: ~800px), so "scale to width" is satisfied by letting the
   chart's own box fit its container (max-width:100%) and scroll horizontally *within
   itself* rather than blowing out the whole page's horizontal scroll — same "swipeable
   panel, never drag across a boundary that doesn't work on a phone" reasoning as the lab
   Kanban's own phone treatment. .teeth__arch stays a plain flex row (no wrap), so the
   anatomical layout and the ≥44px floor both hold unconditionally, not just below a
   breakpoint. */
.chart{padding:var(--s5) var(--s4);background:var(--surface);border:1px solid var(--rule);
       border-radius:var(--radius);display:inline-block;max-width:100%;overflow-x:auto}
.chart .teeth__arch{gap:5px}
.chart .teeth__gap{width:22px}
.chart .tooth{width:44px;height:52px;font-size:var(--text-caption);font-weight:600;
              border-width:2px;position:relative}
.tooth--history{background:var(--neutral-subtle);border-color:var(--neutral);color:var(--ink-secondary)}
.tooth--active{background:var(--warning-subtle);border-color:var(--warning);color:var(--warning)}
.tooth--flagged{background:var(--danger-subtle);border-color:var(--danger);color:var(--danger)}
.tooth--flagged::after{content:"";position:absolute;top:3px;right:3px;width:6px;height:6px;
                       border-radius:50%;background:var(--danger)}
.chart__legend{display:flex;gap:var(--s4);justify-content:center;margin-top:var(--s4);flex-wrap:wrap}
.lg{display:flex;align-items:center;gap:7px;font-size:var(--text-caption);color:var(--ink-secondary)}
.lg__sw{width:16px;height:16px;border-radius:4px;border:2px solid}
.chart__arch-label{text-align:center;font-size:var(--text-micro);color:var(--ink-muted);
                   letter-spacing:.06em;text-transform:uppercase;margin:var(--s2) 0}

/* Tooth history drawer (Phase 5 slice 2) */
.drawer{width:420px;max-width:100%;background:var(--surface);border-left:1px solid var(--rule);
        box-shadow:-4px 0 16px rgba(11,18,32,.06);display:flex;flex-direction:column}
.drawer__head{display:flex;align-items:center;justify-content:space-between;padding:var(--s4);
              border-bottom:1px solid var(--rule)}
.drawer__title{font-size:var(--text-title);font-weight:700}
.drawer__sub{font-size:var(--text-caption);color:var(--ink-muted)}
.drawer__body{flex:1;overflow:auto}
.drawer__foot{padding:var(--s3) var(--s4);border-top:1px solid var(--rule);background:var(--surface-sunken)}
/* Phase 7 slice 4 (ClickUp 86d3upap9): "History drawer | Side drawer → full-screen sheet."
   patients/show.html.erb lays .drawer out as a plain flex sibling next to the tooth chart
   (it was never a true overlay to begin with — flex-wrap already stacked it below the chart
   at narrow widths), so no markup change is needed here, only making it cover the viewport
   like a real sheet instead of sitting in normal document flow at phone width. The close
   link's own Turbo Frame re-request (patient_path, no default block content) is unchanged
   and remains the dismiss action. */
@media (max-width:640px){
  .drawer{position:fixed;inset:0;z-index:45;width:auto;max-width:none;border-left:none}
}
.acc{border-bottom:1px solid var(--rule)}
.acc__head{display:flex;align-items:center;gap:10px;padding:var(--s3) var(--s4);cursor:pointer;
           list-style:none}
.acc__head::-webkit-details-marker{display:none}
.acc__head:hover{background:var(--surface-hover)}
.acc__date{font-size:var(--text-caption);color:var(--ink-muted);font-variant-numeric:tabular-nums;
           width:76px;flex:0 0 76px}
.acc__sum{flex:1;font-size:var(--text-dense);font-weight:550}
.acc__body{padding:0 var(--s4) var(--s4) 102px;font-size:var(--text-dense)}
.acc__row{display:grid;grid-template-columns:84px 1fr;gap:var(--s3);padding:5px 0}
.acc__k{color:var(--ink-muted);font-size:var(--text-caption)}
.acc__thumbs{display:flex;gap:6px;margin-top:8px}
.acc__thumb{width:54px;height:54px;border-radius:var(--radius-sm);overflow:hidden;
            background:linear-gradient(135deg,#EDEFF3,#DDE2EA);border:1px solid var(--rule);display:block}
.acc__by{margin-top:10px;font-size:var(--text-micro);color:var(--ink-muted)}

/* Voice capture — one job, everything else hidden (Phase 5 slice 3). Ported verbatim from
   docs/dental-os-handoff/design/dental-os-screens-phase5.html's own .rec block. */
.rec{display:flex;flex-direction:column;align-items:center;justify-content:center;
     padding:var(--s7) var(--s4);gap:var(--s4);min-height:440px}
.rec__ctx{font-size:var(--text-body);color:var(--ink-secondary);text-align:center}
.rec__ctx b{color:var(--ink)}
.rec__btn{width:132px;height:132px;border-radius:50%;border:0;cursor:pointer;
          background:var(--danger);color:#fff;display:grid;place-items:center;
          box-shadow:0 0 0 10px var(--danger-subtle)}
.rec__btn svg{width:48px;height:48px;stroke:currentColor;fill:none;stroke-width:1.75}
.rec__btn[disabled]{opacity:.5;cursor:not-allowed}
.rec__btn--active{animation:rec-pulse 1.6s ease-in-out infinite}
@keyframes rec-pulse{0%,100%{box-shadow:0 0 0 10px var(--danger-subtle)}50%{box-shadow:0 0 0 16px var(--danger-subtle)}}
.rec__time{font-size:30px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:.02em}
.rec__wave{display:flex;align-items:center;gap:3px;height:38px}
.rec__wave i{display:block;width:4px;border-radius:2px;background:var(--danger);opacity:.75}
.rec__hint{font-size:var(--text-body);color:var(--ink-muted);text-align:center;max-width:34ch}
.rec__actions{display:flex;gap:var(--s3);width:100%;max-width:420px}
.rec__actions .btn{flex:1}
/* [hidden] and .rec__actions{display:flex} are equal-specificity selectors, and this
   stylesheet loads after the UA stylesheet — without this, the plain HTML `hidden`
   attribute this component relies on to hide Discard/Done before recording starts is
   silently overridden and the actions render visible from page load. Found live, not by
   spec (view specs render static markup and never assert computed display). */
.rec__actions[hidden]{display:none}

/* Tooth-select-for-capture mini chart (Phase 5 slice 3, ToothChartComponent :select mode) —
   same .chart/.teeth/.tooth classes as the :chart-mode history chart, just smaller and
   embedded inline on the visit page rather than the full patient profile. */
.chart--compact{padding:var(--s3)}
.chart--compact .tooth{width:34px;height:40px;font-size:var(--text-caption)}

/* AI draft review — split, transcript left, draft right (Phase 5 slice 4). Ported verbatim
   from docs/dental-os-handoff/design/dental-os-screens-phase5.html's own .review block. */
.review{display:grid;grid-template-columns:.85fr 1.15fr;gap:0;
        border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden;background:var(--surface)}
.review__left{background:var(--surface-sunken);border-right:1px solid var(--rule);padding:var(--s4)}
.review__right{padding:var(--s4)}
/* Phase 7 slice 4 (ClickUp 86d3upap9): .review__left is a <details> — the transcript is
   read-only reference material, not the primary editing surface, so it's collapsed by
   default everywhere (not just below the stack breakpoint) rather than open on desktop and
   closed on mobile, which server-rendered HTML can't express without JS. Marker hidden the
   same way .acc__head already does for the history-drawer accordion — no custom arrow drawn
   there either, so none added here for consistency. */
.review__label{font-size:var(--text-micro);text-transform:uppercase;letter-spacing:.06em;
               font-weight:700;color:var(--ink-muted);margin-bottom:var(--s3);
               cursor:pointer;list-style:none}
.review__label::-webkit-details-marker{display:none}
.review__label:hover{color:var(--ink-secondary)}
.transcript{font-size:var(--text-large);line-height:1.7;color:var(--ink);font-style:italic}
.transcript__meta{margin-top:var(--s4);padding-top:var(--s3);border-top:1px solid var(--rule);
                  font-size:var(--text-caption);color:var(--ink-muted)}
.draft-note{display:flex;gap:10px;background:var(--warning-subtle);border:1px solid var(--warning-border);
            border-radius:var(--radius-sm);padding:10px var(--s3);font-size:var(--text-dense);
            color:#8A4A08;margin-bottom:var(--s4)}
@media(max-width:900px){.review{grid-template-columns:1fr}}
@media (max-width:640px){
  /* "Confirm pinned to the bottom" — the draft form's own action row can end up well below
     the fold on a phone (chart fields + finding/diagnosis/plan), so it sticks to the bottom
     of the scroll container instead of requiring a scroll-to-submit. Desktop is untouched —
     still inline, end of form. */
  .review__actions{position:sticky;bottom:0;margin:var(--s4) calc(-1 * var(--s4)) calc(-1 * var(--s4));
                    padding:var(--s3) var(--s4);background:var(--surface);
                    border-top:1px solid var(--rule)}
}

/* ============================================================
   COMPONENT · Modal
   ============================================================ */
.modal{width:580px;max-width:100%;background:var(--surface);border:1px solid var(--rule);
       border-radius:var(--radius-lg);box-shadow:var(--shadow-lg)}
.modal--narrow{width:460px}
.modal__header{display:flex;align-items:center;gap:10px;padding:var(--s4);
               border-bottom:1px solid var(--rule);font-size:var(--text-section);font-weight:650}
.modal__body{padding:var(--s4)}
.modal__footer{display:flex;justify-content:flex-end;gap:var(--s2);padding:var(--s3) var(--s4);
               border-top:1px solid var(--rule);background:var(--surface-sunken);
               border-radius:0 0 var(--radius-lg) var(--radius-lg)}

/* ============================================================
   APP SHELL — clinic
   ============================================================ */
.app{display:grid;grid-template-columns:224px 1fr;min-height:560px;border:1px solid var(--rule);
     border-radius:var(--radius-lg);overflow:hidden;background:var(--surface);box-shadow:var(--shadow)}
.app--solo{grid-template-columns:1fr}
/* Phase 7 slice 4 (ClickUp 86d3upap9): a grid item's default min-width is auto, not 0 — the
   same root cause this codebase already fixed once for .today-grid (see that fix's own
   comment). Without this, the 1fr track sizes itself to fit whatever wide content lives
   inside .content (e.g. the tooth chart's own intrinsic ~865px), and .app's own
   overflow:hidden (there to clip the shell's rounded corners) silently clips the excess
   instead of the track ever actually being constrained to the viewport — found live: the
   tooth chart's own max-width:100%/overflow-x:auto never engaged because its immediate
   parent had already grown past the viewport to accommodate it. */
.app__main{min-width:0}
.sidebar{background:var(--ink);padding:var(--s4) 0;color:#fff}
.sidebar__brand{display:flex;gap:10px;align-items:center;padding:0 var(--s4) var(--s4);margin-bottom:var(--s2);
                border-bottom:1px solid rgba(255,255,255,.09)}
.sidebar__logo{width:32px;height:32px;border-radius:8px;background:var(--accent);display:grid;
               place-items:center;font-weight:700;font-size:var(--text-dense);flex:0 0 32px}
.sidebar__name{font-size:var(--text-dense);font-weight:650;line-height:1.3}
.sidebar__name small{display:block;font-size:var(--text-micro);font-weight:400;color:rgba(255,255,255,.5)}
/* Also applied to a <button> (the sign-out item, inside button_to's form) — the reset
   properties below are no-ops on the existing <a> items (links have no background/border by
   default) but stop a raw <button> from carrying default browser chrome that doesn't match. */
.sidebar__item{display:flex;align-items:center;gap:10px;padding:9px var(--s4);margin:1px var(--s2);
               border-radius:var(--radius-sm);font-size:var(--text-dense);font-weight:500;
               color:rgba(255,255,255,.68);text-decoration:none;
               background:none;border:0;width:calc(100% - 2 * var(--s2));text-align:left;
               font-family:inherit;cursor:pointer}
.sidebar__item:hover{background:rgba(255,255,255,.07);color:#fff}
.sidebar__item.is-active{background:var(--accent);color:#fff}
.sidebar__badge{margin-left:auto;min-width:18px;height:18px;padding:0 5px;border-radius:var(--radius-pill);
                background:var(--danger);color:#fff;font-size:10px;font-weight:700;display:grid;place-items:center}
/* Not in the original package — needed once real nav existed to add the "Coming soon" state
   CLAUDE.md's UI conventions require for not-yet-built modules. Same modifier-class shape as
   the rest of this file (kcard--overdue, pill--bare, stepper--mini). */
.sidebar__item--disabled{opacity:.4;cursor:default;pointer-events:none}
.topbar{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);height:60px;
        padding:0 var(--s4);border-bottom:1px solid var(--rule);background:var(--surface)}
.topbar__title{font-size:var(--text-title);font-weight:700}
.content{padding:var(--s4);background:var(--surface-sunken);min-height:460px}

/* Phase 7 slice 4 (ClickUp 86d3upap9): sidebar → hamburger-triggered off-canvas drawer below
   900px. nav-toggle/nav-overlay are always in the DOM (rendered by application.html.erb) but
   only matter — and only render visibly interactive — inside this media query; above it the
   sidebar stays exactly the fixed 224px grid column it always was. */
.nav-toggle{display:none}
.topbar__hamburger{display:none}
.nav-overlay{display:none}
@media (max-width:900px){
  .app{grid-template-columns:1fr}
  .sidebar{position:fixed;top:0;bottom:0;left:0;width:260px;max-width:82vw;z-index:50;
           transform:translateX(-100%);transition:transform .2s ease;overflow-y:auto}
  .nav-toggle:checked ~ .sidebar{transform:translateX(0)}
  .nav-toggle:checked ~ .nav-overlay{display:block;position:fixed;inset:0;z-index:40;
                                      background:rgba(11,18,32,.4)}
  .topbar__hamburger{display:inline-flex;align-items:center;justify-content:center;
                      width:var(--tap-min);height:var(--tap-min);margin:0 calc(-1 * var(--s2)) 0 calc(-1 * var(--s3));
                      border-radius:var(--radius-sm);cursor:pointer;color:var(--ink)}
  .topbar__hamburger:hover{background:var(--surface-hover)}
}

/* ClickUp 86d42wtm3: a simple, single-focus create/edit form (Book appointment, New/Edit
   patient) shouldn't stretch its inputs the full width of .content — a 1,150px input for a
   first name makes the eye travel and looks unconsidered. Scoped to just the pages named in
   the ticket, not applied globally to .card: several other forms (lab case's tooth-chart
   picker, invoice/prescription's multi-column line-item rows) genuinely need the extra width. */
.form-col{max-width:580px}

/* Lab shell — SUB-BRAND. No clinic navigation anywhere. */
.labshell{border:1px solid var(--rule);border-radius:var(--radius-lg);overflow:hidden;
          background:var(--surface);box-shadow:var(--shadow)}
.labtop{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);height:62px;
        padding:0 var(--s4);border-bottom:1px solid var(--rule)}
.labtop__brand{display:flex;align-items:center;gap:10px;font-size:var(--text-section);font-weight:650;
               min-width:0}
.labtop__brand > div:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.labtop__logo{width:30px;height:30px;border-radius:7px;background:var(--ink);color:#fff;
              display:grid;place-items:center;font-size:var(--text-caption);font-weight:700;
              flex:0 0 30px}
.labtop__brand span{color:var(--ink-muted);font-weight:400;font-size:var(--text-dense)}
.labtop__controls{display:flex;gap:var(--s3);align-items:center}

/* ============================================================
   KANBAN — tap to advance, never drag-only
   ============================================================ */
.kanban{display:flex;gap:var(--s3);padding:var(--s4);background:var(--surface-sunken);overflow-x:auto}
.kcol{flex:0 0 252px;background:var(--surface-raised);border:1px solid var(--rule);border-radius:var(--radius)}
.kcol__head{display:flex;align-items:center;gap:7px;padding:10px var(--s3);
            border-bottom:1px solid var(--rule);font-size:var(--text-dense);font-weight:650}
.kcol__dot{width:7px;height:7px;border-radius:50%;flex:0 0 7px}
.kcol__count{margin-left:auto;font-size:var(--text-micro);color:var(--ink-muted);
             font-variant-numeric:tabular-nums;background:var(--surface-sunken);
             padding:1px 7px;border-radius:var(--radius-pill)}
.kcol__body{padding:var(--s2);display:flex;flex-direction:column;gap:var(--s2);min-height:130px}
.kcard{padding:var(--s3);border:1px solid var(--rule);border-radius:var(--radius-sm);
       background:var(--surface);box-shadow:var(--shadow-sm);cursor:grab}
.kcard:hover{border-color:var(--rule-strong);box-shadow:var(--shadow)}
.kcard--overdue{border-left:3px solid var(--danger)}
.kcard__top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s2)}
.kcard__type{font-size:var(--text-body);font-weight:650;line-height:1.35}
.kcard__meta{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:6px;
             font-size:var(--text-micro);color:var(--ink-muted);font-variant-numeric:tabular-nums}
.kcard__clinic{display:flex;align-items:center;gap:7px;margin-top:10px;padding-top:10px;
               border-top:1px solid var(--rule);font-size:var(--text-caption);color:var(--ink-secondary)}
.kcard__advance{width:100%;margin-top:10px}
.newflag{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:700;
         color:var(--accent);text-transform:uppercase;letter-spacing:.05em}

/* Phase 7 slice 3 (ClickUp 86d3upap9): lab portal Kanban at phone width — "columns
   horizontally swipeable, one at a time, segmented status control at top." Desktop/tablet
   above this breakpoint are untouched: same fixed-252px multi-column layout as before.
   .seg.kanban-jump, not bare .kanban-jump: a real, previously-shipped bug, found only while
   fixing the identical mistake fresh in Slice 5's .cal-jump — .seg's own
   display:inline-flex is defined later in this file, so a plain .kanban-jump{display:none}
   at equal specificity loses the cascade to it and the strip has been visible on desktop
   since Slice 3 shipped. Two classes on the same selector beats source order. */
.seg.kanban-jump{display:none}
.kanban-jump .seg__opt{white-space:nowrap;flex:0 0 auto}
@media (max-width:640px){
  .seg.kanban-jump{display:flex;overflow-x:auto;margin:0 var(--s4) var(--s3);flex:0 0 auto}
  .kanban{scroll-snap-type:x mandatory}
  .kcol{flex:0 0 100%;scroll-snap-align:start}

  /* .labtop's brand name + practice filter + Add case + Log out genuinely don't fit one
     62px-tall row at phone width (confirmed live — the name wrapped to 3 lines and the
     buttons were pushed off-screen entirely, not just visually tight). Two rows instead:
     brand stays put, everything else wraps below at auto height. */
  .labtop{flex-wrap:wrap;height:auto;padding:var(--s3) var(--s4)}
  .labtop__controls{flex-wrap:wrap;width:100%}
  .labtop__practice-filter{flex:1 1 auto;min-width:0}
}

/* ============================================================
   Week calendar (Phase 2 Slice 2) — CalendarWeek
   ============================================================ */
.cal-week{display:flex;background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden}
.cal-week__hours{flex:0 0 52px;border-right:1px solid var(--rule)}
.cal-week__hour{height:56px;font-size:var(--text-micro);color:var(--ink-muted);text-align:right;
                padding-right:6px;transform:translateY(-6px)}
.cal-week__day{flex:1;border-right:1px solid var(--rule);position:relative}
.cal-week__day:last-child{border-right:none}
.cal-week__day.is-today{background:var(--accent-subtle)}
.cal-week__day-head{padding:var(--s2);text-align:center;border-bottom:1px solid var(--rule);
                     font-size:var(--text-caption);color:var(--ink-secondary);position:sticky;top:0;background:inherit}
.cal-week__day-head strong{display:block;font-size:var(--text-body);color:var(--ink)}
.cal-week__grid{position:relative}
.cal-week__now-line{position:absolute;left:0;right:0;height:2px;background:var(--danger);z-index:2}
.cal-week__now-line::before{content:"";position:absolute;left:-4px;top:-3px;width:8px;height:8px;
                             border-radius:50%;background:var(--danger)}
.cal-block{position:absolute;left:2px;right:2px;border-radius:var(--radius-sm);padding:4px 6px;
           font-size:var(--text-micro);overflow:hidden;border:1px solid;text-decoration:none;
           display:block;color:var(--ink)}
.cal-block strong{display:block;font-size:var(--text-caption);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-block--accent{background:var(--accent-subtle);border-color:var(--accent-border)}
.cal-block--warning{background:var(--warning-subtle);border-color:var(--warning-border)}
.cal-block--success{background:var(--success-subtle);border-color:var(--success-border)}
.cal-block--danger{background:var(--danger-subtle);border-color:var(--danger-border)}
.cal-block--neutral{background:var(--neutral-subtle);border-color:var(--neutral-border)}
.cal-block--double{width:calc(50% - 3px)}
.cal-block--double + .cal-block--double{left:auto;right:2px}
.cal-block__flag{position:absolute;top:2px;right:2px;color:var(--warning)}

/* Phase 7 slice 5 (ClickUp 86d3upap9): "Week grid → single-day view with a horizontal date
   strip." Same scroll-snap + anchor-jump technique as the lab Kanban's own phone treatment
   (Slice 3) — .cal-week itself becomes the snapping scroll container, each .cal-week__day a
   full-width snap child, .cal-week__hours pinned to the left edge via position:sticky so the
   time-of-day gutter stays visible while swiping between days. Desktop/tablet above this
   breakpoint are untouched: same equal-width 7-column row as before. */
/* .seg.cal-jump, not bare .cal-jump: .seg's own display:inline-flex is defined later in
   this file, so a plain .cal-jump{display:none} at equal specificity loses the cascade to
   it and the strip stays visible on desktop regardless — found live, not by any spec.
   Two classes on the same selector beats source order rather than depending on it. */
.seg.cal-jump{display:none}
.cal-jump .seg__opt{white-space:nowrap;flex:0 0 auto}
@media (max-width:640px){
  .seg.cal-jump{display:flex;overflow-x:auto;margin:0 0 var(--s3);flex:0 0 auto}
  .cal-week{overflow-x:auto;scroll-snap-type:x mandatory}
  .cal-week__hours{position:sticky;left:0;z-index:2;background:var(--surface)}
  .cal-week__day{flex:0 0 100%;scroll-snap-align:start}
}

/* Book appointment (SlotPicker/SegmentedControl) */
.slots{display:grid;grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:var(--s2)}
.slot{height:40px;border:1px solid var(--rule);border-radius:var(--radius-sm);background:var(--surface);
      font-size:var(--text-dense);font-variant-numeric:tabular-nums;cursor:pointer}
.slot:hover{border-color:var(--accent)}
.slot:has(input:checked){background:var(--accent);border-color:var(--accent);color:#fff}
.slot--busy{color:var(--ink-muted);text-decoration:line-through;background:var(--surface-sunken)}
.slot--busy:has(input:checked){color:#fff;text-decoration:line-through}
.seg{display:inline-flex;border:1px solid var(--rule);border-radius:var(--radius-pill);padding:2px;gap:2px}
.seg__opt{padding:5px 14px;border-radius:var(--radius-pill);font-size:var(--text-caption);
          font-weight:600;color:var(--ink-secondary);cursor:pointer;text-decoration:none}
.seg__opt.is-active,.seg__opt:has(input:checked){background:var(--ink);color:#fff}

/* Patient picker — live-search dropdown (rebrand/QA fix #1), same shell as a native
   <select>'s popup would occupy: absolutely positioned, anchored to the wrapping field. */
.picker__results{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:20;
                  max-height:240px;overflow-y:auto;background:var(--surface);
                  border:1px solid var(--rule);border-radius:var(--radius-sm);
                  box-shadow:0 8px 24px rgba(0,0,0,.12)}
.picker__option{display:block;width:100%;text-align:left;padding:10px 12px;border:none;
                 background:none;font-size:var(--text-dense);color:var(--ink);cursor:pointer}
.picker__option:hover{background:var(--surface-hover)}
.picker__empty{padding:10px 12px;font-size:var(--text-dense);color:var(--ink-muted)}

/* DeliveryTicks */
.ticks{display:inline-flex;align-items:center;gap:5px;font-size:var(--text-micro);color:var(--ink-muted)}
.ticks__icon{display:inline-flex;position:relative;width:14px}
.ticks__icon .icon{width:14px;height:14px}
.ticks__icon--double .icon:last-child{position:absolute;left:5px}
.ticks--accent{color:var(--accent)}
.ticks--success{color:var(--success)}
.ticks--danger{color:var(--danger)}
.ticks--neutral{color:var(--ink-muted)}

/* PhotoPair — before/after, the forcing function (Phase 4 slice 2) */
.pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4)}
.pslot{border-radius:var(--radius);overflow:hidden;border:1px solid var(--rule);background:var(--surface)}
.pslot__head{display:flex;align-items:center;justify-content:space-between;padding:10px var(--s3);
             border-bottom:1px solid var(--rule);font-size:var(--text-dense);font-weight:650}
.pslot__img{height:230px;display:grid;place-items:center;background:linear-gradient(135deg,#EDEFF3,#DDE2EA);
            color:var(--ink-muted);font-size:var(--text-caption)}
.pslot__meta{padding:9px var(--s3);font-size:var(--text-caption);color:var(--ink-muted);
             border-top:1px solid var(--rule)}
.pslot--empty{border:2px dashed var(--rule-strong);background:var(--surface-sunken)}
.pslot--empty .pslot__img{background:none;flex-direction:column;gap:var(--s3);cursor:pointer;
                          min-height:var(--tap-clinical)}
.pslot--empty:hover{border-color:var(--accent)}

/* Consent chip (Phase 4 slice 3) */
.consent{display:inline-flex;align-items:center;gap:6px;font-size:var(--text-caption);
         padding:4px 10px;border-radius:var(--radius-pill);font-weight:600}
.consent .icon{width:13px;height:13px;flex-basis:13px}
.consent--yes{background:var(--success-subtle);color:var(--success);border:1px solid var(--success-border)}
.consent--no{background:var(--neutral-subtle);color:var(--neutral);border:1px solid var(--neutral-border)}

/* BlockNotice — a hard gate, not a reminder (Phase 4 slice 2) */
.block{display:flex;gap:12px;align-items:flex-start;background:var(--danger-subtle);
       border:1px solid var(--danger-border);border-left:4px solid var(--danger);
       border-radius:var(--radius-sm);padding:var(--s3) var(--s4);color:#8F1D1D;font-size:var(--text-dense)}
.block strong{display:block;margin-bottom:2px}

/* Agenda (Today view) */
.agenda-row{display:flex;align-items:center;gap:var(--s3);padding:var(--s3);border-bottom:1px solid var(--rule)}
.agenda-row:last-child{border-bottom:none}
.agenda-row.is-now{box-shadow:inset 3px 0 0 var(--accent);background:var(--accent-subtle)}
.agenda-row.is-past{opacity:.5}
.agenda-row__time{flex:0 0 48px;font-size:var(--text-dense);font-weight:600;font-variant-numeric:tabular-nums}
.agenda-row__who{flex:1;min-width:0}
.agenda-row__meta{font-size:var(--text-caption);color:var(--ink-muted)}
.agenda-row__action{flex:0 0 auto}

/* AttentionList — actions stack below the icon+text row (mockup: "Chase lab" sits on its
   own line under the subtitle), not beside it — a narrow sidebar column has no room for
   icon+text+two buttons all in one row without crushing the text into single-word wraps. */
.att{padding:var(--s3);border-bottom:1px solid var(--rule)}
.att:last-child{border-bottom:none}
.att__top{display:flex;align-items:flex-start;gap:var(--s2)}
.att__icon{flex:0 0 auto;margin-top:2px}
.att--warning .att__icon{color:var(--warning)}
.att--danger .att__icon{color:var(--danger)}
.att__body{flex:1;min-width:0}
.att__title{font-size:var(--text-dense);font-weight:600}
.att__subtitle{font-size:var(--text-caption);color:var(--ink-muted)}
.att__actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;margin-left:26px}

/* Today view layout */
.today-grid{display:grid;grid-template-columns:2fr 1fr 1fr;gap:var(--s4);align-items:start}
/* Grid items default to min-width:auto, so a long patient name's min-content width was
   stretching the 2fr track itself rather than being clipped by .who__name's own ellipsis —
   the actual mechanism behind "agenda row layout breaking on long names" (rebrand/QA fix
   #3). min-width:0 here is what lets that ellipsis rule actually take effect. */
.today-grid > .card{min-width:0}
.today-col__head{display:flex;align-items:center;justify-content:space-between;padding:var(--s3);border-bottom:1px solid var(--rule)}
.today-col__head h2{font-size:var(--text-section);margin:0}
@media (max-width: 1024px){
  .today-grid{grid-template-columns:1fr}
  /* Phase 7 slice 5 (ClickUp 86d3upap9): "3 columns → 1, stacked Needs attention first, then
     schedule, then recalls." DOM order stays Schedule/Needs attention/Recalls (unchanged
     desktop reading order, left to right) — only the stacked *visual* order changes, via
     grid `order`, so nothing about the desktop layout or its markup needed touching. */
  .today-col--attention{order:-1}
  .today-col--recalls{order:1}
}

/* ---- COMPONENT · PrintDoc — A4, print-safe, no accent colour (dental-os-screens-phase3-4
   mockup, verbatim). ".doc" is a document, not an app screen — it has to survive a cheap
   inkjet and satisfy a CA, so no design-system colour tokens here, just black on white. */
.doc{width:794px;max-width:100%;background:#fff;border:1px solid var(--rule);
     padding:44px 48px;font-size:12px;color:#000;box-shadow:var(--shadow)}
.doc--narrow{width:700px}
.doc__head{display:flex;justify-content:space-between;align-items:flex-start;
           border-bottom:2px solid #000;padding-bottom:14px;margin-bottom:18px}
.doc__clinic{font-size:16px;font-weight:700;margin-bottom:3px}
.doc__meta{text-align:right;font-size:11px;line-height:1.7;font-variant-numeric:tabular-nums}
.doc__label{font-size:15px;font-weight:700;letter-spacing:.08em;margin-bottom:6px}
.doc table{width:100%;border-collapse:collapse;font-size:11px;margin:14px 0}
.doc th{border-bottom:1px solid #000;padding:6px 5px;text-align:left;font-size:10px;
        text-transform:uppercase;letter-spacing:.04em}
.doc td{border-bottom:1px solid #DDD;padding:7px 5px;vertical-align:top}
.doc .r{text-align:right;font-variant-numeric:tabular-nums}
.doc__totals{margin-left:auto;width:290px;font-size:11px}
.doc__totals div{display:flex;justify-content:space-between;padding:4px 0}
.doc__totals .sum{border-top:1.5px solid #000;margin-top:5px;padding-top:8px;font-weight:700;font-size:13px}
.doc__words{margin-top:10px;font-size:11px;font-style:italic}
.doc__foot{margin-top:34px;display:flex;justify-content:space-between;align-items:flex-end;
           font-size:10px;color:#444;line-height:1.6}
.doc__sig{border-top:1px solid #000;padding-top:5px;width:180px;text-align:center}

/* Print: only .doc survives — sidebar, topbar, flash, and anything marked .no-print
   disappear. Test with "Background graphics" unchecked in the browser print dialog (mockup's
   own handoff note) — most people never turn it on, so .doc can't rely on background colours
   to read correctly; it already doesn't use any. */
@media print{
  .sidebar,.topbar,.no-print,.toast{display:none!important}
  .content{padding:0;background:#fff}
  .print-frame{padding:0!important;background:#fff!important;border:none!important;box-shadow:none!important}
  .doc{box-shadow:none;border:none;width:auto}
  .app{display:block}
}

/* Setup checklist (Phase 6 slice 2) — dismissible, resumable, on Today. Ported verbatim from
   docs/dental-os-screens-phase6.html's own .setup/.step block. */
.setup{border:1px solid var(--accent-border);background:var(--accent-subtle);border-radius:var(--radius)}
.setup__head{display:flex;align-items:center;justify-content:space-between;padding:var(--s3) var(--s4);
             border-bottom:1px solid var(--accent-border)}
.setup__title{font-size:var(--text-section);font-weight:650;color:#1B3FA8}
.setup__prog{font-size:var(--text-caption);color:#1B3FA8;font-variant-numeric:tabular-nums}
.setup__body{background:var(--surface);border-radius:0 0 var(--radius) var(--radius)}
.step{display:flex;align-items:center;gap:12px;padding:12px var(--s4);border-bottom:1px solid var(--rule)}
.step:last-child{border-bottom:0}
.step__tick{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;flex:0 0 22px;
            border:2px solid var(--rule-strong);color:transparent}
.step.is-done .step__tick{background:var(--success);border-color:var(--success);color:#fff}
.step.is-next .step__tick{border-color:var(--accent)}
.step__label{flex:1;font-size:var(--text-dense);font-weight:550}
.step.is-done .step__label{color:var(--ink-muted);text-decoration:line-through}
.step__why{font-size:var(--text-caption);color:var(--ink-muted);font-weight:400;display:block;margin-top:1px;text-decoration:none}

/* WhatsApp connection card + Clinic settings rows (Phase 6 slice 3) — both ported verbatim
   from docs/dental-os-screens-phase6.html's own .conn/.setrow/.logodrop blocks. */
.conn{border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);overflow:hidden}
.conn__head{display:flex;align-items:center;gap:12px;padding:var(--s4);border-bottom:1px solid var(--rule)}
.conn__ico{width:40px;height:40px;border-radius:var(--radius);display:grid;place-items:center;flex:0 0 40px}
.conn__ico--on{background:var(--success-subtle);color:var(--success)}
.conn__ico--off{background:var(--neutral-subtle);color:var(--neutral)}
.conn__ico--warn{background:var(--warning-subtle);color:var(--warning)}
.conn__title{font-size:var(--text-section);font-weight:650}
.conn__sub{font-size:var(--text-caption);color:var(--ink-muted);margin-top:1px}
.conn__body{padding:var(--s4)}
.conn__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:var(--s3);
            padding:var(--s3) var(--s4);background:var(--surface-sunken);border-top:1px solid var(--rule)}
.conn__stat{font-size:var(--text-caption);color:var(--ink-muted)}
.conn__stat b{display:block;font-size:var(--text-large);font-weight:700;color:var(--ink);
              font-variant-numeric:tabular-nums;margin-bottom:1px}

.setrow{display:grid;grid-template-columns:200px 1fr;gap:var(--s4);padding:var(--s4);
        border-bottom:1px solid var(--rule);align-items:start}
.setrow:last-child{border-bottom:0}
.setrow__k{font-size:var(--text-dense);font-weight:600}
.setrow__hint{font-size:var(--text-caption);color:var(--ink-muted);margin-top:2px;font-weight:400}
.logodrop{display:flex;align-items:center;gap:var(--s3)}
.logodrop__prev{width:60px;height:60px;border-radius:var(--radius);border:1px dashed var(--rule-strong);
                display:grid;place-items:center;background:var(--surface-sunken);flex:0 0 60px;overflow:hidden}
.logodrop__prev img{width:100%;height:100%;object-fit:contain}
@media(max-width:800px){.setrow{grid-template-columns:1fr}}

/* Settings sub-nav (Phase 6 slice 3) — not in the mockup itself (Screens 22/23/24 were each
   shown as isolated component demos, only Screen 23 got the full app shell), but three real
   settings pages need something to move between them. Deliberately minimal: reuses .pill's
   own visual language rather than inventing a new tab component for one nav row. */
.subnav{display:flex;gap:8px;margin-bottom:var(--s4);border-bottom:1px solid var(--rule);padding-bottom:var(--s3)}
.subnav__item{display:inline-flex;align-items:center;height:30px;padding:0 14px;border-radius:var(--radius-pill);
              font-size:var(--text-dense);font-weight:600;color:var(--ink-secondary);text-decoration:none}
.subnav__item:hover{background:var(--neutral-subtle)}
.subnav__item.is-active{background:var(--accent-subtle);color:var(--accent)}
.subnav__item.is-disabled{color:var(--ink-muted);cursor:default}
.subnav__item.is-disabled:hover{background:transparent}

/* Plan cards (Phase 6 slice 4) — ported verbatim from docs/dental-os-screens-phase6.html's
   own .plans/.plan block. Deliberately auto-fit, not a hard-coded two-column grid, per the
   mockup's own explicit handoff note — a third plan drops in with zero CSS changes. */
.plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:var(--s4);align-items:start}
.plan{border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);overflow:hidden}
.plan.is-current{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-subtle)}
.plan__head{padding:var(--s4);border-bottom:1px solid var(--rule)}
.plan__name{font-size:var(--text-section);font-weight:700}
.plan__price{font-size:30px;font-weight:800;letter-spacing:-.02em;margin-top:6px;font-variant-numeric:tabular-nums}
.plan__price small{font-size:var(--text-dense);font-weight:500;color:var(--ink-muted);letter-spacing:0}
.plan__for{font-size:var(--text-caption);color:var(--ink-muted);margin-top:4px}
.plan__list{padding:var(--s4);display:flex;flex-direction:column;gap:9px;font-size:var(--text-dense)}
.plan__li{display:flex;gap:9px;align-items:flex-start}
.plan__li svg{color:var(--success);margin-top:2px;flex:0 0 15px;width:15px;height:15px}
.plan__foot{padding:0 var(--s4) var(--s4)}

/* Usage meters + plan gates (Phase 6 slice 5) — ported verbatim from
   docs/dental-os-screens-phase6.html's own .meter/.gate blocks. */
.meter{margin-bottom:var(--s4)}
.meter__top{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:7px}
.meter__label{font-size:var(--text-dense);font-weight:600}
.meter__val{font-size:var(--text-dense);font-variant-numeric:tabular-nums;color:var(--ink-secondary)}
.meter__bar{height:9px;border-radius:var(--radius-pill);background:var(--neutral-subtle);overflow:hidden;display:flex}
.meter__fill{height:100%}
.meter__fill--ok{background:var(--success)}
.meter__fill--warn{background:var(--warning)}
.meter__fill--full{background:var(--danger)}
.meter__fill--grace{background:repeating-linear-gradient(45deg,var(--warning),var(--warning) 4px,#F5C97A 4px,#F5C97A 8px)}
.meter__note{margin-top:7px;font-size:var(--text-caption);color:var(--ink-muted)}

.gate{border-radius:var(--radius);padding:var(--s5);text-align:center;border:1px solid}
.gate--hard{background:var(--danger-subtle);border-color:var(--danger-border)}
.gate--soft{background:var(--warning-subtle);border-color:var(--warning-border)}
.gate__ico{width:48px;height:48px;border-radius:50%;display:grid;place-items:center;margin:0 auto var(--s3);background:var(--surface)}
.gate__title{font-size:var(--text-section);font-weight:700;margin-bottom:6px}
.gate__body{font-size:var(--text-dense);max-width:44ch;margin:0 auto var(--s4);line-height:1.6}
.gate--hard .gate__title,.gate--hard .gate__body{color:#8F1D1D}
.gate--soft .gate__title,.gate--soft .gate__body{color:#8A4A08}

