/* =====================================================================
   Quotation System styles
   Phones first; the two-column desk layout starts at 900px wide.
   ===================================================================== */

:root {
  --brand: #1f5fa8;
  --brand-ink: #ffffff;
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-2: #f6f8fb;
  --text: #17202b;
  --muted: #5b6878;
  --border: #dbe1e8;
  --border-strong: #c2cbd6;
  --focus: #2d7ff9;
  --danger: #c0392b;
  --danger-soft: #fdecea;
  --success: #1b8a57;
  --success-soft: #e5f5ed;
  --warn-soft: #fff5d6;
  --warn-ink: #6f5200;
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 2px 10px rgba(16, 24, 40, .05);
  --shadow-lg: 0 16px 48px rgba(16, 24, 40, .22);
  --topbar-h: 60px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --link: var(--brand);
  color-scheme: light; /* always white, even when the phone or PC is set to dark mode */
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; font: 15px/1.45 var(--font); color: var(--text); background: var(--bg); }
button, input, select, textarea { font: inherit; color: inherit; }
input, select, textarea { font-size: 16px; } /* 16px stops iPhones zooming in */
@media (min-width: 900px) { input, select, textarea { font-size: 15px; } }
[hidden] { display: none !important; }
a { color: var(--link); }
h1, h2, h3 { line-height: 1.25; }
code { font-size: .92em; background: rgba(127, 127, 127, .14); padding: 1px 5px; border-radius: 4px; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.spacer { flex: 1; }
.pad { padding: 16px; margin: 0; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 40px; padding: 0 14px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font-weight: 600; cursor: pointer; text-decoration: none;
  white-space: nowrap; transition: background .15s, border-color .15s, filter .15s;
}
.btn:hover { background: var(--surface-2); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-primary { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.btn-primary:hover { background: var(--brand); filter: brightness(1.1); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn-ghost:hover { background: var(--surface-2); color: var(--text); }
.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger:hover { background: var(--danger); filter: brightness(1.1); }
.btn-danger-ghost { background: transparent; border-color: transparent; color: var(--danger); }
.btn-danger-ghost:hover { background: var(--danger-soft); }
.btn-small { min-height: 32px; padding: 0 10px; font-size: 14px; }
.btn-lg { min-height: 48px; font-size: 16px; }
.btn-block { width: 100%; }
.icon-btn {
  width: 36px; height: 36px; flex: none; border: 0; border-radius: 8px; background: none;
  color: var(--muted); cursor: pointer; font-size: 16px;
}
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.link-btn {
  display: block; margin: 12px auto 0; padding: 6px; border: 0; background: none;
  color: var(--link); font-weight: 600; cursor: pointer; text-align: center; text-decoration: none;
}

/* ---------- Form fields ---------- */
.field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; min-width: 0; }
.field > span { font-size: 13px; font-weight: 600; color: var(--muted); }
.field input, .field select, .field textarea, .toolbar input[type="search"],
.i-qty input, .i-disc input, .i-rate input {
  width: 100%; height: 42px; padding: 0 10px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
}
/* Drop-down lists (unit of measure): drawn like the other boxes on every phone and
   computer (text lined up with theirs), with a small arrow on the right. The list that opens is the
   phone's or computer's own. */
.field select {
  -webkit-appearance: none; appearance: none; padding-right: 36px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6' fill='none' stroke='%235b6878' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 18px 18px;
}
.field select option { color: var(--text); background: var(--surface); }
.field textarea { height: auto; min-height: 64px; padding: 8px 10px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus, .toolbar input:focus,
.i-qty input:focus, .i-disc input:focus, .i-rate input:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}
input.invalid { border-color: var(--danger) !important; background: var(--danger-soft) !important; }
.field-row { display: grid; gap: 0 12px; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.check input { width: 18px; height: 18px; margin: 0; accent-color: var(--brand); }
.form-msg { margin: 4px 0 12px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 14px; }
.form-msg.error { background: var(--danger-soft); color: var(--danger); }
.form-msg.success { background: var(--success-soft); color: var(--success); }
.form-msg.info { background: var(--warn-soft); color: var(--warn-ink); }
.hint { margin: 8px 0 0; color: var(--muted); font-size: 13px; }
.field > .hint { margin: 2px 0 0; }

/* ---------- Loading and errors ---------- */
.spinner {
  display: inline-block; width: 26px; height: 26px; vertical-align: middle;
  border: 3px solid var(--border); border-top-color: var(--brand); border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.page-loader { display: none; }
body.loading .page-loader { display: grid; place-items: center; position: fixed; inset: var(--topbar-h) 0 0; z-index: 20; background: var(--bg); }
body.loading main, body.loading .bottom-bar, body.loading .usermenu, body.loading .topnav { visibility: hidden; }
.loading-row { display: flex; align-items: center; gap: 10px; padding: 18px 16px; color: var(--muted); }
.loading-row .spinner { width: 18px; height: 18px; border-width: 2px; }
.fatal {
  width: min(560px, calc(100% - 32px)); margin: 40px auto; padding: 20px 22px; background: var(--surface);
  border: 1px solid var(--border); border-left: 4px solid var(--danger); border-radius: var(--radius);
}
.fatal h2 { margin: 0 0 8px; font-size: 18px; }
.fatal.is-offline { border-left-color: var(--border-strong); } /* no internet: not an error of the app */
.fatal.is-offline p { color: var(--muted); }

/* "Install the app" on iPhones and iPads (core.js): the steps in Safari */
.install-intro { margin: 0 0 6px; }
.install-steps { margin: 0 0 12px; padding-left: 22px; }
.install-steps li { margin: 6px 0; }
.ios-share { width: 18px; height: 18px; margin: 0 2px; vertical-align: -3px; color: var(--brand); }
.install-note { margin: 0 0 12px; color: var(--muted); font-size: 14px; }

/* ---------- Top bar ---------- */
.topbar {
  position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: 12px;
  height: var(--topbar-h); padding: 0 16px;
  /* (No extra space for a phone's notch: some phone browsers report one even with their address bar
     above the page, which left an empty white band above the bar.) */
  background: #fff; color: var(--text); border-bottom: 1px solid var(--border);
}
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; color: inherit; text-decoration: none; }
.brand img { flex: none; border-radius: 8px; }
.brand-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
.brand-text strong { font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Admin (for admins) sits on the right, next to the user button */
.topnav { display: flex; gap: 2px; margin-left: auto; }
.topnav:not([hidden]) + .usermenu { margin-left: -8px; } /* (the bar's 12px gap) - 8px: Admin stays close to the user button */
.topnav a { padding: 8px 12px; border-radius: 8px; color: var(--muted); font-weight: 600; text-decoration: none; }
.topnav a:hover { background: var(--surface-2); color: var(--text); }
.topnav a.current { background: color-mix(in srgb, var(--brand) 10%, #fff); color: var(--brand); }
.usermenu { position: relative; margin-left: auto; flex: none; }
.user-btn {
  display: flex; align-items: center; gap: 8px; padding: 3px 12px 3px 3px; border: 1px solid var(--border); border-radius: 999px;
  background: #fff; color: var(--text); cursor: pointer;
}
.user-btn:hover { background: var(--surface-2); }
.avatar { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--brand); color: #fff; font-weight: 800; }
.user-name { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 50; min-width: 230px; padding: 6px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.menu-info { display: flex; flex-direction: column; padding: 8px 10px 10px; margin-bottom: 6px; border-bottom: 1px solid var(--border); }
.menu-info small { color: var(--muted); word-break: break-all; }
.menu button { display: block; width: 100%; padding: 10px; border: 0; border-radius: 8px; background: none; text-align: left; cursor: pointer; }
.menu button:hover { background: var(--surface-2); }
@media (max-width: 600px) {
  /* Phones: a slimmer bar (48px), so the page starts close under the browser's address bar */
  :root { --topbar-h: 48px; }
  .brand img { width: 30px; height: 30px; }
  .user-name { display: none; }
  .user-btn { padding: 3px; }
  .topnav a { padding: 6px 8px; }
}

/* ---------- Calculator layout ---------- */
.layout { display: grid; gap: 16px; max-width: 1440px; margin: 0 auto; padding: 16px; }
.panel { min-width: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }

/* Search */
.search-box { position: relative; padding: 12px; }
.search-box input {
  width: 100%; height: 46px; padding: 0 42px 0 42px; border: 1px solid var(--border-strong); border-radius: 10px;
  background: var(--surface);
}
.search-box input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent); }
.search-box input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.search-icon { position: absolute; top: 50%; left: 26px; width: 18px; height: 18px; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.search-clear {
  position: absolute; top: 50%; right: 18px; width: 34px; height: 34px; transform: translateY(-50%);
  border: 0; border-radius: 50%; background: none; color: var(--muted); cursor: pointer;
}
.results { padding: 4px 6px 8px; border-top: 1px solid var(--border); }
.result {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 11px 10px; border: 0; border-radius: 8px;
  background: none; text-align: left; cursor: pointer;
}
.result:hover, .result.active { background: color-mix(in srgb, var(--brand) 9%, var(--surface)); }
.r-name { flex: 1; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.r-badge { flex: none; padding: 3px 9px; border-radius: 999px; background: var(--success-soft); color: var(--success); font-size: 12px; font-weight: 700; }
.results-empty { padding: 22px 14px; color: var(--muted); text-align: center; }

/* Quote panel. Narrow (phones): one short line per product; tap it for more.
   Wide (desktop): a table that can be edited directly. It switches on the panel's width. */
.quote-panel { container: quote / inline-size; }
/* Title, the Weight / Price / Both switch and Clear: one row (a slim one on phones, see below) */
.quote-head { display: flex; align-items: center; gap: 8px; padding: 12px 12px 12px 16px; border-bottom: 1px solid var(--border); }
.quote-head h1 { flex: 1; min-width: 0; margin: 0; overflow: hidden; font-size: 18px; text-overflow: ellipsis; white-space: nowrap; }
.count { color: var(--muted); font-weight: 500; }

/* The switch for what the quote shows: Weight / Price / Both */
.seg { display: flex; flex: none; padding: 3px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2); }
.seg label { position: relative; flex: 1; cursor: pointer; touch-action: manipulation; }
.seg input { position: absolute; top: 0; left: 0; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.seg span {
  display: block; padding: 7px 16px; border-radius: 7px; color: var(--muted); font-size: 14px; font-weight: 600;
  text-align: center; white-space: nowrap; user-select: none;
}
.seg input:checked + span { background: var(--surface); color: var(--brand); box-shadow: 0 0 0 1px var(--border-strong), 0 1px 2px rgba(16, 24, 40, .08); }
.seg input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 1px; }
@media (hover: hover) { .seg label:hover input:not(:checked) + span { color: var(--text); } }

/* What the switch hides: with Weight, the prices and amounts; with Price, the weights. In the table,
   the totals, the phone's bottom bar and the quantity pop-up. (The phone lines and the image are
   made to match in app.js.) Only on the Quotation tab: the price list always shows prices. */
body[data-tab="quote"][data-show="weight"] :is(.h-price, .h-amount, .i-rate, .i-amount, #t-money, #bb-money, .rate-field, #qty-amount-row,
  .h-disc, .i-disc, .disc-field),
body[data-tab="quote"][data-show="price"] :is(.h-weight, .i-weight, #t-weight, #bb-weight, #qty-weight-row) { display: none; }
body[data-show="price"] .trow.grand { margin-top: 0; padding-top: 5px; border-top: 0; } /* the only total: no line above it */

.items-head { display: none; }
.items:empty { display: none; }
.item + .item { border-top: 1px solid var(--border); }
.item.flash { animation: flash 1s ease; }
@keyframes flash { from { background: color-mix(in srgb, var(--brand) 20%, var(--surface)); } }

/* Compact line: number, name, quantity and amount on one line, under a small heading. The number,
   quantity and amount columns are as wide as their longest entry (--num-w, --qty-w and --val-w, set
   in app.js), so they line up from line to line. The quantity keeps some room on its right
   (--qty-room), so it stands a little apart from the amount. Tapping a line opens its weight, unit
   price and discount underneath (.i-more); an open line can be swiped to remove it or to change it
   (.swipe, below). */
.lines-head, .i-summary, .l-summary {
  display: grid; align-items: center; column-gap: 8px; padding: 12px 16px;
  grid-template-columns: var(--num-w, 16px) minmax(0, 1fr) calc(var(--qty-w, 56px) + var(--qty-room, 16px)) var(--val-w, 84px);
}
.lines-head {
  padding-top: 6px; padding-bottom: 6px; background: var(--surface-2); border-bottom: 1px solid var(--border);
  color: var(--muted); font-size: 12px; font-weight: 600;
}
.i-summary, .l-summary {
  width: 100%; border: 0; background: var(--surface); color: inherit; font-size: 15px; text-align: left;
  cursor: pointer; touch-action: pan-y pinch-zoom; /* sideways moves are swipes; up and down scrolls */
}
.l-summary { padding-top: 8px; padding-bottom: 8px; } /* price list: shorter lines, still easy to tap */
.i-summary:active, .l-summary:active, .item.open .i-summary { background: var(--surface-2); }
@media (hover: hover) { .i-summary:hover, .l-summary:hover { background: var(--surface-2); } } /* not on phones: it would stay grey after a tap */
.s-num { color: var(--muted); font-size: 13px; font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }
.s-name { font-weight: 600; overflow-wrap: anywhere; text-wrap: pretty; }
.s-qty { padding-right: var(--qty-room, 16px); text-align: center; white-space: nowrap; font-variant-numeric: tabular-nums; }
.s-value { text-align: right; font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Swipe (phones, quote lines): an open line slides with the finger, its details with it (.swipe-line).
   Behind it: Edit on blue at the left as it goes right; Remove on red at the right as it goes left,
   brighter once far enough to count (.armed). Closed lines do not move. */
.swipe { position: relative; overflow: hidden; }
.swipe-line { position: relative; background: var(--surface); transition: transform .2s ease; touch-action: pan-y pinch-zoom; }
.swipe.dragging > .swipe-line { transition: none; }
.swipe-acts {
  position: absolute; inset: 0; display: none; align-items: center; padding: 0 18px;
  color: #fff; font-size: 15px; font-weight: 700;
}
.swipe.to-right .swipe-acts { display: flex; justify-content: flex-start; background: var(--brand); }
.swipe.to-left .swipe-acts { display: flex; justify-content: flex-end; background: var(--danger); }
.swipe.to-right .sa-del, .swipe.to-left .sa-edit { display: none; }
.swipe-acts span { display: inline-flex; align-items: center; gap: 6px; opacity: .7; transition: opacity .15s, transform .15s; }
.swipe.armed .swipe-acts span { opacity: 1; transform: scale(1.06); }
.swipe.to-right .swipe-acts span { opacity: 1; } /* (Edit opens as soon as it is far enough: no waiting state) */
.swipe-acts svg { width: 18px; height: 18px; }
/* A line's details: one slim row under it, from under the name to the amount's right edge, in three
   equal thirds: Unit price at the left, Discount in the middle (centred), Weight at the right. With
   just two (no discount, or no weight with Price only), one sits at the left and the other at the
   right. (A third only grows if its number is too long to fit.) No buttons: the open line is swiped
   to change or remove it. With Weight only there are none: the open line just turns grey, ready to
   be swiped. */
.item.open .i-summary { padding-bottom: 4px; }
body[data-show="weight"] .item.open .i-summary { padding-bottom: 12px; }
.i-more:empty { display: none; }
.i-more {
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px 10px calc(24px + var(--num-w, 16px)); background: var(--surface-2);
}
.m-facts { display: grid; grid-template-columns: repeat(3, minmax(max-content, 1fr)); column-gap: 12px; flex: 1; min-width: 0; margin: 0; }
.m-facts > :nth-child(2):not(:last-child) { grid-column: 2; text-align: center; } /* the middle one of three */
.m-facts > :last-child:not(:first-child) { grid-column: 3; text-align: right; } /* the last one of two or three */
.m-facts dt { color: var(--muted); font-size: 12px; line-height: 1.25; white-space: nowrap; }
.m-facts dd { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.3; white-space: nowrap; font-variant-numeric: tabular-nums; }
.i-num, .i-name, .i-qty, .i-weight, .i-disc, .i-rate, .i-amount, .i-remove, .h-disc { display: none; }

/* Table row parts (shown in the wide layout). Qty, weight and price sit in the middle of their columns. */
.i-num { color: var(--muted); font-size: 14px; font-weight: 600; }
.i-name { flex-direction: column; min-width: 0; }
.i-qty, .i-disc, .i-rate { align-items: center; justify-content: center; min-width: 0; }
.i-disc input { width: 64px; flex: none; text-align: center; }
.i-weight { font-weight: 600; text-align: center; font-variant-numeric: tabular-nums; }
.i-amount { font-weight: 700; }
.i-title { font-weight: 600; overflow-wrap: anywhere; }
.i-qty input { width: 84px; flex: none; text-align: center; font-weight: 600; }
.i-rate input { width: 100px; flex: none; text-align: center; }
.item .i-rate input { width: 84px; } /* the quote: the Price box the size of the Qty box (and its column as wide as Qty's) */
.mid { text-align: center; }
.i-remove {
  place-items: center; width: 36px; height: 36px; border: 0; border-radius: 8px; background: none; color: var(--muted); cursor: pointer;
}
.i-remove:hover { background: var(--danger-soft); color: var(--danger); }

@container quote (min-width: 760px) {
  .lines-head, .swipe, .i-summary, .i-more { display: none; }
  .items-head, .item {
    display: grid; grid-template-columns: 28px minmax(0, 1fr) 104px 112px 104px 124px 36px;
    gap: 12px; align-items: center; padding: 4px 16px;
  }
  /* short lines: slimmer boxes and ✕, close together */
  .item :is(.i-qty, .i-disc, .i-rate) input { height: 34px; }
  .item .i-remove { height: 32px; }
  .items-head {
    padding-top: 8px; padding-bottom: 8px; background: var(--surface-2); border-bottom: 1px solid var(--border);
    color: var(--muted); font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  }
  body[data-show="weight"] :is(.items-head, .item) { grid-template-columns: 28px minmax(0, 1fr) 104px 140px 36px; }
  body[data-show="price"] :is(.items-head, .item) { grid-template-columns: 28px minmax(0, 1fr) 104px 104px 124px 36px; }
  /* A discount rate product in the quote: a Disc % column before the price (app.js sets .has-disc) */
  .quote-panel.has-disc :is(.items-head, .item) { grid-template-columns: 28px minmax(0, 1fr) 96px 104px 76px 96px 116px 36px; }
  body[data-show="price"] .quote-panel.has-disc :is(.items-head, .item) { grid-template-columns: 28px minmax(0, 1fr) 104px 76px 104px 124px 36px; }
  .quote-panel.has-disc .h-disc { display: block; }
  .quote-panel.has-disc .i-disc { display: flex; }
  .i-name, .i-qty, .i-rate { display: flex; }
  .i-num, .i-weight, .i-amount { display: block; }
  .i-remove { display: grid; }
}

/* Price list: like the quote, without quantities, weights and amounts. Phones: number, name, discount
   (only when the list has a discount rate product) and price on one line; tapping it opens the pop-up.
   Wide (desktop): a table to type in. It switches on the panel's width. */
.list-panel { container: list / inline-size; }
.litem + .litem { border-top: 1px solid var(--border); }
.litem.flash { animation: flash 1s ease; }
.list-panel :is(.lines-head, .l-summary) { grid-template-columns: var(--num-w, 16px) minmax(0, 1fr) var(--val-w, 64px); }
.list-panel.has-disc :is(.lines-head, .l-summary) { grid-template-columns: var(--num-w, 16px) minmax(0, 1fr) var(--disc-w, 40px) var(--val-w, 64px); }
.list-panel:not(.has-disc) .s-disc { display: none; }
.s-disc { color: var(--muted); font-size: 14px; text-align: center; white-space: nowrap; font-variant-numeric: tabular-nums; }
.lines-head .s-disc { font-size: 12px; }
.list-note { margin: 0; padding: 10px 16px; border-top: 1px solid var(--border); background: var(--surface-2); border-radius: 0 0 var(--radius) var(--radius); color: var(--muted); font-size: 13px; }
@container list (min-width: 600px) {
  .list-panel :is(.lines-head, .l-summary) { display: none; }
  .list-table-head, .litem {
    display: grid; grid-template-columns: 28px minmax(0, 1fr) 132px 36px;
    gap: 12px; align-items: center; padding: 4px 16px;
  }
  /* short lines, as on the quote: slimmer boxes and ✕, close together */
  .litem :is(.i-disc, .i-rate) input { height: 34px; }
  .litem .i-remove { height: 32px; }
  .list-panel.has-disc :is(.list-table-head, .litem) { grid-template-columns: 28px minmax(0, 1fr) 84px 132px 36px; }
  .list-table-head {
    padding-top: 8px; padding-bottom: 8px; background: var(--surface-2); border-bottom: 1px solid var(--border);
    color: var(--muted); font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  }
  .list-panel.has-disc .list-table-head .h-disc { display: block; }
  .list-panel.has-disc .litem .i-disc { display: flex; }
  .litem :is(.i-name, .i-rate) { display: flex; }
  .litem .i-num { display: block; }
  .litem .i-remove { display: grid; }
}

.empty { padding: 40px 16px; text-align: center; color: var(--muted); }
.empty p { margin: 4px 0; }
.quote-foot { display: flex; justify-content: flex-end; padding: 16px; background: var(--surface-2); border-top: 1px solid var(--border); border-radius: 0 0 var(--radius) var(--radius); }
/* Totals, on the right under the quote: as on the image, each value ends at the same right edge with
   its label just before it (not at the far side of a wide box). */
.totals { padding: 14px 18px 16px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.totals .trow { justify-content: flex-end; }
.totals .hint { text-align: right; }
.trow { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 5px 0; }
.trow strong { font-variant-numeric: tabular-nums; }
.t-big { font-size: 22px; }
.trow.grand { margin-top: 6px; padding-top: 11px; border-top: 1px solid var(--border); font-size: 17px; } /* the money Total: plain, no coloured band */
.trow.grand span { font-weight: 700; }
.trow.grand strong { font-size: 22px; }

/* The WhatsApp number box of the quote and of the price list (computers): for a customer not saved
   in the phone. A small box in the title row, after "Quote" or "Price list"; when the card is too
   narrow for it, on a row of its own under the title. What is wrong with the number (or, on a touch
   screen, the tap that comes next) shows in a small bubble under it, over the table's headings.
   Phones: a number button in the bottom bar instead, which opens a pop-up (see the phone layout). */
.send-to { position: relative; flex: 0 1 150px; min-width: 120px; }
@container quote (max-width: 539px) {
  .quote-panel .quote-head { flex-wrap: wrap; }
  .quote-panel .send-to { order: 9; flex: 0 0 100%; }
  .quote-panel .send-to .to-box { max-width: 190px; }
}
@container list (max-width: 379px) {
  .list-panel .quote-head { flex-wrap: wrap; }
  .list-panel .send-to { order: 9; flex: 0 0 100%; }
  .list-panel .send-to .to-box { max-width: 190px; }
}
.to-box { position: relative; }
.to-input {
  width: 100%; height: 38px; padding: 0 30px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font-size: 14px;
}
.send-to .to-input:placeholder-shown { padding-right: 8px; } /* (no ✕ while empty: the words get the room) */
.to-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent); }
.to-icon { position: absolute; top: 50%; left: 8px; width: 16px; height: 16px; transform: translateY(-50%); color: #25d366; pointer-events: none; }
.to-clear {
  position: absolute; top: 50%; right: 3px; width: 28px; height: 28px; transform: translateY(-50%);
  border: 0; border-radius: 50%; background: none; color: var(--muted); font-size: 13px; cursor: pointer;
}
.to-clear:hover { background: var(--surface-2); color: var(--text); }
.to-note {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 6; width: max-content; max-width: 330px;
  margin: 0; padding: 7px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); box-shadow: var(--shadow); color: var(--muted); font-size: 13px; line-height: 1.4;
}
.to-note.error { border-color: color-mix(in srgb, var(--danger) 40%, var(--surface)); color: var(--danger); font-weight: 600; }
.to-note .link-inline { white-space: nowrap; }
/* Phones: the pop-up for the number. Bigger box and words, easy to type in. */
.dialog-to .to-input { height: 50px; padding: 0 46px 0 44px; font-size: 20px; }
.dialog-to .to-icon { left: 13px; width: 22px; height: 22px; }
.dialog-to .to-clear { right: 5px; width: 40px; height: 40px; font-size: 15px; }
.dialog-to .form-msg { margin: 10px 0 0; }
.to-help { margin: 10px 0 12px; color: var(--muted); font-size: 14px; line-height: 1.45; }
.to-help + .to-help { margin-top: -4px; }

/* WhatsApp button. Computers: round, floating at the bottom right.
   Phones: at the end of the totals bar along the bottom (see the phone layout). */
.bottom-bar { position: fixed; right: 24px; bottom: 24px; z-index: 25; }
.bb-totals, .bb-list, .bb-to, .to-btn { display: none; }
.wa-btn {
  position: relative; display: grid; place-items: center; flex: none; width: 58px; height: 58px; padding: 0;
  border: 0; border-radius: 50%; background: #25d366; color: #fff; cursor: pointer; touch-action: manipulation;
  box-shadow: 0 4px 14px rgba(16, 24, 40, .25); transition: transform .15s, opacity .15s, box-shadow .15s;
}
.wa-btn svg { width: 31px; height: 31px; }
.wa-btn:hover { transform: scale(1.06); box-shadow: 0 6px 18px rgba(16, 24, 40, .3); }
.wa-btn:active { transform: scale(.95); }
.wa-btn.is-empty { opacity: .45; box-shadow: none; transform: none; } /* nothing to send yet */
.wa-btn.busy { opacity: .7; cursor: progress; }
/* Phones, with a number in the box: the tap that comes next, 1 (the hello) or 2 (the image) */
.wa-btn[data-step]::after {
  content: attr(data-step); position: absolute; top: -4px; right: -4px; display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%; background: #fff; color: #128c4b; box-shadow: 0 0 0 2px #25d366;
  font-size: 13px; font-weight: 800; line-height: 1;
}

/* Quotation | Price list tabs (the same look as the Admin tabs) */
.layout > .doc-tabs { margin: 0; }

/* Desk layout: search list on the left; on the right the tabs, and the quote or price list under them */
@media (min-width: 900px) {
  .layout {
    grid-template-columns: minmax(300px, 370px) minmax(0, 1fr); grid-template-areas: "search tabs" "search doc";
    grid-template-rows: auto 1fr; /* the tab row only as tall as the tabs: the long product list beside them must not stretch it */
    align-items: start; padding-bottom: 100px; /* room to scroll the totals clear of the WhatsApp button */
  }
  .doc-tabs { grid-area: tabs; }
  /* the title keeps its words; the WhatsApp number box beside it gives way first */
  .quote-head h1 { flex: 1 0 auto; }
  .search-panel { grid-area: search; }
  .quote-panel, .list-panel { grid-area: doc; }
  /* No space between the tabs and the quote (or price list): together they are one card, the tabs as
     its top row. (clip-path keeps each part's shadow off the seam between them.) */
  .layout { row-gap: 0; }
  .layout > .doc-tabs {
    padding: 0 8px; background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius) var(--radius) 0 0; box-shadow: var(--shadow); clip-path: inset(-16px -16px 0 -16px);
  }
  .quote-panel, .list-panel { border-top: 0; border-radius: 0 0 var(--radius) var(--radius); clip-path: inset(0 -16px -16px -16px); }
  .search-panel {
    position: sticky; top: calc(var(--topbar-h) + 16px); display: flex; flex-direction: column;
    max-height: calc(100vh - var(--topbar-h) - 32px); overflow: hidden;
  }
  .search-panel .results { flex: 1; overflow-y: auto; }
}

/* Phone layout: search on top, results drop down while typing, totals bar at the bottom */
@media (max-width: 899px) {
  .layout { gap: 12px; padding: 0 12px 112px; }
  .item, .litem { scroll-margin: 150px 0 96px; } /* an opened line scrolls clear of the search box and the totals bar */
  /* with the WhatsApp number line on the bar, the bar is taller */
  body.has-to .layout { padding-bottom: 150px; }
  body.has-to :is(.item, .litem) { scroll-margin-bottom: 134px; }
  body.has-to > .toasts { bottom: calc(134px + env(safe-area-inset-bottom)); }
  /* the tabs: a row under the top bar, edge to edge, two equal halves (it scrolls away; the search stays) */
  .layout > .doc-tabs { margin: 0 -12px -12px; padding: 0 8px; background: var(--surface); } /* (-12px: no gap above the search) */
  .doc-tabs .tab { flex: 1; padding: 9px 8px; }
  .search-panel {
    position: sticky; top: var(--topbar-h); z-index: 15;
    margin: 0 -12px; border-width: 0 0 1px; border-radius: 0; box-shadow: 0 2px 8px rgba(16, 24, 40, .06);
  }
  .search-panel .results { display: none; }
  body.searching .search-panel .results {
    display: block; position: absolute; top: 100%; left: 0; right: 0; max-height: 58vh; overflow-y: auto;
    background: var(--surface); border-bottom: 1px solid var(--border); box-shadow: var(--shadow-lg);
  }
  body.searching::after { content: ""; position: fixed; inset: 0; z-index: 10; background: rgba(10, 15, 22, .35); }
  .bottom-bar {
    right: 0; bottom: 0; left: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
    padding: 8px 12px calc(8px + env(safe-area-inset-bottom)) 16px;
    background: var(--surface); border-top: 1px solid var(--border); box-shadow: 0 -4px 16px rgba(16, 24, 40, .08);
  }
  body.searching .bottom-bar { display: none; }
  .bottom-bar .wa-btn { width: 52px; height: 52px; box-shadow: none; }
  .bottom-bar .wa-btn svg { width: 29px; height: 29px; }
  /* The WhatsApp number button, just before the green one: opens the pop-up for the number of a
     customer not saved in the phone. Green while there is a number. */
  .to-btn {
    display: grid; place-items: center; flex: none; width: 44px; height: 44px; padding: 0;
    border: 1px solid var(--border-strong); border-radius: 50%; background: var(--surface); color: var(--muted);
    cursor: pointer; touch-action: manipulation;
  }
  .to-btn svg { width: 20px; height: 20px; }
  .to-btn.has-num { border-color: #25d366; background: color-mix(in srgb, #25d366 12%, var(--surface)); color: #128c4b; }
  /* With a number: a line across the top of the bar with it and the tap that comes next; tap it to
     change the number, ✕ to remove it */
  .bb-to { display: flex; flex: 1 1 100%; align-items: center; gap: 4px; min-width: 0; margin: 0 -6px 0 -8px; }
  .bb-to-edit {
    display: flex; flex: 1; align-items: center; gap: 6px; min-width: 0; height: 32px; padding: 0 10px 0 8px;
    border: 0; border-radius: 8px; background: color-mix(in srgb, #25d366 12%, var(--surface));
    color: var(--text); font: inherit; font-size: 13px; text-align: left; cursor: pointer;
  }
  .bb-to-edit svg { flex: none; width: 16px; height: 16px; color: #25d366; }
  .bb-to-edit strong { flex: none; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .bb-to-step { min-width: 0; overflow: hidden; color: var(--muted); text-overflow: ellipsis; white-space: nowrap; }
  .bb-to-step:not(:empty)::before { content: "· "; }
  .bb-to.error .bb-to-edit { background: var(--danger-soft); }
  .bb-to.error .bb-to-step { color: var(--danger); font-weight: 600; }
  .bb-to-clear {
    flex: none; width: 32px; height: 32px; border: 0; border-radius: 50%; background: none;
    color: var(--muted); font-size: 14px; cursor: pointer;
  }
  /* The totals live here on phones (as on the image: each value ends at the same right edge, its
     label just before it), so the quote itself does not repeat them. Only the "no price yet" note
     stays under the list. */
  .bb-totals { display: flex; flex-direction: column; align-items: flex-end; min-width: 0; margin-right: auto; line-height: 1.25; }
  .bb-row { display: flex; align-items: baseline; gap: 8px; white-space: nowrap; }
  .bb-row span { color: var(--muted); font-size: 13px; }
  .bb-row strong { font-size: 18px; font-weight: 800; font-variant-numeric: tabular-nums; }
  body[data-show="both"] #bb-weight strong { font-size: 15px; font-weight: 700; } /* with both, the money Total is the big one */
  /* Price list tab: no totals, just how many products are in it */
  body[data-tab="list"] .bb-totals { display: none; }
  body[data-tab="list"] .bb-list { display: block; margin-right: auto; color: var(--muted); font-size: 14px; font-weight: 600; }
  .totals .trow { display: none; }
  .totals { padding: 0; border: 0; border-radius: 0; background: none; }
  .totals .hint { margin: 0; text-align: left; }
  .quote-foot { display: block; padding: 10px 16px; }
  .quote-foot.no-note { display: none; }
  .send-to { display: none; } /* (the number button in the bottom bar instead) */
}
@media (max-width: 600px) {
  /* Phones: the quote runs edge to edge right under the search box, so product names have room.
     Its title row is slim: title, a small Weight / Price / Both switch and Clear, on one line. */
  .layout { gap: 0; }
  .layout > .doc-tabs { margin-bottom: 0; } /* (no gap to cancel here) */
  .search-box { padding: 8px 12px; } /* less white above and below the search box */
  .quote-panel, .list-panel { margin: 0 -12px; border-width: 0 0 1px; border-radius: 0; box-shadow: none; }
  .quote-foot, .list-note { border-radius: 0; }
  .quote-head { gap: 6px; padding: 6px 6px 6px 16px; }
  .quote-head h1 { font-size: 16px; }
  .quote-head .seg { padding: 2px; border-radius: 9px; }
  .quote-head .seg span { padding: 5px 10px; border-radius: 7px; font-size: 13px; }
  #btn-clear { padding: 0 8px; }
}

/* ---------- Dialogs ---------- */
.dialog {
  width: min(560px, calc(100vw - 24px)); max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px);
  padding: 0; border: 0; border-radius: 16px; background: var(--surface); color: var(--text); box-shadow: var(--shadow-lg);
}
.dialog[open] { display: flex; flex-direction: column; }
.dialog::backdrop { background: rgba(10, 15, 22, .55); }
.dialog > form { display: flex; flex-direction: column; min-height: 0; }
.dialog-head { display: flex; flex: none; align-items: center; gap: 8px; padding: 12px 12px 12px 18px; border-bottom: 1px solid var(--border); }
.dialog-head h3 { flex: 1; margin: 0; font-size: 17px; }
.dialog-body { min-height: 0; padding: 16px 18px 4px; overflow-y: auto; }
.dialog-foot { display: flex; flex: none; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--border); }
.dialog-narrow { width: min(420px, calc(100vw - 24px)); }
.dialog-confirm { width: min(380px, calc(100vw - 32px)); }
.dialog-confirm .dialog-body { padding: 22px 20px 14px; }
.confirm-text { margin: 0; font-size: 16px; font-weight: 600; }
.dialog-confirm .dialog-foot { border-top: 0; padding-top: 4px; }
.dialog-confirm .dialog-foot .btn { min-width: 96px; min-height: 44px; }

/* Quantity pop-up */
.dialog-qty .dialog-head { align-items: flex-start; }
.qty-head { flex: 1; min-width: 0; padding-top: 7px; } /* lines the name up with the close button */
.qty-head h3 { margin: 0; font-size: 18px; overflow-wrap: anywhere; }
.qty-field > span { text-align: center; }
.field.qty-field input { height: 58px; font-size: 26px; font-weight: 700; text-align: center; }
.field.rate-field input, .field.disc-field input { height: 50px; font-size: 20px; }
.qty-preview { margin: 2px 0 12px; padding: 8px 14px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.qty-preview strong { font-size: 16px; }
.dialog-qty .dialog-foot .btn-primary { min-width: 130px; }
/* Discount rate products: a Discount % box before the price */
.disc-field { display: none; }
.dialog-qty.is-disc .disc-field { display: flex; }
/* Price list: no quantity, weight or amount; just the price (and the discount for discount rate products) */
.dialog-qty.for-list :is(#qty-field, .qty-preview) { display: none; }
@media (min-width: 601px) { /* computers: quantity on top, discount and price side by side under it */
  .dialog-qty.is-disc .dialog-body { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 12px; }
  .dialog-qty.is-disc :is(#qty-field, .qty-preview, .form-msg) { grid-column: 1 / -1; }
}

/* Phones: pop-ups you type in sit at the top of the screen and stay inside the part the keyboard
   leaves free (--view-top and --view-h come from core.js), so the keyboard never covers them. */
@media (max-width: 600px) {
  .dialog:not(.dialog-confirm) {
    width: min(420px, calc(100% - 16px)); max-width: none;
    margin: calc(var(--view-top, 0px) + 8px) auto auto;
    max-height: calc(var(--view-h, 100dvh) - 16px);
  }
  /* The quantity pop-up is also squeezed: quantity and price side by side, weight and amount under them */
  .dialog-qty .dialog-head { padding: 6px 6px 6px 14px; }
  .qty-head { padding-top: 8px; }
  .qty-head h3 { font-size: 16px; }
  .dialog-qty .dialog-body { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 10px; padding: 12px 14px 2px; }
  .dialog-qty .field { margin-bottom: 8px; }
  .field.qty-field input { height: 48px; padding: 0 4px; font-size: 22px; }
  /* gap 32px puts the weight and amount right under the middle of the quantity and price boxes */
  .qty-preview { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; margin: 0 0 10px; padding: 6px 10px; }
  .dialog-qty .form-msg { grid-column: 1 / -1; margin: 0 0 10px; padding: 8px 12px; }
  .qty-preview .trow { flex-direction: column; align-items: center; gap: 0; padding: 0; text-align: center; }
  .qty-preview .trow span { font-size: 12px; }
  .qty-preview strong { overflow-wrap: anywhere; }
  /* Weight only: no price box, so the quantity box takes the whole row. A single figure under the
     boxes (weight, or amount) sits in the middle. */
  body[data-tab="quote"][data-show="weight"] .dialog-qty .qty-field { grid-column: 1 / -1; }
  body:not([data-show="both"]) .qty-preview { grid-template-columns: minmax(0, 1fr); }
  /* Discount rate products: quantity, discount and price in a row of three; the weight under the
     quantity and the amount under the price (the same 32px gap keeps them under the boxes' middles) */
  .dialog-qty.is-disc .dialog-body { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body:not([data-show="weight"]) .dialog-qty.is-disc .qty-preview { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dialog-qty.is-disc #qty-amount-row { grid-column: 3; }
  /* Price list: the price alone across the pop-up, or the discount and price side by side */
  .dialog-qty.for-list .dialog-body { grid-template-columns: minmax(0, 1fr); }
  .dialog-qty.for-list.is-disc .dialog-body { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dialog-qty .dialog-foot { padding: 8px 14px; }
  .dialog-qty .dialog-foot .btn { min-height: 44px; }
}

/* ---------- Toasts ---------- */
.toasts {
  position: fixed; bottom: calc(96px + env(safe-area-inset-bottom)); left: 50%; z-index: 1000;
  display: flex; flex-direction: column; gap: 8px; width: min(92vw, 420px); transform: translateX(-50%); pointer-events: none;
}
.toast {
  padding: 12px 14px; border-radius: 10px; background: #1d2733; color: #fff; font-size: 14px;
  box-shadow: var(--shadow-lg); transition: opacity .3s, transform .3s;
}
.toast-error { background: #b3362a; }
.toast-success { background: #17784b; }
.toast.hide { opacity: 0; transform: translateY(8px); }
@media (min-width: 900px) { .toasts { bottom: 24px; } }
/* In a pop-up: just above the phone keyboard (--view-top/--view-h, from core.js), or the bottom of the screen */
.dialog .toasts { bottom: calc(100% - var(--view-top, 0px) - var(--view-h, 100%) + 12px + env(safe-area-inset-bottom)); }

/* ---------- Sign-in pages ---------- */
.page-auth {
  display: grid; place-items: center; min-height: 100vh; min-height: 100dvh; padding: 24px 16px;
  background: var(--bg);
}
.auth { width: 100%; max-width: 410px; }
.auth-card { padding: 24px; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; box-shadow: var(--shadow-lg); }
.auth-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.auth-brand img { flex: none; border-radius: 10px; }
.auth-brand strong { display: block; font-size: 17px; }
.auth h1 { margin: 0 0 14px; font-size: 22px; }
.auth .muted { margin-top: -6px; }
.auth-help { margin: 14px 0 0; color: var(--muted); font-size: 14px; text-align: center; }
.pw-wrap { position: relative; display: block; }
.pw-wrap input { padding-right: 68px; }
.pw-toggle {
  position: absolute; top: 50%; right: 4px; padding: 6px 10px; transform: translateY(-50%);
  border: 0; background: none; color: var(--link); font-weight: 600; cursor: pointer;
}

/* ---------- Admin ---------- */
.admin { max-width: 1100px; margin: 0 auto; padding: 16px; }
.tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--border); }
.tab {
  margin-bottom: -1px; padding: 10px 16px; border: 0; border-bottom: 3px solid transparent;
  background: none; color: var(--muted); font-weight: 700; cursor: pointer;
}
.tab.active { color: var(--link); border-bottom-color: var(--link); }
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 10px; }
.toolbar input[type="search"] { flex: 1; width: auto; min-width: 200px; }
.list-card { overflow: hidden; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
.plist-head, .prow { display: grid; grid-template-columns: minmax(0, 1fr) 120px 120px 110px 120px 60px; gap: 12px; align-items: center; padding: 4px 16px; }
.plist-head, .ulist-head {
  padding-top: 8px; padding-bottom: 8px; background: var(--surface-2); border-bottom: 1px solid var(--border);
  color: var(--muted); font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
}
.prow + .prow { border-top: 1px solid var(--border); }
.p-name { display: flex; flex-direction: column; min-width: 0; }
.p-name strong { overflow-wrap: anywhere; }
.p-name small { color: var(--muted); }
.p-name small:empty { display: none; }
.p-mobile-meta { display: none; }
/* Unit of measure, weight per unit, pricing and standard price: centred in their columns, under their headings */
.p-unit, .p-kg, .p-price, .p-std { min-width: 0; text-align: center; }
.p-kg, .p-std { font-variant-numeric: tabular-nums; }
.p-actions { text-align: right; }
.p-actions .btn-small { min-height: 26px; } /* computers: slim rows, close together */
/* Phones and tablets: no room for the columns, so they go in one line under the name */
@media (max-width: 899px) {
  .plist-head, .ulist-head { display: none; }
  .prow { grid-template-columns: minmax(0, 1fr) auto; }
  .p-unit, .p-kg, .p-price, .p-std { display: none; }
  .p-mobile-meta { display: block; }
  .p-actions .btn-small { min-height: 32px; } /* easy to tap */
}
/* Product form: Net rate / Discount rate, as a two-way switch the width of the form */
.field .seg span { padding: 7px 12px; font-size: 15px; }

.switch { position: relative; display: inline-block; flex: none; width: 44px; height: 26px; }
.switch input { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.switch span { position: absolute; inset: 0; border-radius: 999px; background: var(--border-strong); transition: background .2s; }
.switch span::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25); transition: transform .2s;
}
.switch input:checked + span { background: var(--success); }
.switch input:checked + span::after { transform: translateX(18px); }
.switch input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }
.switch input:disabled { cursor: not-allowed; }
.switch input:disabled + span { opacity: .5; }

/* Users: like the product list. Name and email | Role | Sign in | Edit */
.ulist-head, .urow { display: grid; grid-template-columns: minmax(0, 1fr) 110px 130px 60px; gap: 12px; align-items: center; padding: 4px 16px; }
.urow { min-height: 50px; }
.urow + .urow { border-top: 1px solid var(--border); }
.u-main { display: flex; flex-direction: column; min-width: 0; }
.u-main strong { overflow-wrap: anywhere; }
.u-main small { color: var(--muted); overflow-wrap: anywhere; }
.u-role, .u-status { text-align: center; }
.urow.inactive :is(.u-main, .u-role) { opacity: .6; }
.urow.inactive .u-status { color: var(--danger); }
.u-mobile-meta { display: none; }
.badge {
  display: inline-block; margin-left: 4px; padding: 1px 8px; border-radius: 999px; vertical-align: 1px;
  background: color-mix(in srgb, var(--brand) 14%, var(--surface)); color: var(--link); font-size: 11px; font-weight: 700;
}
@media (max-width: 899px) {
  .ulist-head { display: none; }
  .urow { grid-template-columns: minmax(0, 1fr) auto; }
  .u-role, .u-status { display: none; }
  .u-mobile-meta { display: inline; }
}
/* "Can sign in" with its switch on the same line */
.field.switch-field { flex-direction: row; align-items: center; justify-content: space-between; }
.dialog .hint:not(.field > .hint) { margin: 0 0 12px; }
/* "import the file": a button that reads as a link in the sentence */
.link-inline { padding: 0; border: 0; background: none; color: var(--link); font: inherit; font-weight: 600; cursor: pointer; text-decoration: underline; }
#import-msg { white-space: pre-line; } /* one line per mistake */
