/* ═══════════════════════════════════════════════════════════════════════════
   Centre Rx — portal restyle layer.
   Source of truth for every value: elia-pixel123/centre-lab@main index.html.

   TWO JOBS, ONE FILE:
   §A  Portal-wide layer — load AFTER /css/portal.css on every review-world
       page. portal.css is token-driven, so re-pointing its custom properties
       restyles the whole portal without touching page markup.
   §B  Login screen — login.html loads THIS FILE ALONE (no portal.css), so §B
       is self-sufficient: reset, type, form controls, alert, footer.

   WHY §B IS SCOPED UNDER body.login-page (2026-08-20, implementation):
   the handoff said "the login rules are folded in here unchanged". Folding
   them in unchanged would have broken every other page: login styles generic
   class names that portal.css also defines — .btn (login sets width:100%),
   .form-input, .form-group, .form-label, .alert — plus a global `*` reset and
   an `a` colour. Loaded after portal.css on a review-world page those would
   win everywhere. Every login rule that touches a shared class name is
   therefore scoped to body.login-page. Values are unchanged; only reach is.
   ═══════════════════════════════════════════════════════════════════════════ */


:root {
  /* Centre Rx tokens (canonical) */
  --black:#0f0f10; --text:#141416; --text-secondary:#6b6b70; --text-muted:#9a9aa0;
  --bg:#f4f4f5; --bg-soft:#fafafa; --bg-white:#ffffff;
  --border:#e5e5e8; --border-light:#ededf0;
  --red:#e0142c; --red-dark:#c10f24; --red-light:#fdeaec;
  --green:#1a9e4a; --green-light:#e8f8ee;
  --amber:#c98a12; --amber-light:#fbf1de;
  --radius:10px; --radius-sm:7px;
  --shadow:0 1px 3px rgba(0,0,0,.06);
  --shadow-lg:0 4px 24px rgba(0,0,0,.06);
  /* Re-point the template's palette so portal.css rules inherit the system */
  --primary:var(--red-dark); --secondary:var(--black);
  --accent-pink:var(--red); --accent-gradient:var(--red); /* gradients die */
  --purple:var(--red-dark); --purple-light:var(--red-light);
  --blue:var(--black); --blue-light:var(--bg);
  --pink:var(--red); --pink-light:var(--red-light);
  --teal:var(--green); --teal-light:var(--green-light);
  --cyan:var(--black); --cyan-light:var(--bg);
  --error:var(--red-dark); --success:var(--green);
  /* Sidebar goes light */
  --sidebar-bg:#ffffff;
  --sidebar-active:var(--red-light);
  --sidebar-hover:var(--bg);
  --sidebar-text:var(--text-secondary);
  --sidebar-text-active:var(--red-dark);
}

/* Type: Inter everywhere; the serif display face dies */
html, body, h1, h2, h3, h4, .sidebar-brand h1, .footer-brand,
.login-brand-title, .dashboard-brand {
  font-family:'Inter',-apple-system,BlinkMacSystemFont,system-ui,sans-serif !important;
}
h1,h2,h3{font-weight:800;letter-spacing:-.3px;color:var(--black)}

/* De-branding of template-specific rules that don't follow tokens */
.sidebar{border-right:1px solid var(--border)}
.sidebar-brand h1{background:none!important;-webkit-text-fill-color:var(--black);color:var(--black);font-size:17px;letter-spacing:.5px;font-weight:800}
.sidebar-brand-sub{color:var(--text-muted);letter-spacing:2.5px}
.nav-item{color:var(--sidebar-text);font-weight:600;border-radius:var(--radius-sm)}
.nav-item.active{background:var(--sidebar-active);color:var(--sidebar-text-active)}
.nav-item:hover{background:var(--sidebar-hover);color:var(--text)}
.nav-badge{background:var(--red)} .nav-badge.amber{background:var(--amber)}
.sidebar-footer{border-top:1px solid var(--border);color:var(--text-muted)}

.btn-primary{background:var(--red);color:#fff;font-weight:700}
.btn-primary:hover:not(:disabled){background:var(--red-dark);opacity:1}
.btn-outline:hover:not(:disabled){border-color:var(--black);color:var(--text)}
.btn-success{background:var(--green)}
.doctor-avatar,.patient-avatar,.inbox-item-avatar,.conversation-header-avatar{background:var(--black)}
.bubble.outbound{background:var(--red)}
.stat-card::before{background:var(--red)}
.stat-card.amber::before{background:var(--amber)}
.stat-card.teal::before,.stat-card.purple::before,.stat-card.blue::before{background:var(--red)}
.stat-icon{display:none} /* no icon set in the system */
.stage.review{background:var(--amber-light);color:var(--amber)}
.stage.approved,.stage.shipped{background:var(--green-light);color:var(--green)}
.stage.captured,.stage.intake{background:var(--bg);color:var(--text-secondary)}
.urgent-dot{animation:none;background:var(--amber)}
.plat-badge{background:var(--bg)!important;color:var(--text)!important}
.plat-badge .bdot{ /* brand colour stays on the dot only (inline style from JS wins) */ }
.approved-pill.active{background:var(--red);color:#fff;border-color:var(--red)}
.page-tab.active{color:var(--red-dark);border-bottom-color:var(--red)}
.banner.warning{background:var(--amber-light);border-color:#f0dcb4;color:#8a5e0c}
.banner.error{background:var(--red-light);border-color:#f5c9cf;color:var(--red-dark)}
.banner.success{background:var(--green-light);border-color:#c5e6d1;color:#127034}
.nt-type-btn.active,.nt-btn-primary,.nt-checkbox input:checked+span{background:var(--red)!important;filter:none}
.inbox-item.active{background:var(--red-light)} .inbox-item.active::before{background:var(--red)}
.inbox-unread-badge,.nav-badge.pink{background:var(--red)}
.footer-brand{color:var(--text-secondary);font-weight:800;letter-spacing:.5px}
.modal-overlay{background:rgba(15,15,16,.45)}

/* Icon glyphs: Font Awesome is dropped portal-wide. Until each page's markup
   pass strips <i class="fa…"> tags, neutralise them: */
i[class^="fa"],i[class*=" fa"]{display:none}
.topbar-btn.is-spinning{animation:spin .6s linear infinite}

/* Sidebar group labels + logo lockup.
   shared/nav.html emits .side-label and .logo-lockup, and NEITHER portal.css nor
   the handoff's stylesheet defined them (found live during the dashboard pass,
   2026-08-20) — unstyled, the group headers render as bare text and the lockup
   loses its row. Defined here, in the system's own scale. */
.logo-lockup{display:flex;align-items:center;gap:10px}
/* R5 — the brand lockup sat flush at y=0. `aside.sidebar` has no padding of its own and the
   lockup carried `padding:0 8px 16px` inline from renderNav, so the ring touched the top of the
   viewport on all 12 doctor-portal pages (measured live at 1440x1000: lockupTop = 0 on every
   one). The reference gives it room above; 20px matches the aside's horizontal rhythm.

   Scoped with `>` on purpose, and this is the whole reason it is one rule rather than two:
     · the DOCTOR nav emits `aside.sidebar > .logo-lockup`   → gains 20px, was 0
     · the REP nav emits `aside.sidebar > .sidebar-brand > .logo-lockup`, and .sidebar-brand
       already contributes `padding:24px 20px 4px` → measured at 24px, already correct, and must
       NOT be pushed to 44px by a blanket rule
     · order-new.html's in-page header lockup is not inside an aside at all → untouched
   The inline padding was removed from renderNav in the same commit; an inline style would have
   beaten this rule silently, which is how a "one-line CSS fix" ships as a no-op. */
aside.sidebar > .logo-lockup{padding:20px 8px 16px}
.side-label{font-size:10.5px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  color:var(--text-muted);padding:0 10px;margin:0 0 6px}

/* Ring logo (no image asset exists — the ring is code) */
.logo-ring{width:34px;height:34px;border-radius:50%;position:relative;flex-shrink:0;
  background:conic-gradient(from 0deg,#22c1c3,#e0142c,#f2b807,#1a9e4a,#22c1c3)}
.logo-ring::after{content:'';position:absolute;inset:6px;background:#fff;border-radius:50%}
.logo-text{line-height:1}
.logo-text .l1{font-weight:800;font-size:17px;letter-spacing:.5px;color:var(--black)}
.logo-text .l2{font-weight:600;font-size:9.5px;letter-spacing:2.5px;color:var(--text-muted)}

/* .form-hint — PRE-EXISTING undefined class, found during the S3 Account pass: settings.html
   has used it since before this program ("Minimum 8 characters" under the password field) and
   NEITHER stylesheet ever declared it, so it has been rendering as plain body text all along.
   The S3 panels lean on it heavily, so it is defined here rather than left to inherit. */
.form-hint{font-size:12px;color:var(--text-muted);line-height:1.5;font-weight:400}

/* Card-on-file row (Account → Payment Method). Brand + gateway-masked last-4, fetched at
   display time and never stored (S4 ruling). Monospace on the number so the dots align. */
.crx-card-row{border:1px solid var(--border);border-radius:var(--radius);padding:16px 18px;background:#fff}
.crx-card-brand{font-size:12px;font-weight:800;letter-spacing:.6px;text-transform:uppercase;color:var(--text-secondary)}
.crx-card-num{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:17px;font-weight:600;
  color:var(--black);letter-spacing:1px;margin:6px 0 8px}
.crx-card-note{font-size:12px;color:var(--text-muted);line-height:1.5}

/* Read-only chips (Account → "States you can order in"). Defined WITH the markup that uses
   them — BP-CRX-210 was exactly this shape: a class the design used and no stylesheet
   declared, invisible to both reviews because each artifact was individually correct. */
.crx-chips{display:flex;flex-wrap:wrap;gap:6px}
.crx-chip{display:inline-block;padding:4px 10px;border-radius:999px;background:var(--bg);
  border:1px solid var(--border);font-size:12px;font-weight:700;color:var(--text-secondary);
  letter-spacing:.3px}

/* Protocol drawer + wizard shell (new-module shared components) */
.crx-drawer-scrim{position:fixed;inset:0;background:rgba(15,15,16,.35);z-index:900}
.crx-drawer{position:fixed;top:0;right:0;bottom:0;width:520px;max-width:92vw;background:#fff;
  box-shadow:-8px 0 40px rgba(0,0,0,.18);z-index:901;overflow-y:auto}
.crx-choice{border:1.5px solid var(--border);border-radius:var(--radius);padding:22px;cursor:pointer;background:#fff}
.crx-choice.selected{border-color:var(--red);background:var(--red-light)}
.crx-step-dot{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;
  font-size:11.5px;font-weight:700;border:1.5px solid var(--border);color:var(--text-muted);background:#fff}
.crx-step-dot.done{background:var(--green);border-color:var(--green);color:#fff}
.crx-step-dot.current{border:2px solid var(--red);color:var(--red)}
.crx-sig{margin-top:8px;font-size:12px;background:var(--bg);border-left:2.5px solid var(--red);
  border-radius:0 6px 6px 0;padding:7px 10px}
.crx-locked-strip{background:var(--green-light);border:1px solid #c5e6d1;border-radius:9px;
  padding:12px 16px;font-size:12.5px;color:#127034;line-height:1.6}

/* ── S7 (designs 14/15/16 + the wizard) ────────────────────────────────────
   Every class below is `crx-`-prefixed for the reason S5 established: the review
   world and the ordering world share one stylesheet, and an unprefixed name here
   would restyle pages this program never audited (BP-CRX-209's exact shape). */

.crx-muted{color:var(--text-muted)}
.crx-strong{font-weight:700;color:var(--text)}
.crx-sub{font-size:12px;color:var(--text-secondary);margin-top:2px}
.crx-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px}
.crx-desc{max-width:380px;font-size:12.5px;color:var(--text-secondary);line-height:1.5}
.crx-desc-block{font-size:13.5px;line-height:1.65;color:var(--text-secondary);margin-bottom:16px}
.crx-clickable{cursor:pointer}
.crx-clickable:hover{background:var(--bg)}
.crx-link{color:var(--red-dark);font-weight:600;text-decoration:none}
.crx-link:hover{text-decoration:underline}
.crx-back{display:inline-block;margin-bottom:16px;font-size:13px;font-weight:600;
  color:var(--text-secondary);text-decoration:none}
.crx-back:hover{color:var(--red-dark)}
.crx-empty{padding:28px 16px;text-align:center;color:var(--text-muted);font-size:13.5px}
.crx-footnote{margin-top:14px;font-size:12.5px;color:var(--text-muted)}
.crx-note-line{font-size:12.5px;color:var(--text-secondary);margin-top:10px;line-height:1.6}
.crx-h2{font-size:19px;font-weight:800;color:var(--black)}

.crx-kv{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}
.crx-kv>div{display:flex;flex-direction:column;gap:3px}
.crx-kv-k{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;
  color:var(--text-muted)}
.crx-kv-v{font-size:14px;font-weight:600;color:var(--text)}

/* ── QA1-UX-013 · THE REP SURFACES SCROLLED SIDEWAYS AT PHONE WIDTH ──────────────────────────
   Measured: document.scrollWidth 520 against innerWidth 390 on /rep/physician.html and the rep
   dashboard — 130px of sideways scroll — with `.crx-stat-row` and its `.stat-card` children's
   right edges at 520px. At 1024 the same surfaces were clean, so this is phone-width only.

   `auto-fit`/`minmax` was already correct and is not the fault. Two defaults are:
     · the grid's ANCESTOR (`.main`, a flex item) would not shrink below its content — the same
       `min-width: auto` root cause as QA1-UX-021, fixed once in portal.css;
     · a grid ITEM also defaults to `min-width: auto`, so a card whose value is a long figure
       ("$12,345.67" at 22px/800) widens its own track past the 160px minimum and drags the row.

   `min-width: 0` on the item lets the track hold, and below 420px a single column removes the
   question entirely — a rep reading their roster on a phone between meetings is the single most
   likely session on this platform.
   ───────────────────────────────────────────────────────────────────────────────────────────── */
.crx-stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
  gap:12px;margin-bottom:16px;min-width:0}
.crx-stat-row>*{min-width:0}
.crx-stat-row .stat-value{overflow-wrap:anywhere}
@media (max-width:420px){
  .crx-stat-row{grid-template-columns:1fr}
}
/* portal.css's `.stat-card` is `display:flex` with a ROW direction (it was built for an
   icon-beside-value tile). The ordering world's cards stack a label over a value, so the
   direction is overridden HERE rather than by editing the shared rule — the review world's
   tiles still want the row. Without this the cards render "TOTAL ORDERS 0" side by side and
   the longer labels wrap into a column of single words. */
.crx-stat-row .stat-card{padding:14px 16px;display:flex;flex-direction:column;align-items:stretch}
.crx-stat-row .stat-label{font-size:11.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.03em;color:var(--text-muted)}
.crx-stat-row .stat-value{font-size:22px;font-weight:800;color:var(--black);margin-top:4px}
.crx-stat-row .stat-note{font-size:11.5px;color:var(--text-muted);margin-top:3px}

/* Split bars (15). Two segments only — a third would need a ruling about what it means. */
.crx-split{display:flex;height:12px;border-radius:6px;overflow:hidden;background:var(--bg);
  border:1px solid var(--border)}
.crx-split-a{background:var(--red)}
.crx-split-b{background:#8c9099}
.crx-split-legend{display:flex;gap:20px;flex-wrap:wrap;margin-top:10px;font-size:12.5px;
  color:var(--text-secondary)}
.crx-split-legend i{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px}
.crx-dot-a{background:var(--red)}
.crx-dot-b{background:#8c9099}

/* Access states (16) + the wizard (11). */
.crx-gate{max-width:660px;margin:0 auto}
.crx-gate-card{background:#fff;border:1px solid var(--border);border-radius:14px;
  padding:30px 28px;box-shadow:0 2px 12px rgba(0,0,0,.04)}
.crx-gate-title{font-size:20px;font-weight:800;color:var(--black);margin-bottom:6px}
.crx-gate-sub{font-size:13.5px;color:var(--text-secondary);line-height:1.6;margin-bottom:22px}
.crx-check-list{list-style:none;margin:0 0 22px;padding:0;display:flex;flex-direction:column;gap:10px}
.crx-check-list li{display:flex;gap:10px;align-items:flex-start;font-size:13.5px;
  color:var(--text-secondary);line-height:1.55}
.crx-check{color:var(--green);font-weight:800}
.crx-pending{color:var(--text-muted);font-weight:800}
.crx-attest{display:flex;gap:10px;align-items:flex-start;padding:13px 15px;background:var(--bg);
  border:1px solid var(--border);border-radius:9px;margin-bottom:11px;font-size:13px;
  line-height:1.55;color:var(--text-secondary);cursor:pointer}
.crx-attest input{margin-top:3px;flex-shrink:0}
.crx-attest.checked{background:var(--green-light);border-color:#c5e6d1;color:#1a5c33}

.crx-rail{display:flex;align-items:center;gap:0;margin-bottom:24px;flex-wrap:wrap}
.crx-rail-step{display:flex;align-items:center;gap:8px}
.crx-rail-label{font-size:12.5px;font-weight:600;color:var(--text-muted);white-space:nowrap}
.crx-rail-step.current .crx-rail-label{color:var(--red);font-weight:700}
.crx-rail-step.done .crx-rail-label{color:var(--text)}
.crx-rail-line{flex:1;min-width:16px;height:1.5px;background:var(--border);margin:0 10px}
/* C2 — the wizard had a max-width and NO auto margin, so inside the full-width
   `body.crx-taskpage .main { width:100% }` it sat flush LEFT: "squeezed into a narrow left
   column — the centered layout lost". A max-width without an auto margin is a constraint with
   no instruction about where the leftover space goes. */
.crx-wizard{max-width:780px;margin:0 auto}
.crx-wizard-foot{display:flex;justify-content:space-between;align-items:center;gap:12px;
  margin-top:22px;padding-top:18px;border-top:1px solid var(--border)}
.crx-hint{font-size:12.5px;color:var(--text-muted)}
.crx-radio-row{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:14px}
.crx-radio{flex:1;min-width:220px;border:1.5px solid var(--border);border-radius:10px;
  padding:14px 16px;cursor:pointer;background:#fff}
.crx-radio.selected{border-color:var(--red);box-shadow:0 0 0 3px rgba(224,20,44,.09)}
.crx-radio-title{font-size:14px;font-weight:700;color:var(--text)}
.crx-radio-note{font-size:12.5px;color:var(--text-secondary);margin-top:4px;line-height:1.5}
.crx-qty{width:74px;text-align:center}
.crx-drop{border:1.5px dashed var(--border);border-radius:10px;padding:26px;text-align:center;
  font-size:13.5px;color:var(--text-secondary);background:var(--bg)}
.crx-drop.has-file{border-style:solid;border-color:var(--green);background:var(--green-light);
  color:#1a5c33}


/* ── THE UNCONFIGURED-TENANT BRAND COLOUR LIVES HERE, NOT IN JS — BP-CRX-238 ──────────────────
   `dashboard.js`/`prescriptions.js` used to default `tenant.brandColor` to a hex literal, and that
   literal was `#7B3F9D` — Serena's primary. With `portal_tenants.brand_color` NULL (which it is
   for centre-lab) another company's brand colour painted the platform card and every platform dot.

   The JS no longer carries ANY colour: when a tenant has no brand colour it emits no inline style,
   and these rules supply the default from the design token. Replacing one hardcoded hex with
   another would have been the same D12a violation in our own colours — a brand value baked into
   code, unchangeable without a deploy (Vulcan tier-2 HIGH on the first attempt, and it was right).
   A tenant that DOES set `brand_color` still wins: its inline style outranks these. */
.platform-card{border-top-color:var(--red)}
.plat-badge{background:color-mix(in srgb, var(--red) 12%, #fff);color:var(--red)}
.bdot{background:var(--red)}

/* ── THE TOPBAR REFRESH BUTTON HAD NOTHING IN IT — BP-CRX-237 ─────────────────────────────────
   `.topbar-btn#refresh-btn` is a 38px circle. On ten pages it ships EMPTY
   (`<button class="topbar-btn" id="refresh-btn" title="Refresh"></button>`), so it rendered as a
   bare white circle top-right; on dashboard.html it ships the word "Refresh", which overflows a
   38px circle and collided with the avatar beside it. Both are the same hole: the design assumes
   an icon font, and Font Awesome is not loaded on any portal page (only styleguide.html links it,
   and S8's inherited-surface gate now 404s that).

   Drawn in CSS rather than by adding a CDN icon font: the design system ships no image assets and
   its logo is already CSS-only, so a whole webfont for one glyph is the wrong trade — and adding
   an external stylesheet to a product mid-demo is a dependency decision, not a visual fix. The
   remaining inline `fa-*` icons in the review world are REPORTED, not silently papered over.

   `font-size:0` on the button kills the stray "Refresh" text without touching the markup, and the
   ::before restores a real glyph at a real size. `title`/`aria-label` still carry the accessible
   name, so nothing about the button's semantics changes. */
.topbar-btn#refresh-btn{font-size:0;line-height:0}
.topbar-btn#refresh-btn::before{content:"\21BB";font-size:17px;line-height:1;display:block}

/* ═══════════════════════════════════════════════════════════════════════════
   §B — LOGIN SCREEN (scoped; see header note)
   ═══════════════════════════════════════════════════════════════════════════ */

/* logo-ring tokens used by the login lockup only */
:root { --ring-teal:#22c1c3; --ring-amber:#f2b807; }

/* ⚠️ THE RESET IS (0,1,1) AND OUTRANKS EVERY PLAIN `.class` RULE ON THIS PAGE — BP-CRX-236.
   `body.login-page *` scores one class + one type, so it beats a bare `.login-card` (0,1,0)
   REGARDLESS of source order. Every login-only rule below is therefore scoped under
   `body.login-page` too (0,2,1) so it can win. Do not "simplify" any of them back to a bare
   class: the rule will still be present, still be correct, and silently do nothing. That is
   exactly how this page shipped with zero padding and zero margins while every value read right.

   ⚠️ AND THE RESET DOES NOT MATCH `body` ITSELF — `body.login-page *` selects DESCENDANTS. So the
   UA's `body{margin:8px}` survived it, and `body` stayed `content-box`, which made
   `min-height:100vh` + `padding:24px` compute to 100vh+48px and overflow. Between them the card
   sat 8px right of centre and pushed below the fold. `margin:0` and `box-sizing` are set here,
   on the element the reset cannot reach. */
body.login-page *{box-sizing:border-box;margin:0;padding:0}
body.login-page{box-sizing:border-box;margin:0;
  min-height:100vh;display:flex;align-items:center;justify-content:center;
  padding:24px;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased}
body.login-page a{color:var(--text-secondary);text-decoration:none}
body.login-page a:hover{color:var(--red-dark);text-decoration:underline}

/* Login lockup ring is larger than the sidebar's 34px (§A) */
body.login-page .logo-ring{width:38px;height:38px}
body.login-page .logo-ring::after{inset:7px}
body.login-page .logo-text .l1{font-size:19px}
body.login-page .logo-text .l2{font-size:10.5px}

body.login-page .login-card{width:100%;max-width:420px;background:#fff;border:1px solid var(--border);
  border-radius:14px;box-shadow:0 4px 24px rgba(0,0,0,.06);padding:36px 34px}
body.login-page .login-logo{display:flex;align-items:center;gap:10px;justify-content:center;margin-bottom:28px}
body.login-page .login-title{font-size:20px;font-weight:800;text-align:center;margin-bottom:4px;color:var(--black)}
body.login-page .login-sub{font-size:13px;color:var(--text-secondary);text-align:center;margin-bottom:26px}

body.login-page .roletabs{display:flex;gap:4px;background:var(--bg);padding:4px;border-radius:9px;margin-bottom:24px}
body.login-page .roletab{flex:1;padding:9px;border-radius:6px;font-size:13.5px;font-weight:700;
  color:var(--text-secondary);cursor:pointer;border:none;background:transparent;
  font-family:inherit;text-align:center}
body.login-page .roletab.active{background:#fff;color:var(--red-dark);box-shadow:0 1px 3px rgba(0,0,0,.08)}

body.login-page .role-notice{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap;background:var(--bg);
  border:1px solid var(--border);border-radius:8px;padding:11px 14px;margin-bottom:20px;
  font-size:12.5px;line-height:1.5;color:var(--text-secondary)}
body.login-page .role-notice b{color:var(--text);font-weight:700}
/* Keep the hidden branch scoped too, so it still outranks the display:flex above it. */
body.login-page .role-notice[hidden]{display:none}

body.login-page .form-group{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}
body.login-page .form-label{font-size:13px;font-weight:700;color:var(--text)}
body.login-page .form-input{font-family:inherit;font-size:14px;padding:11px 13px;
  border:1px solid var(--border);border-radius:8px;width:100%;background:#fff;color:var(--text);
  transition:border-color .15s,box-shadow .15s}
body.login-page .form-input:focus{outline:none;border-color:var(--red);
  box-shadow:0 0 0 3px rgba(224,20,44,.12)}
body.login-page .form-input:disabled{background:var(--bg);color:var(--text-muted);cursor:not-allowed}

body.login-page .btn{font-family:inherit;font-size:14px;font-weight:700;padding:12px 18px;
  border-radius:8px;cursor:pointer;border:none;width:100%}
body.login-page .btn-primary{background:var(--red);color:#fff;margin-top:8px}
body.login-page .btn-primary:hover:not(:disabled){background:var(--red-dark)}
body.login-page .btn-primary:disabled{background:var(--border);color:var(--text-muted);cursor:not-allowed}

/* auth.js drives this: it toggles .is-visible, or sets style.display directly */
body.login-page .alert{border-radius:8px;padding:11px 14px;margin-bottom:16px;font-size:13px;display:none}
body.login-page .alert.is-visible{display:block}
body.login-page .alert-error,body.login-page .alert:not(.alert-success){
  background:var(--red-light);border:1px solid #f5c9cf;color:var(--red-dark)}
body.login-page .alert-success{background:var(--green-light);border:1px solid #c5e6d1;color:#127034}
/* auth.js injects "Resend verification email" as an <a> INSIDE the alert
   (auth.js:72). Without this it inherits the muted page link colour and sits at
   poor contrast on the red error field. */
body.login-page .alert a{color:inherit;font-weight:700;text-decoration:underline}

body.login-page .login-footer{margin-top:18px;text-align:center;font-size:12.5px;color:var(--text-muted);line-height:1.6}
body.login-page .login-footer a{color:var(--text-secondary);font-weight:600}

/* Same specificity rule applies inside the media query — a bare `.login-card` here loses to the
   (0,1,1) reset just as it does above, and a media query adds no specificity of its own. */
@media (max-width:480px){
  body.login-page{padding:14px}
  body.login-page .login-card{padding:26px 20px}
  body.login-page .logo-ring{width:32px;height:32px}
  body.login-page .login-title{font-size:18px}
  body.login-page .roletab{font-size:12.5px;padding:8px 4px}
}

/* ─────────────────────────────────────────────────────────────────────────────
   ORDERING WORLD (S5) — the four net-new pages' own primitives.
   Kept here rather than in portal.css: portal.css is the review world's base
   layer and is shared with surfaces this program does not own. Everything below
   is prefixed .crx-* so it cannot collide with a review-world class, and it is
   loaded after portal.css so it never needs !important to win.
   ───────────────────────────────────────────────────────────────────────────── */
.crx-toolbar{display:flex;align-items:center;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin-bottom:18px}
.crx-toolbar .crx-search{flex:1;min-width:220px;max-width:420px}
.crx-toolbar .form-input{width:100%}

/* Filter chips. Share the pill geometry of .crx-chip but are buttons, so they
   carry their own focus + active states rather than inheriting a span's none. */
.crx-filter{display:inline-block;padding:6px 13px;border-radius:999px;background:#fff;
  border:1px solid var(--border);font-family:inherit;font-size:12.5px;font-weight:600;
  color:var(--text-secondary);cursor:pointer;transition:background .12s,color .12s,border-color .12s}
.crx-filter:hover{border-color:var(--black);color:var(--text)}
.crx-filter.active{background:var(--red);border-color:var(--red);color:#fff}
.crx-filter:focus-visible{outline:2px solid var(--red);outline-offset:2px}

/* Row actions on the Orders list — View / Continue / Delete (Elia's ruling, 2026-09-08;
   REBUILT 2026-09-08 after Elia: "they are ugly").

   WHAT WAS WRONG. The behaviour was already right and is UNCHANGED — View always, Continue and
   Delete only on an incomplete order. What was wrong was that all three rendered as `.btn-link`,
   i.e. three bare text links of the same weight and size, one of them red. Nothing said which
   action was the likely one, nothing said which was destructive except a colour, and the row's
   `<a>` sat on the text baseline while the `<button>` sat on its own box — the misalignment
   visible in Elia's screenshot.

   THE TREATMENT, in the estate's existing .btn family rather than a new one:
     View      .btn .btn-outline .crx-btn-sm   — always present, recessive
     Continue  .btn .btn-primary .crx-btn-sm   — the row's ONE primary action
     Delete    .btn .crx-btn-danger .crx-btn-sm — WHITE ground, red ink, red border

   SOLID RED IS RESERVED FOR THE CONFIRM. Delete is deliberately NOT filled: within the delete
   flow the only solid-red button is the confirm dialog's confirm, which `confirmModal({danger:true})`
   already emits. So the row offers a destructive action in outline and the commitment is the only
   thing that looks committing. (Continue is solid because it is the brand primary, as every other
   primary button in this portal is; it is not the danger colour doing double duty.)

   .crx-btn-sm, NOT .btn-sm. `.btn-sm` is declared only inside pharmacy-dashboard.html's own
   <style> block, so a global rule of that name would silently restyle that page — and
   `public/js/settings.js:118` already ships a `.btn.btn-sm` that resolves to nothing outside it.
   The crx- prefix rule exists to stop exactly that collision; that pre-existing one is registered,
   not widened here. */
/* ⚠️ THE FLEX GOES ON AN INNER DIV, NOT ON THE <td>. This class was applied directly to the cell,
   and `display:flex` takes an element OUT of table layout — the browser stops sizing it as a
   column at all. With three text links that was invisible; with three real buttons the actions
   ran past the right edge of the card and Delete was CLIPPED OFF THE PAGE. Caught by looking at
   the screenshot: every DOM assertion passed (the buttons existed, were styled, were aligned)
   because none of them asked whether the buttons were still ON the page. That is BP-CRX-474's
   lesson arriving from the other direction, and it is why the screenshot is part of the proof.
   `width:1%` + `white-space:nowrap` is the shrink-to-fit idiom for an auto-layout table: the cell
   takes exactly what its content needs and the wide columns give up the rest. */
td.crx-actions-cell{width:1%;white-space:nowrap;padding-left:10px;padding-right:14px;text-align:right}
/* Measured on the deployed build at 1440: with three buttons the row's right edge reached 1476
   against a 1440 viewport — the buttons were inside the TABLE but the table was 36px wider than
   the screen, so the page scrolled sideways to reach Delete. Two changes, because one was not
   enough and neither alone is a hack:
     · the group is tightened (cell padding, gap, button padding) to give back ~30px; and
     · the card scrolls its own overflow, so however dense this table gets, the PAGE never
       scrolls horizontally — the wide-table rule, applied to the container that owns the width.
   Scoped to #orders-region so no other card on the estate gains a scrollbar. */
#orders-region .card{overflow-x:auto}
/* …and the table has to actually FIT, or "the card scrolls" just relocates the problem: measured
   after the tightening above, Delete still sat 54px past the card's visible edge, so a reader had
   to scroll the card sideways to reach it. The three buttons replaced three text links and the
   actions column grew by roughly 90px; the space comes back from the CELL PADDING, which is 24px
   a side on eight columns — 16px a side returns 128px, comfortably more than is needed, and a
   data-dense list is the right place to spend it. Scoped to this region: no other table changes. */
#orders-region table th,#orders-region table td{padding-left:16px;padding-right:16px}
.crx-row-actions{display:flex;align-items:center;justify-content:flex-end;gap:6px;white-space:nowrap}
/* The cell is right-aligned, so the buttons hug the row's edge and every row's actions line up
   vertically with each other rather than drifting with the content width to their left. */
.crx-row-actions .btn{text-decoration:none}
.crx-btn-sm{font-size:12.5px;font-weight:600;padding:6px 10px;line-height:1.2;border-radius:6px}
/* .btn.crx-btn-sm (0,2,0) outranks .btn-primary (0,1,0), so the primary's own 15px/13px-20px
   does not win here. Stated because a size class that loses to the thing it is sizing is the
   quiet failure this kind of rule usually has. */
.btn.crx-btn-sm{font-size:12.5px;padding:6px 10px}
/* ── THE 1px THE HARNESS CAUGHT ────────────────────────────────────────────────────────────
   `.btn-outline` and `.crx-btn-danger` each carry a 1px border; `.btn-primary` inherits
   `border:none` from `.btn`. So Continue's border box was 2px shorter than its neighbours' and
   `align-items:center` centred it 1px LOWER — measured on the deployed build at 1440:
   tops [338, 339, 338]. That is precisely the misalignment Elia was looking at, one element over
   from where I first assumed it. A transparent border equalises the box without drawing anything.
   Scoped to the row group so no other .btn-primary on the estate changes shape. */
.crx-row-actions .btn-primary{border:1px solid transparent}
.crx-btn-danger{background:var(--bg-white,#fff);color:var(--red);border:1px solid var(--red)}
.crx-btn-danger:hover:not(:disabled){background:var(--red-light);border-color:var(--red-dark);color:var(--red-dark)}
.crx-btn-danger:focus-visible{outline:2px solid var(--red);outline-offset:2px}
.crx-btn-danger:disabled{opacity:.55;cursor:not-allowed}
/* Kept: `public/js/settings.js` and any other surface still carrying the old link treatment
   continue to render as before. Retiring it is a sweep of its own, not a side effect of this one. */
.crx-danger-link{color:var(--red)}
.crx-danger-link:disabled{opacity:.55;cursor:default;text-decoration:none}

.crx-pager{display:flex;align-items:center;justify-content:center;gap:14px;margin:18px 0 4px}
.crx-pager-label{font-size:12.5px;color:var(--text-muted);font-weight:600}
.crx-pager .btn[disabled]{opacity:.45;cursor:not-allowed}

/* The detail-page header strip (order + patient). Reuses .patient-header-*
   internals from portal.css so the two detail worlds look like one product. */
.crx-order-head{background:#fff;border:1px solid var(--border);border-radius:var(--radius);
  padding:20px 22px;margin-bottom:20px}
.crx-order-head .patient-header-row{display:flex;align-items:flex-start;
  justify-content:space-between;gap:18px;flex-wrap:wrap}
.crx-order-head .patient-name-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}

/* Stated absences ("clinic deliveries are not tracked") and the spend-not-revenue
   line. Deliberately NOT an override of portal.css's `.doc-note`: that class is the
   review world's Documents tab and restyling it would change a surface this work has
   no business changing. A new class costs one line and touches nothing. */
.crx-note{font-size:12px;color:var(--text-muted);line-height:1.55;margin-top:10px}

@media (max-width:720px){
  .crx-toolbar{flex-direction:column;align-items:stretch}
  .crx-toolbar .crx-search{max-width:none}
}

/* Wizard task-page: no sidebar (design 11 — "Cancel and return to Orders" is the only exit). */
body.crx-taskpage{display:block}
body.crx-taskpage .main{margin-left:0;width:100%}
.crx-form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:0 14px}

/* ── `.card-header` — USED BY FOUR FILES, DEFINED BY NONE ──────────────────────────────────
   `dashboard.js` (review world) plus the three S7 ordering scripts all emit
   `<div class="card-header"><h3>…</h3></div>`, and no stylesheet in this repo has ever defined
   it. The heading therefore rendered as a bare h3 with browser-default margins, sitting on top
   of the card's own padding — visible on Statistics as titles overlapping the panel edge.
   Third instance of BP-CRX-210's class (a class used in markup and declared nowhere), so it is
   recorded rather than just patched: BP-CRX-228.

   Defined here rather than in portal.css because theme.css loads after it and this is the
   Centre Rx layer; the rule is additive — nothing previously matched, so nothing can regress. */
.card-header{padding:16px 18px 0;display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.card-header h3{font-size:14.5px;font-weight:700;color:var(--text);margin:0}
.card-header + table{margin-top:12px}
.card-body{padding:14px 18px 18px}

/* ═══════════════════════════════════════════════════════════════════════════
   shared/rep-additions.css — APPEND to public/css/theme.css.

   Not a new stylesheet: every rep page loads portal.css + theme.css exactly
   like the ordering world does, and these rules join the Centre Rx layer at the
   end of that file. Nothing here is a new colour, radius or type size — every
   value resolves to a token already declared at the top of theme.css.

   Prefix rule (S5, BP-CRX-209): every class is `rep-`- or `crx-`-prefixed so it
   cannot restyle a review-world surface this work never audited.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Status badges — the FOUR-value vocabulary, and only these four ──────────
   SUSPENDED is its own state, never a flavour of INACTIVE: "stopped ordering"
   and "was suspended" are different facts and a rep who cannot tell them apart
   chases the wrong physician (contract §0.3). */
.rep-badge{display:inline-flex;align-items:center;gap:5px;padding:4px 11px;border-radius:20px;
  font-size:11.5px;font-weight:700;white-space:nowrap}
.rep-badge::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}
.rep-badge.is-active{background:var(--green-light);color:var(--green)}
.rep-badge.is-inactive{background:var(--bg);color:var(--text-secondary)}
.rep-badge.is-pending{background:var(--amber-light);color:var(--amber)}
.rep-badge.is-suspended{background:var(--red-light);color:var(--red-dark)}
/* Aggregates unavailable: the server derived status from data it just said it
   does not have. The badge renders an em dash rather than asserting Inactive. */
.rep-badge.is-unknown{background:var(--bg);color:var(--text-muted)}

/* ── The null-not-zero cell ──────────────────────────────────────────────────
   Used for BOTH absent keys (a PENDING_APPROVAL row has no orderCount at all)
   and null figures (aggregatesAvailable:false). Muted, em dash, never "0" and
   never "$0.00" — rendering null as a zero re-creates the exact failure
   `aggregatesAvailable` exists to prevent, and it is invisible (law L18). */
.rep-nil{color:var(--text-muted);font-weight:700}

/* Sub-line under a pending physician's name in the roster. */
.rep-row-note{font-size:11.5px;font-weight:600;color:var(--amber);margin-top:2px}
/* Optional row tint for pending rows. The badge + sub-line carry the meaning on
   their own; delete this one rule if the tint reads as alarm. */
.rep-row-pending{background:#fffdf7}

/* ── Referral link block ─────────────────────────────────────────────────── */
.rep-linkcard{background:var(--bg-white);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:18px 22px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.rep-linkcard .rep-linkinput{flex:1;min-width:260px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:13px;padding:10px 13px;border:1px solid var(--border);border-radius:8px;
  background:var(--bg);color:var(--text-secondary)}
/* The refusal state. NOT an error card: nothing is broken from the rep's side
   and there is nothing for them to do (F-3 / BP-CRX-240). */
.rep-linkcard.is-unavailable{border-style:dashed;display:block}
.rep-linkcard .rep-link-title{font-size:13.5px;font-weight:700;color:var(--text);margin-bottom:4px}
.rep-linkcard .rep-link-note{font-size:12.5px;color:var(--text-secondary);line-height:1.55}

/* ── Window preset chips (F-1: presets, not a custom start/end range) ─────── */
.rep-winrow{display:flex;gap:6px;flex-wrap:wrap}
.rep-win{font-family:inherit;font-size:12px;font-weight:600;padding:6px 12px;border-radius:20px;
  border:1px solid var(--border);background:var(--bg-white);color:var(--text-secondary);cursor:pointer}
.rep-win:hover{border-color:var(--black);color:var(--text)}
.rep-win.active{background:var(--red);border-color:var(--red);color:#fff}
.rep-win:focus-visible{outline:2px solid var(--red);outline-offset:2px}
.rep-showing{font-size:13px;color:var(--text-secondary);font-weight:600}

/* ── Privacy banner. The response-key test is what makes this sentence true;
      keep it (contract §2.2). ───────────────────────────────────────────── */
.rep-privacy{display:flex;align-items:center;gap:10px;background:var(--bg);border:1px solid var(--border);
  border-radius:8px;padding:12px 16px;font-size:13px;color:var(--text-secondary);line-height:1.55}
.rep-privacy b{color:var(--text)}

/* Platform-unreachable notice. Amber = "we could not compute this", not red:
   nothing is broken for the rep and the roster below is still true. */
.rep-unavailable-note{background:var(--amber-light);border:1px solid #f0dcb4;border-radius:8px;
  padding:12px 16px;font-size:13px;color:#8a5e0c;line-height:1.55}

/* ── Contact card (physician detail) ─────────────────────────────────────── */
.rep-contact{display:flex;flex-direction:column;gap:14px}
.rep-contact-k{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;
  color:var(--text-muted);margin-bottom:3px}
.rep-contact-v{font-size:14px;font-weight:600;color:var(--text)}
/* clinicAddress is NULL for most physicians and is not in the payload at all —
   this is the designed empty state, never a blank line (contract §5). */
.rep-contact-empty{font-size:13px;color:var(--text-muted);line-height:1.5;font-style:italic}

/* ── Mobile: every table has a row-cards equivalent (contract §6) ─────────── */
.rep-bottomnav{display:none;position:sticky;top:0;background:var(--bg-white);
  border-bottom:1px solid var(--border);box-shadow:0 2px 6px rgba(0,0,0,.04);z-index:40}
.rep-bottomnav-scroll{display:flex;overflow-x:auto;-webkit-overflow-scrolling:touch}
.rep-bottomnav-item{flex:1 0 auto;min-width:64px;text-align:center;padding:9px 6px 8px;font-size:10.5px;
  font-weight:700;color:var(--text-secondary);text-decoration:none;border-top:2px solid transparent}
.rep-bottomnav-item.active{color:var(--red-dark);border-top-color:var(--red)}

.rep-rowcards{display:none}
.rep-rowcard{background:var(--bg-white);border:1px solid var(--border);border-radius:10px;
  padding:14px 16px;margin-bottom:10px;box-shadow:var(--shadow)}
.rep-rowcard-head{display:flex;justify-content:space-between;align-items:center;gap:10px;
  margin-bottom:9px;padding-bottom:9px;border-bottom:1px solid var(--border)}
.rep-rowcard-title{font-weight:700;font-size:14.5px}
.rep-rowcard-line{display:flex;justify-content:space-between;gap:10px;padding:4px 0;font-size:13px}
.rep-rowcard-label{color:var(--text-muted);font-weight:600;flex-shrink:0}

@media (max-width:900px){
  .sidebar{display:none}
  .rep-bottomnav{display:block}
  .rep-table-card table{display:none}
  .rep-table-card{background:transparent;border:none;box-shadow:none;overflow:visible}
  .rep-rowcards{display:block}
  .rep-linkcard{flex-direction:column;align-items:stretch}
  .rep-linkcard .rep-linkinput{min-width:0}
}

/* ── QA1-UX-025 · A FIELD ERROR RENDERS ON THE FIELD ─────────────────────────────────────────
   The wizard's create-customer refusals were page-level banners with no field association and
   focus left on the Next button, so on a twelve-field form the physician had to find the field
   themselves. `refuseCreate` in order-new.js attaches the sentence, marks the input and moves
   focus; these two rules are what make that visible. Deliberately quiet — a red hairline and a
   red sentence, no icon, no shake, in the same register as `.crx-hint` directly above it. */
.crx-field-error {
  margin: 6px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: #b42318;
}

/* C92: the pharmacy's missing requirements, listed inside the existing banner. Not a new component
   — it is a plain list with the banner's own colour, so a refusal that names four things reads as
   four things instead of one run-on sentence. */
.crx-refusal-list {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.5;
}
.crx-refusal-list li { margin: 2px 0; }
.form-input.has-error,
select.form-input.has-error {
  border-color: #b42318;
  box-shadow: 0 0 0 3px rgba(180, 35, 24, .12);
}

/* ── THE "ALL PATIENTS" BACK-LINK — ONE CLASS, TWO INCOMPATIBLE USES (2026-09-08) ─────────────
   Elia: broken on every page that carries it; his screenshot shows it running into the patient
   name in the header.

   IT WAS NOT UNSTYLED. `portal.css:1044` styles `.back-link` as a fixed **32x32 icon button**
   (`width:32px;height:32px`, centred, no padding). That is right for exactly one of its two uses
   and impossible for the other:

     · patient.html          — icon use. At genesis the anchor held
                               `<i class="fas fa-arrow-left">`, and this stylesheet's own
                               de-branding rule (`i[class^="fa"]{display:none}`, line ~97) kills
                               every Font Awesome glyph. The icon went invisible, a later pass
                               removed the dead <i>, and what shipped is an EMPTY 32x32 anchor:
                               clickable, invisible, and taking up space beside "Patient Detail".
     · order.html            — TEXT use. "← All orders" / "← All patients" cannot fit a 32px box,
       ordering-patient.html   so the text overflows its own control and collides with the title
                               next to it. That is what Elia photographed.

   So the fix is to let the control size to what is IN it: a text instance grows, an icon-only
   instance still resolves to the 32px square via min-width. One rule, all three carriers.
   Loaded after portal.css, so this wins without touching the base file. */
.back-link{width:auto;min-width:32px;padding:0 10px;gap:6px;white-space:nowrap;
  font-size:13px;font-weight:600;color:var(--text-secondary);flex:none}
.back-link:hover{background:var(--bg);color:var(--red-dark);text-decoration:none}
.back-link:focus-visible{outline:2px solid var(--red);outline-offset:2px}
/* An icon-only instance carries no text node, so it keeps the original square. */
.back-link:empty,.back-link.icon-only{width:32px;padding:0}
.back-link svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;flex:none}

/* "Ordering for {name} — change" — shown when the wizard was handed a customer by the patient
   page's New order button, so a wizard that opens on step 2 says WHY it did. */
.crx-preresolved{display:flex;align-items:center;gap:8px;margin:0 0 14px;padding:10px 14px;
  border:1px solid var(--border);border-left:3px solid var(--red);border-radius:8px;
  background:var(--bg-white);font-size:13.5px;color:var(--text)}
.crx-preresolved .btn-link{font-size:13px;font-weight:600;color:var(--red-dark)}
