/* ============================================================================
   app-shell.css — the B+ global navigation frame (M1_5_BPLUS_GLOBAL_SHELL).

   Visual source of truth:
     apps/erp/docs/ui-proposals-2026-07-28/_shell-snippet.html  BLOCK 0 + BLOCK 6

   NEW FILE rather than an addition to css/header.css, which was already 384
   lines — over the Iron Rule 12 hard cap of 350 — before this run started.

   ---------------------------------------------------------------------------
   VISUAL IDENTITY: OWN_NAMESPACE (P-AUTHOR-2026-05-28-B), and why.

   Two project rules meet head-on in this file and both are right:

     * The mockup kit (line 169) mandates LITERAL values and forbids var(),
       because the live pages reference tokens no mockup defines, and a var()
       that resolves to nothing drops the whole declaration at parse time and
       renders the sidebar as bare text. That is the DLU_SPEC3_FULL_FLEET bug.
     * The repo's design-token gates forbid raw hex and literal font-sizes in
       any NEW css file.

   Resolved by declaring the shell's own scoped scale here, in .bplus-shell, and
   referencing it everywhere below. The token-definition lines are the only place
   a literal appears, which is exactly the form all three gates strip before
   checking — so the mockup's values survive byte-for-byte AND nothing is
   allowlisted or grandfathered.

   Brand navy is deliberately NOT bound to var(--primary). --color-primary is
   tenant-overridable at runtime by theme-loader.js, so binding it would let a
   tenant repaint the global navigation — which is how M17 silently shipped in
   Prizma green. B+ locks this navy; a tenant's brand belongs on the content,
   not on the frame.

   The NEUTRALS below are the same Slate values as shared/css/variables.css
   (--color-gray-900 is #0f172a, --color-gray-200 is #e2e8f0, and so on), so this
   namespace agrees with the design system rather than competing with it.
   ========================================================================== */

.bplus-shell{
  /* brand — locked by decision B+, not tenant-themeable */
  --bplus-navy:        #1e3a8a;
  --bplus-navy-lift:   #3b5bd0;
  --bplus-accent:      #60a5fa;
  --bplus-accent-soft: #93c5fd;
  --bplus-danger:      #ef4444;
  --bplus-chipline:    #c7d2fe;
  --bplus-chipbg:      #eef2ff;

  /* neutrals — same Slate values as shared/css/variables.css */
  --bplus-ink:      #0f172a;
  --bplus-surface:  #ffffff;
  --bplus-canvas:   #f0f2f5;
  --bplus-line:     #e2e8f0;
  --bplus-mute-100: #f1f5f9;
  --bplus-mute-300: #cbd5e1;
  --bplus-mute-400: #94a3b8;
  --bplus-mute-500: #64748b;

  /* type scale — the mockup's exact pixel values. Deliberately NOT bound to
     var(--font-size-*): that scale is rem-based (0.75/0.85/0.92rem) and none of
     its steps equal 10.5px, 12.5px or 13px, so binding to it would quietly
     redraw approved typography. */
  --bplus-fs-logo:  18px;
  --bplus-fs-name:  15px;
  --bplus-fs-tiny:  10.5px;
  --bplus-fs-item:  13px;
  --bplus-fs-hint:  11.5px;
  --bplus-fs-ctl:   12.5px;
  --bplus-fs-bdg:   10px;
  --bplus-fs-tog:   14px;
  --bplus-fs-tip:   12px;
  --bplus-fs-bdg-s: 9px;

  /* The sidebar's CURRENT width, PUBLISHED for other components to read — it does
     NOT drive this sidebar's own width. A host that pins its own furniture with
     position:fixed resolves inset-inline-start against the VIEWPORT, which in RTL
     is the exact strip this sidebar occupies, so without an offset the two land on
     top of each other. inventory.html's category sidebar did (SHELL_FIXPACK D3).

     Deliberately a MIRROR of the literals on .bplus-sbar below, not their source.
     Driving width from this var looked tidier and silently broke the collapse:
     .bplus-sbar carries `transition: width .22s`, and an unregistered custom
     property is not animatable, so a var-driven width never reached its new value
     — the rail stayed 240px forever while the labels hid, measured on the develop
     preview. (Disabling the transition snapped it to 56px, which is what isolated
     it.) Keep this value equal to the literal below and to the rail override. */
  --bplus-sbar-w: 240px;

  /* The header row's height, PUBLISHED for the same reason as --bplus-sbar-w
     above: a host whose own root is sized to the viewport (crm.html's
     `.crm-app{height:100vh}`) is no longer the viewport's only occupant once the
     shell puts a header row above it, and needs to subtract this. Same discipline
     as the width: a MIRROR of the literal on .bplus-hdr below, never its source —
     keep the two equal. */
  --bplus-hdr-h: 60px;

  display:flex;align-items:stretch;direction:rtl;font-family:'Heebo',sans-serif;min-height:100vh;text-align:right;
}

/* ============================================================================
   THE SHELL DEFENDS ITS OWN LAYOUT  (SHELL_MAKE_IT_RIGHT, 2026-08-06)

   The rule this section exists to enforce, ruled by the owner after he found a
   broken shell on his own screen twice:

     > The shell renders identically regardless of what the host's <body>, or
     > any bare element selector in any stylesheet the host loads, happens to
     > declare.

   Two defects, one failure: the shell re-parents into an environment it does
   not control, and did not defend against it.

     * A stylesheet reached IN. `shared/css/tabs.css` styled the legacy tab
       strip as a bare `nav{}`; the shell's sidebar nav is a <nav>, so an opaque
       white background, a row flex-direction and position:sticky landed on it.
     * The host's own box CONTAINED it. `mount()` appends to document.body, so
       `.cust-page{padding:18px}` inset the entire global frame 18px from every
       edge on customers.html and prescriptions.html.

   Fixing only the first would leave the class alive: the next host stylesheet
   with a bare element selector rebuilds the same bug. So the shell now
   re-declares every property such a selector can reach.
   ========================================================================== */

/* --- the host's body box must not contain the frame ---------------------- */
/* Only ever active when the shell is actually mounted, so the legacy arm for a
   non-opted-in employee is untouched — that page has no .bplus-shell child and
   this rule does not match it.
   Specificity is (0,1,1): `:has()` takes its argument's, so tag + class. That
   clears `.cust-page`/`.rx-page`/`.lab-page` (0,1,0) without !important. */
body:has(> .bplus-shell){margin:0;padding:0}

/* --- hardening: properties a bare element selector can reach ------------- */
/* .bplus-nav is a <nav>, .bplus-sbar an <aside>, .bplus-item an <a>, and the
   controls are <button>. Each re-states what it needs, at class specificity,
   so an element-level rule in any host stylesheet loses. The .bplus-nav list
   is not guesswork: it is exactly the declaration set that flipped the D3
   harness from FAIL to PASS on the reproduction (DIAGNOSIS.md §6). */
.bplus-shell .bplus-sbar{position:relative;inset:auto;float:none;margin:0;box-sizing:border-box}
.bplus-shell .bplus-nav{background:transparent;border:0;display:flex;flex-direction:column;
  align-items:stretch;justify-content:flex-start;gap:0;margin:0;padding:0 0 8px;
  position:static;inset:auto;z-index:auto;overflow-x:hidden;float:none;box-sizing:border-box}
.bplus-shell .bplus-item{float:none;margin:0;box-sizing:border-box;text-transform:none}
.bplus-shell .sb-toggle,.bplus-shell .bplus-hbtn,.bplus-shell .bplus-burger{
  float:none;box-sizing:border-box;text-transform:none;letter-spacing:normal;line-height:1}

.bplus-main{flex:1;min-width:0;background:var(--bplus-canvas);display:flex;flex-direction:column;overflow:visible;position:relative}
.bplus-main>*{max-width:100%}

/* ---- the daily sidebar (240px, pinned right in RTL) ------------------- */
/* No width/flex-basis transition — it never completed and left the rail broken.
   Measured on the develop preview (SHELL_FIXPACK): clicking ‹ flipped the class,
   hid the labels and wrote the preference, while the sidebar's BOX stayed at its
   previous width indefinitely — 240px wide showing rail content, or 56px wide
   after expanding. Still wrong 2.5s after a .22s transition, so it is wedged, not
   slow. Removing the transition fixes it deterministically: three consecutive
   toggles measured 240 → 56 → 240 exactly, and the category sidebar (which reads
   --bplus-sbar-w, an untransitioned custom property that always updated instantly)
   stays glued at overlap 0 instead of drifting 184px out of register. */
.bplus-sbar{width:240px;flex:0 0 240px;background:linear-gradient(180deg,var(--bplus-ink),var(--bplus-navy) 140%);color:var(--bplus-line);display:flex;flex-direction:column;position:relative}
.bplus-brand{display:flex;align-items:center;gap:9px;padding:13px 14px;border-bottom:1px solid rgba(255,255,255,.12)}
.bplus-brand .bplus-logo{font-size:var(--bplus-fs-logo)}
.bplus-brand .bplus-nm{font-weight:800;font-size:var(--bplus-fs-name);color:var(--bplus-surface)}
.bplus-brand .bplus-tn{font-size:var(--bplus-fs-tiny);opacity:.7;display:block}
.bplus-nav{flex:1;min-height:0;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.25) transparent;padding-bottom:8px}
.bplus-group{padding:8px 8px 2px}
.bplus-gl{font-size:var(--bplus-fs-tiny);font-weight:800;letter-spacing:.6px;color:var(--bplus-mute-400);padding:0 8px 4px}
.bplus-item{display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:8px;font-size:var(--bplus-fs-item);font-weight:600;color:var(--bplus-mute-300);text-decoration:none;position:relative}
.bplus-item:hover{background:rgba(255,255,255,.08);color:var(--bplus-surface)}
.bplus-item.bplus-act{background:rgba(59,130,246,.28);color:var(--bplus-surface);border-inline-start:3px solid var(--bplus-accent);padding-inline-start:6px}
.bplus-item .bplus-bdg{margin-inline-start:auto;background:var(--bplus-danger);color:var(--bplus-surface);font-size:var(--bplus-fs-bdg);font-weight:800;border-radius:99px;min-width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;padding:0 4px}
.bplus-item .ic{flex:0 0 auto;width:18px;text-align:center}
.bplus-item .lbl{white-space:nowrap;overflow:hidden}
.bplus-hint{padding:10px 14px;font-size:var(--bplus-fs-hint);color:var(--bplus-mute-400);line-height:1.6;border-top:1px solid rgba(255,255,255,.1);margin-top:8px}
.bplus-hint b{color:var(--bplus-surface)}

/* ---- in-shell header row; burger is the LAST child => top-LEFT in RTL -- */
.bplus-hdr{height:60px;background:var(--bplus-surface);color:var(--bplus-ink);border-bottom:1px solid var(--bplus-line);display:flex;align-items:center;gap:14px;padding:0 20px;flex:0 0 auto}
.bplus-crumbs{font-size:var(--bplus-fs-item);color:var(--bplus-mute-500);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bplus-crumbs .bplus-sep{color:var(--bplus-mute-300)}
.bplus-crumbs b{color:var(--bplus-ink)}
.bplus-crumbs a{color:inherit;text-decoration:none}
.bplus-crumbs a:hover{text-decoration:underline}
.bplus-grow{flex:1}
.bplus-hbtn{font-family:inherit;background:var(--bplus-chipbg);border:1px solid var(--bplus-chipline);color:var(--bplus-navy);border-radius:9px;padding:6px 12px;font-size:var(--bplus-fs-ctl);font-weight:600;cursor:pointer;white-space:nowrap}
.bplus-bell{position:relative}
.bplus-bell .bplus-bdg{position:absolute;top:-6px;inset-inline-start:-7px;background:var(--bplus-danger);color:var(--bplus-surface);font-size:var(--bplus-fs-bdg);font-weight:800;min-width:16px;height:16px;border-radius:99px;display:inline-flex;align-items:center;justify-content:center;padding:0 4px}
.bplus-chip{background:var(--bplus-mute-100);color:var(--bplus-ink);border-radius:99px;padding:5px 12px;font-size:var(--bplus-fs-ctl);display:flex;gap:6px;align-items:center;white-space:nowrap}
.bplus-burger{font-family:inherit;background:var(--bplus-navy);border:1.5px solid var(--bplus-navy);color:var(--bplus-surface);border-radius:9px;padding:7px 14px;font-size:var(--bplus-fs-item);font-weight:800;cursor:pointer;white-space:nowrap}
/* MENU-reached screens: no sidebar item is active, so the burger carries it */
.bplus-burger.bplus-act{background:var(--bplus-navy-lift);border-color:var(--bplus-accent-soft);box-shadow:0 0 0 3px rgba(147,197,253,.45)}

/* ============================================================================
   Collapse — option א׳. BLOCK 0's collapse section records three measured
   departures from the sketch file, all kept here:
     1. the state class is `bplus-rail`, not a bare `rail` — stock-count.html and
        marketing.html already carry a bare .rail{} rule of equal specificity
        that would restyle the whole shell;
     2. .bplus-item{position:relative} is required — the tooltip and the
        collapsed corner badge have nothing to anchor to without it (folded into
        the .bplus-item rule above);
     3. the tooltip opens toward the content (inline-START in RTL) and the
        collapsed nav stops clipping — inline-END put the box outside the shell,
        where .bplus-nav's overflow hid it completely.
   ========================================================================== */
.sb-toggle{font-family:inherit;margin-inline-start:auto;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.22);color:var(--bplus-line);border-radius:8px;width:26px;height:26px;font-size:var(--bplus-fs-tog);line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.sb-toggle:hover{background:rgba(255,255,255,.2);color:var(--bplus-surface)}

/* collapsed: 240px => 56px icon rail */
.bplus-shell.bplus-rail{--bplus-sbar-w:56px}   /* mirror — see the note in .bplus-shell */
.bplus-shell.bplus-rail .bplus-sbar{width:56px;flex-basis:56px}
.bplus-shell.bplus-rail .bplus-brand{padding:13px 0;justify-content:center;flex-direction:column;gap:6px}
.bplus-shell.bplus-rail .bplus-brand .txt{display:none}
.bplus-shell.bplus-rail .sb-toggle{margin:0}
.bplus-shell.bplus-rail .bplus-gl{display:none}
.bplus-shell.bplus-rail .bplus-hint{display:none}
.bplus-shell.bplus-rail .bplus-group{padding:8px 6px 2px}
.bplus-shell.bplus-rail .bplus-nav{overflow:visible}
.bplus-shell.bplus-rail .bplus-item{justify-content:center;padding:9px 0}
.bplus-shell.bplus-rail .bplus-item .lbl{display:none}
.bplus-shell.bplus-rail .bplus-item.bplus-act{border-inline-start:0;box-shadow:inset 0 0 0 1.5px var(--bplus-accent);padding-inline-start:0}
.bplus-shell.bplus-rail .bplus-item .bplus-bdg{position:absolute;top:3px;inset-inline-start:5px;margin:0;min-width:14px;height:14px;font-size:var(--bplus-fs-bdg-s)}

/* hovering a collapsed icon shows its name, opening toward the content */
.bplus-shell.bplus-rail .bplus-item::after{content:attr(data-label);position:absolute;inset-inline-start:calc(100% + 8px);top:50%;transform:translateY(-50%);background:var(--bplus-ink);color:var(--bplus-surface);font-size:var(--bplus-fs-tip);font-weight:600;padding:4px 9px;border-radius:7px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s;z-index:30;box-shadow:0 4px 14px rgba(0,0,0,.3)}
.bplus-shell.bplus-rail .bplus-item:hover::after{opacity:1}

/* ---- live-app only: the legacy sticky header is redundant inside the shell,
   because .bplus-hdr already carries identity, employee and exit. Hidden rather
   than deleted, so the legacy arm keeps rendering it byte-identically. ------ */
.bplus-shell .app-header{display:none}
