/* BehaviorDash — primitives (v172)
 *
 * The only implementation of each control. Every component here is accessible
 * by construction: real semantics, a visible focus ring inherited from
 * foundations.css, a target no smaller than 32px, and never colour alone to
 * carry meaning.
 *
 * Requires foundations.css. Uses only its tokens — no literal hex.
 *
 * Button weights follow Apple's hierarchy: exactly one prominent button per
 * context, bordered for secondary, plain for tertiary. Destructive and cancel
 * are ROLES, not weights, so a destructive action can be prominent in a
 * confirmation and quiet in a table row.
 */

/* V251 C5. THE SEVEN CONTROL COMPONENTS BELOW TAKE --fs-control, NOT
   --fs-footnote: the button, the form control, the popover item, the skip
   link, the search field, the tab and the row button. They had all asked for
   a footnote's size, which read as settled on this presentation (both are
   13px here) and made every one of them 11px on the Gentelella shell, where
   --fs-footnote is deliberately a step tighter for SUPPORTING TEXT. A control
   is the same control on both presentations; a footnote need not be. On the
   console this change is a no-op, by construction. */
/* ── Button ───────────────────────────────────────────────────────────── */
.bd-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  font-family:var(--font-ui);font-size:var(--fs-control);font-weight:var(--fw-semi);
  line-height:1;white-space:nowrap;cursor:pointer;
  min-height:var(--target-dense);padding:0 var(--s-4);
  border-radius:var(--r-md);border:1px solid transparent;
  background:transparent;color:var(--ink-secondary);
  transition:background var(--dur-quick) var(--ease-snappy),
             color var(--dur-quick) var(--ease-snappy),
             border-color var(--dur-quick) var(--ease-snappy),
             box-shadow var(--dur-quick) var(--ease-snappy),
             transform var(--dur-instant) var(--ease-snappy);
}
.bd-btn:active:not(:disabled){transform:scale(var(--lift-press))}
.bd-btn:disabled,.bd-btn[aria-disabled="true"]{opacity:.4;cursor:not-allowed}

.bd-btn--prominent{background:var(--accent-primary);color:var(--ink-on-accent);border-color:var(--accent-primary)}
.bd-btn--prominent:hover:not(:disabled){background:var(--accent-hover);border-color:var(--accent-hover);box-shadow:var(--elev-2)}

.bd-btn--bordered{background:var(--bg-raised);color:var(--ink-secondary);border-color:var(--line-default)}
.bd-btn--bordered:hover:not(:disabled){background:var(--bg-sunken);color:var(--ink-primary);border-color:var(--line-strong)}

.bd-btn--plain{background:transparent;color:var(--accent-primary);padding:0 var(--s-2)}
.bd-btn--plain:hover:not(:disabled){background:var(--accent-soft)}

/* Roles compose with weights.

   V251 C4. INK AND FILL ARE NOT THE SAME RED, and they cannot be on a product
   with a dark theme. --danger (#d63939) is a FILL colour: white on it reads,
   and it reads on white. As INK on a dark surface it does not - measured on
   review.html's "Return to staff" at 1440x950 in prefers-color-scheme: dark,
   #d63939 on #1a2332 is 3.39:1, under the 4.5:1 floor for body text.

   --danger-ink is the theme-aware ink (--status-ink-red, added at B7 for
   exactly this: #c62828 on light, #e27474 on dark - 5.9:1 and 5.1:1). The
   `var(--danger-ink, var(--danger))` fallback is deliberate: app/admin.css does
   not define it, so the legacy console keeps precisely the colour it has today
   and this file stays one description of the button rather than two. */
.bd-btn--destructive.bd-btn--prominent{background:var(--danger);border-color:var(--danger)}
.bd-btn--destructive.bd-btn--bordered{color:var(--danger-ink,var(--danger));border-color:var(--line-default)}
.bd-btn--destructive.bd-btn--bordered:hover:not(:disabled){background:var(--danger-soft);border-color:var(--danger);color:var(--danger-ink,var(--danger))}
.bd-btn--destructive.bd-btn--plain{color:var(--danger-ink,var(--danger))}
.bd-btn--destructive.bd-btn--plain:hover:not(:disabled){background:var(--danger-soft)}

.bd-btn--lg{min-height:var(--target-touch);font-size:var(--fs-body);padding:0 var(--s-5);border-radius:var(--r-lg)}
.bd-btn--sm{min-height:28px;font-size:var(--fs-caption);padding:0 var(--s-3)}
.bd-btn--block{width:100%}
/* Icon-only buttons still meet the target floor; they REQUIRE an aria-label. */
.bd-btn--icon{width:var(--target-dense);padding:0}
.bd-btn--icon.bd-btn--lg{width:var(--target-touch)}

/* ── Field ────────────────────────────────────────────────────────────── */
.bd-field{display:flex;flex-direction:column;gap:var(--s-1);min-width:0}
.bd-field__label{font-size:var(--fs-caption);font-weight:var(--fw-semi);color:var(--ink-secondary)}
.bd-field__req{color:var(--danger);margin-left:2px}
.bd-field__hint{font-size:var(--fs-caption);color:var(--ink-tertiary);line-height:1.45}
.bd-field__control{
  font-family:var(--font-ui);font-size:var(--fs-control);color:var(--ink-primary);
  background:var(--bg-raised);border:1px solid var(--line-default);border-radius:var(--r-md);
  min-height:var(--target-dense);padding:var(--s-2) var(--s-3);width:100%;
  transition:border-color var(--dur-quick) var(--ease-snappy),box-shadow var(--dur-quick) var(--ease-snappy);
}
textarea.bd-field__control{min-height:76px;resize:vertical;line-height:var(--lh-body)}
.bd-field__control:hover:not(:disabled){border-color:var(--line-strong)}
.bd-field__control:disabled{opacity:.5;cursor:not-allowed;background:var(--bg-sunken)}
/* Error is never colour alone — the message text and an icon carry it too. */
.bd-field--invalid .bd-field__control{border-color:var(--danger)}
.bd-field__error{display:flex;align-items:flex-start;gap:var(--s-1);
  font-size:var(--fs-caption);font-weight:var(--fw-medium);color:var(--danger);line-height:1.45}
.bd-field__error::before{content:"⚠";font-size:11px;line-height:1.35}

/* ── Sheet (native dialog) ────────────────────────────────────────────── */
.bd-sheet{
  border:0;padding:0;background:var(--bg-raised);color:var(--ink-secondary);
  border-radius:var(--r-xl);box-shadow:var(--elev-4);
  width:min(560px,calc(100vw - var(--s-6)));max-height:min(84vh,760px);
  overflow:hidden;display:flex;flex-direction:column;
}
.bd-sheet::backdrop{background:rgba(16,36,62,.42);backdrop-filter:blur(2px)}
.bd-sheet[open]{animation:bd-sheet-in var(--dur-smooth) var(--ease-smooth)}
.bd-sheet[open]::backdrop{animation:bd-fade-in var(--dur-smooth) var(--ease-smooth)}
@keyframes bd-sheet-in{from{opacity:0;transform:translateY(12px) scale(.985)}to{opacity:1;transform:none}}
@keyframes bd-fade-in{from{opacity:0}to{opacity:1}}
.bd-sheet--wide{width:min(820px,calc(100vw - var(--s-6)))}
.bd-sheet__head{display:flex;align-items:flex-start;gap:var(--s-3);
  padding:var(--s-5) var(--s-5) var(--s-3);border-bottom:1px solid var(--line-subtle)}
.bd-sheet__title{font-family:var(--font-brand);font-size:var(--fs-title-3);font-weight:var(--fw-semi);
  color:var(--ink-primary);margin:0;line-height:var(--lh-tight)}
.bd-sheet__desc{font-size:var(--fs-footnote);color:var(--ink-tertiary);margin:var(--s-1) 0 0}
.bd-sheet__body{padding:var(--s-5);overflow-y:auto;flex:1;min-height:0}
/* Cancel left, primary right — Apple's sheet button placement. */
.bd-sheet__foot{display:flex;justify-content:space-between;gap:var(--s-3);
  padding:var(--s-3) var(--s-5) var(--s-5);border-top:1px solid var(--line-subtle);flex-wrap:wrap}
.bd-sheet__foot-end{display:flex;gap:var(--s-2);margin-left:auto}
/* On a short screen the sheet's own chrome is the scarce resource, not the
   content: at 320x568 the head and footer ate 247px of a 477px dialog and
   left the list 7px tall. Reclaim the padding and take more of the screen
   where there is least of it. */
@media (max-height:600px){
  .bd-sheet{max-height:calc(100vh - var(--s-4))}
  .bd-sheet__head{padding:var(--s-3) var(--s-4) var(--s-2)}
  .bd-sheet__body{padding:var(--s-3) var(--s-4)}
  .bd-sheet__foot{padding:var(--s-2) var(--s-4) var(--s-3)}
}

/* ── Popover ──────────────────────────────────────────────────────────── */
.bd-pop{
  position:absolute;z-index:var(--z-drawer);min-width:200px;
  background:var(--bg-raised);border:1px solid var(--line-subtle);border-radius:var(--r-lg);
  box-shadow:var(--elev-3);padding:var(--s-1);
  animation:bd-pop-in var(--dur-snappy) var(--ease-snappy);
}
@keyframes bd-pop-in{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}
.bd-pop__item{
  display:flex;align-items:center;gap:var(--s-3);width:100%;
  min-height:var(--target-dense);padding:0 var(--s-3);
  font-family:var(--font-ui);font-size:var(--fs-control);color:var(--ink-secondary);
  background:transparent;border:0;border-radius:var(--r-sm);cursor:pointer;text-align:left;
}
/* :not(:disabled) is not decoration here — it is what out-specifies
   admin.css's button:hover:not(:disabled) (0,2,1). Without it these rules
   measured 1.00:1 on hover: the menu item disappeared under the pointer. */
.bd-pop__item:hover:not(:disabled),
.bd-pop__item:focus-visible:not(:disabled){background:var(--bg-sunken);color:var(--ink-primary)}
.bd-pop__item--destructive{color:var(--danger)}
.bd-pop__item--destructive:hover:not(:disabled),
.bd-pop__item--destructive:focus-visible:not(:disabled){background:var(--danger-soft);color:var(--danger)}
.bd-pop__sep{height:1px;background:var(--line-subtle);margin:var(--s-1) 0}

/* ── Table ────────────────────────────────────────────────────────────── */
.bd-table-wrap{overflow-x:auto;border:1px solid var(--line-subtle);border-radius:var(--r-lg);background:var(--bg-raised)}
.bd-table{width:100%;border-collapse:collapse;font-size:var(--fs-footnote)}
.bd-table caption{text-align:left;padding:var(--s-3) var(--s-4);font-size:var(--fs-caption);
  color:var(--ink-tertiary);border-bottom:1px solid var(--line-subtle)}
.bd-table th{text-align:left;font-size:var(--fs-micro);font-weight:var(--fw-bold);
  letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--ink-tertiary);
  padding:var(--s-3) var(--s-4);background:var(--bg-sunken);border-bottom:1px solid var(--line-default);white-space:nowrap}
.bd-table td{padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--line-subtle);
  color:var(--ink-secondary);vertical-align:top}
.bd-table tbody tr:last-child td{border-bottom:0}
.bd-table td b{color:var(--ink-primary);font-weight:var(--fw-semi)}
.bd-table--num{font-variant-numeric:tabular-nums;text-align:right}

/* ── Status pill — label AND colour, never colour alone ───────────────── */
.bd-pill{display:inline-flex;align-items:center;gap:var(--s-1);
  font-size:var(--fs-micro);font-weight:var(--fw-bold);letter-spacing:var(--tracking-micro);
  text-transform:uppercase;padding:4px var(--s-2);border-radius:var(--r-pill);white-space:nowrap;
  background:var(--bg-sunken);color:var(--ink-tertiary)}
/* V251 E11. THE FILL HUE IS NOT THE TEXT INK, and these four read the fill.
   Measured on the built pages in light mode: "Connected" 2.74:1 and "Requires
   setup" 2.13:1 on the Integrations screen — a status label nobody can read.

   `--*-ink` is the page bundle's per-theme readable ink (ui/scss/v4/_bd-
   transplanted.scss). The FALLBACK is what keeps the console right: there,
   `--success`/`--warning`/`--danger`/`--info` are ALREADY the readable values
   app/foundations.css chose (#1F6F4A, #9A5412), and no `--*-ink` exists. One
   declaration, correct on both presentations, without adding four tokens to
   the console that only the pages would read. */
.bd-pill--ok{background:var(--success-soft);color:var(--success-ink, var(--success))}
.bd-pill--warn{background:var(--warning-soft);color:var(--warning-ink, var(--warning))}
.bd-pill--bad{background:var(--danger-soft);color:var(--danger-ink, var(--danger))}
.bd-pill--info{background:var(--info-soft);color:var(--info-ink, var(--info))}

/* ── Empty state ──────────────────────────────────────────────────────── */
.bd-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;gap:var(--s-2);padding:var(--s-7) var(--s-5);color:var(--ink-tertiary)}
.bd-empty__icon{font-size:28px;line-height:1;color:var(--line-strong);margin-bottom:var(--s-1)}
.bd-empty__title{font-size:var(--fs-headline);font-weight:var(--fw-semi);color:var(--ink-primary);margin:0}
.bd-empty__desc{font-size:var(--fs-footnote);max-width:44ch;margin:0;line-height:var(--lh-body)}
.bd-empty__action{margin-top:var(--s-3)}

/* v249 Workstream 39: .bd-empty inside a table cell. The component already
   brings its own padding, centring and max-width, so the cell only needs to
   stop adding its own - otherwise an empty state in a one-row table sits
   off-centre against the column padding. */
.bd-empty-cell{padding:0;background:transparent}

/* ── Skeleton ─────────────────────────────────────────────────────────── */
.bd-skel{background:var(--bg-sunken);border-radius:var(--r-sm);position:relative;overflow:hidden}
.bd-skel::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.42),transparent);
  animation:bd-shimmer 1.25s infinite}
@keyframes bd-shimmer{100%{transform:translateX(100%)}}
@media (prefers-reduced-motion:reduce){.bd-skel::after{animation:none;display:none}}
.bd-skel--text{height:12px;margin:var(--s-1) 0}
.bd-skel--title{height:18px;width:42%;margin-bottom:var(--s-2)}
.bd-skel--row{height:44px;margin-bottom:var(--s-2)}

/* SPECIFICITY TRAP — read before writing any `.something:hover` rule.
   admin.css ships:
       button:hover:not(:disabled) { background: var(--brand-2) }   (0,2,1)
   A bare `.my-card:hover` is (0,2,0) and LOSES to it regardless of load
   order, so any button-shaped component silently turns #10243E on hover.
   Where the text is also dark that is 1:1 contrast — invisible. Every hover
   rule for a <button> in this layer therefore carries :not(:disabled) to tie
   the specificity and let source order decide. */

/* ── Toast ────────────────────────────────────────────────────────────
   Replaces window.alert(). Bottom-centre on a phone, bottom-right on a
   desktop: away from the primary work area either way, and never over the
   thing the message is about. */
.bd-toasts{position:fixed;z-index:var(--z-toast);bottom:var(--s-4);right:var(--s-4);
  display:flex;flex-direction:column;gap:var(--s-2);align-items:flex-end;
  max-width:min(420px,calc(100vw - var(--s-8)));pointer-events:none}
/* max-width:100% and overflow-wrap are load-bearing, not tidiness. The host
   caps at 420px, but a flex child with no max-width ignores that cap, and an
   error toast carries error.message straight from the database — which can be
   one unbroken token like a constraint name or a URL. Without these two
   declarations a Supabase error rendered 2670px wide, off the side of the
   screen, and error toasts never auto-dismiss. */
.bd-toast{display:flex;gap:var(--s-2);align-items:flex-start;pointer-events:auto;
  max-width:100%;box-sizing:border-box;
  background:var(--bg-raised);color:var(--ink-primary);
  border:1px solid var(--line-default);border-left-width:3px;
  border-radius:var(--r-md);box-shadow:var(--elev-3);
  padding:var(--s-3) var(--s-3) var(--s-3) var(--s-4);
  font-size:var(--fs-footnote);line-height:var(--lh-body);
  animation:bd-toast-in var(--dur-snappy) var(--ease-snappy)}
.bd-toast--info{border-left-color:var(--info)}
.bd-toast--success{border-left-color:var(--success)}
.bd-toast--error{border-left-color:var(--danger)}
.bd-toast>.bi{flex:0 0 auto;margin-top:1px;font-size:15px}
.bd-toast--info>.bi{color:var(--info)}
.bd-toast--success>.bi{color:var(--success)}
.bd-toast--error>.bi{color:var(--danger)}
.bd-toast__text{flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
.bd-toast__close{flex:0 0 auto;background:none;border:0;padding:0;margin:-2px -2px 0 0;
  color:var(--ink-tertiary);cursor:pointer;border-radius:var(--r-sm);
  min-width:24px;min-height:24px;display:grid;place-items:center;
  font-family:var(--font-ui);font-size:18px;line-height:1}
.bd-toast__close:hover:not(:disabled){color:var(--ink-primary);background:var(--bg-sunken)}
.bd-toast--leaving{animation:bd-toast-out var(--dur-quick) var(--ease-snappy) forwards}
@keyframes bd-toast-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes bd-toast-out{to{opacity:0;transform:translateY(4px)}}
@media(max-width:640px){
  .bd-toasts{left:var(--s-3);right:var(--s-3);bottom:var(--s-3);align-items:stretch;max-width:none}
}

/* ── Visually-hidden (announcer + skip link live here) ────────────────── */
.bd-sr{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.bd-skip{position:absolute;left:var(--s-3);top:-64px;z-index:var(--z-toast);
  background:var(--bg-raised);color:var(--ink-primary);border:1px solid var(--line-default);
  border-radius:var(--r-md);padding:var(--s-2) var(--s-4);font-size:var(--fs-control);
  font-weight:var(--fw-semi);text-decoration:none;transition:top var(--dur-quick) var(--ease-snappy)}
.bd-skip:focus{top:var(--s-3)}

/* ── Idle lock ─────────────────────────────────────────────────────────
 * Shared by both surfaces. Devices here are mixed — shared classroom iPads
 * and staff phones — and either way an unattended screen was showing student
 * data indefinitely. The lock keeps the session alive behind a password so
 * nobody loses their place; see session_guard.js for why it locks rather
 * than signs out.
 *
 * Every hover rule carries :not(:disabled) and declares background —
 * admin.css's button:hover:not(:disabled) is (0,2,1). See CLAUDE.md. */
.bd-idle-warn{position:fixed;left:50%;bottom:var(--s-5);transform:translateX(-50%);
  z-index:var(--z-toast);display:flex;align-items:center;gap:var(--s-4);
  max-width:min(440px,calc(100vw - var(--s-6)));box-sizing:border-box;
  padding:var(--s-3) var(--s-4);border-radius:var(--r-lg);
  background:var(--bg-raised);border:1px solid var(--line-default);
  box-shadow:var(--elev-3)}
.bd-idle-warn__body{display:flex;flex-direction:column;gap:2px;min-width:0}
.bd-idle-warn__body b{font-size:var(--fs-footnote);font-weight:var(--fw-semi);
  color:var(--ink-primary)}
.bd-idle-warn__body span{font-size:var(--fs-caption);color:var(--ink-secondary);
  line-height:var(--lh-body)}
.bd-idle-warn .bd-idle-warn__stay{flex:none;min-height:var(--target-dense);
  padding:0 var(--s-4);border-radius:var(--r-pill);cursor:pointer;
  font-family:var(--font-ui);font-size:var(--fs-caption);font-weight:var(--fw-semi);
  background:var(--accent-primary);color:var(--ink-on-accent);
  border:1px solid var(--accent-primary)}
.bd-idle-warn .bd-idle-warn__stay:hover:not(:disabled){background:var(--accent-hover);
  border-color:var(--accent-hover);color:var(--ink-on-accent)}

.bd-lock{border:0;padding:0;background:transparent;max-width:none;max-height:none;
  width:100%;height:100%}
/* Opaque, not translucent: the point is that the screen behind it is not
   readable by whoever picked the device up. */
.bd-lock::backdrop{background:var(--bg-base)}
.bd-lock__panel{box-sizing:border-box;
  width:min(420px,calc(100vw - var(--s-6)));margin:auto;
  display:flex;flex-direction:column;gap:var(--s-3);
  padding:var(--s-6) var(--s-5);border-radius:var(--r-xl);
  background:var(--bg-raised);border:1px solid var(--line-default);
  box-shadow:var(--elev-4)}
.bd-lock__title{margin:0;font-family:var(--font-brand);font-size:var(--fs-title-3);
  font-weight:var(--fw-bold);color:var(--ink-primary);line-height:var(--lh-tight)}
.bd-lock__desc{margin:0;font-size:var(--fs-footnote);color:var(--ink-secondary);
  line-height:var(--lh-body)}
.bd-lock__who{margin:0;font-size:var(--fs-caption);color:var(--ink-tertiary);
  overflow-wrap:anywhere}
.bd-lock__who b{color:var(--ink-secondary);font-weight:var(--fw-semi)}
.bd-lock__field{display:flex;flex-direction:column;gap:var(--s-1);
  font-size:var(--fs-caption);font-weight:var(--fw-semi);color:var(--ink-secondary)}
.bd-lock .bd-lock__field input{width:100%;box-sizing:border-box;
  min-height:var(--target-touch);padding:var(--s-2) var(--s-3);
  font-family:var(--font-ui);font-size:var(--fs-body);color:var(--ink-primary);
  background:var(--bg-raised);border:1px solid var(--line-default);
  border-radius:var(--r-md)}
.bd-lock .bd-lock__field input:focus-visible{outline:2px solid var(--accent-primary);
  outline-offset:1px;box-shadow:var(--focus-glow)}
.bd-lock__msg{margin:0;font-size:var(--fs-caption);min-height:1em;
  color:var(--ink-tertiary)}
/* Never colour alone: the message says what happened in words. */
.bd-lock__msg.is-error{color:var(--danger);font-weight:var(--fw-semi)}
.bd-lock__actions{display:flex;gap:var(--s-2);justify-content:space-between;
  align-items:center;margin-top:var(--s-1)}
.bd-lock .bd-lock__out,
.bd-lock .bd-lock__go{min-height:var(--target-touch);padding:0 var(--s-5);
  border-radius:var(--r-md);cursor:pointer;font-family:var(--font-ui);
  font-size:var(--fs-footnote);font-weight:var(--fw-semi)}
.bd-lock .bd-lock__out{background:var(--bg-raised);color:var(--ink-secondary);
  border:1px solid var(--line-default)}
.bd-lock .bd-lock__out:hover:not(:disabled){background:var(--bg-sunken);
  color:var(--ink-primary);border-color:var(--line-strong)}
.bd-lock .bd-lock__go{background:var(--accent-primary);color:var(--ink-on-accent);
  border:1px solid var(--accent-primary)}
.bd-lock .bd-lock__go:hover:not(:disabled){background:var(--accent-hover);
  border-color:var(--accent-hover);color:var(--ink-on-accent)}
.bd-lock .bd-lock__go:disabled{opacity:.5;cursor:wait}

/* ── Command palette (v248) ───────────────────────────────────────────── */
/* Content of a bd-sheet - inherits its dialog/backdrop/focus behavior. */
.cmdk{display:flex;flex-direction:column;gap:var(--s-3);min-height:0}
.cmdk__input{
  font-family:var(--font-ui);font-size:var(--fs-body);color:var(--ink-primary);
  background:var(--bg-sunken);border:1px solid var(--line-default);border-radius:var(--r-md);
  min-height:var(--target-touch);padding:0 var(--s-4);width:100%;
  transition:border-color var(--dur-quick) var(--ease-snappy),box-shadow var(--dur-quick) var(--ease-snappy);
}
.cmdk__input:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 3px var(--accent-soft)}
.cmdk__list{list-style:none;margin:0;padding:0;overflow-y:auto;max-height:min(48vh,420px)}
.cmdk__item{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-2) var(--s-3);
  border-radius:var(--r-md);cursor:pointer;color:var(--ink-secondary)}
.cmdk__item:hover,.cmdk__item.is-active{background:var(--accent-soft);color:var(--ink-primary)}
.cmdk__item>.bi{flex:0 0 auto;font-size:15px;color:var(--ink-tertiary)}
.cmdk__item.is-active>.bi{color:var(--accent-primary)}
.cmdk__labels{display:flex;flex-direction:column;min-width:0;flex:1 1 auto;overflow:hidden}
.cmdk__label{font-size:var(--fs-footnote);font-weight:var(--fw-medium);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.cmdk__sub{font-size:var(--fs-caption);color:var(--ink-tertiary);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.cmdk__star{flex:0 0 auto;background:none;border:0;cursor:pointer;color:var(--ink-tertiary);
  min-width:var(--target-dense);min-height:var(--target-dense);display:flex;align-items:center;justify-content:center}
.cmdk__star:hover{color:var(--warning)}
.cmdk__star.is-favorite{color:var(--warning)}
.cmdk__empty{padding:var(--s-4) var(--s-3);color:var(--ink-tertiary);font-size:var(--fs-footnote);text-align:center}
/* Discoverability affordance in the sidebar (touch users have no ⌘K). */
.cmdk-trigger{display:flex;align-items:center;gap:var(--s-2);width:100%;
  min-height:var(--target-dense);padding:0 var(--s-3);border-radius:var(--r-md);
  border:1px solid var(--line-default);background:var(--bg-sunken);color:var(--ink-tertiary);
  font-family:var(--font-ui);font-size:var(--fs-caption);cursor:pointer}
.cmdk-trigger:hover{border-color:var(--line-strong);color:var(--ink-secondary)}
/* v249 Phase 3: was five inline declarations on #notif-bell. Moved to a class
   so the icon rail can widen it to a 44px touch target at <=1024px — an inline
   width beats any stylesheet rule, which is why the rail could not reach it. */
.cmdk-trigger--icon{flex:0 0 auto;width:var(--target-dense);padding:0;
  justify-content:center;position:relative}
.cmdk-trigger__kbd{margin-left:auto;font-family:var(--font-ui);font-size:10px;
  padding:1px 5px;border:1px solid var(--line-default);border-radius:var(--r-sm);color:var(--ink-tertiary)}

/* ── Notification bell badge (v248) ───────────────────────────────────── */
.notif-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;
  padding:0 3px;border-radius:var(--r-pill);background:var(--danger);color:var(--ink-on-accent);
  font-size:10px;font-weight:var(--fw-semi);line-height:16px;text-align:center}

/* ── Search field (v249, Workstream 25) ──────────────────────────────────
   Six independent hand-rolled search-input implementations existed before
   this: .roster-search (students AND staff - one class, two call sites),
   .plan-store-search, .audit-search, .ptf-search, .program-searchbox, and
   one Reports filter input with no class at all - a literal inline
   style="..." attribute. Rather than inventing a seventh, this promotes
   .roster-search input's own ruleset (already built entirely from this
   file's tokens - the one existing implementation that was already
   correct) into the shared layer. Existing wrapper classes are untouched
   here (a full visual sweep is out of scope for a foundation-consolidation
   pass - see the V249 Workstream 25 tracker entry); new search inputs, and
   the plain inline-styled one this same workstream migrates, use this. */
.bd-search{
  width:100%;min-height:var(--target-dense);
  font-family:var(--font-ui);font-size:var(--fs-control);color:var(--ink-primary);
  background:var(--bg-raised);border:1px solid var(--line-default);border-radius:var(--r-md);
  padding:0 var(--s-3);box-sizing:border-box;
  transition:border-color var(--dur-quick) var(--ease-snappy),box-shadow var(--dur-quick) var(--ease-snappy);
}
.bd-search:hover{border-color:var(--line-strong)}
.bd-search:focus{border-color:var(--accent-primary);box-shadow:var(--focus-glow);outline:none}
.bd-search::placeholder{color:var(--ink-tertiary)}

/* ── Tabs (v249, Workstream 25) ───────────────────────────────────────────
   Several independent in-page tab/segmented-control implementations
   already exist (.program-library-tabs, .workspace-tabs, .school-planner-
   tabs, .audit-view-toggle) - all already follow the one rule that
   actually matters here (active = fill + ink + weight, never a border or
   corner accent - CLAUDE.md's own non-negotiable rule). This is the one
   new surfaces should reach for; existing ones are untouched in this pass. */
.bd-tabs{display:inline-flex;gap:var(--s-1);flex-wrap:wrap;padding:var(--s-1);
  background:var(--bg-sunken);border-radius:var(--r-lg);max-width:100%}
.bd-tab{display:inline-flex;align-items:center;gap:var(--s-2);
  min-height:var(--target-dense);padding:0 var(--s-4);
  font-family:var(--font-ui);font-size:var(--fs-control);font-weight:var(--fw-semi);
  color:var(--ink-tertiary);background:transparent;border:0;border-radius:var(--r-md);
  cursor:pointer;white-space:nowrap;
  transition:background var(--dur-quick) var(--ease-snappy),color var(--dur-quick) var(--ease-snappy)}
.bd-tab:hover:not(:disabled):not(.bd-tab--active){background:var(--bg-raised);color:var(--ink-secondary)}
.bd-tab--active{background:var(--bg-raised);color:var(--accent-primary);box-shadow:var(--elev-1)}
.bd-tab__count{min-width:20px;height:20px;padding:0 5px;border-radius:var(--r-pill);
  background:var(--bg-inset);color:var(--ink-tertiary);font-size:var(--fs-micro);font-weight:var(--fw-bold);
  display:inline-flex;align-items:center;justify-content:center}
.bd-tab--active .bd-tab__count{background:var(--accent-primary);color:var(--ink-on-accent)}
@media(max-width:600px){.bd-tabs{overflow-x:auto;flex-wrap:nowrap;max-width:100%}}

/* ── Filter bar (v249, Workstream 25) ─────────────────────────────────────
   Generalizes .program-filterbar's own responsive shape (one wide search/
   text field, narrower selects alongside it, collapsing to one column on
   a small screen) using tokens instead of a literal-pixel breakpoint per
   version (that file alone has plain, .v66 and .v67 variants of the same
   idea). Existing filter rows are untouched here; this is what a new one
   reaches for. */
.bd-filterbar{display:grid;
  grid-template-columns:minmax(240px,1.6fr) repeat(auto-fit,minmax(150px,1fr));
  gap:var(--s-2);margin-bottom:var(--s-4)}
.bd-filterbar>*{min-width:0}
@media(max-width:760px){.bd-filterbar{grid-template-columns:1fr}}

/* ── List row (v249, Workstream 33) ───────────────────────────────────────
   The canonical clickable list row. "Leading icon, a title line, a meta
   line under it, something small on the right, and the whole thing is a
   button" is reimplemented at least five times in this codebase already
   (.audit-row, .home-recent-row, .roster-card, .sched-row, the Review
   Center's own queue rows), each with its own literal pixels and its own
   hover colour. This is the one a NEW surface reaches for — the same
   consolidation Workstream 25 did for search, tabs and the filter bar, and
   the same deal: existing rows are untouched here, and nothing above is
   reworded to point at this.

   Unread is carried by WEIGHT and INK — a heavier title and a filled
   accent dot — never by an edge sliver or a corner marker (CLAUDE.md's
   non-negotiable rule). The dot is a real element with an accessible
   label, not a ::before decoration, precisely so it can say what it means.

   Hover declares `background` explicitly and carries :not(:disabled), so
   it both ties admin.css's global button:hover:not(:disabled) on
   specificity AND actually sets the property that rule would otherwise
   win — the exact trap CLAUDE.md documents. */
.bd-row{appearance:none;width:100%;display:flex;gap:var(--s-3);align-items:flex-start;
  text-align:left;margin:0;padding:var(--s-3) var(--s-4);
  border:0;border-bottom:1px solid var(--line-subtle);border-radius:0;
  background:var(--bg-raised);color:var(--ink-primary);box-shadow:none;
  font-family:var(--font-ui);font-size:var(--fs-control);cursor:pointer;
  min-height:var(--target-touch);box-sizing:border-box;
  transition:background var(--dur-quick) var(--ease-snappy)}
.bd-row:last-child{border-bottom:0}
.bd-row:hover:not(:disabled){background:var(--bg-inset);color:var(--ink-primary)}
.bd-row:focus-visible{outline:none;box-shadow:var(--focus-glow);position:relative;z-index:1}
.bd-row--selected{background:var(--accent-soft);color:var(--accent-on-soft)}
.bd-row--selected:hover:not(:disabled){background:var(--accent-soft)}
.bd-row__icon{flex:0 0 auto;color:var(--ink-tertiary);font-size:var(--fs-body);line-height:1.2;margin-top:1px}
.bd-row__main{flex:1 1 auto;min-width:0}
/* V251 R11. `display:block` is what makes the two ellipsis rules below WORK.
   Both are <span>s, and `overflow`/`text-overflow` do not apply to a
   non-replaced INLINE element - so the rules were inert and the text simply
   ran past its container, which clipped it with no ellipsis and no way to
   reach the rest. Measured on messages.html at 390px: .bd-row__main is a
   240px box (it already carries display:block and min-width:0, which is why
   the flex side of this was right), and .bd-row__meta inside it was 679px
   wide - a reader saw the first third of every message preview, cut
   mid-word, with nothing saying there was more. .bd-row__title looked fine
   only because the seeded titles happen to fit; it has the same rule and the
   same flaw. The page never scrolled sideways, which is why every overflow
   check in this repository passed on it. */
.bd-row__title{display:block;font-size:var(--fs-footnote);font-weight:var(--fw-semi);line-height:var(--lh-snug);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bd-row__meta{display:block;margin-top:2px;font-size:var(--fs-caption);color:var(--ink-tertiary);
  line-height:var(--lh-snug);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bd-row__aside{flex:0 0 auto;display:flex;align-items:center;gap:var(--s-2);
  font-size:var(--fs-micro);color:var(--ink-tertiary);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.bd-row--unread .bd-row__title{font-weight:var(--fw-bold);color:var(--ink-primary)}
.bd-row--unread .bd-row__meta{color:var(--ink-secondary)}
.bd-row__dot{width:8px;height:8px;border-radius:var(--r-pill);background:var(--accent-primary);
  flex:0 0 auto;display:inline-block}
.bd-rows{background:var(--bg-raised);border:1px solid var(--line-subtle);
  border-radius:var(--r-lg);overflow:hidden;box-shadow:var(--elev-1)}

/* ── Message thread (v249, Workstream 33) ─────────────────────────────────
   A transcript: who wrote it, when, and what they said, reading downward.
   Placed in the canonical layer rather than behind a thirteenth `msg-`
   prefix in admin.css — a transcript is not unique to Messages (a review
   discussion and an annotated audit entry are the same shape), and
   CLAUDE.md is explicit that a new component generation alongside the
   existing twelve makes this codebase worse.

   `--mine` is a fill difference only. No tail, no pointer, no corner
   bracket: the author is already named in words above every message, so
   the fill is reinforcement rather than the only signal. */
.bd-thread{display:flex;flex-direction:column;gap:var(--s-3)}
.bd-thread__msg{background:var(--bg-inset);border-radius:var(--r-lg);
  padding:var(--s-3) var(--s-4);min-width:0}
.bd-thread__msg--mine{background:var(--accent-soft)}
.bd-thread__msg--reply{margin-left:var(--s-5)}
.bd-thread__who{display:flex;gap:var(--s-2);align-items:baseline;flex-wrap:wrap;
  font-size:var(--fs-caption)}
.bd-thread__who b{font-weight:var(--fw-semi);color:var(--ink-primary);font-size:var(--fs-caption)}
.bd-thread__when{color:var(--ink-tertiary);font-size:var(--fs-micro)}
.bd-thread__body{margin-top:var(--s-2);font-size:var(--fs-footnote);
  line-height:var(--lh-body);color:var(--ink-primary);white-space:pre-wrap;
  overflow-wrap:anywhere}
.bd-thread__composer{display:flex;flex-direction:column;gap:var(--s-2);margin-top:var(--s-4);
  padding-top:var(--s-4);border-top:1px solid var(--line-subtle)}
.bd-thread__composer-actions{display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap}
.bd-thread__note{font-size:var(--fs-micro);color:var(--ink-tertiary);line-height:var(--lh-snug)}

/* ── The table scroll wrapper, in ONE place ───────────────────────────────
   V251 G45 — APP REGRESSION, mine, introduced at G40 (efe5742).

   G40 wrapped three transplanted tables in `.settings-table-wrap` so a
   roster would scroll inside its own box instead of dragging the whole
   document sideways at 390px. It worked on the pages and did NOTHING in the
   console: the only rule for that class lived in ui/scss/v4/_bd-transplanted.scss,
   which app/admin.html does not load — so the wrapper was an unstyled <div>
   there, the table squeezed to fit it instead of scrolling, and
   `.card{overflow-wrap:anywhere}` (admin.css:2133) broke the student codes
   down the middle: `A-001` rendered 10px wide and 96px tall, one character
   per line. Measured, before and after, at 390x844 with a coarse pointer.

   `app/bd_students.js` and `app/bd_settings.js` render into BOTH
   presentations, so a class they emit cannot be styled on only one side.
   primitives.css is the sheet admin.html, ui/main.js and collect.html all
   load — the one home from which the class means the same thing everywhere,
   which is what the SCSS copy (now removed) was asserting and not
   delivering.

   `min-width: max-content` on the table is the part that does the work.
   overflow-x alone is not enough: shell.css's phone block already gives
   `#app table` `max-width:100%`, so without a min-width the table obeys the
   wrapper's width and squeezes rather than scrolls. Verified by measuring
   with overflow-x alone (still 10px) and with both (38px, wrapper scrolls,
   document overflow 0). */
.settings-table-wrap{overflow-x:auto}
.settings-table-wrap>table{min-width:max-content}

/* ── The Program Library's favourite toggle ───────────────────────────────
   V251 B3, and found by a check written for a different control rather than
   by looking at the screen.

   `.program-favorite-btn` had no rule anywhere: not in app/admin.css, not in
   ui/scss/v4/_bd-transplanted.scss, nowhere. It is a star toggle — `bi-star` /
   `bi-star-fill` with `aria-pressed` — so the transplanted markup's catch-all
   gave it the primary navy fill, and every program card carried what looked
   like a filled primary button in its top corner.

   A toggle whose whole job is on-or-off must not look like an action, and its
   STATE is carried by the glyph (outline vs filled) — fill and ink, never a
   decorative marker, per the owner's standing rule.

   IT LIVES HERE, not in the page stylesheet, because app/bd_program_library.js
   renders into BOTH presentations. A rule only one side carries is the exact
   shape of the `.settings-table-wrap` regression G45a had to repair. Sized to
   Gentelella's own `.btn-icon` (32px square, centred, no padding), which is
   also this project's dense touch floor, and written against the tokens both
   token sets define. */
.program-favorite-btn{width:32px;min-width:32px;min-height:32px;padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  background:transparent;border:0;color:var(--ink-tertiary);font-size:14px;
  border-radius:var(--r-sm,6px);cursor:pointer}
.program-favorite-btn:hover:not(:disabled){background:var(--bg-inset);color:var(--ink-primary)}
.program-favorite-btn.is-favorite{color:var(--warning)}
.program-favorite-btn.is-favorite:hover:not(:disabled){color:var(--warning)}

/* ── The response-level editor, the target editor and the plan store ──────
   V251 R4 — APP REGRESSION, the same family as G45a and B8.

   `app/bd_program_library.js` and `app/bd_plan_builder.js` render every class
   below into BOTH presentations, and all 136 lines of it lived in
   `app/admin.css`, which only the console loads. Measured in the browser: the
   console resolves 45 `.rl-*` rules; program-library.html and students.html
   resolve ZERO. So the screen where a BCBA defines the prompt levels an RBT
   will tap — Ind / P / NC and their meanings, the scoring choice that decides
   what counts as independent, and the live preview of the buttons themselves —
   rendered completely unstyled on the built product.

   This is the trade primitives.css exists for, in its own words a few hundred
   lines above: it is the sheet admin.html, ui/main.js and collect.html all
   load, the one home from which a class means the same thing everywhere. A
   renderer that reaches two presentations cannot be styled by one of them.

   THE MOVE IS PROVED NEUTRAL FOR THE CONSOLE, not assumed. primitives.css
   loads BEFORE admin.css, so a rule moving earlier can lose a cascade it used
   to win. Every class below was injected into the console at 1280px and 390px
   and 23 computed properties read off each of 35 elements, before and after.
   See the commit for the comparison. */
/* ── v203 response / prompt level editor ──────────────────────────────────
   Shared by Task Analysis and Discrete Trial in the plan builder. Deliberately
   a plain checklist rather than a drag-and-drop: a BCBA configuring five
   levels wants to see all of them at once, and order is a number they can
   type without fighting a pointer. */
.rl-editor{grid-column:1/-1;border-top:1px solid var(--line);margin-top:10px;padding-top:10px}
.rl-editor>b{display:block;margin-bottom:4px}
.rl-help{font-size:12px;color:var(--muted);margin:0 0 10px;line-height:1.45;max-width:70ch}
.rl-rows{display:flex;flex-direction:column;gap:6px}
/* v221. FLEX, not a fixed grid.
   A grid template sized for the Programs tab overflowed the moment the same
   row rendered inside the narrower student-plan panel: the scoring select
   clipped and the remove button was pushed outside the row entirely. A
   viewport media query cannot fix that - the viewport was 1280px, the
   CONTAINER was ~300px. Wrapping on the available width works at every
   container size and replaces the two breakpoints this used to need. */
.rl-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:7px 8px;
  border:1px solid var(--line);border-radius:8px;background:#fff}
.rl-row .rl-move{flex:0 0 auto}
.rl-row .rl-use{flex:1 1 140px;min-width:0}
.rl-row .rl-order{flex:0 0 50px}
.rl-row .rl-short{flex:0 0 64px}
/* V251 R4. 160px was narrower than the shipped level labels. Measured in the
   Target Editor: the box was 177px at 1280 and 173px at 390 while "Full
   physical (hand-over-hand)" needs 192px, so a BCBA reading their own prompt
   hierarchy saw it cut off and had to click into the field and scroll. 240px
   is wider than the longest shipped label, so when the row wraps this field
   onto a line of its own it fits; when the row is wide it still shares, and
   flex-grow:2 keeps it the widest field there. */
.rl-row .rl-label{flex:2 1 240px;min-width:0}
.rl-row .rl-scoring{flex:1 1 150px;min-width:0}
.rl-row .rl-remove{flex:0 0 auto;margin-left:auto}
.rl-row.is-on{background:#f4f8f7}
.rl-row input[type=text],.rl-row input[type=number],.rl-row .rl-short,.rl-row .rl-label,.rl-row .rl-order{
  width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:6px;font-family:inherit;font-size:13px}
.rl-use,.rl-ind{display:flex;align-items:center;gap:6px;font-size:13px;white-space:nowrap}
.rl-use em{color:var(--muted);font-size:11.5px;font-style:italic}
/* v218. A level this build does not ship. Carried by fill and the "(custom)"
   word in the label - no edge marker, no corner sliver. */
.rl-row.is-custom{background:#fbf7ef;border-color:#e6dcc6}
.rl-row.is-custom.is-on{background:#f7f0e2}
.rl-add{display:grid;gap:8px;align-items:center;margin-top:8px;
  grid-template-columns:minmax(150px,1.4fr) 78px auto}
.rl-add input{width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:6px;
  font-family:inherit;font-size:13px}
.rl-add-help{margin-top:6px}
/* v221 --------------------------------------------------------------------
   The controls Steve asked for: reorder without typing a number, a plain x to
   drop a level, an explicit scoring choice, and a preview of the buttons an
   RBT will actually tap.

   State is carried by FILL and INK throughout - no edge marker, no corner
   sliver (see CLAUDE.md). Every hover rule carries :not(:disabled) and sets
   `background` explicitly, because admin.css ships
   button:hover:not(:disabled)
 at (0,2,1) and a bare
   .class:hover would lose the cascade and paint these #10243E. */
.rl-move{display:flex;flex-direction:column;gap:2px}
.rl-move-btn{width:22px;height:15px;padding:0;display:grid;place-items:center;
  border:1px solid var(--line);border-radius:4px;background:#fff;color:#475569;
  font-size:9px;line-height:1;cursor:pointer}
.rl-move-btn:hover:not(:disabled){background:#e8eef6;color:#10243e}
.rl-move-btn:disabled{opacity:.35;cursor:default}
.rl-remove{width:26px;height:26px;padding:0;display:grid;place-items:center;
  border:1px solid var(--line);border-radius:6px;background:#fff;color:#8b2d1e;
  font-size:11px;line-height:1;cursor:pointer}
.rl-remove:hover:not(:disabled){background:#fdedeb;border-color:#e6c3bc;color:#712418}
.rl-remove:disabled{opacity:.3;cursor:default}
.rl-scoring{width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:6px;
  font-family:inherit;font-size:12.5px;background:#fff;color:#334155}
/* Undecided. Stronger fill and darker ink, not a marker on the edge - the
   select is the thing that needs attention, so the select is what changes. */
.rl-scoring.is-unset{background:#fff4e5;border-color:#e0b483;color:#8c4313;font-weight:600}
.rl-preview{margin-top:10px;padding:9px 10px;border:1px solid var(--line);border-radius:9px;background:#fff}
.rl-preview-label{display:block;font-size:9px;text-transform:uppercase;letter-spacing:.08em;
  color:var(--muted);font-weight:700;margin-bottom:6px}
.rl-preview-btns{display:flex;flex-wrap:wrap;gap:6px}
.rl-preview-btn{display:flex;flex-direction:column;align-items:center;gap:1px;min-width:56px;
  padding:6px 9px;border:1px solid var(--line);border-radius:8px;background:#f6f8fb}
.rl-preview-btn b{font-size:12.5px;color:#10243e;line-height:1.1}
.rl-preview-btn small{font-size:9.5px;color:var(--muted);line-height:1.1;text-align:center}
/* Counts toward independence: carried by fill and weight, like the rest. */
.rl-preview-btn.is-ind{background:#eaf4ec;border-color:#bcd8c2}
.rl-preview-btn.is-ind b{color:#2f5d3a}
.rl-preview.is-empty{font-size:12px;color:#8c4313;background:#fff4e5;border-color:#e0b483}
.rl-add-semantic{width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:6px;
  font-family:inherit;font-size:13px;background:#fff}

/* The unified target editor. */
.target-editor{margin-top:8px;padding:12px;background:#f8fafc;border:1px solid var(--line);border-radius:11px}
.target-editor-section{margin-bottom:14px}
.target-editor-section:last-of-type{margin-bottom:8px}
.target-editor-section h4{margin:0 0 4px;font-size:11px;text-transform:uppercase;
  letter-spacing:.07em;color:#123d73;font-weight:700}
.target-editor-note{margin:0 0 8px;font-size:11.5px;color:var(--muted);line-height:1.45;max-width:70ch}
.target-editor .program-form-grid label small{font-weight:400}
.target-editor input[readonly]{background:#eef2f7;color:#475569;cursor:default}
.target-editor-actions{display:flex;gap:6px;margin-top:4px}
/* What staff will tap, on the closed plan card. */
.plan-item-levels{display:flex;flex-wrap:wrap;gap:4px;align-items:center;margin-top:6px}
.plan-item-level{display:inline-block;padding:2px 6px;border:1px solid var(--line);border-radius:5px;
  background:#f6f8fb;font-size:10.5px;font-weight:600;color:#475569}
.plan-item-level.is-ind{background:#eaf4ec;border-color:#bcd8c2;color:#2f5d3a}
.plan-item-level-warn{font-size:11px;color:#8c4313;background:#fff4e5;border:1px solid #e0b483;
  border-radius:5px;padding:2px 7px;line-height:1.35}

@media(max-width:900px){
  /* v221. Save/Cancel were 26px tall on a phone - under the 44px touch target
     the recorder work already holds itself to, and the two most consequential
     buttons in the editor. Scoped to this editor; nothing else is resized. */
  .target-editor-actions button{min-height:44px;padding-left:18px;padding-right:18px}
  /* The row wraps on its own; on a phone the controls only need to be bigger. */
  .rl-move{flex-direction:row}
  .rl-move-btn{width:32px;height:28px}
  .rl-remove{width:32px;height:28px}
  .rl-row .rl-use{flex:1 1 100%}
  .rl-row .rl-scoring{flex:1 1 100%}
  /* V251 R4. The label is the longest field and the one a BCBA has to be able
     to READ: at 390px its box was 173px while "Full physical (hand-over-hand)"
     needs 192px, so the level's meaning was cut off in the editor that defines
     it. A full line here, like the two fields above. */
  .rl-row .rl-label{flex:1 1 100%}
  .rl-add{grid-template-columns:1fr 78px}
  .rl-add-semantic{grid-column:1/-1}
  .rl-add button{grid-column:1/-1}
}

/* v221 — inspect a Program target before adding it, and trim it for one
   student in the same motion. State by fill and ink; nothing on an edge. */
.plan-store-target-row{display:flex;align-items:stretch;gap:8px}
.plan-store-target-row .plan-store-target{flex:1;min-width:0}
.plan-store-detail-toggle{flex:0 0 auto;align-self:center;padding:5px 10px;border:1px solid var(--line);
  border-radius:7px;background:#fff;color:#123d73;font-size:11.5px;font-weight:600;cursor:pointer;white-space:nowrap}
.plan-store-detail-toggle:hover:not(:disabled){background:#e8eef6;color:#10243e}
.plan-store-detail{margin:6px 0 2px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:#f8fafc}
.plan-store-detail-def{margin:0 0 6px;font-size:12px;color:#334155;line-height:1.45}
.plan-store-detail-line{margin:0 0 6px;font-size:11.5px;color:#475569}
.plan-store-detail-group{margin-top:8px}
.plan-store-detail-label{display:block;font-size:9px;text-transform:uppercase;letter-spacing:.08em;
  color:var(--muted);font-weight:700;margin-bottom:5px}
.plan-store-detail .rl-preview-btn{cursor:pointer;position:relative}
.plan-store-detail .rl-preview-btn input{position:absolute;opacity:0;width:1px;height:1px}
/* Excluded for this student: drained fill and muted ink, never a marker. */
.plan-store-detail .rl-preview-btn.is-off{background:#f1f3f6;border-color:#e2e6ec;opacity:.55}
.plan-store-detail .rl-preview-btn.is-off b,.plan-store-detail .rl-preview-btn.is-off small{color:var(--muted)}
.plan-store-detail .rl-preview-btn:focus-within{outline:2px solid #123d73;outline-offset:2px}
.plan-store-steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:3px}
.plan-store-steps li{font-size:12px;color:#334155}
