/* BehaviorDash — app shell (v172)
 *
 * Loaded after admin.css so it wins over the legacy layout rules it replaces.
 * Three responsibilities:
 *
 *   1. Stop the sidebar collapsing. The audit traced the truncated "OVERVI" /
 *      "ORGANI" labels at 390px to .sidebar having no flex-shrink inside a flex
 *      shell, plus .main-col{max-width:calc(100% - 150px)} assuming a width the
 *      sidebar no longer had.
 *   2. Give the shell a detail column, so inspecting one thing stops destroying
 *      the list you were working through.
 *   3. Make the navigation legible at every width — a labelled sidebar on wide
 *      screens, an icon rail when narrow, never truncated text.
 */

/* ── Shell ────────────────────────────────────────────────────────────── */
.layout{display:flex;min-height:100vh;background:var(--bg-base)}

.sidebar{
  /* The fix: never shrink below the declared width. */
  flex:0 0 232px;width:232px;min-width:0;
  background:var(--sidebar-bg);color:var(--sidebar-ink);
  display:flex;flex-direction:column;
  padding:var(--s-4) var(--s-3);
  position:sticky;top:0;height:100vh;
  transition:flex-basis var(--dur-snappy) var(--ease-snappy),
             width var(--dur-snappy) var(--ease-snappy);
}
.main-col{
  flex:1 1 auto;min-width:0;
  /* Legacy rule assumed a 150px sidebar and capped the column short of it. */
  max-width:none;
  display:flex;flex-direction:column;
}

/* ── Navigation ───────────────────────────────────────────────────────── */
.sidebar .brand{display:flex;align-items:center;gap:var(--s-2);
  padding:var(--s-2) var(--s-2) var(--s-5);color:var(--sidebar-ink-active);
  font-family:var(--font-brand);font-size:var(--fs-headline);font-weight:var(--fw-bold);min-width:0}
.sidebar .brand span{white-space:nowrap;overflow:hidden}
.sidebar .brand-icon{width:26px;height:26px;flex:0 0 auto;border-radius:var(--r-sm)}

.nav{display:flex;flex-direction:column;gap:var(--s-4);overflow-y:auto;flex:1;min-height:0}
.nav-group{display:flex;flex-direction:column;gap:2px}
.nav-group-label{
  font-size:var(--fs-micro);font-weight:var(--fw-bold);letter-spacing:var(--tracking-micro);
  text-transform:uppercase;color:var(--sidebar-ink-dim);padding:0 var(--s-2) var(--s-1);white-space:nowrap;overflow:hidden}

.tab{
  display:flex;align-items:center;gap:var(--s-3);
  width:100%;min-height:var(--target-dense);padding:0 var(--s-2);
  font-family:var(--font-ui);font-size:var(--fs-footnote);font-weight:var(--fw-medium);
  color:var(--sidebar-ink);background:transparent;border:0;border-radius:var(--r-md);
  cursor:pointer;text-align:left;position:relative;
  transition:background var(--dur-quick) var(--ease-snappy),color var(--dur-quick) var(--ease-snappy);
}
.tab>i{font-size:15px;flex:0 0 18px;text-align:center}
.tab__label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.tab:hover:not(:disabled){background:rgba(255,255,255,.07);color:var(--sidebar-ink-active)}
/* admin.css ships `.nav .tab.active{box-shadow:inset 3px 0 0 #8CCB32}` at
   (0,3,0) — the green edge sliver. Removing my own ::before was not enough;
   the marker lives in a box-shadow on a higher-specificity selector. This
   rule is (0,4,0) so it wins, and it kills the shadow explicitly rather
   than hoping a later file is enough. */
.sidebar .nav .tab.active{background:rgba(255,255,255,.16);
  color:var(--sidebar-ink-active);font-weight:var(--fw-semi);box-shadow:none}
/* NO corner-bracket, edge-sliver or partial-outline accent markers. Ever.
   The active destination is carried by its filled background, its weight and
   its ink — the same way macOS and iOS sidebars do it — not by a decorative
   chip clinging to one edge. (A 3px green ::before used to live here. It was
   not inherited from BehaviorDash; it was added during the shell step and
   then mislabelled as brand heritage. Removed at the owner's instruction.) */
.tab:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}

.side-utilities{display:flex;flex-direction:column;gap:2px;padding-top:var(--s-3);
  margin-top:var(--s-3);border-top:1px solid rgba(255,255,255,.10)}
.side-utility,.side-link{
  display:flex;align-items:center;gap:var(--s-3);width:100%;min-height:var(--target-dense);
  padding:0 var(--s-2);font-family:var(--font-ui);font-size:var(--fs-footnote);
  color:var(--sidebar-ink);background:transparent;border:0;border-radius:var(--r-md);
  cursor:pointer;text-align:left;text-decoration:none;
  transition:background var(--dur-quick) var(--ease-snappy),color var(--dur-quick) var(--ease-snappy)}
.side-utility>i,.side-link>i{font-size:14px;flex:0 0 18px;text-align:center}
.side-utility:hover:not(:disabled),.side-link:hover:not(:disabled){background:rgba(255,255,255,.07);color:var(--sidebar-ink-active)}
.side-utility>span,.side-link>span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}

/* ── Topbar ───────────────────────────────────────────────────────────── */
.topbar{
  background:var(--bg-raised);border-bottom:1px solid var(--line-subtle);
  padding:var(--s-3) var(--s-5);display:flex;align-items:center;gap:var(--s-3);
  position:sticky;top:0;z-index:var(--z-sticky);min-height:56px}
.topbar .org{font-size:var(--fs-footnote);font-weight:var(--fw-semi);color:var(--ink-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.topbar .user{font-size:var(--fs-caption);color:var(--ink-tertiary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:26ch}

/* ── Content + detail column ──────────────────────────────────────────── */
.main-col>main#app{flex:1;min-width:0;padding:var(--s-5)}
.detail-pane{
  position:fixed;top:0;right:0;bottom:0;width:min(440px,92vw);
  background:var(--bg-raised);border-left:1px solid var(--line-subtle);
  box-shadow:var(--elev-4);z-index:var(--z-drawer);
  overflow-y:auto;padding:var(--s-5);
  animation:bd-detail-in var(--dur-smooth) var(--ease-smooth)}
.detail-pane[hidden]{display:none}
@keyframes bd-detail-in{from{opacity:0;transform:translateX(16px)}to{opacity:1;transform:none}}
/* Above 1280px the detail column sits beside the content instead of over it,
   so the list stays fully visible — the point of a split view. It is a sibling
   of .main-col, not a child: nesting it inside made the topbar and ticker into
   columns of the same row. */
@media (min-width:1281px){
  .detail-pane:not([hidden]){
    position:sticky;top:0;height:100vh;box-shadow:none;
    width:400px;flex:0 0 400px;min-width:0;animation:none}
}

/* ── Icon rail ────────────────────────────────────────────────────────────
 * Below 1024px the sidebar becomes an icon rail. Labels are hidden VISUALLY
 * (clipped, not display:none) so each button keeps its accessible name and a
 * screen reader still hears "Review", not "button". */
@media (max-width:1024px){
  .sidebar{flex:0 0 60px;width:60px;min-width:60px;max-width:60px;
    padding:var(--s-4) var(--s-2);align-items:center}
  .sidebar .brand{padding:var(--s-2) 0 var(--s-5);justify-content:center}
  .sidebar .brand span,
  .nav-group-label,
  .tab__label,
  .side-utility>span,
  .side-link>span{
    position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
  .nav-group{gap:2px}
  .nav-group+.nav-group{padding-top:var(--s-2);border-top:1px solid rgba(255,255,255,.09)}
  .tab,.side-utility,.side-link{justify-content:center;padding:0;width:44px;min-height:44px;gap:0}
  /* v249 Phase 3 (iPad torture pass). Search + Notifications sit ABOVE the nav
     and were never part of this block, so at 60px they overflowed the rail and
     the bell ended up under the ticker — dead to a real tap in portrait on
     every iPad size. Stacked here, and sized like every other rail control. */
  .sidebar .side-search{flex-direction:column;gap:var(--s-1)!important;width:44px;align-self:center}
  .sidebar .side-search .cmdk-trigger{width:44px;min-height:44px;padding:0;
    justify-content:center;gap:0;flex:0 0 auto!important}
  /* Clipped, not removed — same as .tab__label above, for the same reason:
     the button keeps its accessible name. */
  .sidebar .side-search .cmdk-trigger__label,
  .sidebar .side-search .cmdk-trigger__kbd{
    position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
  .tab>i,.side-utility>i,.side-link>i{flex:0 0 auto;font-size:17px}
  .main-col>main#app{padding:var(--s-4)}
}

/* ── Phone ────────────────────────────────────────────────────────────────
 * The rail moves to the bottom as a horizontal bar, which is where a thumb
 * actually reaches. Tables scroll inside their own container rather than
 * clipping the page — the audit found Approve cut off at 390px. */
@media (max-width:640px){
  /* admin.css:1926 sets .layout{align-items:flex-start} — correct in row
     direction, where it stops the rail stretching to the page height. In
     column direction it means .main-col is never stretched to the phone
     width: it sizes to its own content instead. On a 390px phone the
     Behavior Plan builder measured 1140px wide and everything past ~390px
     was simply unreachable — you could see the left edge of a screen you
     could not scroll to. Restore stretch for the column layout. shell.css
     loads after admin.css, so this wins on order at equal specificity. */
  .layout{flex-direction:column;align-items:stretch}
  /* THE RAIL STAYS AT THE TOP ON A PHONE.
   *
   * It was moved to a fixed bottom bar here on the theory that a bottom tab
   * bar is where a thumb reaches. Two things were wrong with that:
   *
   *   1. Nobody asked for it. The Recorder already had a phone navigation
   *      that works — a row of text pills under the topbar that scrolls
   *      sideways — and the owner's answer, holding both screens side by
   *      side, was "it should be at the top like in the other screenshot".
   *   2. It rendered as garbage. Each tab stacked an icon above a label in
   *      a 56px column; with Bootstrap Icons actually loaded the icon and
   *      the label were drawn ON TOP OF EACH OTHER on every one of the
   *      eleven destinations, and labels 47-69px wide overflowed the 56px
   *      tab. The harnesses block the icon CDN, so every icon measured 0px
   *      wide and the collision was invisible to them.
   *
   * So this is the Recorder's pattern, not a second invention: a sticky
   * horizontal row of text pills, no icons, scrolling when it runs out of
   * width. The sidebar is already first in the DOM, so it needs no ordering.
   */
  .sidebar{
    position:sticky;top:0;bottom:auto;left:auto;right:auto;
    flex-direction:row;height:auto;flex:none;
    width:auto;min-width:0;max-width:none;
    padding:var(--s-2) var(--s-3);gap:var(--s-2);
    border-top:0;border-bottom:1px solid rgba(255,255,255,.12);
    z-index:var(--z-sticky);overflow-x:auto;align-items:center;
    /* admin.css:1941 sets align-self:flex-start for the desktop rail. On the
       cross axis of a column layout that means "size to your content", and
       the bar came out 1013px wide inside a 390px phone — clipped, with the
       last four destinations unreachable rather than scrollable. It has to
       stretch to the phone so overflow-x:auto has something to scroll. */
    align-self:stretch;
    overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch}
  /* "Open Recorder" and "Send feedback" live in .side-utilities. Hiding
     them on the phone removed the link staff use to GET TO THE RECORDER —
     the app's other half — with no replacement anywhere. They join the row
     as pills instead, after a divider, so they are visible and reachable. */
  .sidebar .brand{display:none}
  .side-utilities{display:flex;flex-direction:row;gap:var(--s-1);flex:0 0 auto;
    margin-top:0;padding-top:0;border-top:0;
    padding-left:var(--s-2);margin-left:var(--s-1);
    border-left:1px solid rgba(255,255,255,.12)}
  .sidebar .side-utility,
  .sidebar .side-link{flex:0 0 auto;width:auto;min-width:0;
    min-height:var(--target-touch);padding:var(--s-2) var(--s-3);
    border-radius:var(--r-md);white-space:nowrap;justify-content:center}
  .sidebar .side-utility>i,
  .sidebar .side-link>i{display:none}
  .sidebar .side-utility>span,
  .sidebar .side-link>span{
    position:static!important;width:auto;height:auto;margin:0;clip:auto;
    overflow:visible;white-space:nowrap;
    font-size:var(--fs-footnote);font-weight:var(--fw-semi);line-height:1.2}
  /* admin.css gives the Recorder link a ::before "Recorder" label for the
     <=1024 icon rail. With the real label visible again that prints it
     twice. v249 Workstream 36: this must carry the SAME selector as the
     rule it neutralises. When admin.css's was scoped by href it went to
     (0,3,1) and this one, at (0,2,1), silently stopped applying — the label
     came back on the phone and v172-shell caught it. Ties on specificity,
     wins on source order, which is the relationship this pair has always
     depended on. */
  .sidebar .side-link[href="collect.html"]::before{content:none}

  /* Text pills, like the Recorder. Dropping the icon is what removes the
     collision: one of the two things drawn in the same place is gone, and
     the label is the half that tells you where you are going. */
  .sidebar .nav .tab>i{display:none}
  /* THE !important IS LOAD-BEARING, AND THIS IS THE WHOLE BUG.
   *
   * The <=1024px rail hides the label with:
   *     .tab__label{position:absolute!important;width:1px;clip:rect(0 0 0 0)}
   * Only `position` carries the !important. That media query still matches at
   * 390px, so this block was overriding width and clip but LOSING on
   * position — leaving the label absolutely positioned at its full natural
   * width, drawn on top of the icon and contributing nothing to the row.
   * That is why every tab measured 24px (padding only) with a 47-69px label
   * printed across it, and why the phone bar read as garbage.
   *
   * Matching the !important is the honest fix: it is what the competing
   * declaration uses, and the two rules are the same property in the same
   * file fighting over the same element. */
  .sidebar .nav .tab>.tab__label{
    display:inline;position:static!important;width:auto;height:auto;margin:0;clip:auto;
    overflow:visible;max-width:none;
    font-size:var(--fs-footnote);font-weight:var(--fw-semi);line-height:1.2;
    white-space:nowrap}
  /* Auto width, so a label is never squeezed into a 56px column, and the
     44px touch target comes from the pill's own height. */
  .sidebar .nav .tab{flex:0 0 auto;flex-direction:row;gap:0;width:auto;
    min-width:0;min-height:var(--target-touch);
    padding:var(--s-2) var(--s-3);border-radius:var(--r-md);white-space:nowrap}
  /* Thirteen destinations in one row means most of them are off-screen, and
     a row that gives no sign it scrolls reads as a row with things MISSING —
     which is exactly how it was reported. Fade the right edge while there is
     more to reach. Not a state marker on a corner: it is the standard
     "keep swiping" affordance, and it disappears at the end of the row. */
  .sidebar{
    -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 28px),transparent);
    mask-image:linear-gradient(to right,#000 calc(100% - 28px),transparent)}
  .sidebar.is-scroll-end{-webkit-mask-image:none;mask-image:none}
  .nav-group{flex:0 0 auto}
  /* ONE scroller: the bar itself. The nav was flex:1 (shrinks) while
     .side-utilities was flex:0 0 auto (does not), so the two utility pills
     took 247px of a 390px bar and squeezed the nav to 107px — three
     destinations visible and eight pushed out of a nested scroller nobody
     would think to swipe. The nav sizes to its content and the whole row
     scrolls as one: every destination first, the two utilities after. */
  /* .sidebar .nav is what admin.css:1949 uses, so this has to match it —
     a bare .nav here is (0,1,0) and loses, which is exactly how the nav
     stayed at flex:1 and got squeezed to 107px on the first attempt. */
  .sidebar .nav{flex-direction:row;gap:var(--s-1);overflow:visible;flex:0 0 auto;
    padding:0;min-height:0;height:auto}
  .nav-group{flex-direction:row;gap:var(--s-1)}
  .nav-group+.nav-group{padding-top:0;border-top:0;border-left:1px solid rgba(255,255,255,.12);
    padding-left:var(--s-1);margin-left:var(--s-1)}
  .main-col>main#app{padding:var(--s-3)}
  .topbar{padding:var(--s-2) var(--s-3);min-height:48px}
  .detail-pane{width:100vw;top:auto;height:82vh;border-left:0;
    border-top-left-radius:var(--r-xl);border-top-right-radius:var(--r-xl)}
  #app table{display:block;overflow-x:auto;max-width:100%}
}

/* Reduce Transparency / Increase Contrast: solid surfaces, stronger edges. */
@media (prefers-contrast:more){
  .tab.active{background:rgba(255,255,255,.2)}
  .detail-pane{border-left-width:2px}
}

/* ── Global control floors (v172) ─────────────────────────────────────────
 * The audit measured the review checklist at 13x13px, a quarter of WCAG 2.2's
 * 24x24 minimum, on the primary approve/return workflow. Raising the control
 * itself fixes every instance at once rather than per surface. */
input[type="checkbox"],input[type="radio"]{
  width:18px;height:18px;accent-color:var(--accent-primary);flex:0 0 auto;cursor:pointer}
/* A small control still needs a large hit area: the label around it supplies
   the rest of the 24px, which is how WCAG's spacing exception is satisfied. */
#app label:has(> input[type="checkbox"]),
#app label:has(> input[type="radio"]){
  display:inline-flex;align-items:center;gap:var(--s-2);min-height:24px;cursor:pointer}
/* v249 Phase 3 (iPad / classroom pass). 24px is WCAG 2.2's floor and is the
   right answer for a mouse. foundations.css states the other half of the rule
   — "Pointer devices may go denser; touch may not" — and names --target-dense
   (32px) as the tier below --target-touch. On an iPad the Review Center's
   checklist is three of these label rows stacked, and it is the supervisor's
   whole daily task, so on a coarse pointer they go to the dense tier.

   This lives HERE, not in admin.css, for a specificity reason worth writing
   down: the rule above is (1,1,1) because of the #app id. admin.css's
   .roster-toggle{min-height:44px} is (0,1,0) and has been silently losing to
   it — the 44px that rule asks for has never once been applied. A coarse-
   pointer bump written in admin.css would lose the same way twice over, since
   shell.css also loads after it. Matching the selector exactly, in the same
   file, immediately below, is the only version that actually takes effect. */
@media (pointer: coarse){
  #app label:has(> input[type="checkbox"]),
  #app label:has(> input[type="radio"]){min-height:var(--target-dense)}
}
