:root{
  --ink:#1b1f1d; --paper:#eeede6; --surface:#f8f7f2; --surface-2:#eeeee5;
  --steel:#6b7370; --line:#d9d7cb;
  --amber:#c06a13; --amber-ink:#7a4610; --amber-soft:#f8e3c4;
  --teal:#137a70; --teal-soft:#d3efe9;
  --good:#2f9a5b; --good-soft:#dbf0e2;
  --warn:#c06a13; --warn-soft:#f8e3c4;
  --crit:#d43f3f; --crit-soft:#fadbd8;
  --info:#2f72c4; --info-soft:#dae8f9;
  --violet:#7c4fd6; --violet-soft:#ece3fb;
  /* Brand identity (Nityam-Flow) — a fixed teal-to-terracotta gradient used for the sidebar and
     logo mark. Kept constant across light/dark theme (a brand mark shouldn't flip with the OS
     setting), only the content surfaces below respond to prefers-color-scheme. */
  --brand-1:#0d4d47; --brand-2:#c2570f; --brand-accent:#ffc857;
  --font-display:'Archivo',system-ui,sans-serif; --font-body:'IBM Plex Sans',system-ui,sans-serif; --font-mono:'IBM Plex Mono',monospace;
  --shadow: 0 1px 2px rgba(27,31,29,.06), 0 6px 20px rgba(27,31,29,.08);
  color-scheme: light;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --ink:#ece9e2; --paper:#141715; --surface:#1c201d; --surface-2:#232722;
    --steel:#9aa39d; --line:#333833;
    --amber:#e2924a; --amber-ink:#f2b57d; --amber-soft:#3a2a17;
    --teal:#5fb3ac; --teal-soft:#173330;
    --good:#63b787; --good-soft:#173327;
    --warn:#e2924a; --warn-soft:#3a2a17;
    --crit:#e0685f; --crit-soft:#3a1e1c;
    --info:#6fa0d6; --info-soft:#182a3a;
    --violet:#a084d0; --violet-soft:#2a2138;
    --shadow: 0 1px 2px rgba(0,0,0,.3), 0 6px 24px rgba(0,0,0,.35);
    color-scheme: dark;
  }
}
:root[data-theme="dark"]{
  --ink:#ece9e2; --paper:#141715; --surface:#1c201d; --surface-2:#232722;
  --steel:#9aa39d; --line:#333833;
  --amber:#e2924a; --amber-ink:#f2b57d; --amber-soft:#3a2a17;
  --teal:#5fb3ac; --teal-soft:#173330;
  --good:#63b787; --good-soft:#173327;
  --warn:#e2924a; --warn-soft:#3a2a17;
  --crit:#e0685f; --crit-soft:#3a1e1c;
  --info:#6fa0d6; --info-soft:#182a3a;
  --violet:#a084d0; --violet-soft:#2a2138;
  color-scheme: dark;
}
*{box-sizing:border-box;}
body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--font-body);font-size:14px;line-height:1.5;}
h1,h2,h3{font-family:var(--font-display);text-wrap:balance;}
.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}

/* ---------- login ---------- */
.center-screen{min-height:100vh;display:flex;align-items:center;justify-content:center;background:
  radial-gradient(circle at 15% 15%, color-mix(in srgb, var(--brand-1) 14%, transparent), transparent 45%),
  radial-gradient(circle at 85% 85%, color-mix(in srgb, var(--brand-2) 14%, transparent), transparent 45%),
  var(--paper);}
.login-card{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:32px;width:340px;box-shadow:var(--shadow);}
.login-card .brand{padding:0;margin:0 0 18px;border:none;}
.login-card .brand-name{color:var(--ink);}
.login-card .brand-name span{color:var(--brand-2);}
.login-card .brand-logo-fallback{background:linear-gradient(155deg, var(--brand-1), var(--brand-2));border:none;color:#fff;}
.login-card p{color:var(--steel);font-size:13px;margin:0 0 20px;}
.field{margin-bottom:14px;}
.field label{display:block;font-size:12px;font-weight:600;color:var(--steel);margin-bottom:4px;}
/* :not([type=checkbox]):not([type=radio]) — this generic full-width/boxed styling is meant for
   text-like controls; without the exclusion it also caught checkbox/radio inputs nested in a
   .field, stretching and boxing them like a text field instead of leaving them as a normal small
   control (this broke the "Log this as a complaint/issue" checkbox on the New Call form). */
.field input:not([type=checkbox]):not([type=radio]),.field select,.field textarea,.map-row select,.import-preview select{width:100%;padding:9px 10px;border:1px solid var(--line);border-radius:6px;font-size:14px;background:var(--surface);color:var(--ink);font-family:inherit;}
.field label.checkbox-label{display:flex;align-items:center;gap:9px;font-size:13.5px;font-weight:400;color:var(--ink);}
textarea{resize:vertical;}
.btn{border:none;border-radius:6px;padding:9px 15px;font-weight:600;cursor:pointer;font-size:13.5px;background:var(--surface-2);color:var(--ink);border:1px solid var(--line);transition:transform .12s ease,box-shadow .12s ease,border-color .12s ease,filter .12s ease;}
.btn:hover{border-color:var(--amber);transform:translateY(-1px);box-shadow:0 3px 8px rgba(27,31,29,.10);}
.btn:active{transform:translateY(0);box-shadow:none;}
.btn-primary{background:linear-gradient(135deg,var(--brand-1),var(--brand-2));color:#fff;border-color:transparent;}
.btn-primary:hover{filter:brightness(1.08);box-shadow:0 4px 12px rgba(194,87,15,.28);}
.btn-ghost{background:none;border:1px solid var(--line);}
.btn-danger{background:none;border:1px solid var(--crit);color:var(--crit);}
.login-card .btn-primary{width:100%;}
.error{color:var(--crit);font-size:12.5px;margin-top:8px;}

/* ---------- shell: left sidebar + content ---------- */
.app-shell{display:flex;align-items:stretch;min-height:100vh;}
.sidebar{
  width:236px;flex:none;display:flex;flex-direction:column;gap:4px;
  background:linear-gradient(165deg, var(--brand-1) 0%, var(--brand-2) 130%);
  color:#fff;padding:20px 14px;position:sticky;top:0;align-self:flex-start;height:100vh;overflow-y:auto;
}
.shell-main{flex:1;min-width:0;display:flex;flex-direction:column;}
.topbar{display:flex;align-items:center;justify-content:flex-end;gap:14px;padding:14px 22px;border-bottom:1px solid var(--line);background:var(--surface);}
main{padding:22px;max-width:1200px;width:100%;margin:0 auto;}

/* .sidebar-top wraps .brand + the mobile hamburger toggle together. display:contents makes the
   wrapper invisible to layout above the 880px breakpoint below — .brand behaves exactly as if it
   were still a direct child of .sidebar, so desktop is completely unaffected by this wrapper
   existing at all. Only inside the mobile media query does it become a real flex row. */
.sidebar-top{display:contents;}
.mobile-nav-toggle{display:none;}
.mobile-nav-toggle .nav-icon{width:20px;height:20px;display:block;}
.mobile-nav-toggle .nav-icon svg{width:100%;height:100%;display:block;}
.brand{display:flex;align-items:center;gap:11px;padding:2px 6px 18px;margin-bottom:10px;border-bottom:1px solid rgba(255,255,255,.2);}
.brand-logo-wrap{position:relative;flex:none;}
.brand-logo-img{width:100%;height:100%;border-radius:10px;object-fit:cover;display:block;background:rgba(255,255,255,.12);}
.brand-logo-fallback{position:absolute;inset:0;border-radius:10px;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.3);align-items:center;justify-content:center;font-family:var(--font-display);font-weight:800;font-size:15px;color:#fff;letter-spacing:-.5px;}
.brand-name{font-family:var(--font-display);font-size:17px;font-weight:800;line-height:1.2;color:#fff;letter-spacing:-.2px;}
.brand-name span{display:block;color:var(--brand-accent);}

.nav{display:flex;flex-direction:column;gap:2px;flex:1;}
.nav-btn{
  display:flex;align-items:center;gap:11px;text-align:left;background:none;border:none;
  padding:10px 11px;border-radius:9px;font-size:13px;font-weight:600;color:rgba(255,255,255,.8);
  cursor:pointer;font-family:inherit;transition:background .12s,color .12s;
}
.nav-btn .nav-icon{flex:none;width:19px;height:19px;color:inherit;opacity:.9;}
.nav-btn .nav-icon svg{width:100%;height:100%;display:block;}
.nav-btn:hover{background:rgba(255,255,255,.12);color:#fff;}
.nav-btn.active{background:#fff;color:var(--brand-1);box-shadow:0 3px 10px rgba(0,0,0,.2);}
.nav-btn.active .nav-icon{color:var(--brand-2);opacity:1;}

.sidebar-footer{margin-top:auto;padding-top:14px;border-top:1px solid rgba(255,255,255,.2);display:flex;flex-direction:column;gap:8px;}
.sidebar-user{display:flex;flex-direction:column;gap:1px;padding:2px 4px 6px;}
.sidebar-user-name{font-size:12.5px;font-weight:700;color:#fff;}
.sidebar-user-role{font-size:11px;color:rgba(255,255,255,.72);}
.btn-ghost-light{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.28);color:#fff;border-radius:7px;padding:7px 10px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit;text-align:left;}
.btn-ghost-light:hover{background:rgba(255,255,255,.2);}

/* Mobile nav drawer (Sept 2026) — below 880px the sidebar collapses to a slim sticky top bar
   (brand + hamburger button only); tapping the button adds .nav-open (see mobileNavOpen /
   renderShell in app.js), which expands the full nav list and the account/sign-out footer as one
   scrollable panel beneath the bar. Everything here lives inside this single media query, so none
   of it can ever apply above 880px — the desktop layout above stays completely untouched. */
@media (max-width:880px){
  .app-shell{flex-direction:column;}
  .sidebar{
    width:100%;height:auto;max-height:100vh;position:sticky;top:0;z-index:30;align-self:auto;
    flex-direction:column;gap:0;padding:12px 16px;overflow:hidden;
    box-shadow:0 2px 14px rgba(0,0,0,.18);
  }
  .sidebar.nav-open{overflow-y:auto;}
  .sidebar-top{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;}
  .brand{border-bottom:none;margin-bottom:0;padding:2px 0;}
  .mobile-nav-toggle{
    display:flex;align-items:center;justify-content:center;flex:none;
    width:40px;height:40px;border-radius:9px;
    background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.3);color:#fff;cursor:pointer;
    transition:background .12s;
  }
  .mobile-nav-toggle:hover{background:rgba(255,255,255,.22);}
  .nav,.sidebar-footer{display:none;}
  .sidebar.nav-open .nav{display:flex;flex-direction:column;flex:none;gap:4px;width:100%;margin-top:14px;}
  .sidebar.nav-open .sidebar-footer{
    display:flex;flex-direction:column;gap:8px;width:100%;margin-top:16px;margin-left:0;
    padding-top:14px;border-top:1px solid rgba(255,255,255,.2);
  }
  /* Bigger tap targets for touch once the list is actually reachable without a long scroll. */
  .nav-btn{padding:13px 12px;font-size:14.5px;}
}

.cell-sub{color:var(--steel);font-size:11.5px;margin-top:2px;}

/* ---------- dashboard ---------- */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:12px;margin-bottom:20px;}
.tile{
  background:var(--surface);border:1px solid var(--line);border-left:4px solid var(--line);border-radius:9px;
  padding:14px 16px 14px 15px;box-shadow:var(--shadow);position:relative;overflow:hidden;
  transition:transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.tile::after{content:"";position:absolute;top:0;right:0;width:64px;height:64px;border-radius:50%;
  background:var(--tile-tint, transparent);opacity:.5;transform:translate(30%,-40%);pointer-events:none;}
.tile .stat-num{font-size:26px;font-weight:800;line-height:1;font-family:var(--font-display);position:relative;}
.tile .stat-lbl{font-size:11.5px;color:var(--steel);margin-top:6px;letter-spacing:.2px;position:relative;}
.tile-clickable{cursor:pointer;}
.tile-clickable:hover{transform:translateY(-3px);box-shadow:0 6px 18px rgba(27,31,29,.14),0 14px 32px rgba(27,31,29,.10);}
/* Semantic accent per tile — same tokens as the pill/status-dot colors elsewhere, so a tile's
   color already tells you roughly what kind of number it is before you read the label. */
.tile-info{border-left-color:var(--info);--tile-tint:var(--info-soft);} .tile-info .stat-num{color:var(--info);}
.tile-amber{border-left-color:var(--amber);--tile-tint:var(--amber-soft);} .tile-amber .stat-num{color:var(--amber-ink);}
.tile-violet{border-left-color:var(--violet);--tile-tint:var(--violet-soft);} .tile-violet .stat-num{color:var(--violet);}
.tile-teal{border-left-color:var(--teal);--tile-tint:var(--teal-soft);} .tile-teal .stat-num{color:var(--teal);}
.tile-good{border-left-color:var(--good);--tile-tint:var(--good-soft);} .tile-good .stat-num{color:var(--good);}
.tile-crit{border-left-color:var(--crit);--tile-tint:var(--crit-soft);} .tile-crit .stat-num{color:var(--crit);}
.dist-row{display:grid;grid-template-columns:150px 1fr 34px;align-items:center;gap:10px;padding:6px 0;font-size:12.5px;}
.dist-bar{height:9px;border-radius:5px;background:var(--surface-2);overflow:hidden;}
.dist-bar > i{display:block;height:100%;border-radius:5px;transition:width .5s ease;box-shadow:0 0 0 1px rgba(255,255,255,.15) inset;}
.dist-count{text-align:right;color:var(--steel);font-family:var(--font-mono);}
.dash-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px;}
@media (max-width:820px){ .dash-grid{grid-template-columns:1fr;} }

/* ---------- panels / tables ---------- */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow);margin-bottom:18px;overflow:hidden;position:relative;}
.panel::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--teal),var(--brand-2));}
.panel-head{padding:16px 18px 14px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;}
.panel-head h3{margin:0;font-size:14.5px;font-weight:700;}
.panel-body{padding:16px 18px;}
.panel-form{max-width:520px;display:flex;flex-direction:column;}
.panel-form .btn{margin-right:8px;margin-top:4px;}

/* One row in the Brand Header/Footer per-line editor (brandLinesEditor in app.js) — a text line
   or an image line, each with its own font/size/bold (text) or image/width (image) plus shared
   align/spacing/reorder/remove controls. Deliberately lighter than .panel (no gradient top bar) —
   these are nested rows inside an already-boxed form, not standalone cards. */
.brand-line-row{background:var(--surface);border:1px solid var(--line);border-radius:8px;}

/* Orders for Production form — a wider, sectioned layout (Customer / Schedule / Items), each
   section visually grouped so a long form with several items still reads as organised rather
   than one continuous scroll of identical-looking fields. */
.po-form.panel-form{max-width:none;}
.po-form-section{margin-bottom:24px;padding-bottom:20px;border-bottom:1px dashed var(--line);}
.po-form-section:last-of-type{border-bottom:none;padding-bottom:0;}
.po-form-section h4{margin:0 0 14px;font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--brand-2);}
.po-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:0 16px;}
.po-grid-3{display:grid;grid-template-columns:2fr 1.2fr 100px;gap:0 14px;}
@media (max-width:640px){.po-grid-2,.po-grid-3{grid-template-columns:1fr;}}
.production-items{display:flex;flex-direction:column;gap:12px;margin-bottom:12px;}
.production-item-row{padding:14px 16px;}
.production-item-row-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;}
.production-item-badge{display:inline-block;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#fff;background:linear-gradient(135deg,var(--brand-1),var(--brand-2));padding:3px 10px;border-radius:20px;}

.table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:9px;background:var(--surface);}
table{width:100%;border-collapse:collapse;font-size:13px;min-width:640px;}
thead th{text-align:left;font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--teal);font-weight:700;padding:10px 12px;border-bottom:2px solid var(--teal-soft);background:var(--teal-soft);white-space:nowrap;}
tbody td{padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:top;}
tbody tr{transition:background .1s ease;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:var(--amber-soft);}
.empty-row td{text-align:center;color:var(--steel);padding:28px;}
.order-no{font-family:var(--font-mono);font-weight:600;}

/* ---------- pills ---------- */
.pill{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap;}
.pill::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;}
.pill-received{background:var(--info-soft);color:var(--info);}
.pill-production{background:var(--warn-soft);color:var(--amber-ink);}
.pill-dispatched{background:var(--violet-soft);color:var(--violet);}
.pill-transit{background:var(--violet-soft);color:var(--violet);}
.pill-delivered{background:var(--good-soft);color:var(--good);}
.pill-installation_planned{background:var(--teal-soft);color:var(--teal);}
.pill-installation_done{background:var(--teal-soft);color:var(--teal);}
.pill-waiting_review{background:var(--surface-2);color:var(--steel);}
.pill-review_done{background:var(--good-soft);color:var(--good);}
.pill-open{background:var(--crit-soft);color:var(--crit);}
.pill-closed{background:var(--good-soft);color:var(--good);}
.pill-cancelled{background:var(--crit-soft);color:var(--crit);}
/* Set of N Pcs — an informational callout (not a status), reusing the same pill shape but its own
   colour so it never reads as a stage/status pill. See itemNameNode() in app.js. */
.pill-setsize{background:var(--info-soft);color:var(--info);}
.pill-setsize::before{display:none;}

/* ---------- toolbar / status tabs ---------- */
.toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px;}
.toolbar input[type=text],.toolbar select{padding:7px 10px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);font-size:13px;}
.toolbar input[type=text]{min-width:200px;flex:1;}
.spacer{flex:1;}
.status-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px;}
.status-tab{display:inline-flex;align-items:center;gap:7px;padding:7px 13px;border-radius:20px;border:1px solid var(--line);background:var(--surface);color:var(--ink);font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit;white-space:nowrap;transition:transform .12s ease,box-shadow .12s ease,border-color .12s ease;}
.status-tab:hover{border-color:var(--amber);transform:translateY(-1px);}
.status-tab.active{background:linear-gradient(135deg,var(--brand-1),var(--brand-2));color:var(--paper);border-color:transparent;box-shadow:0 3px 10px rgba(194,87,15,.25);}
.status-tab .tab-dot{width:7px;height:7px;border-radius:50%;flex:none;}
.status-tab .tab-count{background:var(--surface-2);color:var(--steel);border-radius:10px;padding:1px 7px;font-size:11px;font-family:var(--font-mono);}
.status-tab.active .tab-count{background:rgba(255,255,255,.2);color:var(--paper);}
.status-select{border:1px solid var(--line);border-radius:6px;padding:4px 6px;font-size:11.5px;background:var(--paper);color:var(--ink);font-family:inherit;}
/* Mattress Foam & Rebonded's own sub-tab rows, collapsed on mobile (Oct 2026, per the owner — "in
   mobile view can we collapse menu headers similar to main menu"). Desktop stays exactly as
   before: .status-tabs-toggle is hidden above the breakpoint, so .status-tabs-wrap is just a
   transparent extra wrapper around the same always-visible pill row. Below the breakpoint the
   toggle (styled like a single status-tab pill, showing the current tab's own label) replaces the
   row until tapped — same collapsed-by-default, tap-to-expand rhythm as the sidebar's own
   mobile-nav-toggle. See foamCollapsibleTabBar in app.js. */
.status-tabs-toggle{display:none;}
@media (max-width:760px){
  .status-tabs-wrap{margin-bottom:14px;}
  .status-tabs-wrap .status-tabs{display:none;margin-bottom:0;padding-top:8px;}
  .status-tabs-wrap.open .status-tabs{display:flex;}
  .status-tabs-toggle{
    display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;
    padding:8px 14px;border-radius:20px;border:1px solid var(--line);background:var(--surface);
    color:var(--ink);font-size:12.5px;font-weight:700;cursor:pointer;font-family:inherit;
  }
  .status-tabs-toggle-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .status-tabs-toggle .nav-icon{width:15px;height:15px;flex:none;display:block;color:var(--steel);}
}

/* ---------- Mattress Foam & Rebonded -> Foam Blocks ---------- */
.inline-combo select,.inline-combo input{width:100%;min-width:78px;padding:5px 6px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--ink);font-size:12.5px;font-family:inherit;}
.foam-quality-tree details{margin-bottom:8px;border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface);}
.foam-quality-tree details details{background:var(--surface-2);}
.foam-quality-tree summary{cursor:pointer;font-weight:700;font-size:13.5px;}
.foam-quality-tree summary:focus{outline:2px solid var(--amber);}

/* ---------- Mattress Foam & Rebonded -> Orders ---------- */
.foam-order-form.panel-form{max-width:none;}

/* ---------- Price List -> Admin edit form (Sept 2026) ---------- */
/* The manual row-by-row price editor (Quality/Length/Width/Thickness/Price/Pcs-per-Bag/actions)
   needs real width to show every column without a horizontal scroll — same override pattern as
   .foam-order-form/.po-form above, scoped to just this form so no other .panel-form is affected. */
.price-list-edit-form.panel-form{max-width:none;}
/* Quality holds the longest text of any column here (a full quality name) but table auto-layout
   otherwise sizes it no wider than Length/Width/Thickness, which are just 1-2 digit numbers —
   give it a floor so the name is never clipped, without touching the shared .inline-combo rule
   used by every other inline-combo field app-wide. */
.price-list-edit-table th:first-child,.price-list-edit-table td:first-child{min-width:170px;}

/* ---------- Mattress Price List -> Compact layout (Oct 2026, per the owner) ----------
   "it is shown in multiple pages... I prefer a few pages price list" — one block per Length x
   Width (see priceListCompactMattressTable in app.js) packs far more rows per page than the
   normal one-block-per-Quality layout, but only if the type itself is tight and each block is
   kept from splitting across a page break (a block cut in half is harder to read than the whole
   price list spanning one extra page). Default view — see mattressPriceListLayout. */
.price-list-compact-block{margin-bottom:10px;}
.price-list-compact-block table{font-size:11.5px;}
.price-list-compact-block th,.price-list-compact-block td{padding:4px 8px;white-space:nowrap;}
.price-list-compact-block th:nth-child(2),.price-list-compact-block td:nth-child(2){white-space:normal;min-width:130px;}
@media print{
  .price-list-compact-block{break-inside:avoid;page-break-inside:avoid;}
}
/* Mattress/Rebonded/Foam/Pillow order forms -> Item Details table: same problem as the price list
   edit table above — the Model/Quality name is the longest text in the row but table auto-layout
   gives it no more room than a 2-digit Length/Width column, so a long name (e.g. "3+1 EP +Foam")
   reads as clipped inside its narrow inline-combo box. Same fix, same narrow scope. */
/* Order item-entry tables (Oct 2026, per the owner: "Order form in computer should also all show
   all columns, nothing to scroll horizontally" — following the same request for mobile just
   above). Foam Orders alone runs to 15 columns (Quality/Density/Colour/Length/Width/Thickness/
   Bag/Pcs-per-Bag/Pcs/Total kg/Price per kg/Discount/Price after discount/Total/Remove), which
   never fit this table's normal app-wide sizing except on a very wide monitor — auto table
   layout was sizing every column off its own content (an uppercase header like "PRICE AFTER
   DISCOUNT" alone forces a wide column under the app's normal white-space:nowrap headers), and
   the total routinely ran past 1600px. table-layout:fixed switches to the opposite model: the
   table is always exactly 100% of its container (so it can never need a horizontal scrollbar down
   to a real laptop's usable width — this only applies at 701px+; the @media block right below
   this one already gives narrower/mobile screens their own stacked-card layout instead), and
   every column gets a fixed SHARE of that 100%, wrapping its own content — including the header
   text, which no longer forces width — to fit instead of forcing the column wider. First column
   (Model/Quality, the longest real text) and last (Remove) get a bit more/less than an even share;
   every other column splits a fixed 6% each, which comfortably covers Foam Orders' 13 middle
   columns (13x6%=78%) — a shorter table (e.g. Pillow's 5 middle columns) just leaves its fixed-
   layout columns scaled up to fill the same 100%, per how table-layout:fixed distributes width
   when declared column widths don't sum to the table's own width. Bag/Pcs/Discount still set
   their own inline pixel width in JS (bagPcsFieldsNode/discountFieldsNode in app.js) — inside a
   fixed-layout cell that's the min the browser tries to honor, not an override of the cell's own
   % width, so it no longer fights this. */
@media (min-width:701px){
  .order-items-table{table-layout:fixed;width:100%;font-size:12px;}
  .order-items-table th,.order-items-table td{padding:7px 3px;overflow-wrap:break-word;}
  .order-items-table thead th{white-space:normal;font-size:10px;line-height:1.25;vertical-align:bottom;}
  .order-items-table th:first-child,.order-items-table td:first-child{width:13%;min-width:0;}
  .order-items-table th:last-child,.order-items-table td:last-child{width:6%;}
  .order-items-table th:not(:first-child):not(:last-child),
  .order-items-table td:not(:first-child):not(:last-child){width:6%;}
  .order-items-table select,.order-items-table input{box-sizing:border-box;}
  .order-items-table .inline-combo select,.order-items-table .inline-combo input{min-width:0;width:100%;max-width:100%;font-size:11.5px;padding:4px 3px;}
  .order-items-table td input[type="number"],.order-items-table td select{width:100%;max-width:100%;min-width:0;font-size:11.5px;padding:4px 3px;}
  .order-items-table td .mono{font-size:11.5px;}
  .order-items-table td .discount-fields,.order-items-table td .colour-fields{max-width:100%;flex-wrap:wrap;}
  .order-items-table td .colour-fields .inline-combo{flex:1 1 auto;min-width:40px;}
  .order-items-table td .colour-fields .btn-chip{flex:none;width:20px;height:20px;}
  .order-items-table .btn-chip{padding:4px 2px;font-size:10.5px;white-space:normal;word-break:break-word;width:100%;box-sizing:border-box;}
}
/* Mattress Order Form redesign (Sept 2026 v4) — each section (Customer/Item/Payment/Transport/
   Notes) reuses the .po-form-section look already established for Production Orders (small
   uppercase brand-colour heading, dashed separator) so the form reads as clearly organised steps
   rather than one long scroll. .po-form-section-last drops the trailing separator explicitly
   (rather than relying on :last-of-type, which only matches when the section really is the last
   DIV in the form — here totals/submit trail after Notes, so that pseudo-class wouldn't fire). */
.po-form-section-last{border-bottom:none;padding-bottom:0;margin-bottom:8px;}
.order-form-panel{padding:24px 26px;}
.order-form-panel .po-form-section:first-child{margin-top:0;}

/* ---------- expected delivery cell ---------- */
.ed-cell{display:flex;flex-direction:column;gap:5px;min-width:158px;}
.ed-current{font-size:12.5px;font-weight:600;color:var(--ink);}
.ed-current.ed-unset{font-weight:500;color:var(--steel);}
.ed-current.ed-overdue{color:var(--crit);}
.ed-quick{display:flex;gap:5px;align-items:center;flex-wrap:wrap;}
.btn-chip{border:1px solid var(--line);background:var(--surface);color:var(--steel);font-size:11px;font-weight:600;padding:3px 8px;border-radius:12px;cursor:pointer;font-family:inherit;white-space:nowrap;}
.btn-chip:hover{border-color:var(--amber);color:var(--amber-ink);}
.ed-date-input{padding:3px 5px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);font-size:11px;width:126px;}
.row-actions{display:flex;gap:6px;}
.icon-btn{background:none;border:1px solid var(--line);border-radius:6px;padding:4px 8px;font-size:11.5px;cursor:pointer;color:var(--ink);font-family:inherit;}
.icon-btn:hover{background:var(--surface-2);}

/* ---------- modal ---------- */
#modal-root{position:fixed;inset:0;z-index:50;display:flex;align-items:flex-start;justify-content:center;background:rgba(0,0,0,.45);padding:40px 16px;overflow-y:auto;}
.modal{background:var(--surface);border-radius:10px;border:1px solid var(--line);box-shadow:var(--shadow);width:100%;max-width:560px;padding:22px 24px 24px;}
.modal h3{margin:0 0 4px;font-size:16.5px;}
.modal .modal-sub{color:var(--steel);font-size:12px;margin:0 0 18px;}
.modal-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px 14px;}
.modal-grid .full{grid-column:1/-1;}
.modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px;padding-top:16px;border-top:1px solid var(--line);}
.hist-list{list-style:none;margin:0;padding:0;font-size:12.5px;}
.hist-list li{display:flex;justify-content:space-between;gap:10px;padding:6px 0;border-bottom:1px dashed var(--line);}
.hist-list li:last-child{border-bottom:none;}

/* ---------- import ---------- */
.dropzone{border:1.5px dashed var(--line);border-radius:9px;padding:26px;text-align:center;color:var(--steel);font-size:13px;background:var(--paper);display:block;cursor:pointer;}
.map-row{display:grid;grid-template-columns:160px 1fr;align-items:center;gap:10px;padding:5px 0;}
.map-row > *{justify-self:start;}
.map-row > select{justify-self:stretch;}
.map-row span{font-size:12.5px;color:var(--steel);}
.import-preview{overflow-x:auto;border:1px solid var(--line);border-radius:8px;margin-top:14px;}
.import-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px;}
.import-card{display:flex;flex-direction:column;gap:4px;text-align:left;background:var(--surface);border:1px solid var(--line);border-radius:9px;padding:16px;cursor:pointer;box-shadow:var(--shadow);font-family:inherit;}
.import-card:hover{border-color:var(--amber);}
.import-card b{font-family:var(--font-display);font-size:14.5px;}
.import-card span{font-size:12px;color:var(--steel);}
.import-section-title{font-family:var(--font-display);font-size:15px;font-weight:800;margin:0 0 4px;}
.catalog-name-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 14px;list-style:none;margin:0;padding:0;max-height:280px;overflow-y:auto;}
.catalog-name-list li{font-size:13px;padding:5px 0;border-bottom:1px dashed var(--line);}
.confidence-tag{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:2px 8px;border-radius:9px;font-weight:700;}
.confidence-verified{background:var(--good-soft);color:var(--good);}
.confidence-best-effort{background:var(--warn-soft);color:var(--amber-ink);}
.banner{border-radius:8px;padding:10px 14px;font-size:12.5px;margin-bottom:16px;}
.banner-warn{background:var(--warn-soft);color:var(--amber-ink);border:1px solid var(--amber);}
.banner-info{background:var(--info-soft);color:var(--info);border:1px solid var(--info);}
/* Dealer Portal (Sept 2026) — a plain success variant (order placed, etc.); same shape as the two
   banners above, this app just never needed a "good" one until now. */
.banner-good{background:var(--good-soft);color:var(--good);border:1px solid var(--good);}

@media (max-width:760px){
  .modal-grid{grid-template-columns:1fr;}
  table{min-width:560px;}
  /* A toolbar's search box (.field wrapping an <input>, e.g. "Search by order # or customer…")
     had no floor of its own — only .toolbar input[type=text] directly did, which this nested
     .field/input pattern never matched — so on a narrow screen it shrank down alongside the
     Search/Export CSV buttons until its own placeholder text no longer fit. This gives it back a
     sensible minimum; .toolbar's existing flex-wrap then wraps the buttons to their own line
     instead of squeezing the box once there isn't room for both. Harmless above this width — a
     280px-max-width box never needed a 180px floor when there was already room to spare. */
  .toolbar .field{min-width:180px;}
  /* Wide data tables (order item-entry rows, price lists) still need to scroll sideways on a
     phone — there's no room to show 8-10 columns at once — but nothing indicated that a table
     scrolled at all. Tightening cell padding gets a bit more on-screen before a scroll is needed,
     and the two background gradients below fade the table's own edges as a visual "there's more
     this way" cue (invisible once you've scrolled that edge into view, and equally invisible on
     any table that already fits without scrolling at all). */
  thead th{padding:8px 9px;}
  tbody td{padding:8px 9px;}
  table{font-size:12.5px;}
  .table-wrap{
    background:
      linear-gradient(to right, var(--surface) 50%, rgba(255,255,255,0)) 0 0,
      linear-gradient(to left, var(--surface) 50%, rgba(255,255,255,0)) 100% 0,
      linear-gradient(to right, rgba(27,31,29,.16), rgba(27,31,29,0)) 0 0,
      linear-gradient(to left, rgba(27,31,29,.16), rgba(27,31,29,0)) 100% 0;
    background-repeat:no-repeat;
    background-size:24px 100%, 24px 100%, 10px 100%, 10px 100%;
    background-attachment:local, local, scroll, scroll;
  }
}

/* ---------- order item-entry tables (Mattress/Rebonded/Foam/Pillow Item Details) on phones ---------- */
/* Sept 2026, per the owner: "check mobile interface of order form,...all columns must be visible"
   — a sales person entering an order on their phone was only ever seeing the first column or two
   of these 7-15 column tables (Model/Quality, Length, Width, Thickness, Bag, Pcs, Colour, Price,
   Discount, Total, …), with the rest sitting off past a horizontal scroll they had no reason to
   notice. Below 700px this turns each row into its own stacked card — one "Label: value" line per
   column, in the exact order the desktop header shows them, via each <td>'s data-label attribute
   (set in JS by labelOrderRowCells against the same array the <thead> is built from, so a label
   can never drift out of sync with its real column) — so every field is visible with no sideways
   scrolling at all. Scoped to .order-items-table alone: every OTHER table in the app (read-only
   listings, price lists, reports) keeps the horizontal-scroll-with-fade-cue behaviour above
   exactly as tuned, since nothing indicated those needed a card layout instead. */
@media (max-width:700px){
  .order-items-wrap{overflow-x:visible;background:none;}
  .order-items-table{display:block;min-width:0;width:100%;font-size:13px;}
  .order-items-table thead{display:none;}
  .order-items-table tbody{display:block;}
  .order-items-table tr{
    display:block;border:1px solid var(--line);border-radius:9px;background:var(--surface);
    padding:4px 12px;margin-bottom:12px;
  }
  .order-items-table td{
    display:flex;align-items:center;justify-content:space-between;gap:12px;
    padding:8px 0;border-bottom:1px dashed var(--line);text-align:right;
  }
  .order-items-table td:last-child{border-bottom:none;padding-top:10px;}
  .order-items-table th:first-child,.order-items-table td:first-child{min-width:0;}
  .order-items-table td[data-label]:before{
    content:attr(data-label);flex:0 0 auto;text-align:left;font-size:10.5px;font-weight:700;
    text-transform:uppercase;letter-spacing:.3px;color:var(--steel);
  }
  .order-items-table td > *{max-width:62%;}
  .order-items-table td select,.order-items-table td input,.order-items-table td .inline-combo{width:auto;min-width:0;}
  .order-items-table .btn-chip{width:auto;}
}

/* ---------- global search ---------- */
.global-search{position:relative;}
.global-search-input{width:260px;padding:7px 10px;border:1px solid var(--line);border-radius:6px;background:var(--paper);color:var(--ink);font-size:12.5px;font-family:inherit;}
.global-search-results{position:absolute;top:calc(100% + 6px);right:0;width:320px;max-height:400px;overflow-y:auto;background:var(--surface);border:1px solid var(--line);border-radius:9px;box-shadow:var(--shadow);z-index:60;padding:6px;}
.gs-heading{font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;color:var(--steel);padding:6px 8px 2px;}
.gs-row{display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;background:none;border:none;padding:7px 8px;border-radius:6px;cursor:pointer;font-family:inherit;}
.gs-row:hover{background:var(--surface-2);}
.gs-title{font-size:12.5px;font-weight:600;color:var(--ink);}
.gs-sub{font-size:11px;color:var(--steel);}
.gs-empty{padding:10px 8px;font-size:12.5px;color:var(--steel);}

/* ---------- bulk actions ---------- */
.bulk-bar{display:flex;align-items:center;gap:10px;padding:10px 14px;margin-bottom:14px;background:var(--amber-soft);border:1px solid var(--amber);border-radius:8px;font-size:13px;font-weight:600;color:var(--amber-ink);}
.bulk-bar select{padding:6px 8px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);font-family:inherit;}

/* ---------- stuck / delayed badge ---------- */
.stuck-badge{display:inline-block;margin-top:3px;font-size:10.5px;font-weight:700;color:var(--crit);background:var(--crit-soft);padding:2px 7px;border-radius:9px;white-space:nowrap;}

/* ---------- notes & photo log ---------- */
.notes-log{max-height:260px;overflow-y:auto;}
.note-entry{display:flex;flex-direction:column;gap:4px;align-items:flex-start;}
.note-photo{max-width:100%;max-height:180px;border-radius:8px;border:1px solid var(--line);object-fit:cover;}

/* ---------- hardware store ---------- */
.item-thumb{width:36px;height:36px;border-radius:7px;border:1px solid var(--line);object-fit:cover;background:var(--surface-2);display:block;}
.item-thumb-empty{display:flex;}
.item-thumb-lg{width:120px;height:120px;}
.pill-lowstock{background:var(--crit-soft);color:var(--crit);}
.pill-instock{background:var(--good-soft);color:var(--good);}

/* Production Orders status pills — one per bottleneck phase (see computeOrderStatus in
   routes/production.js): Not Started, Design File Upload Pending, Ready for Cutting (Not
   Started), Cutting Started, Edge Banding, Drilling, Completed. Ready-for-cutting and
   cutting-started are deliberately two different pills — everything being on file is NOT the
   same as an operator having actually clicked Start. .pill-pending/.pill-completed reuse the
   neutral/good tones above. */
.pill-pending{background:var(--surface-2);color:var(--steel);}
.pill-design_pending{background:var(--warn-soft);color:var(--amber-ink);}
.pill-ready_for_cutting{background:var(--warn-soft);color:var(--amber-ink);}
.pill-cutting_started{background:var(--info-soft);color:var(--info);}
.pill-edgeband{background:var(--violet-soft);color:var(--violet);}
.pill-drilling{background:var(--teal-soft);color:var(--teal);}
.pill-completed{background:var(--good-soft);color:var(--good);}

/* Live Production Details drill-down: a stat card or a day's table cell that's actually clickable
   (has a real breakdown behind it) gets a pointer cursor and a subtle hover lift/highlight, so it
   reads as interactive rather than a plain number. */
.stat-card-clickable{cursor:pointer;transition:transform .12s ease,box-shadow .12s ease;}
.stat-card-clickable:hover{transform:translateY(-2px);box-shadow:0 4px 14px rgba(0,0,0,.08);}
.stat-cell-clickable{cursor:pointer;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:2px;}
.stat-cell-clickable:hover{color:var(--info);}

/* ---------- installation planning: date groups ---------- */
.install-group{margin-bottom:16px;}
.install-group-count{white-space:nowrap;}
.install-tag-overdue{color:var(--crit);font-weight:700;}
.install-tag-today{color:var(--amber-ink);font-weight:700;}
.install-tag-tomorrow{color:var(--info);font-weight:700;}
.install-select{padding:5px 7px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);font-size:12px;font-family:inherit;min-width:120px;}
.install-notes{padding:5px 7px;border:1px solid var(--line);border-radius:6px;background:var(--surface);color:var(--ink);font-size:12px;font-family:inherit;min-width:160px;resize:vertical;}

/* ---------- print (Installation planning) ---------- */
@media print{
  body *{visibility:hidden;}
  .print-area,.print-area *{visibility:visible;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
  .print-area{position:absolute;top:0;left:0;width:100%;}
  .no-print{display:none !important;}
}

/* ---------- packaging (Phase 6) ---------- */
/* The scanner types into this like a keyboard, so it's set large and monospace — easy for a
   staff member to glance at and confirm what was just read, whether or not the scan matched. */
.scan-input{width:100%;padding:14px;border:2px solid var(--line);border-radius:8px;background:var(--surface);color:var(--ink);font-size:20px;font-family:var(--font-mono);letter-spacing:.5px;}
.scan-input:focus{outline:none;border-color:var(--amber);}

.checklist{display:flex;flex-direction:column;gap:2px;padding:8px 0;}
.checklist-item{display:flex;align-items:center;gap:10px;padding:8px 4px;border-bottom:1px solid var(--line);}
.checklist-item:last-child{border-bottom:none;}
.checklist-item .check-mark{flex:none;width:24px;height:24px;border-radius:50%;border:2px solid var(--line);display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:800;color:transparent;}
.checklist-item.checklist-done{background:var(--good-soft);}
.checklist-item.checklist-done .check-mark{border-color:var(--good);background:var(--good);color:#fff;}
.checklist-item.checklist-done .checklist-name{color:var(--good);font-weight:700;}
.checklist-name{flex:1;font-size:13.5px;}

/* Design Phase (Production) — every item on the chosen order shown at once, each row ending in
   either a ✓ (drawing on file) or an inline upload widget, rather than the old order->item->
   upload picker that hid every item but the one currently selected. */
.design-item-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 18px;flex-wrap:wrap;}
.design-item-info{min-width:0;flex:1 1 220px;}
.design-item-name{font-weight:700;font-size:13.5px;}
.design-item-tick{flex:none;display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;background:var(--good);color:#fff;font-size:15px;font-weight:800;}
.design-item-upload{flex:none;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.design-item-upload input[type=file]{font-size:12px;max-width:210px;}

/* The printed label — shown on screen inside a .print-area wrapper (so it's the only thing that
   survives @media print above), sized to read easily at arm's length once it's on a physical box.
   print-color-adjust (+ the -webkit- prefix Chrome still needs) stops the browser's print pipeline
   from stripping the barcode's black bars — without it some browsers can render a printed/PDF'd
   label as blank even though the exact same SVG is clearly visible on screen. The fill is also set
   again here via CSS, on top of the SVG's own fill="#000" attribute on each bar, belt-and-suspenders. */
.label-card{max-width:340px;margin:0 auto;padding:22px;border:1px solid var(--line);border-radius:10px;background:#fff;color:#111;text-align:center;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
.label-item{font-family:var(--font-display);font-size:16px;font-weight:800;margin-bottom:10px;}
.label-barcode{display:flex;justify-content:center;margin:6px 0;}
.label-barcode svg{max-width:100%;height:auto;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
.label-barcode svg rect{fill:#000;}
.label-boxid{font-family:var(--font-mono);font-size:22px;font-weight:700;letter-spacing:1px;margin-top:8px;}
.label-meta{margin-top:10px;display:flex;flex-direction:column;gap:2px;font-size:11.5px;color:#555;}
@media print{
  .label-card{border:none;max-width:100%;}
}

/* ---------- ready stock (Phase 7) ---------- */
/* A batch of labels — one per unit just stocked in — laid out to wrap and print several to a
   page, each kept whole rather than split across a page break. */
.label-batch{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;}
.label-batch .label-card{flex:0 0 240px;max-width:240px;padding:14px;}
.label-batch .label-boxid{font-size:16px;}
@media print{
  .label-batch .label-card{break-inside:avoid;page-break-inside:avoid;}
}

/* ---------- MRP Slip (Sept 2026) ----------
   One card per physical mattress ("Quantity: 1 Pcs, it is always fix") — reuses the .label-batch
   wrap-and-print-several-per-page layout above; only the card's own inner content is specific to
   an MRP slip (Model Name / Quantity / Dimensions / Assured Warranty / MRP, then the brand's own
   3-line footer). Sized a little wider than a barcode label since RR Supreme's own field labels
   run long ("MRP (Inclusive of All Taxes)"). */
/* Header/Footer/Content font-family, size and line-height are set inline per Brand (Oct 2026
   redesign — see mrpSlipMultilineNode/mrpSlipCardNode in app.js and the brands table comment in
   db/schema.sql); the rules below are just shared layout/weight, deliberately left without their
   own font-family/font-size so the inline per-brand values always win. */
.mrp-slip-card{max-width:300px;text-align:left;}
/* Header/Footer alignment is per-line now (Oct 2026 — see mrpSlipLinesNode in app.js and the
   brands table comment in db/schema.sql), set inline on each line's own div, so these two classes
   no longer hardcode a block-wide text-align themselves. */
.mrp-slip-header{margin-bottom:10px;letter-spacing:.3px;}
.mrp-slip-row{display:flex;justify-content:space-between;gap:10px;padding:5px 0;border-bottom:1px dashed var(--line);}
.mrp-slip-row:last-of-type{border-bottom:none;}
.mrp-slip-row-label{color:#555;flex:none;}
/* min-width:0 + word-break (Oct 2026, per the owner: "item name is long, how we can print this")
   — a flex item's default min-width:auto stops it shrinking below its content's natural width, so
   a long Model Name would overflow the card instead of wrapping; this lets it wrap onto a 2nd line
   within the space .mrp-slip-row-label leaves it, same "wrap, never shrink the font" choice
   mrpSlipLinesNode makes for Header/Footer lines. */
.mrp-slip-row-value{font-weight:700;text-align:right;flex:1;min-width:0;word-break:break-word;}
.mrp-slip-footer{margin-top:12px;padding-top:10px;border-top:1px solid var(--line);color:#333;}
.label-batch .mrp-slip-card{flex-basis:300px;max-width:300px;}
/* Live design preview shown inside the Brand editor — a smaller, boxed rendering of the same
   mrp-slip-card markup so it reads as "a slip", not a floating unstyled block. */
.mrp-slip-design-preview{border:1px dashed var(--line);border-radius:10px;padding:14px;background:var(--bg-soft,#f6f3ec);}

/* ---------- Toast notifications (Nov 2026, per the owner: "when stock in is made, message
   should be displayed successfully added item name and details, same way for other subsections
   as well in which entries are made") — a small, auto-dismissing confirmation after any entry
   (Stock In/Out, Foam/Rebonded Block In, Raw Materials, a new Requirement, ...) is successfully
   saved, so staff get an immediate visual confirmation without needing to scan the page for a
   change. See showToast() in app.js. ---------- */
#toast-root{position:fixed;top:16px;right:16px;z-index:70;display:flex;flex-direction:column;gap:8px;max-width:min(360px,calc(100vw - 32px));pointer-events:none;}
.toast{pointer-events:auto;background:var(--surface);border:1px solid var(--good);border-left:4px solid var(--good);border-radius:8px;box-shadow:var(--shadow);padding:11px 14px;font-size:13px;line-height:1.4;color:var(--ink);display:flex;align-items:flex-start;gap:9px;animation:toast-in .22s ease-out;}
.toast.toast-leaving{animation:toast-out .22s ease-in forwards;}
.toast-icon{flex:none;width:18px;height:18px;border-radius:50%;background:var(--good);color:#fff;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;margin-top:1px;}
.toast-body{flex:1;min-width:0;word-break:break-word;}
@keyframes toast-in{from{opacity:0;transform:translateX(16px);}to{opacity:1;transform:translateX(0);}}
@keyframes toast-out{from{opacity:1;transform:translateX(0);}to{opacity:0;transform:translateX(16px);}}
@media (max-width:640px){#toast-root{left:16px;right:16px;top:auto;bottom:16px;max-width:none;}}

/* ---------- Furniture Catalog: category grouping, Grid/POS view + cart, Manage Categories
   (Oct 2026, per the owner: "products of one category must be shown together... option to
   rearrange category must be there" / "make it more interactive, show like a POS system") ----
   ---------- */
.frn-cat-heading{margin:22px 0 10px;padding-bottom:6px;border-bottom:2px solid var(--line);font-size:14px;font-weight:700;color:var(--ink);display:flex;align-items:center;gap:8px;}
.frn-cat-heading:first-child{margin-top:4px;}
.frn-cat-count{font-size:11.5px;font-weight:600;color:var(--steel);background:var(--surface-2);border-radius:10px;padding:1px 8px;}

.frn-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;margin-bottom:8px;}
.frn-card{background:var(--surface);border:1px solid var(--line);border-radius:10px;overflow:hidden;cursor:pointer;text-align:left;padding:0;display:flex;flex-direction:column;transition:transform .12s ease,box-shadow .12s ease,border-color .12s ease;font-family:inherit;}
.frn-card:hover{transform:translateY(-2px);box-shadow:var(--shadow);border-color:var(--brand-2);}
.frn-card:disabled{cursor:not-allowed;opacity:.5;transform:none;}
.frn-card-photo{width:100%;aspect-ratio:1/1;background:var(--surface-2);display:flex;align-items:center;justify-content:center;color:var(--steel);font-size:28px;object-fit:cover;}
.frn-card-body{padding:9px 11px 11px;display:flex;flex-direction:column;gap:3px;flex:1;}
.frn-card-name{font-size:13px;font-weight:700;color:var(--ink);line-height:1.3;}
.frn-card-spec{font-size:11.5px;color:var(--steel);}
.frn-card-price{margin-top:auto;font-size:13.5px;font-weight:700;color:var(--brand-2);}
.frn-card-add{font-size:11px;font-weight:600;color:var(--teal);}

.frn-pos-layout{display:flex;gap:18px;align-items:flex-start;}
.frn-pos-main{flex:1;min-width:0;}
.frn-cart{width:300px;flex:none;background:var(--surface);border:1px solid var(--line);border-radius:10px;position:sticky;top:12px;max-height:calc(100vh - 100px);display:flex;flex-direction:column;overflow:hidden;}
.frn-cart-head{padding:12px 14px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;}
.frn-cart-head h4{margin:0;font-size:13.5px;}
.frn-cart-lines{overflow-y:auto;flex:1;padding:6px 10px;}
.frn-cart-line{display:flex;align-items:center;gap:8px;padding:8px 2px;border-bottom:1px solid var(--line);}
.frn-cart-line:last-child{border-bottom:none;}
.frn-cart-line-name{flex:1;min-width:0;font-size:12.5px;font-weight:600;}
.frn-cart-line-sub{font-size:11px;color:var(--steel);}
.frn-cart-qty{display:flex;align-items:center;gap:4px;}
.frn-cart-qty button{width:22px;height:22px;border-radius:5px;border:1px solid var(--line);background:var(--surface-2);color:var(--ink);cursor:pointer;font-weight:700;line-height:1;}
.frn-cart-qty input{width:34px;text-align:center;padding:3px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--ink);font-size:12.5px;}
.frn-cart-remove{color:var(--crit);background:none;border:none;cursor:pointer;font-size:12px;flex:none;}
.frn-cart-foot{padding:12px 14px;border-top:1px solid var(--line);}
.frn-cart-total{display:flex;justify-content:space-between;font-weight:700;font-size:14px;margin-bottom:10px;}
.frn-cart-empty{padding:24px 14px;text-align:center;color:var(--steel);font-size:12.5px;}
@media (max-width:880px){.frn-pos-layout{flex-direction:column;}.frn-cart{width:100%;position:static;max-height:none;}}

/* Manage Categories — draggable rows + up/down fallback buttons (no prior drag-and-drop pattern
   exists in this app to follow, so kept deliberately simple/accessible). */
.frn-cat-row{display:flex;align-items:center;gap:10px;padding:9px 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface);margin-bottom:6px;cursor:grab;}
.frn-cat-row.dragging{opacity:.4;}
.frn-cat-row.drag-over{border-color:var(--brand-2);box-shadow:0 0 0 2px color-mix(in srgb, var(--brand-2) 30%, transparent);}
.frn-cat-row-handle{color:var(--steel);font-size:14px;flex:none;}
.frn-cat-row-name{flex:1;min-width:0;font-weight:600;font-size:13.5px;}
.frn-cat-row-count{font-size:11.5px;color:var(--steel);flex:none;}
.frn-cat-row-actions{display:flex;gap:4px;flex:none;}
.frn-cat-row-actions button{width:26px;height:26px;border-radius:6px;border:1px solid var(--line);background:var(--surface-2);color:var(--ink);cursor:pointer;font-size:12px;line-height:1;}
.frn-cat-row-actions button:disabled{opacity:.35;cursor:not-allowed;}
