/* ============================================================================
   Pultrack design tokens + chrome — the SAME system as Pultrack POS and F&B.
   Ported from POS static/pultrack.css (source of truth: branding/design-
   handoff-v1, Design Handoff §02–§05). Loads AFTER bootstrap + style.css so
   its tokens win. Keep in step with POS; do not invent Make-only colours.
   ============================================================================ */

@import url('/static/fonts/plex.css');

/* Nav is a server-rendered multi-page app (plain <a href> = full reload). On
   browsers that support MPA view transitions (Chromium / Safari 18+, incl. the
   desktop WebView) this cross-fades between page loads instead of the white
   flash; every other browser just reloads exactly as before. Kept short so the
   POS still feels instant, and disabled under prefers-reduced-motion. */
@view-transition { navigation: auto; }
::view-transition-old(root),
::view-transition-new(root){ animation-duration:.12s; }
@media (prefers-reduced-motion: reduce){
  ::view-transition-old(root),
  ::view-transition-new(root){ animation:none; }
}

:root{
  /* -- colour tokens (handoff §02) -- */
  --green:#1B5739; --green-2:#103A26; --green-soft:#E7F0EA; --green-line:#CDE0D4;
  --ink:#13211B; --gold:#C28A2C; --gold-2:#9C6E1C; --gold-soft:#F7EAD0; --gold-line:#E7D2A0;
  --paper:#F4F1E9;
  --muted:#6E685B; --line:#E3DDD0;
  --debt:#992C22; --debt-soft:#F4E0DC; --debt-line:#E2C2BB;
  --money-2:#564E41;
  /* Informational, not good/bad — "partial", "bank", "service", "salary".
     Bootstrap's own .bg-info is #0DCAF0, a cyan that belongs to no part of
     this palette and shouted off every list that used it. A desaturated
     slate sits beside the green without competing with it. */
  --slate:#3E5560; --slate-soft:#E7EDF0; --slate-line:#CFDCE2;
  --shadow:0 1px 2px rgba(20,30,24,.05),0 8px 28px rgba(20,30,24,.07);
  --app-bg:#EEECE3; --app-card:#FFFFFF; --app-line:#E0DACC;
  --app-line-soft:#ECE7DC; --app-hover:#F6F4EE; --app-ink:#1A241F;
  /* -- layout -- */
  --sidebar-w:236px; --sidebar-rail:62px; --topbar-h:50px;

  /* Re-point Bootstrap's own palette at the brand tokens so every
     .btn-primary / .text-success / .table-striped etc. follows along. */
  --bs-primary:#1B5739; --bs-primary-rgb:27,87,57;
  --bs-success:#1B5739; --bs-success-rgb:27,87,57;
  --bs-danger:#992C22;  --bs-danger-rgb:153,44,34;
  --bs-warning:#C28A2C; --bs-warning-rgb:194,138,44;
  --bs-body-bg:#EEECE3; --bs-body-color:#1A241F;
  --bs-border-color:#E0DACC;
  --bs-font-sans-serif:'IBM Plex Sans',system-ui,-apple-system,'Segoe UI',sans-serif;
  --bs-font-monospace:'IBM Plex Mono',SFMono-Regular,Menlo,Consolas,monospace;
  --bs-link-color:#1B5739; --bs-link-hover-color:#103A26;

  /* Type scale — one source of truth. Before this, 26 distinct font-sizes
     were scattered across the sheet (11/11.5, 12/12.5, 13/13.5, 14/14.5,
     15/15.5 half-pixel twins), because sizes were nudged per component
     instead of chosen from a system. Colours already had --app-* tokens;
     typography now does too. */
  --text-2xs:10.5px;   /* micro labels, badge captions */
  --text-xs:11.5px;    /* table meta, hints */
  --text-sm:12.5px;    /* secondary text */
  --text-base:13.5px;  /* body / table default */
  --text-lg:15px;      /* emphasised body, section labels */
  --text-xl:16px;      /* card titles */
}

body{
  background:var(--app-bg);
  color:var(--app-ink);
  font-family:'IBM Plex Sans',system-ui,sans-serif;
}

/* Numbers are the product — tabular figures wherever amounts line up. */
table, .text-end, .money, input[type=number]{ font-variant-numeric:tabular-nums; }

code, .mono, kbd{ font-family:'IBM Plex Mono',monospace; }
code{ color:var(--green); }

/* ---------------------------------------------------------------- buttons */
.btn-primary, .btn-success{
  --bs-btn-bg:var(--green); --bs-btn-border-color:var(--green);
  --bs-btn-hover-bg:var(--green-2); --bs-btn-hover-border-color:var(--green-2);
  --bs-btn-active-bg:var(--green-2); --bs-btn-active-border-color:var(--green-2);
  --bs-btn-disabled-bg:var(--green); --bs-btn-disabled-border-color:var(--green);
}
.btn-outline-primary, .btn-outline-success{
  --bs-btn-color:var(--green); --bs-btn-border-color:var(--green-line);
  --bs-btn-hover-bg:var(--green); --bs-btn-hover-border-color:var(--green);
  --bs-btn-active-bg:var(--green); --bs-btn-active-border-color:var(--green);
}
.btn-danger{ --bs-btn-bg:var(--debt); --bs-btn-border-color:var(--debt);
  --bs-btn-hover-bg:#7d241c; --bs-btn-hover-border-color:#7d241c; }
.btn-outline-danger{ --bs-btn-color:var(--debt); --bs-btn-border-color:var(--debt-line);
  --bs-btn-hover-bg:var(--debt); --bs-btn-hover-border-color:var(--debt); }
.btn-warning{ --bs-btn-bg:var(--gold); --bs-btn-border-color:var(--gold); }
.btn{ border-radius:9px; }
.btn-sm{ border-radius:7px; }
.btn:focus-visible{ box-shadow:none; outline:2px solid var(--green); outline-offset:2px; }

/* ------------------------------------------------------------------ cards */
.card{
  border:1px solid var(--line);
  border-radius:12px;
  box-shadow:var(--shadow);
  background:var(--app-card);
}
.card-header{
  background:transparent;
  border-bottom:1px solid var(--app-line-soft);
  font-weight:600;
}
/* A live-preview card (e.g. the receipt preview on Company settings) follows the
   long form column on wide screens so the right side isn't left empty; on mobile
   the columns stack and it flows normally. top clears the ~50px top bar. */
@media (min-width: 992px){
  .pt-sticky-preview{ position:sticky; top:66px; }
}

/* ------------------------------------------------------------------ forms */
.form-control, .form-select{
  border-radius:9px;
  border-color:var(--app-line);
  color:var(--app-ink);
}
.form-control:focus, .form-select:focus{
  border-color:var(--green);
  box-shadow:0 0 0 .25rem rgba(27,87,57,.15);
}
.form-label{ font-size:.83rem; font-weight:500; color:var(--muted); margin-bottom:.25rem; }
.input-group .btn{ border-radius:0 9px 9px 0; }
/* Show/hide password eye — sits inside the field; input gets right padding so
   the typed value never slides under the icon. */
.pw-eye{ position:absolute; top:0; right:0; height:100%; width:2.6rem; z-index:5;
  display:flex; align-items:center; justify-content:center; border:0; background:transparent;
  color:var(--muted); cursor:pointer; }
.pw-eye:hover{ color:var(--ink); }
.pw-field{ position:relative; }
.pw-field .form-control{ padding-right:2.6rem; }

/* ----------------------------------------------------------------- tables */
.table{ --bs-table-bg:var(--app-card); --bs-table-hover-bg:var(--app-hover); }
.table > :not(caption) > * > *{ border-bottom-color:var(--app-line-soft); }
.table-light, .table-light th{ background-color:var(--app-hover) !important; color:var(--muted); }
table.sortable thead th, table.sticky-head thead th{ background-color:var(--app-hover); }
table.sortable thead th:hover{ background-color:var(--app-line-soft); }
.pt-table__body table.sortable thead th,
.pt-table__body table.sticky-head thead th,
.pt-table__body table thead th{ background-color:var(--app-hover) !important; }
.pt-table__body table > tbody > tr > td{ background-color:var(--app-card, #fff); }
.pt-table__body table.table-hover > tbody > tr:hover > td{ background-color:var(--app-hover); }
.pt-table__foot{ background:var(--app-hover); border-top-color:var(--app-line); }
.pt-table{ border-color:var(--app-line); background:var(--app-card); }
.pt-table__body{ background:var(--app-card); }
thead th{ font-size:.8rem; font-weight:600; color:var(--muted); }
/* Content density: the back-office content area reads a touch smaller than the
   16px default (the sidebar/topbar keep their own sizes — this is scoped to
   .bo-main). Headings use rem so they stay put; body + table text inherit. */
.bo-main{ font-size:.9rem; }
/* Breathing room at the table's outer edges WITHOUT insetting the whole table.
   The padding lives inside the first/last cells, so the header band (.table-light)
   and row-hover still reach the card edge — no inset "white bars" beside the
   header. Works on every layout: a right-aligned last column (GP, totals) gets
   its gutter; a trailing actions cell gets it too. */
.bo-main .table > :not(caption) > * > *:first-child{ padding-inline-start:.9rem; }
.bo-main .table > :not(caption) > * > *:last-child{ padding-inline-end:.9rem; }
/* Row-action controls (Edit / Open / Void …) are affordances, not primary
   buttons: drop the heavy frame, size them to the smaller content text, and keep
   them on one line so a narrow actions column can't wrap them onto two rows.
   Colour is retained (secondary/primary/danger) so intent still reads; the hover
   fill is the "clickable" cue. */
.bo-main .table tbody td .btn-sm[class*="btn-outline-"]{
  --bs-btn-border-color:transparent;
  --bs-btn-padding-y:.1rem;
  --bs-btn-padding-x:.4rem;
  --bs-btn-font-size:.8rem;
  white-space:nowrap;
}
/* Short structured values must never wrap onto two lines. Doc numbers live in
   <code>; date cells carry .dt. Keeps "2026-06-30" / "PAY-20260630-0002" intact. */
.table code{ white-space:nowrap; }
td.dt, th.dt, td.nowrap, th.nowrap{ white-space:nowrap; }
.table-success, .table-success th, .table-success td{
  --bs-table-bg:var(--green-soft); color:var(--app-ink);
}

/* ----------------------------------------------------------------- badges */
.badge.bg-secondary{ background-color:var(--app-hover) !important; color:var(--muted);
  border:1px solid var(--app-line); font-weight:500; }
.badge.bg-success{ background-color:var(--green-soft) !important; color:var(--green);
  border:1px solid var(--green-line); font-weight:600; }
.badge.bg-danger{ background-color:var(--debt-soft) !important; color:var(--debt);
  border:1px solid var(--debt-line); font-weight:600; }
.badge.bg-warning{ background-color:var(--gold-soft) !important; color:var(--gold-2);
  border:1px solid var(--gold-line); font-weight:600; }
/* These three were never themed, so every badge using them rendered in raw
   Bootstrap next to the four above: cyan "Partial", near-white "Cancelled",
   #212529 "Written off". A status vocabulary is only readable if the whole
   ladder is one palette — grey waiting, slate in progress, gold attention,
   green done, ghost cancelled. */
.badge.bg-info{ background-color:var(--slate-soft) !important; color:var(--slate);
  border:1px solid var(--slate-line); font-weight:600; }
.badge.bg-light{ background-color:var(--app-card) !important; color:var(--muted);
  border:1px solid var(--app-line); font-weight:500; }
.badge.bg-dark{ background-color:var(--ink) !important; color:#FFFFFF;
  border:1px solid var(--ink); font-weight:600; }

/* ------------------------------------------------ semantic text (handoff) */
.text-danger{ color:var(--debt) !important; }          /* red = already owed */
.text-success{ color:var(--green) !important; }
.text-warning{ color:var(--gold-2) !important; }       /* amber = debt this sale adds */
a{ color:var(--green); }
.alert-danger{ --bs-alert-bg:var(--debt-soft); --bs-alert-border-color:var(--debt-line); --bs-alert-color:var(--debt); }
.alert-warning{ --bs-alert-bg:var(--gold-soft); --bs-alert-border-color:var(--gold-line); --bs-alert-color:var(--gold-2); }
.alert-success{ --bs-alert-bg:var(--green-soft); --bs-alert-border-color:var(--green-line); --bs-alert-color:var(--green-2); }

/* Secondary USD figures — --money-2, AA+ on white and paper (handoff §02). */
.money-sec{ color:var(--money-2); font-size:.85em; }

/* ============================================================ BACK OFFICE
   The two chrome modes (brief Revision 1 §7):
   back office = left sidebar; POS = chrome-free top strip. */
.bo{ display:flex; min-height:100vh; min-height:100dvh; }

/* --------------------------------------------------------------- sidebar */
.sidebar{
  width:var(--sidebar-w); flex:none;
  background:var(--green-2); color:#cfdcd2;
  display:flex; flex-direction:column;
  position:sticky; top:0; height:100vh; height:100dvh;
  transition:width .15s ease;
  z-index:1030;
}
.sidebar .sb-head{
  display:flex; align-items:center; gap:9px;
  padding:11px 12px 11px 14px; flex:none;
  border-bottom:1px solid rgba(255,255,255,.08);
}
.sidebar .sb-head .tile{ width:30px; height:30px; border-radius:8px; flex:none; }
.sidebar .sb-head .wm{ font-weight:600; font-size:var(--text-xl); color:var(--paper); letter-spacing:-.01em; white-space:nowrap; }
.sidebar .sb-toggle{
  margin-left:auto; background:none; border:0; color:#9fb3a6;
  width:26px; height:26px; border-radius:6px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.sidebar .sb-toggle:hover{ background:rgba(255,255,255,.08); color:#fff; }
.sidebar .sb-toggle svg{ width:15px; height:15px; }

.sidebar .sb-scroll{ flex:1; overflow-y:auto; overflow-x:hidden; padding:8px 8px 10px; }
.sidebar .sb-scroll::-webkit-scrollbar{ width:5px; }
.sidebar .sb-scroll::-webkit-scrollbar-thumb{ background:rgba(255,255,255,.15); border-radius:3px; }

.sidebar .sb-glabel{
  font-family:'IBM Plex Mono',monospace; font-size:var(--text-2xs); letter-spacing:.14em;
  text-transform:uppercase; color:#94ab95; padding:12px 8px 4px; white-space:nowrap;
}
.sidebar .sb-item{
  display:flex; align-items:center; gap:10px;
  padding:7px 9px; margin:1px 0;
  border-radius:8px; border-left:3px solid transparent;
  color:#cfdcd2; text-decoration:none; font-size:var(--text-base); font-weight:500;
  white-space:nowrap; overflow:hidden;
}
/* Nav polish (matches Pultrack F&B). Deliberately fast (.16s) and eased-out: a
   sidebar is passed through constantly, so anything slower than ~200ms starts
   to feel like lag rather than finish. No bounce, no stagger — those read as
   playful, not premium, and they fight rapid navigation. */
.sidebar .sb-item{
  transition:background .16s ease-out, color .16s ease-out,
             border-left-color .16s ease-out, transform .16s ease-out;
}
.sidebar .sb-item:hover{
  background:rgba(255,255,255,.07); color:#fff;
  transform:translateX(2px);            /* a nudge, not a slide */
}
.sidebar .sb-item:active{ transform:translateX(1px) scale(.995); }  /* press feedback on click */
.sidebar .sb-item.active{
  background:rgba(255,255,255,.10); color:#fff;
  border-left-color:var(--gold);
}
/* The gold rule grows in on hover, so the accent feels attached to the item
   rather than switched on. The ACTIVE item keeps its full bar. */
.sidebar .sb-item:not(.active):hover{ border-left-color:rgba(194,138,44,.45); }
.sidebar.rail .sb-item:hover{ transform:none; }   /* no room to nudge in a rail */
.sidebar .sb-item .ic{ width:17px; height:17px; flex:none; display:flex; }
.sidebar .sb-item .ic i{ font-size:var(--text-lg); line-height:1; }
.sidebar .sb-item .lbl{ overflow:hidden; text-overflow:ellipsis; }

.sidebar .sb-foot{
  flex:none; border-top:1px solid rgba(255,255,255,.08); padding:10px 12px;
}
.sidebar .sb-user{ display:flex; align-items:center; gap:9px; }
.sidebar .sb-user .av{
  width:30px; height:30px; border-radius:50%; flex:none;
  background:rgba(255,255,255,.12); color:var(--paper);
  display:flex; align-items:center; justify-content:center;
  font-size:var(--text-sm); font-weight:600;
}
.sidebar .sb-user .uw{ display:flex; flex-direction:column; min-width:0; }
.sidebar .sb-user .un{ font-size:var(--text-sm); font-weight:600; color:var(--paper);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sidebar .sb-user .ur{ font-size:var(--text-2xs); color:#8aa18e; white-space:nowrap; }
.sidebar .sb-user .out{
  margin-left:auto; background:none; border:0; color:#9fb3a6; cursor:pointer;
  width:26px; height:26px; border-radius:6px;
  display:flex; align-items:center; justify-content:center;
}
.sidebar .sb-user .out:hover{ background:rgba(255,255,255,.1); color:#fff; }

/* Collapsed icon rail */
.sidebar.rail{ width:var(--sidebar-rail); }
.sidebar.rail .wm, .sidebar.rail .sb-glabel, .sidebar.rail .sb-item .lbl,
.sidebar.rail .sb-user .uw{ display:none; }
.sidebar.rail .sb-head{ padding-left:12px; }
.sidebar.rail .sb-item{ justify-content:center; padding:9px 0; }
.sidebar.rail .sb-toggle{ margin-left:0; }
.sidebar.rail .sb-head{ flex-direction:column; gap:7px; }
.sidebar.rail .sb-user{ justify-content:center; }
.sidebar.rail .sb-user .out{ margin-left:0; }

/* Collapsible nav group (e.g. Administration) — folds away the least-used
   section so the list doesn't force a scroll. */
.sidebar .sb-ghead{
  width:100%; display:flex; align-items:center; gap:6px;
  background:none; border:0; cursor:pointer; text-align:left;
}
.sidebar .sb-ghead:hover{ color:#cfdcd2; }
.sidebar .sb-ghead .chev{ margin-left:auto; transition:transform .22s cubic-bezier(.34,1.56,.64,1); line-height:1; }
.sidebar .sb-ghead .chev i{ font-size:var(--text-xs); }
.sidebar .sb-group.collapsible.open .sb-ghead .chev{ transform:rotate(180deg); }

/* Animated group expand/collapse. `display:none` can't be transitioned, so a
   closed group is clipped to max-height:0 instead. The transition is gated
   behind .nav-anim (added by JS one frame after the initial open) so the group
   auto-opened for the current page doesn't slide on every page load. Rail state
   keeps its own icon layout, so it's excluded. */
.sidebar:not(.rail) .sb-group.collapsible .sb-gitems{
  overflow:hidden; max-height:0; opacity:0;
  visibility:hidden;   /* keep collapsed links out of the tab order (as display:none did) */
}
.sidebar:not(.rail) .sb-group.collapsible.open .sb-gitems{
  max-height:600px; opacity:1; visibility:visible;
}
.sidebar.nav-anim:not(.rail) .sb-group.collapsible .sb-gitems{
  /* visibility flips to hidden only AFTER the collapse finishes, so content
     stays on-screen for the whole slide-up. */
  transition: max-height .22s ease, opacity .16s ease, visibility 0s linear .22s;
}
.sidebar.nav-anim:not(.rail) .sb-group.collapsible.open .sb-gitems{
  transition: max-height .28s ease, opacity .22s ease .04s, visibility 0s linear 0s;
}
/* Items settle in with a subtle downward slide when the group opens. */
.sidebar:not(.rail) .sb-group.collapsible .sb-gitems > *{ transform:translateY(-5px); }
.sidebar:not(.rail) .sb-group.collapsible.open .sb-gitems > *{ transform:translateY(0); }
.sidebar.nav-anim:not(.rail) .sb-group.collapsible .sb-gitems > *{ transition:transform .26s ease; }
@media (prefers-reduced-motion: reduce){
  .sidebar .sb-ghead .chev,
  .sidebar .sb-group.collapsible .sb-gitems,
  .sidebar .sb-group.collapsible .sb-gitems > *{ transition:none !important; }
  .sidebar .sb-item{ transition:none !important; }
  .sidebar .sb-item:hover, .sidebar .sb-item:active{ transform:none; }
}
.sidebar.rail .sb-ghead{ pointer-events:none; }   /* rail: items always show as icons */

/* Users page — section access as a multi-select dropdown */
.sec-multi{ min-width:190px; max-width:230px; }
.sec-multi .sec-toggle{ cursor:pointer; overflow:hidden; }
.sec-multi .sec-label{ display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sec-menu{ min-width:210px; border-color:var(--app-line); box-shadow:0 8px 28px rgba(20,30,24,.12); }
.sec-menu .sec-opt{ display:flex; align-items:center; gap:8px; cursor:pointer;
  font-size:var(--text-base); border-radius:7px; padding:6px 9px; }
.sec-menu .sec-opt:hover{ background:var(--app-hover); }
.sec-menu .sec-opt input{ margin:0; }

/* Users page — inline name/username editor */
.name-edit > summary{ cursor:pointer; list-style:none; display:flex; align-items:center; gap:6px; }
.name-edit > summary::-webkit-details-marker{ display:none; }
.name-edit .sec-pen{ color:#9aa39c; font-size:var(--text-sm); opacity:0; transition:opacity .12s; }
.name-edit > summary:hover .sec-pen{ opacity:1; }
.name-edit[open] > summary{ margin-bottom:6px; }
.name-form{ display:flex; flex-direction:column; gap:5px; max-width:220px; }

/* --------------------------------------------------------------- main col */
.bo-main{ flex:1; min-width:0; display:flex; flex-direction:column; }
.botop{
  height:var(--topbar-h); flex:none;
  background:var(--app-card); border-bottom:1px solid var(--app-line);
  display:flex; align-items:center; gap:10px; padding:0 16px;
  position:sticky; top:0; z-index:1020;
}
.botop .crumb{ font-size:var(--text-base); color:var(--muted); font-weight:500;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Named "‹ Parent" breadcrumb at the top of a detail page — always returns to
   the parent list (restoring its last filter), the way QuickBooks does. */
.backlink{ display:inline-flex; align-items:center; gap:5px; font-size:var(--text-base);
  font-weight:600; color:var(--brand,#1B5739); text-decoration:none; margin:0 0 12px; }
.backlink:hover{ text-decoration:underline; }
.backlink .bi{ font-size:var(--text-sm); }
@media print { .backlink{ display:none !important; } }
.botop .right{ margin-left:auto; display:flex; align-items:center; gap:9px; }
.bo-main > main{ padding:1rem; }


/* Currency / language segmented controls in either chrome bar */
.ts-seg{ display:flex; border:1px solid var(--app-line); border-radius:8px; overflow:hidden; }
.ts-seg button{ padding:5px 10px; font-size:var(--text-sm); font-weight:500; color:var(--muted);
  background:none; border:0; cursor:pointer; }
.ts-seg button.on{ background:var(--green); color:#fff; font-weight:600; }

kbd{
  background:var(--app-bg); border:1px solid var(--app-line); border-radius:5px;
  color:var(--app-ink); font-size:.78em; padding:1px 6px;
}

/* Toasts follow the semantic tokens */
.text-bg-success{ background-color:var(--green) !important; }
.text-bg-danger{ background-color:var(--debt) !important; }

/* ---- license renewal banner ---- */
.lic-banner{
  display:flex; align-items:center; gap:10px;
  padding:8px 16px; font-size:var(--text-base); font-weight:500;
  position:sticky; top:0; z-index:1040;
}
.lic-banner i{ font-size:var(--text-xl); }
.lic-banner a{ margin-left:auto; font-weight:600; white-space:nowrap; text-decoration:none; }
.lic-banner.lic-grace{ background:var(--gold-soft); color:var(--gold-2); border-bottom:1px solid var(--gold-line); }
.lic-banner.lic-grace a{ color:var(--gold-2); }
.lic-banner.lic-expired, .lic-banner.lic-tampered, .lic-banner.lic-invalid, .lic-banner.lic-moved{
  background:var(--debt-soft); color:var(--debt); border-bottom:1px solid var(--debt-line); }
.lic-banner.lic-expired a, .lic-banner.lic-tampered a, .lic-banner.lic-invalid a, .lic-banner.lic-moved a{ color:var(--debt); }
.lic-banner-x{ margin-left:auto; background:none; border:0; cursor:pointer; color:inherit;
  font-size:20px; line-height:1; padding:0 2px; opacity:.65; }
.lic-banner-x:hover{ opacity:1; }
/* When the admin renew link is present it already has margin-left:auto, so the
   close button just sits after it instead of fighting for the free space. */
.lic-banner a + .lic-banner-x{ margin-left:8px; }

/* ---- license status page ---- */
.lic-badge{ display:inline-flex; align-items:center; gap:6px; padding:3px 12px;
  border-radius:999px; font-size:var(--text-base); font-weight:600; }
.lic-badge.s-valid{ background:var(--green-soft); color:var(--green); border:1px solid var(--green-line); }
.lic-badge.s-grace{ background:var(--gold-soft); color:var(--gold-2); border:1px solid var(--gold-line); }
.lic-badge.s-expired, .lic-badge.s-tampered, .lic-badge.s-invalid, .lic-badge.s-moved{ background:var(--debt-soft); color:var(--debt); border:1px solid var(--debt-line); }
.lic-badge.s-unlicensed{ background:var(--app-hover); color:var(--muted); border:1px solid var(--app-line); }
.lic-facts{ display:grid; grid-template-columns:auto 1fr; gap:6px 18px; font-size:var(--text-base); margin:4px 0; }
.lic-facts dt{ color:var(--muted); }
.lic-facts dd{ margin:0; font-weight:600; font-variant-numeric:tabular-nums; }

@media print{
  .sidebar, .botop, .topstrip, .lic-banner{ display:none !important; }
  body{ background:#fff; }
}

/* ---------------- top bar: global search + account menu ---------------- */
.botop{ gap:14px; }
.topsearch{ position:relative; flex:1; max-width:520px; display:flex; align-items:center; }
.topsearch > .bi{ position:absolute; left:12px; color:var(--muted); font-size:var(--text-base); pointer-events:none; }
.topsearch input{ width:100%; padding:8px 12px 8px 34px; border:1px solid var(--app-line); border-radius:10px; background:#fff; font-size:var(--text-base); color:var(--app-ink); }
.topsearch input:focus{ outline:none; border-color:var(--green); box-shadow:0 0 0 3px rgba(27,87,57,.12); }
.ts-pop{ position:absolute; top:calc(100% + 6px); left:0; right:0; background:#fff; border:1px solid var(--app-line); border-radius:12px; box-shadow:var(--shadow); z-index:1100; max-height:60vh; overflow:hidden auto; }
.ts-item{ display:flex; flex-direction:column; gap:1px; padding:9px 13px; text-decoration:none; color:var(--app-ink); border-bottom:1px solid var(--app-line-soft); }
.ts-item:last-child{ border-bottom:0; }
.ts-item:hover{ background:var(--app-hover); }
.ts-nm{ font-weight:600; font-size:var(--text-base); }
.ts-meta{ font-size:var(--text-xs); color:var(--muted); font-family:'IBM Plex Mono',monospace; }
.ts-empty{ padding:12px 13px; color:var(--muted); font-size:var(--text-base); font-style:italic; }
.ts-newsale .bi{ font-size:var(--text-base); }
.acct{ position:relative; }
.acct-btn{ display:flex; align-items:center; gap:8px; background:none; border:1px solid transparent; border-radius:10px; padding:5px 9px; cursor:pointer; color:var(--app-ink); }
.acct-btn:hover{ background:var(--app-hover); }
.acct-btn .av{ width:28px; height:28px; border-radius:50%; background:var(--green); color:#fff; display:flex; align-items:center; justify-content:center; font-size:var(--text-sm); font-weight:600; flex:none; }
.acct-nm{ font-size:var(--text-base); font-weight:500; }
.acct-store{ font-size:var(--text-sm); font-weight:600; color:var(--green); background:var(--app-hover); border-radius:999px; padding:2px 9px; display:inline-flex; align-items:center; gap:4px; }
.acct-store .bi{ font-size:var(--text-xs); }
.acct-btn > .bi{ font-size:var(--text-xs); color:var(--muted); }
.acct-pop{ position:absolute; top:calc(100% + 6px); right:0; min-width:222px; background:#fff; border:1px solid var(--app-line); border-radius:12px; box-shadow:var(--shadow); z-index:1100; padding:6px; }
.acct-hdr{ padding:9px 11px 10px; border-bottom:1px solid var(--app-line-soft); margin-bottom:5px; }
.acct-shop{ font-weight:600; font-size:var(--text-base); color:var(--app-ink); }
.acct-u{ font-size:var(--text-xs); color:var(--muted); }
.acct-pop a, .acct-pop button{ display:flex; align-items:center; gap:9px; width:100%; text-align:left; padding:9px 11px; border-radius:8px; border:0; background:none; color:var(--app-ink); font-size:var(--text-base); text-decoration:none; cursor:pointer; }
.acct-pop a:hover, .acct-pop button:hover{ background:var(--app-hover); }
.acct-pop .bi{ font-size:var(--text-base); color:var(--muted); width:16px; text-align:center; }
/* Phone-only currency row (d-md-none in base.html): the bar's toggle moves
   here below 768px. Undo the full-width menu-row styling that
   ".acct-pop button" would otherwise give the two segment buttons. */
.acct-ccy{ display:flex; align-items:center; justify-content:space-between; gap:9px;
  padding:6px 11px; font-size:var(--text-base); color:var(--app-ink); }
.acct-ccy > span{ display:flex; align-items:center; gap:9px; }
.acct-ccy .ts-seg button{ display:block; width:auto; padding:7px 12px; border-radius:0;
  font-size:var(--text-sm); color:var(--muted); }
.acct-ccy .ts-seg button.on{ background:var(--green); color:#fff; }

/* ======================================================= MOBILE SHELL
   Phone breakpoint (≤767.98px = Bootstrap md boundary): the sidebar
   becomes an off-canvas drawer behind a hamburger in the top bar.
   Everything in this section is inert at ≥768px — desktop unchanged. */
.bo-hamburger{
  display:none; align-items:center; justify-content:center;
  width:38px; height:38px; flex:none;
  background:none; border:0; border-radius:9px;
  color:var(--app-ink); font-size:22px; cursor:pointer;
}
.bo-hamburger:hover{ background:var(--app-hover); }
.bo-backdrop{ position:fixed; inset:0; background:rgba(19,33,27,.45); z-index:1045; }
@media (min-width: 768px){ .bo-backdrop{ display:none !important; } }

@media (max-width: 767.98px){
  .bo-hamburger{ display:flex; }
  .sidebar{
    position:fixed; left:0; top:0; bottom:0; height:100dvh;
    width:min(84vw, 300px);
    transform:translateX(-100%);
    transition:transform .18s ease;
    z-index:1050;
  }
  .sidebar.open{ transform:translateX(0); box-shadow:0 0 40px rgba(0,0,0,.35); }
  /* The desktop icon-rail state is meaningless in a drawer: neutralize it
     (JS also strips .rail on phones — this is the CSS backstop). */
  .sidebar.rail{ width:min(84vw, 300px); }
  .sidebar.rail .wm, .sidebar.rail .sb-glabel, .sidebar.rail .sb-item .lbl,
  .sidebar.rail .sb-user .uw{ display:revert; }
  .sidebar.rail .sb-item{ justify-content:flex-start; padding:7px 9px; }
  .sidebar.rail .sb-head{ flex-direction:row; padding-left:14px; }
  .sidebar.rail .sb-ghead{ pointer-events:auto; }
  .sidebar .sb-toggle{ display:none; }   /* rail toggle is desktop-only */
  .botop{ padding:0 10px; gap:8px; }
  .botop .crumb{ display:none; }         /* hamburger + search need the room */
  /* The right cluster ran 38px past a 375px screen (right edge 413px) and
     sat the search on its 48px padding floor. The name and store pill were
     already d-md-none; the chevron goes too (the avatar alone opens the
     menu), and the currency toggle moves into that menu (.acct-ccy). */
  .botop .right{ gap:6px; }
  .acct-btn{ padding:4px; gap:0; }
  .acct-btn > .bi-chevron-down{ display:none; }
  /* The search, not the page, absorbs any shortfall: a flex item's default
     min-width:auto floor is what pushed the cluster off-screen. */
  .topsearch{ min-width:0; }
}

/* ---------------------------------------------- MOBILE CONTENT (≤767.98px) */
@media (max-width: 767.98px){
  .bo-main > main{ padding:12px !important; }
  /* iOS zooms any focused input under 16px — hold the line at 16. */
  .form-control, .form-select{ font-size:var(--text-xl); }
  .topsearch input{ font-size:var(--text-xl); }
  /* Cards flush to the edge feel roomier on a 375px screen. */
  .card{ border-radius:10px; }
}
/* Touch targets on any coarse-pointer device (phone AND tablet). */
@media (pointer: coarse){
  .btn-sm{ padding:.45rem .7rem; }
  .sidebar .sb-item{ padding:10px 9px; }
  .acct-pop a, .acct-pop button{ padding:11px 11px; }
}

/* ------------------------------------------------------- POS ON TABLETS
   The POS grid (col-lg-7 / col-lg-5) already stacks below 992px: portrait
   tablets get item entry above payment. These rules only make the stacked
   layout comfortable to touch. Phones are served by /m/, not /pos. */
@media (pointer: coarse) and (max-width: 1199.98px){
  .topstrip .btn, .topstrip .ts-seg button{ min-height:38px; }
  /* Cart quantity/price/discount inputs need finger-sized hit areas. */
  main table.table input.form-control-sm,
  main table.table .btn-sm{ min-height:40px; font-size:var(--text-lg); }
  .pos-remain{ padding:12px 14px; font-size:var(--text-xl); }
  .pos-confirm{ padding:12px; font-size:var(--text-base); }
  .pos-confirm input{ width:20px; height:20px; }
}

/* Sidebar logo is now a link home (click Pultrack → dashboard). */
.sb-head .sb-brand{ display:flex; align-items:center; gap:8px; text-decoration:none; color:inherit; flex:1; min-width:0; }
.sb-head .sb-brand .wm{ white-space:nowrap; overflow:hidden; }

/* ============ Visual reports (management / operational dashboards) ======= */
.viz-tabs{ gap:6px; border-bottom:1px solid var(--app-line); padding-bottom:10px; }
.viz-tabs .nav-link{ color:var(--muted); font-weight:600; border-radius:8px;
  padding:7px 16px; background:transparent; }
.viz-tabs .nav-link:hover{ background:var(--app-hover); color:var(--app-ink); }
.viz-tabs .nav-link.active{ background:var(--green); color:#fff; }

/* KPI tiles — quiet card, big number, one supporting line. */
.viz-kpi{ border:1px solid var(--app-line); box-shadow:none; }
.viz-kpi .card-body{ padding:14px 16px; }
.viz-kpi-label{ font-size:var(--text-xs); color:var(--muted); font-weight:600;
  text-transform:uppercase; letter-spacing:.03em; }
.viz-kpi-value{ font-size:26px; font-weight:700; color:var(--app-ink);
  line-height:1.15; margin-top:3px; white-space:nowrap; overflow:hidden; }
.viz-kpi-ccy{ font-size:var(--text-base); font-weight:600; color:var(--muted); }
.viz-kpi-sub{ font-size:var(--text-xs); color:var(--muted); margin-top:2px;
  min-height:1em; }
.viz-kpi-neg .viz-kpi-value{ color:var(--debt); }

/* Chart cards — a titled canvas with a fixed plot band so the x-axis never
   gets a nested scrollbar (see dataviz anti-patterns). */
.viz-chart-card{ border:1px solid var(--app-line); box-shadow:none; }
.viz-chart-card .card-body{ padding:14px 16px 10px; }
.viz-chart-title{ font-size:var(--text-sm); font-weight:600; color:var(--app-ink);
  margin-bottom:10px; }
.viz-canvas-wrap{ position:relative; height:260px; }
.viz-canvas-wrap canvas{ max-width:100%; }


/* Guest (non-auth) pages: the lockup above the centred card. */
.guest-brand{ display:flex; align-items:center; justify-content:center; gap:10px;
  margin-bottom:1.5rem; text-decoration:none; color:var(--app-ink); }
.guest-brand .tile{ width:34px; height:34px; border-radius:9px; }
.guest-brand .wm{ font-weight:600; font-size:20px; letter-spacing:-.022em; }

/* ============================================================ BRAND COMPLETION
   Bootstrap defaults Make's pages still leaned on, pulled onto the brand
   palette (Brand Book §04–§05): warm muted grey instead of Bootstrap's cool
   #6C757D, Plex headings at the book's weight/tracking, slate for purely
   informational notices, and quiet secondary buttons on the warm line. */
:root{ --bs-secondary-color:#6E685B; --bs-secondary-rgb:110,104,91; }
.text-secondary, .text-muted, .form-text{ color:var(--muted) !important; }
h1, .h1, h2, .h2, h3, .h3{ font-weight:700; letter-spacing:-.018em; color:var(--app-ink); }
h4, .h4, h5, .h5, h6, .h6{ font-weight:600; letter-spacing:-.01em; color:var(--app-ink); }
.alert-info{ --bs-alert-bg:var(--slate-soft); --bs-alert-border-color:var(--slate-line); --bs-alert-color:var(--slate); }
.alert{ border-radius:10px; }
.btn-outline-secondary{
  --bs-btn-color:var(--app-ink); --bs-btn-border-color:var(--app-line);
  --bs-btn-hover-bg:var(--app-hover); --bs-btn-hover-color:var(--app-ink); --bs-btn-hover-border-color:var(--line);
  --bs-btn-active-bg:var(--app-line-soft); --bs-btn-active-color:var(--app-ink); --bs-btn-active-border-color:var(--line);
}
.table-striped > tbody > tr:nth-of-type(odd) > *{ --bs-table-bg-type:var(--app-hover); }
.list-group{ --bs-list-group-bg:var(--app-card); --bs-list-group-border-color:var(--app-line); border-radius:12px; overflow:hidden; box-shadow:var(--shadow); }
.card-footer{ background:var(--app-card) !important; border-top:1px solid var(--app-line-soft); }
.form-check-input:checked{ background-color:var(--green); border-color:var(--green); }
.form-check-input:focus{ border-color:var(--green); box-shadow:0 0 0 .25rem rgba(27,87,57,.15); }
.bo-main .display-6{ font-weight:700; letter-spacing:-.02em; }
/* Codes (item / warehouse / account / supplier) in Plex Mono — Brand Book §05. */
.table td.mono, .table .mono{ font-family:'IBM Plex Mono',monospace; font-size:.92em; }

/* Bootstrap state colours that the POS token block never reached: pressed,
   disabled and focus shades still resolved to Bootstrap blue/red/grey. */
:root{
  --bs-secondary-bg:#ECE7DC; --bs-tertiary-bg:#F6F4EE;
  --bs-emphasis-color:#13211B; --bs-emphasis-color-rgb:19,33,27;
  --bs-body-color-rgb:26,36,31;
}
.btn-primary, .btn-success, .btn-outline-primary, .btn-outline-success{ --bs-btn-focus-shadow-rgb:27,87,57; }
.btn-outline-primary{ --bs-btn-disabled-color:var(--green); --bs-btn-disabled-border-color:var(--green-line); --bs-btn-active-color:#fff; }
.btn-outline-secondary{ --bs-btn-disabled-color:var(--muted); --bs-btn-disabled-border-color:var(--app-line); --bs-btn-focus-shadow-rgb:110,104,91; }
.btn-danger{ --bs-btn-active-bg:#7d241c; --bs-btn-active-border-color:#7d241c;
  --bs-btn-disabled-bg:var(--debt); --bs-btn-disabled-border-color:var(--debt); --bs-btn-focus-shadow-rgb:153,44,34; }
.btn-outline-danger{ --bs-btn-active-bg:var(--debt); --bs-btn-active-border-color:var(--debt); --bs-btn-active-color:#fff;
  --bs-btn-disabled-color:var(--debt); --bs-btn-disabled-border-color:var(--debt-line); --bs-btn-focus-shadow-rgb:153,44,34; }
.btn-link{ --bs-btn-color:var(--green); --bs-btn-hover-color:var(--green-2); --bs-btn-active-color:var(--green-2);
  --bs-btn-disabled-color:var(--muted); --bs-btn-focus-shadow-rgb:27,87,57; font-weight:500; }
.table{ --bs-table-color:var(--app-ink); }
.viz-kpi-value, .bo-main dd, .money-sec{ font-variant-numeric:tabular-nums; }
.viz-kpi-sub.money-sec{ font-size:var(--text-sm); color:var(--money-2); }

/* ------------------------------------------------ contrast (WCAG 2.2 AA)
   Sidebar group labels were #7e957f on green-2 (3.92:1, AA needs 4.5:1 at this
   size) — now #94ab95 (5.15:1). Focus used a 25%-alpha halo (≈1.5:1, needs 3:1)
   so keyboard users could not see where they were: every control now shows a
   solid 2px ring — green on light surfaces (7–8.5:1), paper on the dark sidebar
   (11:1). Same values as Pultrack POS; change both together. */
a:focus-visible, button:focus-visible, summary:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--green); outline-offset:2px; }
.sidebar a:focus-visible, .sidebar button:focus-visible{ outline-color:var(--paper); outline-offset:-2px; }
.form-control:focus, .form-select:focus, .form-check-input:focus{
  box-shadow:0 0 0 1px var(--green), 0 0 0 .25rem rgba(27,87,57,.15); }
/* Segmented controls clip their children (overflow:hidden) — draw the ring inside. */
.ts-seg button:focus-visible, .acct-pop a:focus-visible, .acct-pop button:focus-visible{ outline-offset:-2px; }

/* Purchase lines on a phone: the item picker kept the narrowest column and
   clipped every name to two letters; give it room and let the table scroll. */
select.line-item{ min-width:12rem; }
