/*
 * portal.css — the RepOS partner-portal shell.
 *
 * Tokens, spacing and component classes are carried over from Viva_Portal_Mockup.html
 * unchanged, so the built portal and the mockup stay the same object. Two things are added
 * at the bottom, marked and explained: a real mobile navigation, and empty-state styling.
 *
 * Brand-variable accents (--acc, --acc-bg) are overwritten at boot by brand.js. Everything
 * else is shared, because the difference between Riser and Viva is a name and an accent,
 * not a different product.
 */

/* ── tokens ─────────────────────────────────────────────────────────────────────────────── */
:root{
  --bg:#0a0f18;--panel:#0f1622;--card:#141d2c;--card2:#1a2537;--line:#22304a;--line2:#2c3c5a;
  --t1:#f2f5fa;--t2:#a9b6c9;--t3:#6f7f97;
  --acc:#3a8dde;--acc-bg:#12304f;--teal:#1fb58f;--teal-bg:#0f3a30;--amber:#e6a23c;--amber-bg:#3d2c0f;--red:#e5534b;--red-bg:#3d1714;--purple:#8b7cf6;--purple-bg:#2a2450;
  --r:12px;--rs:8px;
}
[data-theme="light"]{
  --bg:#f4f6fa;--panel:#ffffff;--card:#ffffff;--card2:#f1f4f9;--line:#e1e6ef;--line2:#cbd3e1;
  --t1:#101827;--t2:#4b5a72;--t3:#7a879b;
  --acc:#1d6fc4;--acc-bg:#e4effb;--teal:#0f8a6a;--teal-bg:#dff5ee;--amber:#b56e0f;--amber-bg:#fbefd8;--red:#c43a33;--red-bg:#fbe3e1;--purple:#5b4bd6;--purple-bg:#e9e6fb;
}

*{box-sizing:border-box}
html,body{margin:0;background:var(--bg);color:var(--t1);font:14px/1.5 Inter,system-ui,sans-serif}
a{color:var(--acc);text-decoration:none}
h1{font:500 30px/1.15 Fraunces,Georgia,serif;margin:0 0 4px;letter-spacing:-.01em}
h2{font:500 18px/1.3 Inter,system-ui,sans-serif;margin:0}
h3{font:600 13px/1.3 Inter,system-ui,sans-serif;margin:0;color:var(--t2)}

/* ── frame ──────────────────────────────────────────────────────────────────────────────── */
.app{display:flex;min-height:100vh}
/* The drawer SCROLLS. It is a fixed-height flex column, and without overflow-y anything past
   the fold is simply unreachable - on a 390x844 phone with the keyboard open the visible
   area is roughly 390x380, which put the account block and every control below it out of
   reach with no way to get at them. overscroll-behavior stops the scroll chaining to the
   page behind the drawer once it hits the end. */
.side{width:236px;flex:none;background:var(--panel);border-right:1px solid var(--line);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
.brand{display:flex;align-items:center;gap:10px;padding:18px 16px;border-bottom:1px solid var(--line)}
.logo{width:36px;height:36px;border-radius:10px;background:var(--acc);display:grid;place-items:center;color:#fff;font:700 18px Inter,system-ui,sans-serif}
.brand b{display:block;font-size:16px;font-weight:600}.brand small{color:var(--t3);font-size:12px}
.toggle{width:32px;height:32px;border-radius:50%;border:1px solid var(--line);background:transparent;color:var(--t2);cursor:pointer;display:grid;place-items:center;font-size:15px;flex:none}
.brand .toggle{margin-left:auto}
.nav{padding:8px 10px;display:flex;flex-direction:column;gap:1px}
/* Plya density (Kevin, 2026-09-23): ~32px rows, 14px type, 18px icons, so nine sections plus
   Settings sit in about half a laptop screen. The mobile drawer keeps its larger touch rows. */
.nav button{all:unset;display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:8px;color:var(--t2);cursor:pointer;font-size:14px;line-height:18px}
/* all:unset + display:flex above outrank the browser's own [hidden] rule, so a nav button shipped hidden (Kevin's Console) showed to every partner. This puts hidden back above it. */
.nav button[hidden]{display:none}
/* The same for the practice page's admin account picker (.bar, display:flex): a practice signed in
   with its own link saw the operator's picker (run item 3). */
.bar[hidden]{display:none}
.nav button svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:none}
.nav button.on{background:var(--card2);color:var(--t1);outline:1px solid var(--line2)}
.nav button:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.nav .b{margin-left:auto;min-width:20px;height:20px;padding:0 6px;border-radius:10px;font-size:11px;font-weight:600;display:grid;place-items:center;color:#fff}
.b.teal{background:var(--teal)}.b.blue{background:var(--acc)}
.who{margin-top:auto;padding:10px 14px;border-top:1px solid var(--line);display:flex;align-items:center;gap:10px}
.who b{display:block;font-weight:600}.who small{color:var(--t3);font-size:12px}
.main{flex:1;min-width:0;padding:18px 24px 60px}
.screen{display:none}.screen.on{display:block}
/* The same rule for TABS, which was simply never written. showTab() toggles .on on the
   right pane and always has, but with no style behind the class every pane stayed
   visible: My business rendered Selling, Recruiting, Account, Help and Settings stacked
   on one page, which is how a partner came to see the platform Connections panel — and
   the Precision DX Group account behind it — underneath their own price list. */
.pane{display:none}.pane.on{display:block}

/* ── tabs ───────────────────────────────────────────────────────────────────────────────── */
/* Sub-sections are PILL TABS across the top of the section page (Plya's two-level pattern,
   Kevin 2026-09-23): the left nav holds sections, never sub-sections. */
.subtabs{display:inline-flex;gap:2px;background:var(--card);border:1px solid var(--line);padding:3px;border-radius:11px;margin:0 0 16px;flex-wrap:wrap;max-width:100%;box-sizing:border-box}
.subtabs span,.subtabs a{padding:6px 12px;border-radius:8px;color:var(--t2);cursor:pointer;font-size:13.5px;line-height:18px;display:flex;align-items:center;gap:6px;text-decoration:none}
.subtabs span.on,.subtabs a.on{background:var(--card2);color:var(--t1);box-shadow:inset 0 0 0 1px var(--line2)}
.subtabs[hidden],.subtabs a[hidden]{display:none}
/* A pill group inside a pane (Find leads, My leads) sits a size down from the section's own. */
.subpills{margin:0 0 12px}
.subpills span{font-size:13px;padding:5px 11px}
.recv-card summary{cursor:pointer;list-style:none;display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.recv-card summary::-webkit-details-marker{display:none}
.recv-card[open] summary{margin-bottom:12px}
/* data-crm-extra tabs ship hidden for default reps; same [hidden] fight as .nav button. */
.subtabs span[hidden]{display:none}
.subtabs span:focus-visible{outline:2px solid var(--acc);outline-offset:-2px}
.pill-tabs{display:inline-flex;gap:2px;background:var(--card);border:1px solid var(--line);padding:4px;border-radius:12px;margin-bottom:18px;flex-wrap:wrap}
.pill-tabs span{padding:8px 14px;border-radius:9px;color:var(--t2);cursor:pointer;font-size:14px;display:flex;align-items:center;gap:6px}
.pill-tabs span.on{background:var(--card2);color:var(--t1)}

/* ── components ── moved to components.css (Kevin, 2026-09-10: one sheet, every pane uses it).
   What stays here is layout that is not a shared piece. */
.chart{height:220px;border-radius:10px;background:var(--card2);position:relative;overflow:hidden}
.chart svg{width:100%;height:100%}
.hdr{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;margin-bottom:18px;flex-wrap:wrap}
.sub{color:var(--t2);font-size:15px}
.footer{padding:28px 0 0;color:var(--t3);font-size:12px;text-align:center}
.footer b{color:var(--t2);font-weight:500}

/* ── empty states ───────────────────────────────────────────────────────────────────────────
 * An unbuilt section must SAY it is unbuilt. A blank panel reads as "you have nothing" —
 * which is a claim about the rep's business, and a false one. The two are styled differently
 * on purpose: .empty is a real answer (no data yet), .notbuilt is an admission (no code yet).
 */
.empty{text-align:center;padding:70px 20px;color:var(--t2)}
.empty svg{width:60px;height:60px;stroke:var(--t3);fill:none;stroke-width:1.2;margin-bottom:14px}
.empty b{display:block;font-size:16px;color:var(--t1);margin-bottom:6px}
.empty p{margin:0 auto;max-width:44ch;font-size:14px}
.notbuilt{border:1px dashed var(--line2);border-radius:var(--r);padding:44px 24px;text-align:center;color:var(--t2)}
.notbuilt .tag{margin-bottom:12px}
.notbuilt b{display:block;font-size:16px;color:var(--t1);margin-bottom:6px}
.notbuilt p{margin:0 auto;max-width:52ch;font-size:14px}

/* ── mobile ─────────────────────────────────────────────────────────────────────────────────
 * The mockup parks the sidebar at left:-236px below 900px with nothing to bring it back, so
 * on a phone the portal has ten sections and no way to reach nine of them. Layout carries the
 * navigation here, so this is not a detail that can be left to "later": a shell you cannot
 * click through on a phone is not a shell.
 *
 * So: a top bar with the brand, a menu button and the theme toggle; the same .side element
 * slides in as a drawer; a scrim closes it. One nav, two presentations — no second copy of
 * the markup to drift out of sync with the first.
 */
.topbar{display:none;position:sticky;top:0;z-index:30;background:var(--panel);border-bottom:1px solid var(--line);padding:10px 14px;align-items:center;gap:12px}
.topbar .logo{width:30px;height:30px;border-radius:9px;font-size:15px}
.topbar b{font-size:15px;font-weight:600}
.topbar .sp{flex:1}
.menubtn{all:unset;width:36px;height:36px;border-radius:9px;border:1px solid var(--line);display:grid;place-items:center;color:var(--t2);cursor:pointer;flex:none}
.menubtn:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.menubtn svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round}
.scrim{display:none;position:fixed;inset:0;background:rgba(4,8,14,.6);z-index:40}
.scrim.on{display:block}

@media (max-width:900px){
  .topbar{display:flex}
  /* The shadow belongs to the OPEN drawer only (punch list #34): parked at translateX(-100%), a
     40px blur still painted a grey band down the left edge of every phone screen. */
  .side{position:fixed;top:0;left:0;height:100dvh;z-index:50;transform:translateX(-100%);transition:transform .22s ease;box-shadow:none;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding-bottom:env(safe-area-inset-bottom)}
  .side.open{transform:translateX(0)}
  .side.open{box-shadow:0 0 40px rgba(0,0,0,.45)}
  .main{padding:16px 16px 60px}
  h1{font-size:24px}
  /* The drawer is touched, not clicked: rows stay at a 44px-class target on phones. */
  .nav button{padding:11px 12px;font-size:15px}
}
@media (max-width:560px){
  /* On a phone the pill strip is one scrolling row the width of the screen. */
  .subtabs{display:flex;overflow-x:auto;flex-wrap:nowrap}
  .subtabs span,.subtabs a{white-space:nowrap;padding:9px 12px}
}
@media (prefers-reduced-motion:reduce){
  .side{transition:none}
}

/* ── sideways scroll cues (punch list #33, #43) ─────────────────────────────────────────────
 * portal.js markOverflow() sets ovf-l / ovf-r on a .tablewrap or .subtabs while content is cut
 * off on that side. The cut edge fades out, which is the cue that there is more; a table that
 * overflows also gets a one-line .ovf-hint under it. The mask is on the box, so it stays put
 * while the content scrolls under it. */
.ovf-r{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 40px),transparent)}
.ovf-l{-webkit-mask-image:linear-gradient(to right,transparent,#000 40px);mask-image:linear-gradient(to right,transparent,#000 40px)}
.ovf-l.ovf-r{-webkit-mask-image:linear-gradient(to right,transparent,#000 40px,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(to right,transparent,#000 40px,#000 calc(100% - 40px),transparent)}
.ovf-hint{font-size:12px;color:var(--t3);margin:6px 0 0;text-align:right}
.ovf-hint[hidden]{display:none}

/* ── "How did it go?" ────────────────────────────────────────────────────────────────────────
 * Centred, and it scrolls: four outcomes, three temperatures, seven follow-up chips and a
 * textarea do not fit in 380px of visible height with a keyboard open.
 */
.disp-scrim{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:70;display:flex;align-items:center;justify-content:center;padding:20px;overflow-y:auto;-webkit-overflow-scrolling:touch}
.disp{background:var(--bg);border:1px solid var(--line);border-radius:16px;padding:20px;width:min(420px,100%);max-height:calc(100vh - 40px);overflow-y:auto;overscroll-behavior:contain}
.disp h3{margin:0 0 2px;font-size:18px}
.disp-sub{margin-bottom:16px;font-size:14px}
.disp-group{margin-bottom:16px}
.disp-group>.k,.disp-group>label.k{display:block;margin-bottom:8px}
.disp-group i{font-style:normal;color:var(--t3)}
.disp-opts{display:flex;flex-direction:column;gap:8px}
.disp-opt{text-align:left;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:11px 13px;color:inherit;font:inherit;cursor:pointer;min-height:44px}
.disp-opt b{display:block;font-size:14px}
.disp-opt span{display:block;font-size:12.5px;color:var(--t3);margin-top:2px}
.disp-opt.on{border-color:var(--acc);background:var(--card2)}
.disp-opt:focus-visible,.disp-chip:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.disp-chips{display:flex;flex-wrap:wrap;gap:6px}
.disp-chip{background:var(--card);border:1px solid var(--line);border-radius:999px;padding:8px 13px;color:inherit;font:inherit;font-size:13px;cursor:pointer;min-height:36px}
.disp-chip.on{border-color:var(--acc);background:var(--card2);color:var(--t1)}
.disp textarea{width:100%;box-sizing:border-box;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:10px 12px;color:inherit;font:14px Inter,system-ui,sans-serif;font-size:16px;resize:vertical}
.disp-foot{display:flex;gap:8px;justify-content:flex-end;margin-top:4px}
.disp-foot .btn{min-height:44px}
#disp-msg{margin-bottom:10px}
#disp-msg.err,.note.err{color:var(--amber,#f0b429)}


/* ── pricing table ───────────────────────────────────────────────────────────────────────────
 * The price is an input, not a figure: this screen exists so a rep can set retail between the
 * floor and the ceiling. 16px on the field, or iOS zooms the page the moment they tap it and
 * the table jumps under them.
 */
.pr-in{width:84px;text-align:right;font-size:16px;padding:8px 10px;border-radius:8px;border:1px solid var(--line);background:var(--card);color:inherit;font-family:inherit;min-height:40px}
.pr-in:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.pr-sf{display:inline-flex;align-items:center;gap:7px;cursor:pointer;font-size:13px;color:var(--t2)}
.pr-sf input{width:18px;height:18px;accent-color:var(--acc);cursor:pointer}
.pr-comm{font-variant-numeric:tabular-nums}
.pr-bar{display:flex;align-items:center;gap:12px;margin-top:14px;flex-wrap:wrap}
.pr-bar .btn{min-height:44px}
#pr-msg.err{color:var(--amber,#f0b429)}

/* ── storefront settings (PR1b) ──────────────────────────────────────────────────────────────
 * 16px on every field on a phone so iOS does not zoom the page when a partner taps to edit
 * their slug. Density (Kevin, 2026-09-24, the same tightening as the menu): small section
 * heads, 36px fields, two columns from 560px up and a label-beside-field row on a phone, so
 * My store is about two phone screens instead of six.
 */
.sf-h{font-size:12px;font-weight:600;margin:14px 0 6px;letter-spacing:.06em;text-transform:uppercase;color:var(--t2)}
.sf-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 12px}
.sf-field{display:flex;flex-direction:column;gap:3px;margin:0 0 6px;min-width:0}
.sf-field>span:first-child{font-size:12px;color:var(--t2);line-height:16px}
.sf-field input,.sf-field select,.sf-field textarea{
  font-size:14px;padding:7px 10px;border-radius:8px;border:1px solid var(--line);
  background:var(--card);color:inherit;font-family:inherit;min-height:36px;width:100%;box-sizing:border-box
}
.sf-field textarea{min-height:56px;resize:vertical}
.sf-color{display:flex;gap:6px;align-items:center}
.sf-color input[type=color]{width:36px;height:36px;padding:3px;border:1px solid var(--line);border-radius:8px;background:var(--card);flex:0 0 36px}
.sf-hex{flex:1}
#sf-msg.err,#disc-msg.err{color:var(--amber,#f0b429)}
.sf-note{font-size:12.5px;margin:4px 0 8px}
.sf-note.err{color:var(--amber,#f0b429)}
.sf-hint{font-size:12px;margin:2px 0 6px}
.sf-locked{margin-top:12px}
.sf-claim{display:flex;align-items:center;gap:10px;margin:4px 0 6px}
.sf-claim .sf-note{flex:1;margin:0}
.sf-claim .btn{flex:none}
/* Sections fold (<details>): the head is the summary, one line, with a chevron. */
.sf-sec{border-top:1px solid var(--line)}
.sf-sec>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:8px;padding:10px 0 6px;margin:0}
.sf-sec>summary::-webkit-details-marker{display:none}
.sf-sec>summary::after{content:"";margin-left:auto;width:7px;height:7px;border-right:1.6px solid var(--t3);border-bottom:1.6px solid var(--t3);transform:rotate(45deg);transition:transform .15s}
.sf-sec[open]>summary::after{transform:rotate(-135deg)}
.sf-sec[open]{padding-bottom:6px}
.sf-sec>summary:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
/* Switches: one per line on a phone, side by side (260px each) where they fit. Flex, not a card grid. */
.sf-toggles{display:flex;flex-wrap:wrap;column-gap:18px}
.sf-toggles>.nrow{flex:1 1 260px;min-width:0}
#biz-site .nrow{padding:7px 0;gap:10px}
#biz-site .nrow .sub{font-size:12px!important}
/* Find my brand: the field and its button on one line. */
.sf-find{display:flex;align-items:flex-end;gap:8px;margin:6px 0 4px}
.sf-find .sf-field{flex:1;margin:0}
.sf-find .btn{flex:none;min-height:36px}
.sf-draft{display:inline-block;margin-right:6px;padding:1px 8px;border-radius:10px;background:var(--acc);color:#fff;font-size:11.5px;font-weight:600;vertical-align:1px}
.sf-bp-card{border:1px solid var(--acc);border-radius:10px;padding:10px 12px;margin:6px 0 8px;background:var(--card2)}
.sf-bp-h{font-size:13.5px;margin-bottom:8px}
.sf-bp-body{display:flex;gap:12px;align-items:flex-start}
.sf-bp-logo{width:56px;height:56px;flex:0 0 56px;border-radius:8px;object-fit:contain;background:#fff;border:1px solid var(--line)}
.sf-bp-none{display:grid;place-items:center;font-size:11px;color:var(--t3);background:var(--card)}
.sf-bp-txt{flex:1;min-width:0}
.sf-bp-name{font-weight:600;font-size:14px;overflow-wrap:anywhere}
.sf-bp-tag{font-size:12.5px;margin-top:2px}
.sf-bp-sw{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;font-size:12px}
.sf-sw{width:18px;height:18px;border-radius:5px;border:1px solid var(--line2);display:inline-block}
.sf-bp-act{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.sf-bp-act .btn{min-height:36px}
@media(max-width:560px){
  .sf-grid{grid-template-columns:1fr}
  /* A phone: label beside the field, one compact row each; 16px type so iOS does not zoom. */
  .sf-field{flex-direction:row;align-items:center;gap:8px;margin:0 0 5px}
  .sf-field>span:first-child{flex:0 0 34%;font-size:12px;line-height:15px}
  .sf-field input,.sf-field select,.sf-field textarea,.sf-color{flex:1;min-width:0}
  .sf-field input,.sf-field select,.sf-field textarea{font-size:16px;padding:6px 9px}
  .sf-find{flex-wrap:wrap;gap:5px}
  .sf-find .sf-field{flex:1 1 100%}
  .sf-find .btn{flex:1 1 auto;justify-content:center}
  .sf-claim{flex-direction:column;align-items:stretch;gap:6px}
}


@media(max-width:560px){
  /* The table scrolls sideways in its own container rather than making the page do it. */
  .pr-in{width:78px}
}

/* ── quick actions + nav badges ──────────────────────────────────────────────────────────────
 * The strip is navigation to places that already exist. It wraps rather than scrolling, so on
 * a phone it becomes two rows of reachable targets instead of a rail with something hidden off
 * the right edge that nobody discovers.
 */
.quick{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px}
.qa{flex:1 1 auto;min-width:150px;min-height:44px;padding:11px 14px;border-radius:10px;border:1px solid var(--line);background:var(--card);color:inherit;font:inherit;font-size:14px;cursor:pointer;text-align:left;text-decoration:none;box-sizing:border-box;display:inline-flex;align-items:center}
a.qa{color:inherit}
.qa:disabled{opacity:.55;cursor:not-allowed}
.qa:hover{border-color:var(--acc)}
.qa:focus-visible{outline:2px solid var(--acc);outline-offset:1px}

/* A badge is only ever drawn for a real, non-zero count — see paintBadges(). It never renders
 * a zero, because a zero badge tells a rep nothing needs them and that may not be true. */
.nav button{position:relative}
.badge{margin-left:auto;min-width:20px;height:20px;padding:0 6px;border-radius:10px;background:var(--acc);color:#fff;font-size:11.5px;font-weight:600;line-height:20px;text-align:center;font-variant-numeric:tabular-nums}

@media(max-width:560px){
  .qa{flex:1 1 calc(50% - 4px);min-width:0}
}

/* ── my business: kit shelf and notification switches ────────────────────────────────────────
 * A piece that is not in the kit is SHOWN and greyed rather than hidden. Hiding it leaves a rep
 * believing the shelf is complete, and the shelf is the answer to "what can I send".
 */
.kitlist{display:flex;flex-direction:column;gap:8px}
.kititem{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 13px;border:1px solid var(--line);border-radius:10px}
.kititem .btn{min-height:38px}
.kititem.off{opacity:.55}
.nrow{align-items:flex-start;gap:14px;cursor:pointer}
.nrow input{width:18px;height:18px;accent-color:var(--acc);cursor:pointer;flex:0 0 18px;margin-top:2px}
#n-msg.err,#bug-msg.err{color:var(--amber,#f0b429)}
#bug-text{width:100%;box-sizing:border-box;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:10px 12px;color:inherit;font:14px Inter,system-ui,sans-serif;font-size:16px;resize:vertical}

/* ── analytics: rail and charts ──────────────────────────────────────────────────────────────
 * The rail wraps rather than scrolling, so on a phone it becomes two rows of reachable chips
 * instead of a strip with the window nobody uses hidden off the right edge.
 */
/* .rail already exists above and does exactly this. Reused rather than redeclared — a
   second .rail rule silently wins over the first and leaves a dead one behind. */

/* The chart is drawn at a fixed aspect and scaled by its wrapper, so strokes keep their weight
 * instead of being stretched by preserveAspectRatio="none". */
.chartwrap{width:100%;overflow-x:auto}
.chart{display:block;width:100%;height:120px;min-width:280px}

/* ── directory filters ───────────────────────────────────────────────────────────────────────
 * Wraps, and each control is a real target. A filter strip that scrolls sideways hides the one
 * nobody uses until they need it.
 */
.dfilters{display:flex;flex-wrap:wrap;gap:12px;margin:0 0 14px}
.dsel{display:flex;flex-direction:column;gap:4px}
.dsel select{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:9px 11px;color:inherit;font:inherit;font-size:16px;min-height:40px}
.dsel select:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
/* 16px, not 15: iOS Safari zooms the whole page on focusing any control under 16px. */
/* An amount never breaks from its sign. At 390px "−$138.00" wrapped to a lone minus over "$138.00",
   which reads as a positive number. The table scrolls in its wrapper instead. */
.tablewrap td.r{white-space:nowrap}
/* ...except the commission column's second line ("$17.94 on a WILLOW10 order"): as one nowrap line
   it made the column 200px and the pricing table scroll sideways below ~1142px. It wraps between
   words; the amount itself (.amt) still never breaks from its sign. */
.tablewrap td.r .pr-code{white-space:normal;min-width:64px}
/* The pricing table is seven columns of short numbers: tighter cells and headers that may wrap
   ("Commission / box" over two lines) keep it inside its card down to the sidebar breakpoint. */
#biz-pricing th,#biz-pricing td{padding:10px 5px}
@media (max-width:1099.98px){ #biz-pricing .pr-in{width:72px} }
#biz-pricing th{white-space:normal;line-height:1.25}
.tablewrap td.r .pr-code .amt{white-space:nowrap}
#comm-earn-body td:first-child{white-space:nowrap}

/* ── customers toolbar ───────────────────────────────────────────────────────────────────────
 * Search at 16px or iOS zooms the page the moment it is tapped. Wraps rather than scrolling. */
.ctools{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 14px}
.csearch{flex:1 1 220px;min-width:0;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:10px 12px;color:inherit;font:inherit;font-size:16px;min-height:44px}
.csearch:focus-visible,.crep:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.crep{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:9px 11px;color:inherit;font:inherit;font-size:15px;min-height:44px}
.ctools .btn{min-height:44px}

/* ── CRM mailbox bar ─────────────────────────────────────────────────────────── */
.mbar{display:flex;flex-wrap:wrap;gap:10px 14px;align-items:center;justify-content:space-between;background:var(--card2);border-radius:10px;padding:10px 14px;margin:0 0 14px}
.mbar .acts{display:flex;gap:8px;flex-wrap:wrap}
.mbar .acts .btn{min-height:40px;text-decoration:none}

/* My business → Recruiting (PARITY row 4) and the Help contact card (row 6). */
.rec-up{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap;margin:10px 0 12px}
.rec-name{font-size:18px}
.rec-contact,.help-b{display:flex;gap:8px;flex-wrap:wrap}
.rec-team{font-size:14px;color:var(--acc);text-decoration:none}
.rec-tile{border:1px solid var(--line);border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:4px}
.rec-k{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--t2);font-weight:600}
.rec-v{font-size:14px;color:var(--t2)}
.rec-v b{font-size:28px;color:var(--t1);font-variant-numeric:tabular-nums;margin-right:4px}
.rec-h{font-size:12px;color:var(--t3)}
.rec-tiles{margin-top:12px}

/* Account (PARITY row 36 gate, 2026-09-10): Plya sets it in one centered column; ours matches. */
#s-acct{max-width:1000px;margin-inline:auto}

/* ── Messages: a thread list beside the open conversation (PARITY row 14) ────────────────────
   The list and the pane share .split; on a phone one shows at a time (.msg-open = the thread). */
.split.msg-split{grid-template-columns:320px minmax(0,1fr);gap:16px}
.msg-side{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:6px;display:flex;flex-direction:column;gap:2px;max-height:calc(100vh - 210px);overflow-y:auto}
.msg-item{all:unset;box-sizing:border-box;display:flex;align-items:center;gap:10px;padding:10px;border-radius:10px;cursor:pointer;min-width:0}
.msg-item:hover{background:var(--card2)}
.msg-item.on{background:var(--card2)}
.msg-item:focus-visible{outline:2px solid var(--acc);outline-offset:-2px}
.msg-mid{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.msg-top{display:flex;align-items:baseline;gap:8px}
.msg-name{flex:1;min-width:0;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.msg-item.unread .msg-name{font-weight:700}
.msg-when{font-size:12px;color:var(--t3);white-space:nowrap}
.msg-prev{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--t2);min-width:0}
.msg-prev .tag{padding:1px 7px;font-size:11px}
.msg-prev-t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.msg-badge{min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--acc);color:#fff;font-size:12px;font-weight:600;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box}
.msg-badge:empty{min-width:10px;width:10px;height:10px;padding:0}
.msg-more{margin:6px auto 4px}
.msg-main{background:var(--card);border:1px solid var(--line);border-radius:var(--r);min-height:420px;display:flex;flex-direction:column;min-width:0}
.msg-none{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:var(--t2);padding:40px 16px}
.msg-none svg{width:44px;height:44px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linejoin:round}
.msg-thread{display:flex;flex-direction:column;min-height:0;flex:1}
.msg-th-h{display:flex;align-items:center;gap:12px;padding:14px 16px;border-bottom:1px solid var(--line)}
.msg-th-who{flex:1;min-width:0}
.msg-th-who .sub{font-size:13px}
.msg-fact{display:inline-block;max-width:100%;overflow-wrap:anywhere}
.msg-backrow{display:none;padding:12px 16px 0}
.msg-list{flex:1;display:flex;flex-direction:column;gap:10px;padding:16px;overflow-y:auto;max-height:calc(100vh - 300px)}
.msg-b{max-width:78%;padding:10px 12px;border-radius:14px;background:var(--card2);align-self:flex-start;min-width:0}
.msg-b.out{align-self:flex-end;background:var(--acc-bg)}
.msg-meta{font-size:12px;color:var(--t3);margin-bottom:4px}
.msg-subj{font-weight:600;margin-bottom:4px;overflow-wrap:anywhere}
.msg-text{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14.5px;line-height:1.45}
.msg-ro{margin:0;padding:10px 16px;border-top:1px solid var(--line);font-size:12.5px;color:var(--t3)}
@media (max-width:759.98px){
  .split.msg-split{grid-template-columns:minmax(0,1fr)}
  .msg-split.msg-open .msg-side{display:none}
  .msg-split:not(.msg-open) .msg-main{display:none}
  .msg-backrow{display:block}
  .msg-th-h .av{display:none}
  .msg-side,.msg-list{max-height:none}
  .msg-b{max-width:90%}
}


/* Precision DX call sheet (lead drawer) — Mark denial/underpay path */
.pdx-sheet .pdx-worth { color: var(--amber, #f59e0b); font-size: 1.05em; }
.pdx-sheet .lead-dl { margin: 0; }

/* PDX call sheet — Charlie Intel strip (weather + local/favorite teams). Call path only. */
.pdx-sheet .pdx-charlie {
  margin: 12px 0 4px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(245, 158, 11, 0.28);
  background: linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(30, 41, 59, 0.75));
}
.pdx-sheet .pdx-charlie-title {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #f59e0b;
  margin-bottom: 8px;
}
.pdx-sheet .pdx-charlie-row {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin-top: 8px;
  font-size: 13px;
  color: #e2e8f0;
}
.pdx-sheet .pdx-charlie-row:first-of-type { margin-top: 0; }
.pdx-sheet .pdx-charlie-ico { flex-shrink: 0; line-height: 1.3; }
.pdx-sheet .pdx-charlie-row .sub { color: #94a3b8; font-size: 12.5px; }


/* By audience (2026-09-24): quick actions and CTAs are hidden per audience, and .qa's
   display:inline-flex outranks the browser's own [hidden] rule, as .nav button's did. */
.qa[hidden],[data-feature][hidden],[data-feature-off][hidden]{display:none!important}
.price-ro td.r{font-variant-numeric:tabular-nums}
.card[hidden]{display:none!important}
/* A dashboard column's rows sit at the top: with the attention card not drawn (no earnings yet)
   the empty Console slot was stretched to fill the column, leaving a gap above Commissions. */
#dash-cols > .grid{align-content:start}

/* ── My business density (Kevin, 2026-09-24: the same tightening as the menu, on every tab) ──
 * Scoped to #s-biz so no other section moves. Smaller heads (the panes set h2 sizes inline,
 * hence !important), tighter cards, rows and empty states. Markup and ids are untouched.
 */
#s-biz .hdr{margin-bottom:10px}
#s-biz .hdr h1{font-size:24px}
#s-biz .hdr .sub{font-size:13.5px}
#s-biz .subtabs{margin-bottom:10px}
#s-biz .grid{gap:10px}
#s-biz .card{padding:12px 14px}
#s-biz .card h2,#s-biz h2{font-size:15px!important;line-height:1.3}
#s-biz h3{font-size:13.5px}
#s-biz .hl{margin-bottom:6px}
#s-biz .row{padding:8px 0;gap:10px}
#s-biz .card .sub,#s-biz .row .sub{font-size:13.5px}
#s-biz .empty{padding:24px 14px}
#s-biz .empty svg{width:40px;height:40px;margin-bottom:8px}
#s-biz .empty b{font-size:14.5px}
#s-biz .empty p{font-size:13px}
#s-biz .pr-bar{margin-top:10px;gap:10px}
#s-biz .pr-bar .btn{min-height:38px}
#s-biz .kitlist{gap:6px}
#s-biz .kititem{padding:8px 11px}
#s-biz .kititem .btn{min-height:34px}
#s-biz .nrow{padding:8px 0}
#s-biz .tablewrap th,#s-biz .tablewrap td{padding-top:7px;padding-bottom:7px}
@media (max-width:900px){
  #s-biz .hdr h1{font-size:21px}
  #s-biz .card{padding:10px 12px}
  #s-biz .card .sub,#s-biz .row .sub{font-size:13px}
}

/* ── Density pass on every page body (Kevin, 2026-09-24, Batch 2 item 7: "same as the menu") ──
 * The My business tightening above, applied to every pane: smaller heads, 12/14px card padding,
 * shorter table rows and list rows, compact empty states, one 4/8/12/16px spacing scale.
 * CSS only, on the classes the panes already draw; nothing is hidden and no colour, font family
 * or brand token changes. Scoped to the portal's own .main: the practice page shares this sheet
 * and its shell (data-surface="practice") and is left exactly as it was. The #s-biz block above
 * still wins inside My business (an id outranks this scope). Buttons and fields keep a 40px
 * target on a phone; only the space around them shrinks.
 */
body:not([data-surface="practice"]) .main .hdr{margin-bottom:12px;gap:12px}
body:not([data-surface="practice"]) .main .hdr h1{font-size:24px}
body:not([data-surface="practice"]) .main .hdr .sub{font-size:13.5px}
body:not([data-surface="practice"]) .main .subtabs{margin-bottom:12px}
body:not([data-surface="practice"]) .main .subpills{margin-bottom:8px}
body:not([data-surface="practice"]) .main .quick{margin-bottom:12px}
body:not([data-surface="practice"]) .main .qa{min-height:40px;padding:8px 12px}
body:not([data-surface="practice"]) .main .grid{gap:12px}
body:not([data-surface="practice"]) .main .card{padding:12px 14px}
body:not([data-surface="practice"]) .main .card h2,
body:not([data-surface="practice"]) .main .split-h h2,
body:not([data-surface="practice"]) .main .k-h{font-size:15px!important;line-height:1.3}
body:not([data-surface="practice"]) .main .card h3{font-size:13.5px}
body:not([data-surface="practice"]) .main .card .sub,
body:not([data-surface="practice"]) .main .row .sub{font-size:13.5px}
body:not([data-surface="practice"]) .main .hl{margin-bottom:8px}
body:not([data-surface="practice"]) .main .k{margin:12px 0 6px}
body:not([data-surface="practice"]) .main .row{padding:8px 0;gap:10px}
body:not([data-surface="practice"]) .main th{padding:8px 10px}
body:not([data-surface="practice"]) .main td{padding:8px 10px}
body:not([data-surface="practice"]) .main .metric .v{font-size:26px;margin:4px 0 2px}
body:not([data-surface="practice"]) .main .empty{padding:28px 16px}
body:not([data-surface="practice"]) .main .empty svg{width:40px;height:40px;margin-bottom:8px}
body:not([data-surface="practice"]) .main .empty b{font-size:14.5px;margin-bottom:4px}
body:not([data-surface="practice"]) .main .empty p{font-size:13px}
body:not([data-surface="practice"]) .main .notbuilt{padding:20px 16px}
body:not([data-surface="practice"]) .main .notbuilt .tag{margin-bottom:8px}
body:not([data-surface="practice"]) .main .notbuilt b{font-size:14.5px;margin-bottom:4px}
body:not([data-surface="practice"]) .main .notbuilt p{font-size:13px}
body:not([data-surface="practice"]) .main .note{padding:8px 12px}
body:not([data-surface="practice"]) .main .mbar{padding:8px 12px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .recv-card[open] summary{margin-bottom:8px}
body:not([data-surface="practice"]) .main .bigcard .hd{padding:10px 14px}
body:not([data-surface="practice"]) .main .bigcard .bd{padding:4px 14px}
/* Lists: side list, row items, profile cards, kanban, link rows, kit and switch rows. */
body:not([data-surface="practice"]) .main .split{gap:16px}
body:not([data-surface="practice"]) .main .split-h{margin-bottom:8px}
body:not([data-surface="practice"]) .main .split-h .sub{font-size:13px}
body:not([data-surface="practice"]) .main .row-item{padding:10px 12px;margin-bottom:8px;gap:12px}
body:not([data-surface="practice"]) .main .kboard{gap:12px}
body:not([data-surface="practice"]) .main .kcard{padding:10px 12px;gap:6px;margin-bottom:8px}
body:not([data-surface="practice"]) .main .kcol-empty{padding:16px 12px}
body:not([data-surface="practice"]) .main .linklist{gap:8px}
body:not([data-surface="practice"]) .main .linkrow{padding:10px 12px;gap:12px}
body:not([data-surface="practice"]) .main .qrbox{padding:10px 12px;gap:12px}
body:not([data-surface="practice"]) .main .kititem{padding:8px 12px}
body:not([data-surface="practice"]) .main .nrow{padding:8px 0}
/* Toolbars: the same controls, less air around them. */
body:not([data-surface="practice"]) .main .filterbar{padding:8px;gap:8px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .infobar{padding:8px 12px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .results-line{margin-bottom:8px}
body:not([data-surface="practice"]) .main .chips{gap:8px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .ctools{gap:8px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .dfilters{gap:8px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .dview{margin-bottom:8px}
body:not([data-surface="practice"]) .main .pipe-tools{margin-bottom:8px}
/* Tiles and the Plya-layout panes (Users, Learning, Manager, Rep/Customer success, Console). */
body:not([data-surface="practice"]) .main .dtiles{gap:8px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .dtile{padding:10px 12px}
body:not([data-surface="practice"]) .main .htree{gap:12px}
body:not([data-surface="practice"]) .main .hcard{padding:10px 12px}
body:not([data-surface="practice"]) .main .lc-alert{padding:10px 14px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .lc-tabs{margin:12px 0}
body:not([data-surface="practice"]) .main .lc-card{padding:12px 14px;gap:6px}
body:not([data-surface="practice"]) .main .mg-card,
body:not([data-surface="practice"]) .main .rs-card,
body:not([data-surface="practice"]) .main .cs-card{padding:12px 14px}
body:not([data-surface="practice"]) .main .mg-card h3,
body:not([data-surface="practice"]) .main .rs-card h3,
body:not([data-surface="practice"]) .main .cs-card h3{font-size:15px}
body:not([data-surface="practice"]) .main .mg-card{margin-bottom:12px}
body:not([data-surface="practice"]) .main .mg-tiles,
body:not([data-surface="practice"]) .main .rs-tiles,
body:not([data-surface="practice"]) .main .cs-tiles{gap:8px;margin-bottom:12px}
body:not([data-surface="practice"]) .main .mg-tile,
body:not([data-surface="practice"]) .main .rs-tile,
body:not([data-surface="practice"]) .main .cs-tile{padding:10px 12px}
body:not([data-surface="practice"]) .main .mg-grid,
body:not([data-surface="practice"]) .main .rs-grid,
body:not([data-surface="practice"]) .main .cs-grid{gap:12px}
body:not([data-surface="practice"]) .main .rs-top,
body:not([data-surface="practice"]) .main .cs-top,
body:not([data-surface="practice"]) .main .cs-banner,
body:not([data-surface="practice"]) .main .cs-life{margin-bottom:12px}
body:not([data-surface="practice"]) .main .mg-row,
body:not([data-surface="practice"]) .main .cs-row{padding:8px 0}
body:not([data-surface="practice"]) .main .rec-tile{padding:10px 12px}
body:not([data-surface="practice"]) .main .rec-up{margin:8px 0}
body:not([data-surface="practice"]) .main .k-tiles{gap:8px}
body:not([data-surface="practice"]) .main .k-tile{padding:10px 12px;gap:2px}
body:not([data-surface="practice"]) .main .k-total{margin-bottom:12px}
body:not([data-surface="practice"]) .main .k-cols{gap:12px}
body:not([data-surface="practice"]) .main .k-p{margin-bottom:6px}
body:not([data-surface="practice"]) .main .k-inv-switch{padding:8px 12px;margin:8px 0}
body:not([data-surface="practice"]) .main .bk-form{padding:12px 14px;gap:10px 12px;margin-top:12px}
body:not([data-surface="practice"]) .main .bk-hours{gap:8px 12px}
body:not([data-surface="practice"]) .main .pr-bar{margin-top:10px;gap:10px}
body:not([data-surface="practice"]) .footer{padding-top:16px}
/* Fields: the booking form's two single fields (name, link) sit side by side wherever two 240px
   fields fit (a desktop, a tablet); on a phone they wrap to one compact column. Its fieldsets and
   button bar keep the full width. A wrap, not a column switch, so it is not a card grid. */
body:not([data-surface="practice"]) .main .bk-form{flex-direction:row;flex-wrap:wrap}
body:not([data-surface="practice"]) .main .bk-form>*{flex:1 1 100%;min-width:0}
body:not([data-surface="practice"]) .main .bk-form>label.bk-f{flex:1 1 240px}
@media (max-width:900px){
  body:not([data-surface="practice"]) .main .hdr h1{font-size:21px}
  body:not([data-surface="practice"]) .main .hdr .sub{font-size:13px}
  body:not([data-surface="practice"]) .main .card{padding:10px 12px}
  body:not([data-surface="practice"]) .main .card .sub,
  body:not([data-surface="practice"]) .main .row .sub{font-size:13px}
}

/* Settings › Where you are signed in (punch list #41): older sign-ins fold under one line. */
.sess-older{margin-top:8px}
.sess-older summary{cursor:pointer;color:var(--t2);font-size:13px;padding:8px 0}
/* A quick-action row with nothing visible in it takes no space (punch list #40, Mark). */
.quick:not(:has(> :not([hidden]))){display:none}

/* ── estimator product picker (rec 7 follow-up, 2026-09-26) ──────────────────────────────────
 * The portal's .sf-field controls, one grid row per product. Stays a row on a phone (product
 * wide, quantity narrow) rather than the label-beside-field layout .sf-field takes under 560px.
 */
.est-picks{display:flex;flex-direction:column;gap:8px}
.est-pick{display:grid;grid-template-columns:minmax(0,1fr) 84px auto;gap:8px;align-items:end}
.est-pick .sf-field{flex-direction:column;align-items:stretch;margin:0}
.est-pick .sf-field>span:first-child{flex:none}
.est-pick-x{min-height:36px}
/* The picker's answer: sale price and per-box price beside the earn sentence (Batch A3, 2026-09-26). */
.est-prices{margin-top:10px;display:flex;flex-direction:column;gap:4px;font-size:14px}
.est-sale{font-size:15px}
.est-box{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}
.est-prices-note{font-size:12px;margin:4px 0 0}
@media(max-width:560px){
  .est-pick{grid-template-columns:minmax(0,1fr) 72px auto}
  .est-pick .sf-field select,.est-pick .sf-field input{font-size:16px}
}
