/* Poshak web theme — GENERATED from theme/tokens.json (THEME.md).
 * Change colors there, never here; no hex in templates (CI check TASK-127).
 *
 * v3: Material Design 3 idiom. Brand tokens map onto MD3 color roles:
 *   primary            -> --color-primary        (#7B2D43 maroon)
 *   on-primary         -> --color-on-primary
 *   secondary-container-> primary @ 14% mix (derived, --md-secondary-container)
 *   surface            -> --color-surface
 *   surface-variant    -> --color-surface-alt
 *   outline            -> --color-border
 *   error              -> --color-danger
 * Shape scale: input 10 (text fields/selects, admin-wide) · sm 8 (chips) ·
 *   md 12 (cards) · full (buttons/nav).
 * Elevation: MD level-1/2 shadows; hover uses 8% state layers.
 */

:root {
  --color-primary: #7B2D43;
  --color-primary-hover: #652335;
  --color-on-primary: #FFFFFF;
  --color-accent: #B8912E;
  --color-background: #FAF7F2;
  --color-surface: #FFFFFF;
  --color-surface-alt: #F3EEE7;
  --color-text-primary: #1F1B16;
  --color-text-secondary: #6E6459;
  --color-text-disabled: #A79D91;
  --color-border: #E7E0D6;
  --color-success: #2E7D4F;
  --color-warning: #B26A00;
  --color-danger: #B3261E;
  --color-info: #2F5D8A;
  --color-refund: #6C4FA0;
  --color-overlay: rgba(31, 27, 22, 0.45);

  --space-xs: 4px;  --space-sm: 8px;   --space-md: 12px; --space-lg: 16px;
  --space-xl: 20px; --space-2xl: 24px; --space-3xl: 32px;

  --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-full: 999px;
  /* All text inputs, textareas, and the custom select trigger (which
   * stands in visually for a native <select>) share this shape, admin
   * panel-wide -- one token so they can never drift apart again. */
  --radius-input: 10px;

  --motion-fast: 150ms; --motion-normal: 250ms;

  /* Derived MD3 roles (token-mixed, never raw hex) */
  --md-secondary-container: color-mix(in srgb, var(--color-primary) 14%, var(--color-surface));
  --md-state-hover: color-mix(in srgb, var(--color-text-primary) 8%, transparent);
  --md-elevation-1: 0 1px 2px rgba(0,0,0,.10), 0 1px 3px 1px rgba(0,0,0,.06);
  --md-elevation-2: 0 1px 2px rgba(0,0,0,.12), 0 2px 6px 2px rgba(0,0,0,.08);
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--color-primary) 22%, transparent);
}

[data-theme="dark"] {
  --color-primary: #D98BA0;
  --color-primary-hover: #E8A3B5;
  --color-on-primary: #3A1420;
  --color-accent: #D9B65A;
  --color-background: #141210;
  --color-surface: #1E1A17;
  --color-surface-alt: #26211D;
  --color-text-primary: #F2EDE6;
  --color-text-secondary: #A79D91;
  --color-text-disabled: #6E6459;
  --color-border: #2E2822;
  --color-success: #5BBD85;
  --color-warning: #E0A040;
  --color-danger: #E57373;
  --color-info: #7FA8CF;
  --color-refund: #B79FE8;
  --color-overlay: rgba(0, 0, 0, 0.6);

  --md-secondary-container: color-mix(in srgb, var(--color-primary) 22%, var(--color-surface));
  --md-state-hover: color-mix(in srgb, var(--color-text-primary) 10%, transparent);
  --md-elevation-1: 0 1px 2px rgba(0,0,0,.45);
  --md-elevation-2: 0 2px 6px rgba(0,0,0,.5);
}

/* --- Base (MD3 type: Roboto) ------------------------------------------- */

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "Roboto", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
    sans-serif;
  font-size: 14px;              /* MD3 body-medium */
  line-height: 1.45;
  letter-spacing: .15px;
  color: var(--color-text-primary);
  background: var(--color-background);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--color-primary); text-decoration: none; }
a:hover { color: var(--color-primary-hover); }

/* --- Layout: navigation drawer + content -------------------------------- */

.layout { display: flex; min-height: 100vh; }

.sidebar {
  width: 244px;
  flex-shrink: 0;
  /* Column flex so the sign-out control (margin-top: auto) sits at the
   * bottom; the brand + nav children stack exactly as they did as blocks. */
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  padding: var(--space-2xl) var(--space-md);
  /* Pin the nav in place so a long page (e.g. a tall form) scrolls the
   * .content column only — the sidebar stays put instead of scrolling away
   * with the page. Its own contents scroll independently if the nav list
   * ever grows taller than the viewport. Overridden to position:fixed by
   * the off-canvas drawer rules in the @media (max-width: 900px) block. */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

/* Brand lockup: small colored mark + wordmark, shared by the sidebar and the
 * mobile top bar (sizes tuned per-context below). */
.brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--color-primary);
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--color-primary);
  color: var(--color-on-primary);
}

.brand-mark svg { width: 18px; height: 18px; }

.brand-name {
  font-weight: 500;
  letter-spacing: 0;
}

.sidebar .brand {
  margin: 0 var(--space-md) var(--space-3xl);
}

.sidebar .brand-name {
  font-size: 22px;              /* MD3 title-large */
}

/* MD3 drawer item: 40px pill, label-large, state layers */
.sidebar nav a {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  height: 40px;
  padding: 0 var(--space-xl);
  border-radius: var(--radius-full);
  color: var(--color-text-secondary);
  font-size: 14px;             /* MD3 label-large */
  font-weight: 500;
  letter-spacing: .1px;
  margin-bottom: var(--space-xs);
  white-space: nowrap;
  transition: background var(--motion-fast);
}

.sidebar nav a:hover { background: var(--md-state-hover); color: var(--color-text-primary); }

/* Icon sits left of the label; stroke follows the link's own color so it
 * automatically shades along with hover/active/dark-mode text color. */
.nav-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.nav-icon svg { width: 100%; height: 100%; }

/* Active item: secondary container, on-secondary-container ink */
.sidebar nav a.active {
  background: var(--md-secondary-container);
  color: var(--color-primary);
}

[data-theme="dark"] .sidebar nav a.active { color: var(--color-text-primary); }

/* Small-screen top bar + hamburger that opens the same drawer as desktop
 * (see the @media (max-width: 900px) block below). Hidden on wide screens. */
.mobile-topbar { display: none; }
.sidebar-backdrop { display: none; }

.menu-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-primary);
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--motion-fast);
}
.menu-btn:hover { background: var(--md-state-hover); }
.menu-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.content {
  flex: 1;
  padding: var(--space-3xl);
  /*max-width: 1000px;*/
  min-width: 0;
}

.page-title {
  font-size: 24px;             /* MD3 headline-small */
  font-weight: 500;
  letter-spacing: 0;
  margin: 0 0 var(--space-2xl);
}

/* Page title + a top-right action button on the same row (currently the
   design add/edit form's second "Save" button, next to its own bottom
   one -- both submit the same form). flex-wrap keeps it usable at phone
   width: the button drops below the title instead of squeezing it down
   to nothing. The title's own bottom margin is zeroed since the row
   itself now carries that spacing. */
.page-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-2xl);
}
.page-header-row .page-title { margin-bottom: 0; }

/* --- Cards: MD3 elevated card ------------------------------------------- */

.card {
  background: var(--color-surface);
  border: none;
  border-radius: var(--radius-md);
  box-shadow: var(--md-elevation-1);
  padding: var(--space-2xl);
  margin-bottom: var(--space-2xl);
}

[data-theme="dark"] .card { border: 1px solid var(--color-border); box-shadow: none; }

a.card { transition: box-shadow var(--motion-fast); }
a.card:hover { box-shadow: var(--md-elevation-2); }

/* --- Buttons: MD3 filled / outlined, 40px pill --------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 var(--space-2xl);
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  font-size: 14px;             /* MD3 label-large */
  font-weight: 500;
  letter-spacing: .1px;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--motion-fast), box-shadow var(--motion-fast);
  margin-bottom: var(--space-xs);
  text-decoration: none;
}

.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Filled button */
.btn-primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  box-shadow: var(--md-elevation-1);
}
.btn-primary:hover {
  background: var(--color-primary-hover);
  color: var(--color-on-primary);
  box-shadow: var(--md-elevation-2);
}

/* Outlined button */
.btn-secondary {
  background: transparent;
  color: var(--color-primary);
  border-color: var(--color-border);
}
.btn-secondary:hover { background: var(--md-state-hover); color: var(--color-primary); }

.btn[disabled] { opacity: .38; cursor: not-allowed; box-shadow: none; }

/* --- Data table (MD3) ---------------------------------------------------- */

.table-wrap { overflow-x: auto; }

table.list {
  width: 100%;
  border-collapse: collapse;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  text-wrap: nowrap;
}

table.subdesign-table th.subdesign-name-col {
  min-width: 220px;
}

table.list th,
table.list td {
  text-align: left;
  height: 48px;
  padding: var(--space-sm) var(--space-lg);
  border-bottom: 1px solid var(--color-border);
  font-size: 14px;
}

table.list th {
  background: var(--color-surface);
  color: var(--color-text-secondary);
  font-weight: 500;            /* MD3 header: medium, sentence case */
  text-transform: none;
  letter-spacing: .1px;
}

table.list tbody tr { transition: background var(--motion-fast); }
table.list tbody tr:hover { background: var(--md-state-hover); }

table.list tr:last-child td { border-bottom: none; }

/* --- Invoice/estimate detail (billing/document_detail.html) ------------- */

table.bill-items th.num,
table.bill-items td.num { text-align: right; }

/* Receipt-style summary instead of a second sparse table -- boxed and
 * tinted so it reads as one grouped block sitting right below the items
 * table, not a small table floating in empty space. */
.bill-totals {
  max-width: 340px;
  margin-left: auto;
  margin-top: var(--space-xl);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-md) var(--space-xl);
}
.bill-totals-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-lg);
  padding: var(--space-sm) 0;
  font-size: 14px;
  color: var(--color-text-secondary);
}
.bill-totals-row + .bill-totals-row { border-top: 1px solid var(--color-border); }
.bill-totals .bill-totals-grand {
  font-weight: 700;
  color: var(--color-text-primary);
  font-size: 16px;
  border-top: 1.5px solid var(--color-primary);
  margin-top: var(--space-xs);
  padding-top: var(--space-md);
}

/* Designs table: thumbnail + name cell, and a stable-width quantity cell so
 * rows without a low-stock/sold-out chip don't shift left of rows that have
 * one. */
.design-cell {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.design-thumb {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  object-fit: cover;
}

.design-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-surface-alt);
  color: var(--color-text-disabled);
}

.design-thumb-empty svg { width: 20px; height: 20px; }

.design-name { font-weight: 500; }

.qty-cell {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.qty-value { font-variant-numeric: tabular-nums; }

/* --- Chips: MD3 assist/tonal chips --------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-sm);   /* MD3 chip shape: 8dp */
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .1px;
  background: var(--color-surface-alt);
  color: var(--color-text-secondary);
  border: 1px solid transparent;
  margin: 0 var(--space-xs) 0 0;
}

a.chip { border-color: var(--color-border); background: transparent; }
a.chip:hover { background: var(--md-state-hover); }

.chip-success { background: color-mix(in srgb, var(--color-success) 14%, transparent); color: var(--color-success); }
.chip-warning { background: color-mix(in srgb, var(--color-warning) 14%, transparent); color: var(--color-warning); }
.chip-danger  { background: color-mix(in srgb, var(--color-danger) 14%, transparent);  color: var(--color-danger); }
.chip-info    { background: color-mix(in srgb, var(--color-info) 14%, transparent);    color: var(--color-info); }
/* Refunded: its own tone (distinct from info/success/warning/danger)
 * so a refunded payment never reads as just another "paid" or
 * "pending" state at a glance. */
.chip-refunded { background: color-mix(in srgb, var(--color-refund) 14%, transparent); color: var(--color-refund); }

a.chip-info { background: var(--md-secondary-container); color: var(--color-primary); border-color: transparent; }

/* Designs list "Who can see" column: an icon-only chip (globe / lock)
 * instead of the "Show to everyone" / "Verified buyers only" text chip --
 * matches the same icon segmented control on the design form. Colors
 * reuse the existing .chip-info modifier above rather than a new pair of
 * rules. title= + role="img"/aria-label carry the same meaning the text
 * used to for a mouse (hover) and a screen reader; there's no tap
 * interaction here (unlike the Categories "+N more" button) so a
 * touch-only title= gap doesn't lose any functionality, just a hover
 * nicety. Left-aligned like every other column now (no more centering). */

.chip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: var(--radius-full);
  background: var(--color-surface-alt);
  color: var(--color-text-secondary);
}
.chip-icon svg { width: 14px; height: 14px; }
/* .chip-info/.chip-warning alone (defined above) lose to .chip-icon on
 * cascade order despite equal specificity -- these combined selectors win
 * over both. */
.chip-icon.chip-info {
  background: color-mix(in srgb, var(--color-info) 14%, transparent);
  color: var(--color-info);
}
.chip-icon.chip-warning {
  background: color-mix(in srgb, var(--color-warning) 14%, transparent);
  color: var(--color-warning);
}

/* "Who can see" column only: no chip fill behind the icon (just the bare
 * glyph), and a slightly bigger glyph than the other icon chips since it's
 * standing on its own without a background shape to help it read. Needs
 * `table.list td.visibility-cell` (element+class+element) to outrank the
 * plain `.chip-icon`/`.chip-icon.chip-info` rules above on specificity. */
table.list td.visibility-cell .chip-icon {
  background: transparent;
  width: 28px;
  height: 28px;
}
table.list td.visibility-cell .chip-icon svg { width: 18px; height: 18px; }

/* Designs list search + filter bar: one GET form -- a search box with an
 * inline magnifier icon, then the filter dropdowns (which reuse the same
 * enhanceSelect() UI as form selects, just sized to their content here
 * instead of the form fields' 100%/420px). Wraps onto extra lines on
 * narrow screens instead of squeezing. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}
.search-field {
  position: relative;
  /* No max-width -- the search box grows to fill whatever room the
   * toolbar row has left instead of stopping at a fixed width. */
  flex: 1 1 210px;
}
.search-field-icon {
  position: absolute;
  left: var(--space-md);
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: var(--color-text-secondary);
  pointer-events: none;
}
/* Beat the generic ".content input:not(...):not(...)..." rule's padding so
 * the text clears the magnifier icon: each of that rule's four :not()s
 * counts as a class, so it weighs (0,5,1) -- and it's declared LATER in
 * this file, so a tie isn't enough. The doubled .search-field plus
 * [type="search"] pushes this to (0,6,1), which wins outright. */
.content .filter-bar .search-field.search-field input[type="search"]:not([hidden]) {
  width: 100%;
  max-width: none;
  padding-left: calc(var(--space-md) * 2 + 18px);
}
/* Filter dropdowns: content-sized, not form-field-sized. The .select-wrap
 * width:100% default suits stacked form fields, not a toolbar row. Same
 * override for the raw <select> covers no-JS. No max-width -- these grow
 * with the rest of the bar instead of stopping at a fixed width. */
.filter-bar .select-wrap,
.content .filter-bar select {
  width: auto;
  min-width: 140px;
  flex: 0 1 auto;
}
.filter-bar .select-trigger { white-space: nowrap; }

/* Staff work types "Allowed work" filter: its option labels ("Manage
 * online payment settings", ...) run noticeably longer than the other
 * filter-bar dropdowns' (Turned on/Turned off, ...), so this one alone
 * gets a wider minimum instead of ellipsis-truncating down to the shared
 * 140px. Scoped to .filter-select-wide so every other filter-bar select
 * keeps its normal content-sized width. */
.filter-bar .select-wrap:has(> select.filter-select-wide),
.content .filter-bar select.filter-select-wide {
  min-width: 260px;
}

/* Date range trigger shows a two-date span ("22 Aug – 25 Aug 2026"),
 * noticeably longer than the other filter-bar dropdowns' 140px. */
.filter-bar .date-range-wrap { min-width: 220px; }

/* Keep a filter bar on one row on desktop (Payments has search + 3
 * filters + a date range); the search box shrinks to make room. Wraps
 * again below 1100px so narrow screens don't squeeze. */
@media (min-width: 1100px) {
  .filter-bar.filter-bar-nowrap { flex-wrap: nowrap; }
  .filter-bar.filter-bar-nowrap .search-field { min-width: 160px; }
}

/* --- Order processing (orders/processing.html) -------------------------- */
/* Step tabs: one per fulfilment status, each with its order count. Scrolls
 * sideways on narrow screens instead of wrapping. */
.process-tabs {
  display: flex;
  gap: var(--space-xs);
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-lg);
  overflow-x: auto;
  overflow-y: hidden;          /* the tabs' -1px underline overlap made it scroll vertically */
  scrollbar-width: none;       /* still swipe/scrolls sideways on narrow screens, just no bar */
}
.process-tabs::-webkit-scrollbar { display: none; }
.process-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  color: var(--color-text-secondary);
  text-decoration: none;
  font-weight: 500;
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
}
.process-tab:hover { color: var(--color-text-primary); background: var(--md-state-hover); }
.process-tab.is-active { color: var(--color-primary); border-bottom-color: var(--color-primary); }
.process-tab-count {
  min-width: 22px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  background: var(--md-secondary-container);
  color: var(--color-primary);
}
/* Bulk bar: a hint until orders are ticked, then that tab's actions. Sits
 * on the page's surface so it reads as part of the table below it. */
.bulk-bar {
  display: flex;
  align-items: center;
  min-height: 56px;
  padding: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.bulk-hint { color: var(--color-text-secondary); font-size: 13px; }
.bulk-bar .bulk-new-labels { margin-left: auto; }
.bulk-bar .bulk-new-labels[hidden] { display: none; }
.bulk-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); width: 100%; }
.bulk-actions[hidden], .bulk-actions [hidden] { display: none; }
.bulk-bar .btn { margin-bottom: 0; white-space: nowrap; }
.bulk-count { font-weight: 600; font-size: 13px; margin-right: var(--space-sm); }
.btn.btn-text.bulk-danger { color: var(--color-danger); margin-left: auto; }
.btn.btn-text.bulk-danger:hover { color: var(--color-danger); }
.btn.bulk-danger-fill { background: var(--color-danger); border-color: var(--color-danger); }
.btn.bulk-danger-fill:hover { background: var(--color-danger); filter: brightness(.92); }
/* The generic ".content input:not(...)x4" field rule weighs (0,5,1);
 * the doubled classes + :not() here outrank it for these compact inputs. */
.content .bulk-bar input.bulk-reason.bulk-reason:not([type="checkbox"]):not([type="radio"]),
.content .process-table input.process-tracking.process-tracking:not([type="checkbox"]):not([type="radio"]) {
  height: 32px;
  padding: 0 var(--space-md);
  font-size: 13px;
}
.content .bulk-bar input.bulk-reason.bulk-reason:not([type="checkbox"]):not([type="radio"]) { width: auto; flex: 1 1 220px; max-width: 360px; }
.content .process-table input.process-tracking.process-tracking:not([type="checkbox"]):not([type="radio"]) { width: 100%; min-width: 100px; max-width: 140px; }

/* Fits the page width -- no sideways scroll: text wraps inside cells
 * (table.list is nowrap by default) and secondary details sit on a
 * second line in each cell instead of in extra columns. */
table.list.process-table { text-wrap: wrap; }
table.process-table th,
table.process-table td { padding-left: var(--space-md); padding-right: var(--space-md); }
table.process-table td { vertical-align: top; padding-top: var(--space-md); padding-bottom: var(--space-md); }
table.process-table .col-check { width: 1%; padding-right: 0; }
table.process-table .col-action { width: 1%; white-space: nowrap; text-align: right; }
table.process-table .col-action .btn { margin-bottom: 0; }
table.process-table .num { text-align: right; white-space: nowrap; }
table.process-table .process-order { white-space: nowrap; font-weight: 500; }
table.process-table .nowrap,
table.process-table .chip,
table.process-table .help { white-space: nowrap; }
table.process-table .help { margin-top: 2px; font-size: 12px; color: var(--color-text-secondary); }

.filter-result-count {
  margin-bottom: var(--space-lg);
  color: var(--color-text-secondary);
  font-size: 13px;
}

/* Backend pagination (Designs list): Previous / numbered pages / Next.
 * Current page reads as a filled primary chip; Previous/Next render as
 * disabled placeholders at the ends of the range so the bar doesn't
 * shift around as buttons appear and disappear between pages. */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  margin-top: var(--space-xl);
}
.pagination .page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 var(--space-md);
  border-radius: var(--radius-full);
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text-primary);
  text-decoration: none;
  transition: background var(--motion-fast);
}
.pagination a.page-link:hover { background: var(--color-surface-alt); }
.pagination .page-link.is-current {
  background: var(--color-primary);
  color: var(--color-on-primary);
}
.pagination .page-link.is-disabled {
  color: var(--color-text-disabled);
  cursor: default;
}
.pagination .page-ellipsis {
  min-width: 24px;
  text-align: center;
  color: var(--color-text-secondary);
}

/* Shared overflow-chip widget: wrap a row of chips in .chip-overflow, mark
 * everything past the visible cutoff with .is-hidden-extra, and add a
 * "+N more" .chip-more button -- keeps a row with many chips (Designs list
 * categories, Staff work types allowed work, ...) from blowing out the
 * row's width now that table.list cells no longer wrap (see the
 * table.list nowrap change above). A real <button>, not a title=""
 * tooltip: hovering a title works with a mouse but never with a
 * touchscreen, so it's tap-to-reveal instead (JS in static/js/panel.js
 * un-hides the rest of that row's chips and removes this button) --
 * works identically for mouse and touch either way. */
.chip-more {
  border: none;
  font: inherit;
  cursor: pointer;
}
.chip-overflow .chip.is-hidden-extra { display: none; }

/* --- Text fields: MD3 outlined ------------------------------------------- */

.content input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]),
.content select,
.content textarea {
  appearance: none;
  -webkit-appearance: none;
  /* No max-width -- every field fills its container (a form-grid cell,
   * the filter bar, wherever it sits) instead of stopping at a fixed
   * width, so it expands with the layout around it. */
  width: 100%;
  height: 48px;                 /* MD3 text field height */
  padding: 0 var(--space-lg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-input);
  background: var(--color-surface);
  color: var(--color-text-primary);
  font-size: 15px;              /* MD3 body-large in fields */
  font-family: inherit;
  transition: border var(--motion-fast), box-shadow var(--motion-fast);
}

.content textarea {
  height: auto;
  min-height: 72px;
  padding: var(--space-md) var(--space-lg);
  resize: vertical;
  line-height: 1.4;
}

.content input:hover, .content select:hover, .content textarea:hover {
  border-color: var(--color-text-secondary);
}

/* MD3 focus: 2dp primary outline (inner shadow doubles the border) */
.content input:focus, .content select:focus, .content textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.content input::placeholder, .content textarea::placeholder {
  color: var(--color-text-disabled);
}

.content input:disabled, .content select:disabled, .content textarea:disabled,
.content input:read-only {
  background: color-mix(in srgb, var(--color-text-primary) 4%, var(--color-surface));
  color: var(--color-text-disabled);
  border-style: dashed;
  cursor: not-allowed;
}

/* Select chevron in token colors */
.content select {
  padding-right: calc(var(--space-lg) * 2 + 6px);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--color-text-secondary) 50%),
    linear-gradient(135deg, var(--color-text-secondary) 50%, transparent 50%);
  background-position:
    calc(100% - 20px) calc(50% - 2px),
    calc(100% - 14px) calc(50% - 2px);
  background-size: 6px 6px;
  background-repeat: no-repeat;
}

.content input[type="number"]::-webkit-outer-spin-button,
.content input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.content input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

[data-theme="dark"] .content input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1) opacity(.6);
}

/* MD3 selection controls */
.content input[type="checkbox"], .content input[type="radio"] {
  width: 18px;
  height: 18px;
  accent-color: var(--color-primary);
  cursor: pointer;
  flex-shrink: 0;
}

/* File field: MD tonal button inside the field */
.content input[type="file"] { padding: var(--space-sm); height: auto; }
.content input[type="file"]::file-selector-button {
  border: none;
  border-radius: var(--radius-full);
  background: var(--md-secondary-container);
  color: var(--color-primary);
  height: 32px;
  padding: 0 var(--space-lg);
  margin-right: var(--space-lg);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .1px;
  cursor: pointer;
  transition: filter var(--motion-fast);
}
.content input[type="file"]::file-selector-button:hover { filter: brightness(.96); }

/* Company logo (Company GST details): square preview frame + "Choose
   logo" / "Remove" buttons. The real <input type="file"> and Django's
   ClearableFileInput "Clear" checkbox stay in the form (hidden, not
   disabled — same pattern as .select-wrap select above) so submission
   and validation are untouched; the buttons just proxy clicks to them
   and update the preview live (static/js/panel.js). */
.logo-field { display: flex; flex-direction: column; gap: var(--space-md); align-items: flex-start; }
.logo-preview-frame {
  position: relative;
  width: 112px;
  height: 112px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  overflow: hidden;
}
.logo-preview {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.logo-preview.is-empty { display: none; }
.logo-preview-placeholder {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  color: var(--color-text-disabled);
}
.logo-preview.is-empty + .logo-preview-placeholder { display: flex; }
.logo-preview-placeholder span { font-size: 11px; }
.logo-actions { display: flex; gap: var(--space-sm); }
.logo-native-controls { display: none; }

/* Small button size + a borderless "quiet" variant, used by the logo
   Remove action (and available for future small/inline actions). */
.btn-sm { height: 32px; padding: 0 var(--space-lg); font-size: 13px; }
.btn-text {
  background: transparent;
  color: var(--color-text-secondary);
  border-color: transparent;
}
.btn-text:hover { background: var(--md-state-hover); color: var(--color-text-primary); }

/* Dynamic row lists (designs/form.html "Bigger order prices",
 * orders/form.html design/quantity lines): rows beyond the first blank
 * one start hidden -- each list's own "Add" button reveals them one at a
 * time (static/js/panel.js) instead of showing a wall of empty rows.
 * A row's real Django "DELETE" checkbox (ModelForm-backed formsets with
 * an already-saved row only) stays in the form but out of sight; the
 * icon button next to it drives it, same hidden-but-functional pattern
 * as the rest of this app. */
.row-list-item.is-hidden-extra { display: none; }
.row-list-delete-field { display: none; }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast);
}
.icon-btn svg { width: 18px; height: 18px; }
.icon-btn:hover { background: color-mix(in srgb, var(--color-danger) 12%, transparent); color: var(--color-danger); }

/* Existing, already-saved design images (designs/form.html "Current
   images"). Each thumbnail gets the same small "x" overlay as a
   newly-picked-file preview below (.images-preview-remove, reused as-is)
   -- clicking it doesn't delete anything itself, it just ticks a real
   but hidden "remove_images" checkbox (catalog/views.py reads the
   submitted list of ids and deletes those DesignMedia rows on save) and
   hides the thumbnail immediately, same hidden-but-functional,
   remove-now-delete-on-save pattern as every other list in this app.
   Dragging a card (static/js/panel.js, Pointer Events -- works with
   touch too, not native HTML5 drag) moves it in the DOM; the repeated
   hidden "media_order" input inside each card is what catalog/views.py
   reads back (in submitted order) to set sort_order. */
.current-images { display: flex; gap: var(--space-md); flex-wrap: wrap; align-items: flex-start; }
.current-image-item { position: relative; width: 190px; display: flex; flex-direction: column; gap: var(--space-xs); }
/* Square, and matches .current-image-item's own 190px width -- the
   thumbnail and the name field below it line up edge to edge instead of
   a small image sitting above a much wider field. */
.current-image-thumb-wrap {
  position: relative;
  width: 190px;
  height: 190px;
  cursor: grab;
  touch-action: none;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-surface-alt);
}
.current-image-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  display: block;
  -webkit-user-drag: none; /* Safari still needs this even with draggable="false" */
  user-select: none;
}
.current-image-remove-field { display: none; }
/* A <textarea>, not an <input> -- a name that still doesn't fit on one
   line at this width wraps onto a second instead of clipping or
   scrolling out of view, and static/js/panel.js's autoResizeNameField
   grows the field's own height (via scrollHeight) to fit however many
   lines that takes, so the text sits fully visible with normal padding
   above and below it rather than being cut off. resize:none/overflow:
   hidden keep it looking like a plain field (no manual resize handle,
   no inner scrollbar) since the JS keeps the height in sync on load and
   on every keystroke. The .content textarea base rule (100% width,
   72px min-height, MD3 padding) is overridden here since this field
   needs to be far more compact -- hence the extra specificity.
   min-height (~2 lines, matching the template/JS rows="2" fallback for
   when JS hasn't run yet) is a deliberate floor, not 0 -- the field
   starts visibly roomy even for a short name, not just once a long one
   forces it to grow. */
.content textarea.current-image-name {
  display: block;
  width: 100%;
  min-height: 44px;
  height: auto;
  resize: none;
  overflow: hidden;
  font-size: 12px;
  font-family: inherit;
  line-height: 1.35;
  padding: var(--space-xs) var(--space-sm);
}
.current-image-order {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 var(--space-xs);
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-text-primary) 65%, transparent);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
}
.current-image-item.is-dragging { opacity: .5; }
.current-image-item.is-drag-target .current-image-thumb-wrap { outline: 2px solid var(--color-primary); outline-offset: 2px; }
/* Play-icon badge over a video thumbnail (current-image-thumb is shared
   by <img> and <video>, sized/cropped identically either way) -- the
   badge is what tells the two apart at a glance since a muted, control-
   less <video> just looks like a still image otherwise. .video-play-icon
   (shared with the newly-picked-file preview below) is a solid circle,
   not just a bare white glyph, so it stays visible over any thumbnail
   colour, light or dark. */
.current-media-video-badge {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.video-play-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-text-primary) 55%, transparent);
}
.video-play-icon svg { width: 16px; height: 16px; color: #fff; }

/* Design images picker (designs/form.html): an "Add images" button +
   live thumbnail previews for newly chosen (not yet saved) files, in
   place of the native multi-file input's own bare "Choose Files / No
   file(s) chosen" row. Same hidden-but-functional pattern as the Company
   logo picker above -- the real <input type="file" multiple"> stays in
   the form so submission and validation are unchanged, the button just
   proxies clicks to it (static/js/panel.js), which also keeps its own
   running list of picked files (rebuilt into the input via DataTransfer)
   so a person can click "Add images" more than once, or remove a single
   bad pick, without losing the rest of their selection. */
.images-field { display: flex; flex-direction: column; gap: var(--space-md); align-items: flex-start; }
.images-native-controls { display: none; }
.images-preview-grid { display: flex; gap: var(--space-md); flex-wrap: wrap; }
/* Item is now a small flex column (thumb + name), same shape as
   .current-image-item below it -- the thumb itself moved into its own
   .images-preview-thumb-wrap so the badge/remove button (position:
   absolute) still anchor to just the thumbnail, not the name field. */
.images-preview-item { display: flex; flex-direction: column; gap: var(--space-xs); width: 130px; }
/* Square, matching .images-preview-item's own 130px width -- same reasoning
   as .current-image-thumb-wrap above. */
.images-preview-thumb-wrap {
  position: relative;
  width: 130px;
  height: 130px;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  overflow: hidden;
  background: var(--color-surface-alt);
}
.images-preview-thumb-wrap img,
.images-preview-thumb-wrap video { width: 100%; height: 100%; object-fit: cover; display: block; cursor: pointer; }
/* <textarea>, auto-grown by static/js/panel.js -- same reasoning
   (including the deliberate min-height floor) as .current-image-name
   above, just smaller to match this field's own smaller font/thumbnail. */
.content textarea.images-preview-name {
  display: block;
  width: 100%;
  min-height: 38px;
  height: auto;
  resize: none;
  overflow: hidden;
  font-size: 11px;
  font-family: inherit;
  line-height: 1.35;
  padding: var(--space-xs) 6px;
}
.images-preview-video-badge {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.images-preview-video-badge .video-play-icon { width: 26px; height: 26px; }
.images-preview-video-badge .video-play-icon svg { width: 12px; height: 12px; }
.images-preview-remove {
  position: absolute;
  top: 3px;
  right: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-text-primary) 55%, transparent);
  color: #fff;
  cursor: pointer;
  transition: background var(--motion-fast);
}
.images-preview-remove svg { width: 13px; height: 13px; }
.images-preview-remove:hover { background: var(--color-danger); }

/* Custom confirm dialog (static/js/panel.js's showConfirmDialog) --
   a small MD3-style card in place of the native, unstyleable
   window.confirm() popup. Built once and reusable for any future
   destructive action; currently used only for removing an already-saved
   design image. Faded/scaled in via the .is-open class, added a frame
   after insertion so the transition actually runs. */
.confirm-overlay {
  position: fixed;
  inset: 0;
  background: var(--color-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  z-index: 100;
  opacity: 0;
  transition: opacity var(--motion-fast);
}
.confirm-overlay.is-open { opacity: 1; }
.confirm-card {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--md-elevation-2);
  padding: var(--space-2xl);
  max-width: 360px;
  width: 100%;
  transform: scale(.96);
  transition: transform var(--motion-fast);
}
.confirm-overlay.is-open .confirm-card { transform: scale(1); }
.confirm-message { margin: 0 0 var(--space-xl); color: var(--color-text-primary); font-size: 14px; line-height: 1.5; }
.confirm-actions { display: flex; justify-content: flex-end; gap: var(--space-sm); }
.confirm-actions .btn { margin-bottom: 0; }

.btn-danger { background: var(--color-danger); color: #fff; }
.btn-danger:hover { background: color-mix(in srgb, var(--color-danger) 88%, black); color: #fff; }

/* Image/video lightbox (static/js/panel.js): clicking any thumbnail
   marked data-lightbox-src -- both "Current images" and the "Design
   images" picker's own new-file previews -- opens it full-size here
   instead of leaving people squinting at a 72-96px thumbnail.
   .lightbox-image is applied to a <video> too (data-lightbox-video) --
   same sizing works either way. Same fade/scale-in and
   dismiss-on-backdrop/Escape/× pattern as the confirm dialog above. */
.lightbox-overlay {
  position: fixed;
  inset: 0;
  background: var(--color-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3xl);
  z-index: 110;
  opacity: 0;
  transition: opacity var(--motion-fast);
}
.lightbox-overlay.is-open { opacity: 1; }
.lightbox-image {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--radius-md);
  box-shadow: var(--md-elevation-2);
  transform: scale(.96);
  transition: transform var(--motion-fast);
  display: block;
}
.lightbox-overlay.is-open .lightbox-image { transform: scale(1); }
.lightbox-close {
  position: fixed;
  top: var(--space-xl);
  right: var(--space-xl);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-text-primary) 55%, transparent);
  color: #fff;
  cursor: pointer;
  transition: background var(--motion-fast);
}
.lightbox-close svg { width: 18px; height: 18px; }
.lightbox-close:hover { background: var(--color-danger); }

/* Approve popup (templates/buyers/detail.html, opened by
   static/js/panel.js when the buyer has no categories yet): the
   confirm-overlay/card shell above, widened to fit a checkbox list,
   with the list capped in height so many categories scroll inside the
   card instead of pushing its buttons off screen. [hidden] wins over
   the overlay's display:flex so it stays closed until opened. */
.approve-dialog[hidden] { display: none; }
.approve-dialog .confirm-card { max-width: 520px; }
.approve-dialog-title { margin: 0 0 var(--space-sm); font-size: 18px; font-weight: 500; }
.approve-dialog .form-field { margin-bottom: var(--space-lg); }
.approve-dialog-error { margin-bottom: var(--space-md); }
/* The two dropdowns' menus are portalled to <body> (z-index 40) -- lift
   them over the popup overlay (z-index 100) only while it's open. */
body.approve-dialog-open .select-menu { z-index: 110; }

/* Required-field marker used by panel and auth forms. */
.required-marker { color: var(--color-danger); font-weight: 700; }
/* Same tight title-to-legend gap whether the title is bare (.page-title)
   or wrapped in a .page-header-row (a top-right action button next to
   it, e.g. the appsettings/company-settings form's second Save button)
   -- .page-header-row .page-title's own margin is already zeroed above,
   so without this the legend would inherit the row's full margin-bottom
   instead of sitting close under the title like it used to. */
.page-title:has(+ .required-legend),
.page-header-row:has(+ .required-legend) { margin-bottom: var(--space-sm); }
.required-legend {
  margin: 0 0 var(--space-xl);
  color: var(--color-text-secondary);
  font-size: 13px;
}

/* Compact controls inside tables (slab rows) -- shorter, no max-width so
 * they fill their table cell like every other field now does. */
.content table.list input:not([type="checkbox"]):not([type="radio"]),
.content table.list select {
  height: 40px;
  padding: 0 var(--space-md);
}

/* --- Custom dropdown: MD3 menu instead of the browser's native <select> --
 * static/js/panel.js wraps every .content select in a .select-wrap and
 * hides the real control, replacing it with a styled trigger button + an
 * MD3 elevated menu. The original <select> stays in the form (hidden, not
 * disabled) so submission and Django validation are unchanged; if JS
 * doesn't run, no wrapper is created and the plain <select> above still
 * works exactly as before — this is a progressive enhancement, not a
 * replacement.
 */

.select-wrap {
  position: relative;
  display: inline-block;
  /* No max-width -- the styled dropdown trigger fills its container like
   * every other field, instead of stopping at a fixed width. */
  width: 100%;
}

.select-wrap select {
  display: none;
}

.select-trigger {
  display: flex;
  align-items: center;
  position: relative;
  width: 100%;
  height: 48px;
  padding: 0 var(--space-lg);
  padding-right: calc(var(--space-lg) * 2 + 6px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-input);
  background: var(--color-surface);
  color: var(--color-text-primary);
  font-size: 15px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: border var(--motion-fast), box-shadow var(--motion-fast);
}

.select-trigger-value {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.select-trigger-value.is-placeholder { color: var(--color-text-disabled); }

/* Same chevron drawn the same way as the native select's used to be. */
.select-trigger::after {
  content: "";
  position: absolute;
  right: 20px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--color-text-secondary);
  border-bottom: 2px solid var(--color-text-secondary);
  transform: translateY(-70%) rotate(45deg);
  transition: transform var(--motion-fast);
  pointer-events: none;
}

.select-wrap.is-open .select-trigger::after { transform: translateY(-30%) rotate(-135deg); }

.select-trigger:hover { border-color: var(--color-text-secondary); }

.select-trigger:focus-visible,
.select-wrap.is-open .select-trigger {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.select-wrap.is-disabled .select-trigger {
  background: color-mix(in srgb, var(--color-text-primary) 4%, var(--color-surface));
  color: var(--color-text-disabled);
  border-style: dashed;
  cursor: not-allowed;
}

/* MD3 elevated menu. Positioned in the viewport and appended straight to
 * <body> by panel.js instead of living inside .select-wrap -- nested
 * inside .select-wrap it would inherit clipping from any ancestor with
 * `overflow` set (e.g. table.list's `overflow: hidden`, there only to
 * round its own corners), which silently clipped the whole options list
 * for selects rendered inside table cells. JS sets left/top/width
 * per-open from the trigger's own position; only the visual chrome
 * lives here. */
.select-menu {
  position: fixed;
  display: none;
  z-index: 40;
  margin: 0;
  padding: var(--space-sm) 0;
  list-style: none;
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  box-shadow: var(--md-elevation-2);
  max-height: 320px;
  overflow-y: auto;
  overflow-x: hidden;
  /* Thin scrollbar, not gone -- a long list (buyer/staff dropdowns) still
   * needs the visual hint that there's more below, just not the browser's
   * default chunky bar. Firefox honors scrollbar-width directly; Chrome/
   * Safari/Edge need the ::-webkit-scrollbar-* rules below instead. */
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}
.select-menu::-webkit-scrollbar { width: 6px; }
.select-menu::-webkit-scrollbar-track { background: transparent; }
.select-menu::-webkit-scrollbar-thumb {
  background: var(--color-border);
  border-radius: var(--radius-full);
}
.select-menu::-webkit-scrollbar-thumb:hover { background: var(--color-text-secondary); }

[data-theme="dark"] .select-menu { border: 1px solid var(--color-border); }

.select-menu.is-open { display: block; }

/* Searchable <select> variant (static/js/panel.js initSearchableSelect,
 * e.g. Create Order "Buyer"): a search box pinned to the top of the
 * same scrolling .select-menu via position: sticky, above a nested
 * .select-menu-options list of .select-option rows (a <ul> can't
 * directly contain another <ul>, hence the nesting) -- the outer
 * .select-menu's own overflow/max-height above already scrolls both
 * together, so the sticky box stays put without any extra wrapper. */
.select-menu-search-wrap {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--color-surface);
  padding: 0 var(--space-md) var(--space-sm);
}
.select-menu-search {
  width: 100%;
  height: 36px;
  padding: 0 var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-input);
  font-size: 14px;
  font-family: inherit;
  background: var(--color-surface);
  color: var(--color-text-primary);
}
.select-menu-search:focus-visible {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}
.select-menu-options { margin: 0; padding: 0; list-style: none; }

.select-option {
  display: flex;
  align-items: center;
  min-height: 40px;
  padding: 0 var(--space-lg);
  font-size: 14px;
  letter-spacing: .15px;
  color: var(--color-text-primary);
  cursor: pointer;
}

.select-option.is-active { background: var(--md-state-hover); }

/* Tonal highlight for the current value, same idiom as the active nav item. */
.select-option.is-selected {
  background: var(--md-secondary-container);
  color: var(--color-primary);
  font-weight: 500;
}

.select-option.is-disabled {
  color: var(--color-text-disabled);
  cursor: not-allowed;
}

/* Multi-select variant (e.g. design categories): same trigger/menu chrome
 * as the single-select dropdown above, but each option shows its own
 * checkbox and picking one doesn't close the menu, so several can be
 * chosen in one visit. */
.select-option-check {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-right: var(--space-md);
  border: 2px solid var(--color-border);
  border-radius: 4px;
  position: relative;
  transition: background var(--motion-fast), border-color var(--motion-fast);
}

.select-option.is-selected .select-option-check {
  background: var(--color-primary);
  border-color: var(--color-primary);
}

.select-option.is-selected .select-option-check::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 0;
  width: 5px;
  height: 9px;
  border-right: 2px solid var(--color-on-primary);
  border-bottom: 2px solid var(--color-on-primary);
  transform: rotate(45deg);
}

/* Icon dropdown variant (e.g. "Who can see this design" on the design
 * form, static/js/panel.js's enhanceIconSelect()): a small icon sits
 * before the text in both the closed trigger and each menu option. */
.visibility-option-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-right: var(--space-sm);
  color: var(--color-text-secondary);
}
.visibility-option-icon svg { width: 100%; height: 100%; }
.select-option.is-selected .visibility-option-icon { color: var(--color-primary); }

/* Date range filter (Activity history): one popover calendar in place of
 * two native <input type="date"> pickers, built by panel.js's
 * enhanceDateRange() around the .select-wrap/.select-trigger chrome above
 * -- only the calendar-specific parts (icon, header, grid) are new here. */
.date-range-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-right: var(--space-sm);
  color: var(--color-text-secondary);
}
.date-range-icon svg { width: 100%; height: 100%; }

/* Wider and taller than a plain option list, so it overrides the generic
 * .select-menu's 320px cap instead of scrolling/clipping the grid. */
.date-range-menu {
  width: 300px;
  /* Shrinks below 300px only on a viewport too narrow to fit it with the
   * JS-side 8px edge margin (positionMenu() in panel.js) on both sides --
   * a no-op at every normal width. */
  max-width: calc(100vw - 16px);
  max-height: none;
  overflow: visible;
  padding: var(--space-lg);
}

/* "From"/"To" tabs above the grid: whichever is active (is-active) is
 * where the next day clicked in the grid gets written -- explicit
 * instead of inferring start/end from click order. */
.date-range-fields {
  display: flex;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
}
.date-range-field-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}
.date-range-field-btn:hover { border-color: var(--color-text-secondary); }
.date-range-field-btn.is-active {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}
.date-range-field-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--color-text-secondary);
}
.date-range-field-value { font-size: 13px; color: var(--color-text-primary); }
.date-range-field-value.is-placeholder { color: var(--color-text-disabled); }

.date-range-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-md);
}
.date-range-month { font-size: 14px; font-weight: 500; }
.date-range-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--motion-fast);
}
.date-range-nav:hover { background: var(--md-state-hover); }
.date-range-nav svg { width: 16px; height: 16px; }

.date-range-weekdays,
.date-range-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}
.date-range-weekdays {
  margin-bottom: var(--space-xs);
  font-size: 12px;
  color: var(--color-text-secondary);
}
.date-range-weekdays span { text-align: center; }

.date-range-day {
  height: 36px;
  border: none;
  background: transparent;
  color: var(--color-text-primary);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: background var(--motion-fast);
}
.date-range-day:hover { background: var(--md-state-hover); }
.date-range-day.is-outside { color: var(--color-text-disabled); }
.date-range-day.is-today { font-weight: 700; color: var(--color-primary); }

/* No grid-gap, so .is-in-range cells sit flush against each other and
 * .is-start/.is-end -- that's what makes the highlight read as one
 * continuous band instead of separate rounded pills per day. */
.date-range-day.is-in-range { background: var(--md-secondary-container); }
.date-range-day.is-start,
.date-range-day.is-end {
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-radius: var(--radius-full);
  font-weight: 500;
}

.date-range-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-border);
}

/* Activity history "Change" column: JSON-derived free text (addresses,
 * long values) can run far longer than the rest of a row -- table.list's
 * shared `text-wrap: nowrap` would then stretch EVERY row to that one
 * cell's width, so most rows show mostly blank space when scrolled to the
 * end. Wrapping this column alone keeps the table's width driven by real
 * content instead of its single longest change.
 *
 * min-width matters just as much as max-width here: with `word-break:
 * break-word`, this column's min-content width (what a narrow viewport
 * would otherwise shrink it to) is basically nothing -- auto table
 * layout would squeeze it into an unreadable one-letter-per-line sliver
 * on a small screen instead of leaving the table to scroll horizontally
 * (which `.table-wrap` already provides, same as every other list page). */
.activity-change-cell {
  min-width: 220px;
  max-width: 420px;
  white-space: normal;
  word-break: break-word;
}

/* Compact variant inside tables (slab rows, order line items) -- shorter,
 * no max-width so it fills its table cell like every other field now does. */
.content table.list .select-trigger {
  height: 40px;
  padding: 0 var(--space-md);
  padding-right: calc(var(--space-md) * 2 + 6px);
}
.content table.list .select-trigger::after { right: 14px; }

.form-field { margin-bottom: var(--space-xl); }

.form-field label {
  display: block;
  font-size: 12px;              /* MD3 label-medium above field */
  font-weight: 500;
  letter-spacing: .4px;
  color: var(--color-text-secondary);
  margin-bottom: var(--space-sm);
}

.form-field .help {
  color: var(--color-text-secondary);
  font-size: 12px;              /* MD3 supporting text */
  letter-spacing: .3px;
  margin-top: var(--space-xs);
}
.form-field .error {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--color-danger);
  font-size: 12px;
  margin-top: var(--space-xs);
}

.form-field .error::before {
  content: "!";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--color-danger);
  color: var(--color-on-primary);
  font-size: 10px;
  font-weight: 700;
  font-style: normal;
}

/* Custom validation (static/js/panel.js replaces the browser's native
 * "Please fill out this field" bubble with this styling, keeping errors
 * in the app's own visual language) -- red outline on the control, same
 * .error message treatment above whether the error came from the server
 * or was caught before submission. */
.form-field.has-error input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]),
.form-field.has-error select,
.form-field.has-error textarea,
.form-field.has-error .select-trigger {
  border-color: var(--color-danger);
}

.form-field.has-error input:focus,
.form-field.has-error select:focus,
.form-field.has-error textarea:focus,
.form-field.has-error .select-trigger:focus-visible {
  border-color: var(--color-danger);
  box-shadow: inset 0 0 0 1px var(--color-danger);
}

.checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-xs) var(--space-xl);
}

.checkbox-grid label {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  min-height: 40px;
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-primary);
  letter-spacing: .15px;
  padding: 0 var(--space-sm);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--motion-fast);
}

.checkbox-grid label:hover { background: var(--md-state-hover); }

.field-row {
  display: flex;
  gap: var(--space-lg);
  flex-wrap: wrap;
  align-items: flex-end;
}

.field-row .form-field { margin-bottom: 0; min-width: 160px; flex: 0 1 auto; }
.field-row .form-field.grow { flex: 1 1 220px; }

/* --- Form grid: side-by-side fields, so long forms don't run tall ------- */

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 var(--space-2xl);
}

.form-grid .form-field { margin-bottom: var(--space-xl); }

.form-grid .form-field input:not([type="checkbox"]):not([type="radio"]),
.form-grid .form-field select,
.form-grid .form-field .select-wrap,
.form-grid .form-field textarea { max-width: 100%; }

/* Address autosuggest (buyers/form.html, static/js/panel.js): a plain
 * search box, not a form field itself -- picking a suggestion fills the
 * real street/city/pincode/state fields below it. Positioned relative to
 * its own wrapper (not fixed+JS-positioned like .select-menu) since this
 * form has no `overflow`-clipping ancestor to worry about. */
.address-search { position: relative; margin-bottom: var(--space-lg); }
.address-suggestions {
  position: absolute;
  z-index: 5;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  margin: 0;
  padding: var(--space-sm) 0;
  list-style: none;
  display: none;
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  box-shadow: var(--md-elevation-2);
  max-height: 280px;
  overflow-y: auto;
}
.address-suggestions.is-open { display: block; }
.address-suggestion {
  padding: var(--space-sm) var(--space-lg);
  font-size: 14px;
  color: var(--color-text-primary);
  cursor: pointer;
}
.address-suggestion:hover,
.address-suggestion.is-active { background: var(--md-state-hover); }
.address-suggestion.is-note {
  color: var(--color-text-secondary);
  cursor: default;
  font-style: italic;
}

/* Create Order design picker (orders/form.html, static/js/panel.js
 * initOrderDesignPicker): reuses .address-search/.address-suggestions for
 * the search box/dropdown chrome above -- .order-design-suggestion just
 * adds a thumbnail (.design-thumb, same class the designs list page uses
 * for its cover images) in front of the name. Each picked design becomes
 * an .order-design-block card with its own subdesign/quantity table. */
.order-design-suggestion { display: flex; align-items: center; gap: var(--space-md); }
.order-design-block {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  margin-top: var(--space-md);
}
.order-design-block-header {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}
.order-design-block-name { font-weight: 600; flex: 1; }
.order-subdesign-table { max-width: 640px; }
.order-subdesign-qty { max-width: 100px; }

/* Wide controls (long text, checkbox groups, files, tables) keep the full row.
 * A multi-select (e.g. design categories) deliberately is NOT in this list --
 * it sits in its own half-width column like any other dropdown, so it lines
 * up side by side with the field right after it (e.g. "Who can see this
 * design") instead of forcing that field onto its own row. */
.form-grid .form-field.span-2,
.form-grid .form-field:has(textarea),
.form-grid .form-field:has(.checkbox-grid),
.form-grid .form-field:has(input[type="file"]),
.form-grid .form-field:has(input[type="checkbox"]),
.form-grid .form-field:has(table) {
  grid-column: 1 / -1;
}

/* --- Messages: MD3 snackbar-ish banners ---------------------------------- */

.messages { list-style: none; padding: 0; margin: 0 0 var(--space-2xl); }

.messages li {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--space-md) var(--space-lg);
  border-radius: var(--space-xs);
  margin-bottom: var(--space-sm);
  background: color-mix(in srgb, var(--color-success) 12%, var(--color-surface));
  color: var(--color-success);
  font-weight: 500;
  box-shadow: var(--md-elevation-1);
  /* Auto-dismissed after a few seconds (static/js/panel.js) -- fades out
   * instead of disappearing abruptly. */
  transition: opacity var(--motion-normal);
}

.messages li.is-leaving { opacity: 0; }

.messages li.error {
  background: color-mix(in srgb, var(--color-danger) 12%, var(--color-surface));
  color: var(--color-danger);
}

.messages li.warning {
  background: color-mix(in srgb, var(--color-warning) 14%, var(--color-surface));
  color: var(--color-warning);
}

.empty {
  color: var(--color-text-secondary);
  text-align: center;
  padding: var(--space-3xl);
}

/* --- Responsive: hamburger opens the sidebar as an off-canvas drawer ---- */

@media (max-width: 900px) {
  .layout { flex-direction: column; }

  /* Sticky top bar carries the hamburger; the real nav drawer lives in
   * .sidebar below and slides in over the page instead of turning into a
   * horizontal pill rail. */
  .mobile-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    height: 56px;
    padding: 0 var(--space-lg);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--md-elevation-1);
    position: sticky;
    top: 0;
    z-index: 20;
  }

  .mobile-topbar .brand-mark {
    width: 26px;
    height: 26px;
  }

  .mobile-topbar .brand-mark svg { width: 15px; height: 15px; }

  .mobile-topbar .brand-name {
    font-size: 18px;
  }

  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: var(--color-overlay);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--motion-normal);
    z-index: 25;
  }
  .sidebar-backdrop.is-open { opacity: 1; pointer-events: auto; }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: 280px;
    max-width: 85vw;
    height: 100vh;
    border-right: none;
    box-shadow: var(--md-elevation-2);
    transform: translateX(-100%);
    transition: transform var(--motion-normal);
    overflow-y: auto;
    z-index: 30;
  }
  .sidebar.is-open { transform: translateX(0); }

  .content { padding: var(--space-lg); }

  .page-title { font-size: 22px; margin-bottom: var(--space-lg); }

  .card { padding: var(--space-lg); }

  table.list {
    /* No `display: block` here on purpose -- setting it directly on a
     * <table> knocks the element out of table layout, so `width: 100%`
     * (set on the base `table.list` rule above) stops being honored: the
     * table shrinks to its intrinsic content width instead of filling the
     * card, leaving a blank gap on short tables (2-3 short columns) even
     * though every other field on the page still fills the width. Table
     * layout stays on `table` (the default), so `width: 100%` keeps
     * working at every size; `overflow-x: auto` on the table itself (on
     * top of the `.table-wrap` ancestor some pages already have) is what
     * lets a table whose content genuinely needs more room than the card
     * scroll horizontally instead of being clipped. */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
  }

  table.subdesign-table th.subdesign-name-col {
    /* Narrower than the desktop min-width so the rest of the row's
     * columns stay reachable without an oversized horizontal scroll. */
    min-width: 140px;
  }

  .content input:not([type="checkbox"]):not([type="radio"]),
  .content select,
  .content .select-wrap,
  .content textarea { max-width: 100%; }

  .field-row { gap: var(--space-md); }
  .field-row .form-field { min-width: 100%; }
  .field-row .btn { width: 100%; }

  .form-grid { grid-template-columns: 1fr; }
}

@media (max-width: 480px) {
  .btn { display: flex; width: 100%; margin-right: 0; }
  .content { padding: var(--space-md); }
}

@media (max-width: 640px) {
  /* The subdesign table has editable inputs in every column, so on a
   * phone the generic horizontal-scroll behavior (table.list above)
   * makes editing awkward -- you can't see a row's other fields while
   * scrolled to one of them. Stack each row into a labeled card instead,
   * scoped to this table only so the rest of table.list keeps the
   * scroll-based approach documented above. */
  table.subdesign-table,
  table.subdesign-table thead,
  table.subdesign-table tbody,
  table.subdesign-table tr,
  table.subdesign-table th,
  table.subdesign-table td {
    display: block;
    width: 100%;
  }

  table.subdesign-table {
    overflow-x: visible;
    white-space: normal;
    max-width: none;
  }

  table.subdesign-table thead {
    position: absolute;
    left: -9999px;
  }

  table.subdesign-table tbody tr.row-list-item {
    border-bottom: 1px solid var(--color-border);
    padding: var(--space-md) 0;
  }

  table.subdesign-table td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    height: auto;
    padding: var(--space-xs) 0;
    border-bottom: none;
  }

  table.subdesign-table td[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-weight: 600;
    color: var(--color-text-secondary);
  }

  table.subdesign-table td .form-field {
    flex: 1;
    max-width: 60%;
    margin-left: auto;
  }

  table.subdesign-table td[data-label=""] {
    justify-content: flex-end;
  }

  table.subdesign-table th.subdesign-name-col { min-width: 0; }
}

/* --- Responsive: wide screens (large monitors) --------------------------
 * Below 1440px .content keeps its 1000px cap, left-aligned next to the
 * drawer. Past 1440px that cap leaves a growing dead zone of background on
 * the right, so widen it and center the extra space evenly instead, and
 * let side-by-side forms use a third column so fields don't just stretch.
 */

@media (min-width: 1440px) {
  .content { max-width: 1320px; margin: 0 auto; }

  .form-grid { grid-template-columns: repeat(3, 1fr); }
}

/* --- Auth screens (login / register / forgot password) --------------------
 * Standalone centered layout for templates/auth/. Lives after every other
 * rule (including the media queries) on purpose: the auth pages reuse
 * <main class="content"> so the shared field styles apply, and these
 * overrides must win over .content's layout rules at any viewport width.
 */

.auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
}

.auth-shell .content {
  flex: 0 1 auto;
  width: 100%;
  max-width: 430px;
  padding: 0;
  margin: 0;
}

.auth-brand { display: flex; justify-content: center; margin-bottom: var(--space-xl); }
.auth-brand .brand-name { font-size: 24px; font-weight: 500; }

.auth-card h1 { font-size: 22px; margin: 0 0 var(--space-xs); }

.auth-sub {
  color: var(--color-text-secondary);
  font-size: 14px;
  margin: 0 0 var(--space-xl);
}

/* One primary action per screen: full-width, always. */
.auth-card .btn { display: flex; width: 100%; margin-right: 0; }

.auth-under-field {
  margin-top: var(--space-sm);
  font-size: 13px;
  text-align: right;
}

.auth-password-wrap {
  position: relative;
}

.content .auth-password-wrap input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]) {
  padding-right: calc(var(--space-lg) * 2 + 24px);
}

.auth-password-toggle {
  position: absolute;
  right: var(--space-sm);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast);
}

.auth-password-toggle:hover {
  background: var(--md-state-hover);
  color: var(--color-text-primary);
}

.auth-password-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.auth-password-toggle svg { width: 20px; height: 20px; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  color: var(--color-text-secondary);
  font-size: 13px;
  margin: var(--space-xl) 0;
}
.auth-divider::before,
.auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--color-border); }

.auth-google-form { margin: 0; }

.auth-google-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 44px;
  padding: 0 var(--space-xl);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text-primary);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .1px;
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast),
    box-shadow var(--motion-fast);
}

.auth-google-button:hover {
  background: var(--md-state-hover);
  border-color: var(--color-text-secondary);
}

.auth-google-button:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.auth-google-icon,
.auth-google-text { flex-grow: 0; }

.auth-google-icon {
  display: inline-flex;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.auth-google-icon svg { width: 18px; height: 18px; }

/* The 6-digit code box: big, centered, spaced digits. */
.auth-otp input {
  text-align: center;
  letter-spacing: 8px;
  font-size: 20px;
  font-weight: 500;
}

.auth-resend-row { margin-top: var(--space-xl); font-size: 14px; text-align: center; }
.auth-resend { color: var(--color-text-secondary); }
.auth-resend .btn-link {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--color-primary);
  cursor: pointer;
  text-decoration: underline;
}
.auth-resend .btn-link:disabled {
  color: var(--color-text-disabled);
  cursor: default;
  text-decoration: none;
}
.auth-resend-wait { color: var(--color-text-disabled); }

.auth-links {
  display: grid;
  gap: var(--space-sm);
  margin-top: var(--space-xl);
  text-align: center;
  font-size: 14px;
  color: var(--color-text-secondary);
}

/* Sidebar sign-out: a real POST form styled as a quiet drawer action. */
.sidebar-signout {
  margin: auto var(--space-md) 0;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-border);
}

.sidebar-signout button {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  width: 100%;
  height: 40px;
  border: none;
  background: none;
  padding: 0 var(--space-xl);
  border-radius: var(--radius-full);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .1px;
  color: color-mix(in srgb, var(--color-danger) 76%, var(--color-text-secondary));
  cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast),
    box-shadow var(--motion-fast);
}

.sidebar-signout button:hover {
  background: color-mix(in srgb, var(--color-danger) 10%, transparent);
  color: var(--color-danger);
}

.sidebar-signout button:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.sidebar-signout .nav-icon { width: 20px; height: 20px; }

/* --- Dashboard charts (reports/dashboard.py + reports/charts.py) --------
 * Inline SVG, no library -- every fill/stroke color is a var(--color-*)
 * token so light/dark mode need zero extra CSS. See DATA-MODEL note in
 * reports/charts.py for the geometry these consume.
 */
/* Fixed column count, not auto-fit: with a KNOWN number of cards per row,
 * auto-fit can size tracks for one more card than actually exists (enough
 * container width for N+1 min-width tracks, only N cards), splitting the
 * leftover space into a phantom empty column instead of the real cards.
 * 2-up, not 3/4-up: a donut chart's ring + legend sit side by side and
 * never wrap onto their own line (see .chart-donut-body's flex-shrink:0
 * children), and more than 2 across leaves too little room for that.
 * Each column's minimum is min-content, not a guessed pixel value, so
 * it's never narrower than its content needs -- below that the grid
 * overflows into a horizontal scroll rather than clipping or squeezing
 * card content onto extra lines. Both chart-grid rows (4 cards, 3 cards)
 * share this class -- 2-up wraps them into 2 rows and 2 rows respectively. */
.chart-grid, .chart-grid-3 { display: grid; grid-template-columns: repeat(2, minmax(min-content, 1fr)); gap: var(--space-lg); }
@media (max-width: 700px) {
  .chart-grid, .chart-grid-3 { grid-template-columns: minmax(min-content, 1fr); }
}

.chart-card { margin-bottom: 0; display: flex; flex-direction: column; gap: var(--space-md); min-width: 300px; }

.chart-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
}

.chart-card-head h3 { margin: 0; font-size: 15px; font-weight: 600; }
.chart-card-head a { font-size: 13px; white-space: nowrap; }

.chart-empty {
  color: var(--color-text-secondary);
  font-size: 14px;
  padding: var(--space-2xl) 0;
  text-align: center;
}

.chart-donut-body { display: flex; align-items: center; gap: var(--space-xl); }
.chart-donut-body svg { flex-shrink: 0; }

.chart-donut-total-number { font-size: 27px; font-weight: 700; fill: var(--color-text-primary); }
.chart-donut-total-label { font-size: 13px; font-weight: 400; fill: var(--color-text-secondary); }

.chart-legend {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--space-sm);
  font-size: 13px; flex-shrink: 0;
}

.chart-legend li { display: flex; align-items: center; gap: var(--space-sm); white-space: nowrap; }

.chart-legend-swatch {
  width: 10px; height: 10px; border-radius: var(--radius-sm);
  flex-shrink: 0;
}

.chart-legend-value { margin-left: auto; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }

.chart-bar-axis-label {
  font-size: 11px;
  fill: var(--color-text-secondary);
}

.chart-bar-value-label {
  font-size: 11px;
  font-weight: 600;
  fill: var(--color-text-primary);
}

.chart-axis-gridline { stroke: var(--color-border); stroke-width: 1; }
.chart-y-axis-label { font-size: 11px; fill: var(--color-text-secondary); text-anchor: end; }

/* This bar chart's viewBox is much narrower than the line chart's (see
 * reports/views.py's collection_trend_chart track/thickness/gap tuning),
 * so it gets scaled up more to fill the same card width -- an identical
 * font-size in viewBox units would render visibly larger here than on
 * the line chart's axis. Smaller units below compensate for that. */
.chart-bar-x-axis-label { font-size: 6px; fill: var(--color-text-secondary); }
.chart-bar-y-axis-label { font-size: 6px; fill: var(--color-text-secondary); text-anchor: end; }

.chart-line-path { fill: none; stroke-width: 2px; }
.chart-line-marker { stroke: var(--color-surface); stroke-width: 2px; }

.chart-hoverable { cursor: pointer; transition: opacity var(--motion-fast); }
.chart-hoverable:hover, .chart-hoverable:focus-visible { opacity: .8; outline: none; }

/* Shared floating tooltip -- one element, positioned by static/js/panel.js's
 * chart-tooltip module, reused by every donut/bar/line chart. */
.chart-tooltip {
  position: fixed;
  z-index: 40;
  background: var(--color-text-primary);
  color: var(--color-surface);
  font-size: 12px;
  font-weight: 500;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
  box-shadow: var(--md-elevation-2, 0 2px 8px rgba(0,0,0,.2));
  pointer-events: none;
  white-space: nowrap;
}

.chart-tooltip[hidden] { display: none; }

/* --- Dashboard topbar (templates/reports/dashboard.html only, via
 * base.html's {% block topbar %} -- every other page renders nothing
 * here, zero visual change elsewhere). ------------------------------- */
.topbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-lg);
  margin-bottom: var(--space-xl);
}

.topbar-greeting h1 { margin: 0; font-size: 24px; }
.topbar-subtitle { margin: 4px 0 0; color: var(--color-text-secondary); font-size: 14px; }
.topbar-right { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-md); }
.topbar-actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); justify-content: flex-end; }
.topbar-actions .btn svg { width: 16px; height: 16px; margin-right: 6px; flex-shrink: 0; }
.topbar-meta { display: flex; align-items: center; gap: var(--space-md); }

.date-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  font-size: 13px;
  color: var(--color-text-secondary);
  white-space: nowrap;
}
.date-chip svg { width: 16px; height: 16px; }

.user-menu { position: relative; }
.user-menu-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  padding: 4px 8px 4px 4px;
  border-radius: var(--radius-full);
}
.user-menu-summary::-webkit-details-marker { display: none; }
.user-menu-summary::marker { content: ""; }
.user-menu-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--color-primary);
  color: var(--color-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 14px;
  flex-shrink: 0;
}
.user-menu-name { font-size: 13px; font-weight: 600; line-height: 1.2; }
.user-menu-role { font-size: 11px; color: var(--color-text-secondary); }
.user-menu-chevron { width: 14px; height: 14px; color: var(--color-text-secondary); transition: transform var(--motion-fast); }
.user-menu[open] .user-menu-chevron { transform: rotate(180deg); }
.user-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 30;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--md-elevation-2, 0 2px 8px rgba(0,0,0,.2));
  min-width: 160px;
  padding: var(--space-sm);
}
.user-menu-panel form button {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: var(--space-sm);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--color-danger);
  font: inherit;
  font-weight: 500;
}
.user-menu-panel form button:hover { background: color-mix(in srgb, var(--color-danger) 10%, transparent); }

/* --- Sidebar footer company box (accounts.context_processors.panel_nav
 * -> panel_company). --------------------------------------------------- */
.sidebar-company {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md);
  margin: var(--space-sm) 0;
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
}
.sidebar-company-logo { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.sidebar-company-logo-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-weight: 600;
}
.sidebar-company-text { display: flex; flex-direction: column; min-width: 0; }
.sidebar-company-name { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-company-role { font-size: 11px; color: var(--color-text-secondary); }

/* --- Stat cards (icon badge + value + delta/status line). ------------- */
.stat-card { display: flex; flex-direction: row; align-items: flex-start; gap: var(--space-md); min-width: 230px; }
.stat-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stat-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.stat-icon svg { width: 26px; height: 26px; }
.stat-icon-primary { background: color-mix(in srgb, var(--color-primary) 14%, transparent); color: var(--color-primary); }
.stat-icon-warning { background: color-mix(in srgb, var(--color-warning) 14%, transparent); color: var(--color-warning); }
.stat-icon-info    { background: color-mix(in srgb, var(--color-info) 14%, transparent);    color: var(--color-info); }
.stat-icon-success { background: color-mix(in srgb, var(--color-success) 14%, transparent); color: var(--color-success); }
.stat-icon-accent  { background: color-mix(in srgb, var(--color-accent) 14%, transparent);  color: var(--color-accent); }

.stat-label { color: var(--color-text-secondary); font-size: 13px; white-space: nowrap; }
.stat-value { font-size: 26px; font-weight: 600; line-height: 1.1; }
.stat-delta { font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 4px; }
.stat-delta svg { width: 13px; height: 13px; }
.stat-delta-up { color: var(--color-success); }
.stat-delta-down { color: var(--color-danger); }
.stat-status { font-size: 12px; color: var(--color-text-secondary); }
.stat-status-warning { color: var(--color-warning); }
.stat-status-info { color: var(--color-info); }
.stat-status-success { color: var(--color-success); }

/* --- Combined "This Month's Bills" summary card. ----------------------- */
.bills-card { display: flex; align-items: stretch; gap: var(--space-xl); flex-wrap: wrap; }
.bills-card-section { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 160px; }
.bills-card-section-icon { flex-direction: row; align-items: flex-start; gap: var(--space-md); }
.bills-card-section-icon .stat-body { display: flex; flex-direction: column; gap: 4px; }
.bills-card-section .stat-value { font-size: 26px; }
.bills-card-divider { width: 1px; background: var(--color-border); align-self: stretch; }
.bills-card-illustration { display: flex; align-items: center; justify-content: center; opacity: .45; flex-shrink: 0; color: var(--color-accent); }
@media (max-width: 900px) { .bills-card-divider, .bills-card-illustration { display: none; } }

/* --- Dot list (Buyer Pending By Age card). -----------------------------
 * max-width stops the list from stretching across the card's full width
 * (this card now shares a row with only one neighbor, see .chart-grid-3,
 * and is much wider than a bare `flex:1` list needs) but is wide enough
 * that margin-left:auto still gives label/value real breathing room and
 * a clean right-aligned value column, instead of either edge-pinning
 * values to the far side of the whole card or crowding them right after
 * the label with unused space past them. */
.pending-aging-body { display: flex; align-items: center; gap: var(--space-lg); }
.dot-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-md); width: 100%; max-width: 420px; }
.dot-list li { display: flex; align-items: center; gap: var(--space-sm); font-size: 14px; }
.dot-list-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.dot-list-value { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 600; }

/* --- Top Designs card (thumbnail + bar). -------------------------------- */
.top-design-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-md); }
.top-design-row { display: flex; align-items: center; gap: var(--space-sm); }
.top-design-thumb { width: 36px; height: 36px; border-radius: var(--radius-sm); object-fit: cover; flex-shrink: 0; }
.top-design-thumb-empty { background: var(--color-surface-alt); }
.top-design-name {
  font-size: 13px;
  flex-shrink: 0;
  width: 90px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Fixed CSS px size matching the viewBox (200x26, see reports/views.py's
 * top_designs_bar track/thickness) -- 1 viewBox unit = 1 real pixel, so
 * each <rect>'s rx renders as a true circle. A flexible/stretched size
 * here (width:100% with a differently-scaled height) would need a
 * non-uniform transform that turns rx into an ellipse on partly-filled
 * bars, or -- clipping via border-radius on the SVG root instead of rx --
 * rounds only the container's own edges, leaving a shorter bar's own
 * trailing (right) edge visibly square. */
.top-design-bar { display: block; flex-shrink: 0; width: 200px; height: 26px; }

/* --- Recent Activity card (owner-only). --------------------------------- */
.activity-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-md); }
.activity-row { display: flex; align-items: flex-start; gap: var(--space-sm); }
.activity-icon {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.activity-icon svg { width: 16px; height: 16px; }
.activity-text { flex: 1; font-size: 13px; min-width: 0; }
.activity-object { display: block; color: var(--color-text-secondary); font-size: 12px; margin-top: 2px; }
.activity-change { display: block; color: var(--color-text-secondary); font-size: 12px; margin-top: 2px; font-style: italic; }
.activity-time { font-size: 12px; color: var(--color-text-secondary); white-space: nowrap; }

/* Scan labels popup (templates/orders/processing.html, static/js/scan.js):
   the confirm-overlay/card shell, widened, with a camera view, a
   "Scanner / type" box and the scanned-parcel list. On a phone it fills
   the screen so the camera and list get all the room. */
.scan-open { display: inline-flex; align-items: center; gap: var(--space-sm); margin-bottom: 0; }
.scan-dialog[hidden] { display: none; }
.scan-dialog .confirm-card {
  max-width: 560px;
  max-height: calc(100vh - 2 * var(--space-lg));
  display: flex;
  flex-direction: column;
  padding: var(--space-xl);
  overflow: hidden;
}
.scan-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); }
.scan-head .approve-dialog-title { margin: 0; }
.scan-close {
  width: 36px; height: 36px; border: none; border-radius: var(--radius-full);
  background: transparent; color: var(--color-text-secondary); font-size: 24px; line-height: 1; cursor: pointer;
}
.scan-close:hover { background: var(--md-state-hover); color: var(--color-text-primary); }
.scan-modes {
  display: flex; gap: 4px; margin: var(--space-md) 0; padding: 4px;
  background: var(--color-surface-alt); border-radius: var(--radius-full);
}
.scan-mode {
  flex: 1; height: 34px; border: none; border-radius: var(--radius-full); background: transparent;
  color: var(--color-text-secondary); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
}
.scan-mode.is-active { background: var(--color-surface); color: var(--color-primary); box-shadow: var(--md-elevation-1); }
.scan-video-wrap {
  position: relative; background: #000; border-radius: var(--radius-md); overflow: hidden;
  aspect-ratio: 16 / 10; max-height: 34vh; width: 100%;
}
.scan-video-wrap video { width: 100%; height: 100%; object-fit: cover; display: block; }
.scan-aim {
  position: absolute; left: 10%; right: 10%; top: 50%; height: 34%; transform: translateY(-50%);
  border: 2px solid rgba(255, 255, 255, .85); border-radius: var(--radius-sm);
  box-shadow: 0 0 0 999px rgba(0, 0, 0, .25);
}
.scan-camera-bar { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--space-sm); margin-top: var(--space-sm); }
.scan-camera-bar .scan-photo { cursor: pointer; }
.scan-dialog .scan-camera-bar .btn { width: auto; flex: 0 1 auto; margin: 0; }
.scan-dialog .scan-camera-bar .scan-photo { margin-left: auto; }
.scan-camera-bar .btn.is-on { color: var(--color-primary); background: var(--md-secondary-container); }
.scan-camera-bar .btn { margin-bottom: 0; }
.scan-type { display: flex; gap: var(--space-sm); align-items: center; }
.scan-type .btn { margin-bottom: 0; flex: none; }
.content .scan-type input[data-scan-input] { flex: 1; min-width: 0; text-transform: uppercase; }
.scan-dialog .help { font-size: 12px; color: var(--color-text-secondary); margin: var(--space-xs, 4px) 0 0; }
.scan-flash { min-height: 20px; margin: var(--space-sm) 0; font-size: 13px; color: var(--color-text-secondary); }
.scan-flash.is-ok { color: var(--color-success); }
.scan-flash.is-warn { color: var(--color-warning); }
.scan-flash.is-error { color: var(--color-danger); }
.scan-dialog form[data-scan-form] { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.scan-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; min-height: 0; flex: 1; }
.scan-item {
  display: flex; gap: var(--space-md); justify-content: space-between; align-items: flex-start;
  padding: var(--space-md) 0; border-top: 1px solid var(--color-border);
}
.scan-item:first-child { border-top: none; }
.scan-item.is-pulse { animation: scan-pulse .8s ease; }
@keyframes scan-pulse { 0%, 100% { background: transparent; } 30% { background: color-mix(in srgb, var(--color-warning) 18%, transparent); } }
.scan-item-main { flex: 1; min-width: 0; }
.scan-item-number { font-weight: 500; word-break: break-word; }
.scan-item-problem { margin-top: 4px; font-size: 12px; color: var(--color-danger); }
.content .scan-item input.scan-tracking.scan-tracking { margin-top: var(--space-sm); width: 100%; max-width: 260px; height: 36px; }
.scan-item.is-awaiting { box-shadow: inset 3px 0 0 var(--color-primary); padding-left: var(--space-sm); }
.scan-item-wait { margin-top: 4px; font-size: 12px; color: var(--color-primary); }
.scan-item-side { display: flex; align-items: center; gap: 4px; flex: none; }
.scan-item-side .chip { white-space: nowrap; }
.scan-remove {
  width: 32px; height: 32px; border: none; border-radius: var(--radius-full); background: transparent;
  color: var(--color-text-secondary); font-size: 20px; line-height: 1; cursor: pointer;
}
.scan-remove:hover { background: var(--md-state-hover); color: var(--color-danger); }
.scan-empty { padding: var(--space-md) 0; }
.scan-actions { margin-top: var(--space-md); padding-top: var(--space-md); border-top: 1px solid var(--color-border); }
.scan-actions [data-scan-clear] { margin-right: auto; }
@media (max-width: 600px) {
  .scan-dialog { padding: 0; align-items: stretch; }
  .scan-dialog .confirm-card { max-width: none; max-height: none; height: 100%; border-radius: 0; padding: var(--space-lg); }
}

/* Order processing on a phone (templates/orders/processing.html): the
   wide table becomes one card per order, so nothing scrolls sideways.
     [x] ORD-…  · date · chips          Rs. amount
         Buyer · phone                  Payment chip
         2 bundles · Courier            Label Print
         [ tracking number ............................ ]
         [               Row action button              ]
   The header row only keeps its "Select all" checkbox. */
.process-mobile-label { display: none; }
.process-select-all { display: inline-flex; align-items: center; gap: var(--space-sm); margin: 0; cursor: pointer; }
@media (max-width: 760px) {
  .process-mobile-label { display: inline; }
  table.list.process-table,
  table.list.process-table thead,
  table.list.process-table tbody { display: block; width: 100%; }
  table.list.process-table {
    background: transparent;
    border: none;
    box-shadow: none;
    overflow: visible;
  }
  table.list.process-table thead tr { display: flex; }
  table.list.process-table thead th { display: none; }
  table.list.process-table thead th.col-check {
    display: block;
    width: auto;
    padding: 0 0 var(--space-sm) var(--space-lg);
    border: none;
    background: transparent;
    font-size: 13px;
    font-weight: 500;
    color: var(--color-text-secondary);
    text-transform: none;
    letter-spacing: 0;
  }
  table.list.process-table tbody tr.process-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "check order  amount"
      ".     buyer  pay"
      ".     items  label"
      "track track  track"
      "act   act    act";
    column-gap: var(--space-md);
    row-gap: var(--space-xs);
    padding: var(--space-md) var(--space-lg);
    margin-bottom: var(--space-md);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
  }
  table.list.process-table tbody tr.process-row:hover { background: var(--color-surface); }
  table.list.process-table tbody td {
    display: block;
    width: auto;
    height: auto;
    padding: 0;
    border: none;
    white-space: normal;
    text-align: left;
    min-width: 0;
  }
  table.list.process-table td.col-check { grid-area: check; padding-top: 2px; }
  table.list.process-table td.col-order { grid-area: order; }
  table.list.process-table td.col-amount { grid-area: amount; text-align: right; font-weight: 500; }
  table.list.process-table td.col-buyer { grid-area: buyer; }
  table.list.process-table td.col-pay { grid-area: pay; text-align: right; }
  table.list.process-table td.col-items { grid-area: items; }
  table.list.process-table td.col-label { grid-area: label; text-align: right; }
  table.list.process-table td.col-label .help { white-space: normal; }
  table.list.process-table td.col-tracking { grid-area: track; margin-top: var(--space-sm); font-size: 13px; }
  table.list.process-table td.col-tracking .process-mobile-label { color: var(--color-text-secondary); }
  table.list.process-table td.col-tracking label.process-mobile-label { display: block; margin-bottom: 4px; font-size: 12px; }
  table.list.process-table td.col-action { grid-area: act; margin-top: var(--space-sm); text-align: left; }
  table.list.process-table td.col-action .btn { width: 100%; height: 40px; }
  .content .process-table input.process-tracking.process-tracking:not([type="checkbox"]):not([type="radio"]) {
    max-width: none;
    height: 40px;
  }
  table.process-table .process-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }

  /* Bulk bar: count on its own line, the actions as an even button grid. */
  .bulk-bar { padding: var(--space-md); }
  .bulk-bar .bulk-actions { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); }
  .bulk-bar .bulk-actions[hidden] { display: none; }
  .bulk-bar .bulk-count { grid-column: 1 / -1; }
  .bulk-bar .btn { width: 100%; height: 40px; margin: 0; }
  .bulk-bar .btn.btn-text.bulk-danger { grid-column: 1 / -1; margin: 0; }
  .content .bulk-bar input.bulk-reason.bulk-reason:not([type="checkbox"]):not([type="radio"]) {
    grid-column: 1 / -1;
    max-width: none;
    width: 100%;
    height: 40px;
  }
}

/* Scan popup "Phone" tab (QR code to pair a phone) and the phone's own
   page (templates/orders/scan_phone.html), which shows the same scanner
   as a plain card instead of a popup. */
.scan-phone-pane { display: flex; gap: var(--space-lg); align-items: flex-start; }
.scan-phone-pane[hidden] { display: none; }
.scan-qr {
  flex: none; width: 180px; height: 180px; padding: 8px; background: #fff;
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  display: flex; align-items: center; justify-content: center; font-size: 12px; color: #555; text-align: center;
}
.scan-qr svg { width: 100%; height: 100%; display: block; }
.scan-phone-info { flex: 1; min-width: 0; }
.scan-phone-info .btn { margin: var(--space-sm) 0 0; }
.scan-phone-state { margin: 0 0 var(--space-sm); font-weight: 500; font-size: 14px; color: var(--color-text-secondary); }
.scan-phone-state::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 6px; border-radius: 50%;
  background: var(--color-text-disabled); vertical-align: middle;
}
.scan-phone-state.is-online { color: var(--color-success); }
.scan-phone-state.is-online::before { background: var(--color-success); }
.scan-phone-steps { margin: 0; padding-left: 18px; }
.scan-phone-steps li { margin-bottom: 2px; }
.scan-phone-link a { word-break: break-all; }
.scan-phone-warn { color: var(--color-warning) !important; }
.scan-item-ok { margin-top: 4px; font-size: 12px; color: var(--color-success); }
@media (max-width: 600px) {
  .scan-phone-pane { flex-direction: column; align-items: center; }
}

.scan-dialog.scan-standalone { display: block; max-width: 560px; padding: var(--space-lg); opacity: 1; }
.scan-standalone .scan-linked { display: flex; gap: 6px; align-items: baseline; margin: 0 0 var(--space-sm); }
.scan-standalone .scan-dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--color-success); }
.scan-standalone .scan-list { max-height: none; overflow: visible; }
/* Phone page: what to scan next (2026-10-02). Compact, so the scanner stays high on the screen: text on the left,
   Skip on the right. */
.scan-step {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--space-sm); align-items: center;
  margin: 0 0 var(--space-sm); padding: 6px var(--space-sm) 6px var(--space-md); border-radius: var(--radius-sm);
  border: 2px solid var(--color-primary); background: color-mix(in srgb, var(--color-primary) 8%, var(--color-surface));
}
.scan-step > div { grid-column: 1; min-width: 0; }
.scan-step.is-tracking { border-color: var(--color-warning); background: color-mix(in srgb, var(--color-warning) 12%, var(--color-surface)); }
.scan-step.is-done { border-color: var(--color-success); background: color-mix(in srgb, var(--color-success) 12%, var(--color-surface)); }
.scan-step-label { font-size: 10px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--color-text-secondary); }
.scan-step-title { font-size: 16px; font-weight: 700; line-height: 1.25; }
.scan-step-detail { font-size: 12px; color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scan-step-buttons { grid-column: 2; grid-row: 1 / span 3; display: flex; gap: 6px; }
/* Step 2: "Type tracking" and "Skip tracking" side by side on their own line, so the text keeps its width. */
.scan-step.is-tracking .scan-step-buttons { grid-column: 1 / -1; grid-row: auto; margin-top: 6px; }
.scan-dialog .scan-step-buttons .btn { width: auto; min-height: 30px; height: 30px; margin: 0; padding: 0 10px; font-size: 13px; }
.scan-dialog .scan-step-buttons .btn[hidden] { display: none; }
/* Phone: typing a label / tracking number that won't scan -- full-width box, small buttons beside it. */
.scan-manual { display: flex; align-items: center; gap: 6px; margin: 0 0 var(--space-sm); }
.scan-manual[hidden] { display: none; }
.content .scan-manual input[data-scan-manual-input] { flex: 1; min-width: 0; width: auto; height: 38px; margin: 0; }
.scan-dialog .scan-manual .btn { width: auto; min-height: 30px; height: 30px; padding: 0 10px; font-size: 13px; flex: none; }
.scan-count { margin: var(--space-sm) 0 0; font-size: 13px; font-weight: 600; }
.scan-count:empty { display: none; }
.scan-item-tracking { margin-top: 4px; font-size: 13px; font-weight: 500; word-break: break-all; }
.scan-standalone .scan-mode { height: 42px; font-size: 14px; }
.scan-standalone .scan-flash { font-size: 15px; }
/* Phone: the result sits above the scanner as a coloured banner. */
.scan-standalone .scan-flash-top { margin: 0 0 var(--space-sm); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); font-weight: 600; }
.scan-standalone .scan-flash-top:empty { display: none; }
.scan-standalone .scan-flash-top.is-ok { color: #fff; background: var(--color-success); }
.scan-standalone .scan-flash-top.is-warn { color: #fff; background: var(--color-warning); }
.scan-standalone .scan-flash-top.is-error { color: #fff; background: var(--color-danger); }
.scan-standalone .scan-flash-top:not(.is-ok):not(.is-warn):not(.is-error) { background: var(--color-surface-alt); }
/* The scanner box flashes the result colour. */
.scan-aim { transition: border-color .15s, box-shadow .15s; }
.scan-dialog[data-scan-state="ok"] .scan-aim { border: 4px solid var(--color-success); box-shadow: 0 0 0 999px color-mix(in srgb, var(--color-success) 30%, transparent); }
.scan-dialog[data-scan-state="warn"] .scan-aim { border: 4px solid var(--color-warning); box-shadow: 0 0 0 999px color-mix(in srgb, var(--color-warning) 30%, transparent); }
.scan-dialog[data-scan-state="error"] .scan-aim { border: 4px solid var(--color-danger); box-shadow: 0 0 0 999px color-mix(in srgb, var(--color-danger) 30%, transparent); }
.scan-dialog.scan-standalone .scan-camera-bar .btn { min-height: 44px; }
.scan-standalone .scan-item-number { font-size: 16px; }
/* Phone: correcting the list (2026-10-05) -- × removes a parcel, "Type/Change tracking" opens a box. */
.scan-standalone .scan-remove { width: 40px; height: 40px; font-size: 24px; }
.scan-dialog .scan-item .scan-item-edit { width: auto; min-height: 32px; margin: 2px 0 0 -8px; padding: 0 8px; }
/* Tracking box with small Save / Cancel on the same line. */
.scan-item.is-editing { flex-wrap: wrap; row-gap: var(--space-sm); }
.scan-item-editor { display: flex; align-items: center; gap: 6px; flex: 0 0 100%; order: 3; }
.content .scan-item-editor input.scan-tracking.scan-tracking { flex: 1; min-width: 0; margin-top: 0; max-width: none; height: 34px; }
.scan-item-editor-buttons { display: flex; gap: 4px; flex: none; }
.scan-dialog .scan-item-editor-buttons .btn { width: auto; min-height: 30px; height: 30px; padding: 0 10px; font-size: 13px; }
@media (max-width: 600px) {
  .scan-dialog.scan-standalone { padding: var(--space-md); }
}

/* Public pages: home, about, contact, policies (templates/pages/, 2026-10-08). */
.public-body { margin: 0; background: var(--color-background); color: var(--color-text-primary); font-family: Roboto, system-ui, sans-serif; }
.public-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-md);
  max-width: 880px; margin: 0 auto; padding: var(--space-lg) var(--space-lg) var(--space-md);
}
.public-brand { text-decoration: none; color: inherit; }
.public-nav { display: flex; gap: var(--space-lg); }
.public-nav a, .public-footer a { color: var(--color-primary); text-decoration: none; }
.public-nav a:hover, .public-footer a:hover, .public-article a:hover { text-decoration: underline; }
.public-main { max-width: 880px; margin: 0 auto; padding: 0 var(--space-lg) var(--space-xl); }
.public-hero h1 { font-size: 32px; line-height: 1.2; margin: var(--space-lg) 0 var(--space-sm); }
.public-lead { font-size: 17px; line-height: 1.6; max-width: 640px; color: var(--color-text-secondary); }
.public-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-md); margin: var(--space-xl) 0; }
.public-cards .card { margin: 0; }
.public-cards h2 { font-size: 16px; margin: 0 0 var(--space-sm); }
.public-cards p { margin: 0; line-height: 1.6; }
.public-section h2 { font-size: 18px; }
.public-links { padding-left: 18px; line-height: 2; }
.public-article { line-height: 1.7; }
.public-article h1 { font-size: 28px; line-height: 1.25; margin: var(--space-lg) 0 var(--space-xs); }
.public-article h2 { font-size: 18px; margin: var(--space-xl) 0 var(--space-sm); }
.public-article ul { padding-left: 20px; }
.public-article li { margin-bottom: 6px; }
.public-article a { color: var(--color-primary); }
.public-meta { color: var(--color-text-secondary); font-size: 13px; margin-top: 0; }
.public-facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px var(--space-lg); margin: var(--space-md) 0; }
.public-facts dt { color: var(--color-text-secondary); }
.public-facts dd { margin: 0; }
.public-footer {
  max-width: 880px; margin: 0 auto; padding: var(--space-lg); border-top: 1px solid var(--color-border);
  font-size: 13px; color: var(--color-text-secondary);
}
.public-footer nav { display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-lg); margin-bottom: var(--space-sm); }
.public-footer p { margin: 4px 0; }
@media (max-width: 600px) {
  .public-facts { grid-template-columns: 1fr; gap: 2px; }
  .public-facts dd { margin-bottom: var(--space-sm); }
  .public-hero h1 { font-size: 26px; }
}
