/* Tailwind input CSS — Phase 2.5.0+ (D-5).
 *
 * Built by `make tailwind` into `web/static/css/app.css` (committed).
 * Edits here require re-running `make tailwind` and committing the
 * regenerated output.
 *
 * Preflight is DISABLED in tailwind.config.js (Risks §1). The component
 * layer below adds back the modern resets scoped to chrome / pilot
 * surfaces only.
 */

*, ::before, ::after {
  --tw-border-spacing-x: 0;
  --tw-border-spacing-y: 0;
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  --tw-pan-x:  ;
  --tw-pan-y:  ;
  --tw-pinch-zoom:  ;
  --tw-scroll-snap-strictness: proximity;
  --tw-gradient-from-position:  ;
  --tw-gradient-via-position:  ;
  --tw-gradient-to-position:  ;
  --tw-ordinal:  ;
  --tw-slashed-zero:  ;
  --tw-numeric-figure:  ;
  --tw-numeric-spacing:  ;
  --tw-numeric-fraction:  ;
  --tw-ring-inset:  ;
  --tw-ring-offset-width: 0px;
  --tw-ring-offset-color: #fff;
  --tw-ring-color: rgb(59 130 246 / 0.5);
  --tw-ring-offset-shadow: 0 0 #0000;
  --tw-ring-shadow: 0 0 #0000;
  --tw-shadow: 0 0 #0000;
  --tw-shadow-colored: 0 0 #0000;
  --tw-blur:  ;
  --tw-brightness:  ;
  --tw-contrast:  ;
  --tw-grayscale:  ;
  --tw-hue-rotate:  ;
  --tw-invert:  ;
  --tw-saturate:  ;
  --tw-sepia:  ;
  --tw-drop-shadow:  ;
  --tw-backdrop-blur:  ;
  --tw-backdrop-brightness:  ;
  --tw-backdrop-contrast:  ;
  --tw-backdrop-grayscale:  ;
  --tw-backdrop-hue-rotate:  ;
  --tw-backdrop-invert:  ;
  --tw-backdrop-opacity:  ;
  --tw-backdrop-saturate:  ;
  --tw-backdrop-sepia:  ;
  --tw-contain-size:  ;
  --tw-contain-layout:  ;
  --tw-contain-paint:  ;
  --tw-contain-style:  ;
}

::backdrop {
  --tw-border-spacing-x: 0;
  --tw-border-spacing-y: 0;
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  --tw-pan-x:  ;
  --tw-pan-y:  ;
  --tw-pinch-zoom:  ;
  --tw-scroll-snap-strictness: proximity;
  --tw-gradient-from-position:  ;
  --tw-gradient-via-position:  ;
  --tw-gradient-to-position:  ;
  --tw-ordinal:  ;
  --tw-slashed-zero:  ;
  --tw-numeric-figure:  ;
  --tw-numeric-spacing:  ;
  --tw-numeric-fraction:  ;
  --tw-ring-inset:  ;
  --tw-ring-offset-width: 0px;
  --tw-ring-offset-color: #fff;
  --tw-ring-color: rgb(59 130 246 / 0.5);
  --tw-ring-offset-shadow: 0 0 #0000;
  --tw-ring-shadow: 0 0 #0000;
  --tw-shadow: 0 0 #0000;
  --tw-shadow-colored: 0 0 #0000;
  --tw-blur:  ;
  --tw-brightness:  ;
  --tw-contrast:  ;
  --tw-grayscale:  ;
  --tw-hue-rotate:  ;
  --tw-invert:  ;
  --tw-saturate:  ;
  --tw-sepia:  ;
  --tw-drop-shadow:  ;
  --tw-backdrop-blur:  ;
  --tw-backdrop-brightness:  ;
  --tw-backdrop-contrast:  ;
  --tw-backdrop-grayscale:  ;
  --tw-backdrop-hue-rotate:  ;
  --tw-backdrop-invert:  ;
  --tw-backdrop-opacity:  ;
  --tw-backdrop-saturate:  ;
  --tw-backdrop-sepia:  ;
  --tw-contain-size:  ;
  --tw-contain-layout:  ;
  --tw-contain-paint:  ;
  --tw-contain-style:  ;
}

/* -------------------------------------------------------------------
 * Design tokens — MakerShelf terracotta direction (Phase 2.7.11).
 *
 * Each COLOR token is emitted in TWO forms:
 *   --<token>      : flat hex   — consumed by the raw-CSS component
 *                    recipes ported from the design handoff
 *                    (they reference `var(--<token>)` directly).
 *   --<token>-rgb  : channel triple "R G B" — consumed by the Tailwind
 *                    config as `rgb(var(--<token>-rgb) / <alpha-value>)`,
 *                    which keeps opacity modifiers (`border-divider/30`,
 *                    `bg-divider/20`) working across the `[data-direction]`
 *                    multi-tenant re-skin seam.
 *
 * Non-color primitives (radii, shadows, ease, icon-stroke, fonts, ring)
 * are flat vars only — `<alpha-value>` never applies to them. `--accent-ring`
 * is a fixed-alpha rgba consumed only as a flat var by focus recipes.
 *
 * Only the terracotta direction is populated. `:root` carries it as the
 * default, so nothing changes visually when `[data-direction]` is absent
 * from `<html>` (wiring that attribute is deferred to a later sub-PR). The
 * ink/sage/ochre/fraunces blocks from the handoff are intentionally dropped.
 * ------------------------------------------------------------------- */

:root {
  /* Surfaces (page chrome) */
  --surface: #ffffff;
  --surface-rgb: 255 255 255;
  --surface-subtle: #faf7f3;
  --surface-subtle-rgb: 250 247 243;
  --surface-muted: #f1ece4;
  --surface-muted-rgb: 241 236 228;
  /* Text on surfaces */
  --primary: #2b2520;
  --primary-rgb: 43 37 32;
  --secondary: #5e564d;
  --secondary-rgb: 94 86 77;
  --muted: #978d81;
  --muted-rgb: 151 141 129;
  --on-inverse: #f4eee7;
  --on-inverse-rgb: 244 238 231;
  /* Borders */
  --subtle: #ebe5dd;
  --subtle-rgb: 235 229 221;
  --divider: #dad1c6;
  --divider-rgb: 218 209 198;
  /* Semantic feedback — warm-tuned emerald / amber / red / slate-blue */
  --success-bg: #ecf3ea;
  --success-bg-rgb: 236 243 234;
  --success-text: #41704c;
  --success-text-rgb: 65 112 76;
  --success-border: #b7d0b9;
  --success-border-rgb: 183 208 185;
  --warning-bg: #faf2e2;
  --warning-bg-rgb: 250 242 226;
  --warning-text: #8f5f1b;
  --warning-text-rgb: 143 95 27;
  --warning-border: #e6c890;
  --warning-border-rgb: 230 200 144;
  --danger-bg: #faece7;
  --danger-bg-rgb: 250 236 231;
  --danger-text: #ad3c2a;
  --danger-text-rgb: 173 60 42;
  --danger-border: #e6ad9d;
  --danger-border-rgb: 230 173 157;
  --info-bg: #e8edf7;
  --info-bg-rgb: 232 237 247;
  --info-text: #3a5d8f;
  --info-text-rgb: 58 93 143;
  --info-border: #c1d0ea;
  --info-border-rgb: 193 208 234;
  /* Non-color primitives (flat vars only) */
  --r-sm: 6px;
  --r-lg: 14px;
  --icon-stroke: 1.75;
  --shadow-card: 0 1px 2px rgba(43, 37, 32, .05), 0 1px 3px rgba(43, 37, 32, .04);
  --shadow-pop: 0 8px 28px rgba(43, 37, 32, .12);
  --ease: 160ms cubic-bezier(.4, 0, .2, 1);
  --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: "Newsreader", Georgia, "Times New Roman", serif;
}

/* Direction: TERRACOTTA (default / only shipped direction) */

:root,
:root[data-direction="terracotta"] {
  --surface-inverse: #2a201b;
  --surface-inverse-rgb: 42 32 27;
  --accent: #a6512f;
  --accent-rgb: 166 81 47;
  --accent-hover: #8c4226;
  --accent-hover-rgb: 140 66 38;
  --accent-text: #94472a;
  --accent-text-rgb: 148 71 42;
  --accent-soft: #f6eae2;
  --accent-soft-rgb: 246 234 226;
  --accent-soft-border: #e3c4b3;
  --accent-soft-border-rgb: 227 196 179;
  --accent-ring: rgba(166, 81, 47, .28);
}

/* Low-risk globals ported from the handoff base layer. The handoff's
   global `* { box-sizing: border-box }` is intentionally NOT ported —
   preflight is off and legacy markup relies on default box-sizing;
   box-sizing stays scoped to the specific control recipes. */

svg {
  stroke-width: var(--icon-stroke);
}

/* Type rendering ported from the handoff base `body` rule. On macOS the
   browser default is subpixel antialiasing, which renders the same font
   visibly heavier/bolder than the mockup's grayscale-antialiased text.
   These three properties — and nothing else from the handoff `body` block —
   match the mockup so titles, section heads, the wordmark and body copy all
   render at the intended weight. Firefox-on-macOS uses the -moz- form. */

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

::-moz-selection {
  background: var(--accent-soft);
}

::selection {
  background: var(--accent-soft);
}

.container {
  width: 100%;
}

@media (min-width: 560px) {
  .container {
    max-width: 560px;
  }
}

@media (min-width: 760px) {
  .container {
    max-width: 760px;
  }
}

@media (min-width: 900px) {
  .container {
    max-width: 900px;
  }
}

@media (min-width: 1000px) {
  .container {
    max-width: 1000px;
  }
}

@media (min-width: 1280px) {
  .container {
    max-width: 1280px;
  }
}

/* `h3.card-title` — load-bearing for the audit-log E2E test
       (tests/e2e/test_audit_log_diff_renders_uc_d.py:66). Style must
       contrast clearly with the card background. */

.card-title {
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* `.wto-card` — the outer card wrapper used by hand-built admin
       pages (audit log is the pilot). Restyles must NOT change the
       <h3 class="card-title"> selector contract.
       Phase 2.5.0 ships only the audit-log restyle; 2.5.1 adds the
       remaining hand-built pages on the same pattern. */

.wto-card {
  width: 100%;
  border-radius: 14px;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
  box-sizing: border-box;
}

.wto-card-header {
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  box-sizing: border-box;
}

.wto-card-body {
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 1rem;
  padding-bottom: 1rem;
  box-sizing: border-box;
}

/* Card anatomy / vertical rhythm (SPEC §4.6a — the "stacked-padding
       void" trap). App-wide, not page-scoped (per the design handoff). */

/* Bordered `.card-head` band + body must not BOTH add vertical space
       against the faint divider (that doubling read as a ~30px void). The
       band supplies its own bottom padding + border, so the body below it
       drops its `py-4` top — header→content lands at the ~14-18px target. */

.wto-card-header + .wto-card-body {
  padding-top: 0.25rem;
}

/* First/last body child sit flush to the card's own padding — strip
       their leading/trailing margins so a heading's top margin or a
       table/list's bottom margin doesn't stack into extra air. */

.wto-card-body > :first-child {
  margin-top: 0;
}

.wto-card-body > :last-child {
  margin-bottom: 0;
}

/* Inline title (a `.t-card-title` directly in the body, no bordered
       band): the content after it gets the single 14px header→content gap
       — EXCEPT a caption, which hugs the title (≈4px via line-box) and then
       owns its own 14px down to the content. One gap, set once. */

.wto-card-body > .t-card-title + *:not(.wto-status-caption) {
  margin-top: 14px;
}

/* Audit-log specific helpers. Note: we do NOT add classes/attrs to
       `<details>` itself (integration tests assert on a literal
       `<details open><summary>` substring — see
       tests/integration/test_admin_audit_view.py:159-160). Style
       `<details>` via the parent selector below. */

.wto-audit-log details {
  font-size: 0.85rem;
  margin-bottom: 0.5rem;
}

.wto-audit-log summary {
  cursor: pointer;
  font-weight: 600;
  color: rgb(var(--secondary-rgb) / 1);
}

/* The `<pre>` MUST have a computed text color that differs from
       its background (E2E contrast check at lines 96-103 of
       test_audit_log_diff_renders_uc_d.py). `text-primary` on
       `bg-surface-subtle` gives a distinct pair. */

.wto-audit-log pre {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-subtle-rgb) / var(--tw-bg-opacity));
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 18rem;
  overflow: auto;
  padding: 0.5rem;
  margin-top: 0.25rem;
}

.wto-audit-table {
  width: 100%;
  font-size: 0.875rem;
  line-height: 1.25rem;
  border-collapse: collapse;
}

.wto-audit-table th,
    .wto-audit-table td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: top;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-audit-table th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-filters-grid {
  margin-bottom: 1rem;
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 1fr));
  gap: 0.75rem;
}

@media (min-width: 560px) {
  .wto-filters-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 900px) {
  .wto-filters-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.wto-filters-grid label {
  display: flex;
  flex-direction: column;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-filters-grid input {
  margin-top: 0.25rem;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-pager {
  margin-top: 1rem;
  display: flex;
  gap: 1rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-pager a {
  --tw-text-opacity: 1;
  color: rgb(var(--accent-rgb) / var(--tw-text-opacity));
  text-decoration-line: underline;
}

.wto-pager a:hover {
  --tw-text-opacity: 1;
  color: rgb(var(--accent-hover-rgb) / var(--tw-text-opacity));
}

/* Chrome — sidebar / topbar / logout. */

.wto-shell {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-subtle-rgb) / var(--tw-bg-opacity));
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

@media (min-width: 760px) {
  .wto-shell {
    flex-direction: row;
  }
}

.wto-sidebar {
  width: 100%;
  flex-shrink: 0;
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-inverse-rgb) / var(--tw-bg-opacity));
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
}

@media (min-width: 760px) {
  .wto-sidebar {
    position: sticky;
    top: 0px;
    height: 100vh;
    width: 232px;
    overflow-y: auto;
  }
}

.wto-sidebar-inner {
  display: flex;
  height: 100%;
  flex-direction: column;
}

.wto-brand {
  border-bottom-width: 1px;
  border-color: rgb(var(--divider-rgb) / 0.3);
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  font-size: 1rem;
  line-height: 1.5rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
}

.wto-brand a {
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
  text-decoration-line: none;
}

.wto-main {
  display: flex;
  min-width: 0px;
  flex: 1 1 0%;
  flex-direction: column;
}

.wto-header {
  display: flex;
  min-height: 60px;
  align-items: center;
  gap: 0.75rem;
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.wto-content {
  flex: 1 1 0%;
  padding-left: 1rem;
  padding-right: 1rem;
  padding-top: 1rem;
  padding-bottom: 1rem;
}

.wto-logout-btn {
  display: inline-flex;
  cursor: pointer;
  align-items: center;
  border-width: 0px;
  background-color: transparent;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-logout-btn:hover {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* D-17: logout now lives in the sidebar footer (inverse surface).
       The base rule above is tuned for the topbar's light surface; the
       sidebar context needs an inverse-surface contrast pair so the
       link stays readable on `bg-surface-inverse`. */

.wto-sidebar .wto-logout-btn {
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
}

.wto-sidebar .wto-logout-btn:hover {
  --tw-text-opacity: 1;
  color: rgb(var(--divider-rgb) / var(--tw-text-opacity));
}

/* D-17: sidebar footer block. `md:mt-auto` pushes the footer to the
       column bottom on desktop (where `.wto-sidebar-inner` is a stretched
       flex column inside `.wto-shell.md:flex-row`); on mobile the
       sidebar is content-driven and the footer flows naturally after
       the nav entries (no available space to absorb). */

.wto-sidebar-footer {
  border-top-width: 1px;
  border-color: rgb(var(--divider-rgb) / 0.3);
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
}

@media (min-width: 760px) {
  .wto-sidebar-footer {
    margin-top: auto;
  }
}

.wto-extra-nav {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.wto-extra-nav a {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  border-radius: 0.25rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
  text-decoration-line: none;
}

.wto-extra-nav a:hover {
  background-color: rgb(var(--divider-rgb) / 0.2);
}

.wto-extra-nav a.active {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--accent-rgb) / var(--tw-bg-opacity));
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
}

/* Phase 2.7.11 — thin-stroke nav icon sizing (B2). Each nav row
       carries an inline `<svg class="nav-link-icon">` as its first child;
       the global `svg { stroke-width: var(--icon-stroke) }` governs the
       weight. `flex-none` keeps the icon box fixed so labels align. */

.nav-link-icon {
  height: 18px;
  width: 18px;
  flex: none;
}

/* Sidebar nav-link-title color (inherits the parent's color but the
       sidebar context wants it white-on-dark explicitly). */

.wto-sidebar .nav-link-title,
    .wto-sidebar .nav-link {
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
  font-size: 13.5px;
  font-weight: 500;
}

/* ---------------------------------------------------------------
     * Phase 2.5.1 — shared form / button / flash helpers used by the
     * three template restyles (prices_grid, exports_select,
     * invoice_export). All semantic colors flow through the
     * danger/warning/success/info token families in tailwind.config.js
     * so v3.0 / v6.0 multi-tenant branding re-skins by token only.
     * --------------------------------------------------------------- */

/* Buttons — 4 intents (DDN-3: ported from the design handoff, replacing
       the incumbent @apply defs). Class names preserved; adds the `.wto-btn`
       base, `:focus-visible` accent ring, and `.wto-btn-sm` the design
       requires. Recipes are raw CSS reading the flat hex `var(--<token>)`
       form, byte-faithful to the handoff. The `-warning`/`-danger`/`-secondary`
       defs live with the other button blocks below (also DDN-3-replaced).

       The base LAYOUT block applies to the base class AND every variant, so a
       template emitting a variant class alone (`class="wto-btn-secondary"`)
       renders a complete button without needing to also carry `.wto-btn`. The
       `:focus-visible` ring is likewise extended to the variants. Variant rules
       below stay color-only (bg/text/border/hover). The disabled state remains
       the existing `.save-bar`-scoped `[disabled]` rule (no global change). */

.wto-btn,
    .wto-btn-primary,
    .wto-btn-secondary,
    .wto-btn-warning,
    .wto-btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  padding: 10px 16px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease), color var(--ease), box-shadow var(--ease);
  white-space: nowrap;
  text-decoration: none;
}

.wto-btn:focus-visible,
    .wto-btn-primary:focus-visible,
    .wto-btn-secondary:focus-visible,
    .wto-btn-warning:focus-visible,
    .wto-btn-danger:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.wto-btn-primary {
  background: var(--accent);
  color: #fff;
}

.wto-btn-primary:hover {
  background: var(--accent-hover);
}

.wto-form-group {
  margin-bottom: 1rem;
}

/* Visually-hidden accessibility helper. Tailwind ships `.sr-only`
       under preflight; preflight is disabled in our config so we add
       the rule here. Used by prices_grid for per-cell input labels. */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------
     * Prices grid — bulk editor body. The `has-error` class token is
     * load-bearing for `tests/integration/test_admin_prices_grid.py:412`
     * (`"has-error" in body`). Keep the class name verbatim — styling
     * is via the rules below.
     * --------------------------------------------------------------- */

.wto-prices-grid-empty {
  font-style: italic;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-prices-table td.has-error {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--danger-bg-rgb) / var(--tw-bg-opacity));
}

.wto-prices-table td.has-error input {
  --tw-border-opacity: 1;
  border-color: rgb(var(--danger-border-rgb) / var(--tw-border-opacity));
}

.wto-delivery-print .signatures .wto-prices-table .err {
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
  padding-top: 0.25rem;
}

.wto-prices-table .err {
  margin-top: 0.25rem;
  display: block;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

/* ---------------------------------------------------------------
     * Invoice export — HTML preview table. `<td>` cells must remain
     * bare per the integration regex at
     * `tests/integration/test_admin_invoice_export.py:700-708`
     * (`re.findall(r"<td>(.*?)</td>", row)` requires the literal `<td>`
     * opening tag with no class attribute). All styling flows from the
     * parent table selector below.
     * --------------------------------------------------------------- */

.wto-invoice-export-table {
  width: 100%;
  border-collapse: collapse;
}

.wto-invoice-export-table th,
    .wto-invoice-export-table td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-export-table th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-invoice-export-table tfoot.wto-totals td {
  font-weight: 600;
}

/* ---------------------------------------------------------------
     * Phase 2.5.1 — POS status page. Highest-traffic admin surface.
     * The five tables share `.wto-status-table` — one class, one set
     * of rules. Per-row variants attach via row classes
     * (e.g., `.cm-row-voided`) without per-table hooks.
     *
     * `<h2>` section headings carry NO class attribute (byte-level
     * test contracts at test_admin_status_view.py:324-330,418,etc.
     * require literal `<h2>...</h2>` opening tags). Style via the
     * parent `.wto-pos-status` selector below — they have no
     * `text-primary` class to compete with Tabler's `!important`
     * rule.
     * --------------------------------------------------------------- */

/* Phase 2.7.12 split the dashboard into sibling `.wto-card` blocks
       (plus the `.stat-grid`) directly under this root. `.wto-card` has
       no `margin-bottom`, so govern the vertical rhythm here with a
       single flex `gap` (18px matches the mockup's `.dash-cols`). */

.wto-pos-status {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Status-page section titles now use the `.t-card-title` class + the
       inline card anatomy (SPEC §4.6a); the old descendant `h2` rule (with
       its `border-b` underline — the divider we removed) is retired. */

.wto-status-table {
  /* No bottom margin: the table is the last element in its card
           body, so the body's own bottom padding is the only trailing
           air (was mb-4 + py-4 ≈ 32px of empty space at the block end). */
  width: 100%;
  border-collapse: collapse;
}

.wto-pos-status th,
    .wto-pos-status td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-pos-status th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-pos-status ul {
  margin-bottom: 1rem;
  list-style-position: inside;
  list-style-type: disc;
  padding-left: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* Phase 2.7.11 sub-PR 3 — carry-in fix. Default-blue unstyled
       links inside the dashboard tables (order id, "Open" / "Abrir",
       payment id, credit-memo number) take the accent link treatment
       (terracotta foreground, underline on hover). No global `a{}`
       reset exists (preflight off), so this is scoped to the dashboard
       table chrome. */

.wto-pos-status .wto-status-table a {
  color: var(--accent-text);
  text-decoration: none;
}

.wto-pos-status .wto-status-table a:hover {
  text-decoration: underline;
}

/* Phase 2.5.3 — POS dashboard identity block (collapsible).
       The toggle button + chevron live above the <dl>; Alpine's
       `x-show` toggles the <dl>'s display. `[x-cloak]` prevents the
       <dl> from flashing visible during Alpine boot. */

/* `.wto-pos-info*` (chip-styled POS-info toggle) retired in favour of
       the `.disclosure` section-header pattern (SPEC 4.8b). */

[x-cloak] {
  display: none !important;
}

.wto-status-caption {
  /* SPEC §4.6a: caption hangs tight under the title (no top margin;
           the ~4px gap comes from the title's line-box) with a single
           14px gap to the content below — not a top+bottom margin pair. */
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
  margin: 0 0 14px;
}

.wto-status-outstanding {
  margin-bottom: 1rem;
  list-style-position: inside;
  list-style-type: disc;
  padding-left: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* Per-row fulfillment type badges (Phase 2.7.11 sub-PR 3): the
       colored `.wto-pos-status .badge-firm/-consignment` rules are
       RETIRED — type carries no hue (color-lives-in-state). status.html
       now emits `class="badge badge-{{ o.type }}"`, picking up the
       neutral standalone `.badge`/`.badge-*` recipe in the MakerShelf
       component block. */

/* Voided credit-memo row. Phase 2.7.17 (C3 #5): dim, not strike-
       through — the "Voided" pill already conveys the state, and a
       full-row strikethrough was overkill. Class name is load-bearing
       (test_admin_status_view.py). */

.wto-pos-status .cm-row-voided {
  --tw-text-opacity: 1;
  color: rgb(var(--muted-rgb) / var(--tw-text-opacity));
}

/* ---------------------------------------------------------------
     * Phase 2.5.1 sub-PR 3 — Sales report editor. Migrates rules from
     * the deleted inline `<style>` block in
     * `web/templates/admin/sales_report_editor.html`. The
     * `.wto-sales-editor` namespace scopes the editor's grid/table
     * rules so they don't leak. POS pills use the canonical `.pill
     * pill-*` recipe; Save/Finalize swap to `.wto-btn-primary`.
     *
     * Two Bootstrap-classed survivor strings stay untouched in the
     * template (test pins at L478/L561). Sub-PR 6 sweeps them with
     * coordinated test+template edits.
     * --------------------------------------------------------------- */

.wto-sales-editor-header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin-bottom: 1rem;
  -moz-column-gap: 1.5rem;
       column-gap: 1.5rem;
  row-gap: 0.75rem;
}

.wto-sales-editor-header > div {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.wto-sales-editor-header label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-header dt {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-header dd {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  margin: 0;
}

.wto-sales-editor-notes-cell {
  grid-column: 1 / span 2;
}

.wto-sales-editor input,
    .wto-sales-editor textarea {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor textarea {
  width: 100%;
  min-height: 4rem;
}

.wto-sales-editor-section {
  margin-top: 1rem;
  margin-bottom: 0.5rem;
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-lines {
  margin-top: 0.5rem;
  width: 100%;
  border-collapse: collapse;
}

.wto-sales-editor-lines th,
    .wto-sales-editor-lines td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-lines th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-sales-editor-lines td input[type="number"] {
  width: 6rem;
}

.wto-sales-editor-clear-btn {
  cursor: pointer;
  border-width: 0px;
  background-color: transparent;
  padding: 0px;
  font-size: 1rem;
  line-height: 1.5rem;
  --tw-text-opacity: 1;
  color: rgb(var(--accent-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-clear-btn:hover {
  --tw-text-opacity: 1;
  color: rgb(var(--accent-hover-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-show-all {
  margin-top: 0.75rem;
  margin-bottom: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.wto-sales-editor-show-all label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-sales-editor-actions {
  margin-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.wto-sales-editor-state-transitions {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-top: 1rem;
}

.wto-sales-editor-summary {
  margin-top: 0.5rem;
}

.wto-sales-editor-summary table {
  border-collapse: collapse;
  width: auto;
}

.wto-sales-editor-summary td {
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.125rem;
  padding-bottom: 0.125rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* Phase 2.5.1 — sub-PR 3.5 shell foundation.
       `.wto-card-scroll` — horizontal-scroll wrapper for tables inside
       `.wto-card-body` that overflow their card on narrow viewports
       (≤640px). Wrap any `<table>` that may exceed the card's width to
       keep the layout shift inside the card rather than pushing the
       viewport's horizontal scrollbar. iOS momentum scroll added via
       `-webkit-overflow-scrolling: touch`. */

.wto-card-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* SPEC §4.4b — overflow cue: a subtle edge shadow that appears
           only when the table overflows (and fades as you reach that
           edge), signaling "swipe for more". Pure-CSS scroll-shadow —
           the `local` cover gradients (card surface → transparent) mask
           the `scroll`-fixed shadow gradients at each edge. */
  background:
            linear-gradient(to right, var(--surface), transparent) 0 0 / 28px 100% no-repeat,
            linear-gradient(to left, var(--surface), transparent) 100% 0 / 28px 100% no-repeat,
            radial-gradient(farthest-side at 0 50%, rgba(43, 37, 32, .18), transparent) 0 0 / 16px 100% no-repeat,
            radial-gradient(farthest-side at 100% 50%, rgba(43, 37, 32, .18), transparent) 100% 0 / 16px 100% no-repeat;
  background-attachment: local, local, scroll, scroll;
}

/* ---------------------------------------------------------------
     * Phase 2.5.1 sub-PR 4 — Fulfillment views (editor + list).
     * Migrates rules from the deleted inline `<style>` blocks in
     * `web/templates/admin/fulfillment_editor.html` and
     * `web/templates/admin/fulfillment_list.html`.
     *
     * Two new semantic button tokens (`wto-btn-danger` / `wto-btn-warning`)
     * derive from the existing danger / warning token families in
     * `tailwind.config.js`. Per plan-review M-7: no invented hex values
     * — palette flows through the semantic tokens only.
     *
     * Load-bearing class tokens preserved verbatim (styled here via
     * parent selectors so the class names survive the restyle):
     *   - `.state-transitions` — E2E locator pin (fulfillment_editor).
     *   - `class="err"` on the filter-error banner — integration-test pin
     *     (fulfillment_list); aliased to the danger-token family.
     *   - `has-error` on per-cell `<td>` — cell-error rendering (2.0.8).
     *   - `pill-{state}` / `badge-{type}` — Jinja-interpolated state +
     *     type tokens; styled via parent selectors (mirrors sub-PR 2's
     *     `.wto-pos-status .badge-firm` / `.badge-consignment` pattern).
     *   - `.remove-row`, `.discrepancy-block`, `.type-fieldset`,
     *     `.icon-trash` — class names kept verbatim, styled via parent.
     * --------------------------------------------------------------- */

/* DDN-3: ported from the design handoff (soft tints match the token
       families). Class names preserved. Pair with `.wto-btn` for the base. */

.wto-btn-warning {
  background: var(--warning-bg);
  color: var(--warning-text);
  border-color: var(--warning-border);
}

.wto-btn-warning:hover {
  filter: brightness(.975);
}

.wto-btn-danger {
  background: var(--danger-bg);
  color: var(--danger-text);
  border-color: var(--danger-border);
}

.wto-btn-danger:hover {
  filter: brightness(.975);
}

/* --- Fulfillment editor namespace --------------------------------- */

.wto-fulfillment-editor-header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin-bottom: 1rem;
  -moz-column-gap: 1.5rem;
       column-gap: 1.5rem;
  row-gap: 0.75rem;
}

/* Collapse the two-column header to a single column on narrow
       viewports so the picker selects + hint text don't push the grid
       past the viewport. Mirrors the `.form-grid` 560px collapse. */

@media (max-width: 560px) {
  .wto-fulfillment-editor-header {
    grid-template-columns: 1fr;
  }
}

.wto-fulfillment-editor-header > div {
  display: flex;
  flex-direction: column;
  /* preflight-off: let grid cells shrink below their content's
           min-content width so the `w-full` select can fit. */
  min-inline-size: 0;
  gap: 0.25rem;
}

.wto-fulfillment-editor-header label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-editor-header dt {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-editor-header dd {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  margin: 0;
}

.wto-fulfillment-editor-notes-cell {
  grid-column: 1 / span 2;
}

/* Phase 2.7.21 (B-2): the create-form conditional blocks (dest picker,
       DIRECT_SALE customer + payment-mode) span both grid columns so their
       stacked fields aren't squeezed into a half-width cell. Collapses with
       the header's single-column @560px rule (a `grid-column: 1 / span 2`
       on a single-column grid is a no-op). */

.wto-fulfillment-editor-span-2 {
  grid-column: 1 / span 2;
}

.wto-fulfillment-editor input,
    .wto-fulfillment-editor select,
    .wto-fulfillment-editor textarea {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  box-sizing: border-box;
}

.wto-fulfillment-editor textarea {
  width: 100%;
  min-height: 4rem;
}

.wto-fulfillment-editor-section {
  margin-top: 1rem;
  margin-bottom: 0.5rem;
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-editor fieldset.type-fieldset {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  border-style: solid;
  min-inline-size: 0;
}

.wto-fulfillment-editor fieldset.type-fieldset legend {
  padding-left: 0.25rem;
  padding-right: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-print .signatures .wto-fulfillment-editor fieldset.type-fieldset label {
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
  padding-top: 0.25rem;
}

.wto-fulfillment-editor fieldset.type-fieldset label {
  margin-top: 0.25rem;
  margin-bottom: 0.25rem;
  display: block;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* Phase 2.7.21 (B-1): the single "Forma de pago" radio group reuses the
       same preflight-off fieldset border recipe as `type-fieldset`. */

.wto-fulfillment-editor fieldset.wto-payment-mode-fieldset {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  border-style: solid;
  min-inline-size: 0;
}

.wto-fulfillment-editor fieldset.wto-payment-mode-fieldset legend {
  padding-left: 0.25rem;
  padding-right: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

/* Radio↔label alignment: the blanket `.wto-fulfillment-editor input`
       rule above applies a text-input border + px-2/py-1 padding + `w-full`
       to ALL inputs, which misaligns radios/checkboxes (they are not text
       inputs). Reset the radio/checkbox box and lay each option out as a
       flex row so the control sits centered next to its label. */

.wto-fulfillment-editor fieldset.type-fieldset label,
    .wto-fulfillment-editor fieldset.wto-payment-mode-fieldset label {
  display: flex;
  /* Override the `flex-direction: column` inherited from the
           `.wto-fulfillment-editor-header label` rule (the fieldsets live
           inside the header grid) so the radio sits inline before its label. */
  flex-direction: row;
  align-items: center;
  gap: .5rem;
}

.wto-fulfillment-editor input[type="radio"],
    .wto-fulfillment-editor input[type="checkbox"] {
  width: auto;
  padding: 0;
  border: 0;
  margin: 0;
  flex: none;
  box-sizing: border-box;
}

/* Phase 2.7.9 Item B — Untracked sentinel option in the source POS
       picker (D-9 visually distinct: italic + muted). The disabled
       separator `<option disabled>──────────</option>` immediately
       above it provides visual breathing room from the real POSes
       above. `option` element styling is conservatively supported
       across Chromium/Firefox/WebKit. */

.wto-fulfillment-editor .wto-untracked-option {
  font-style: italic;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-editor .discrepancy-block {
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--warning-border-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--warning-bg-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
}

/* Phase 2.7.11 sub-PR 4 — the editor's state pill now emits the
       canonical `.pill pill-*` recipe (color-lives-in-state) and the
       type badge the neutral `.badge badge-*` recipe; the editor-scoped
       colored `.pill-*` / `.badge-*` rules were retired here (sub-PR 3
       already retired the list-scoped siblings). */

/* Lines table — uses `class="wto-fulfillment-lines"` directly so the
       `.has-error` per-cell token (load-bearing for 2.0.8 #2b cell-error
       rendering) styles consistently. */

.wto-fulfillment-lines {
  margin-top: 0.5rem;
  width: 100%;
  border-collapse: collapse;
}

.wto-fulfillment-lines th,
    .wto-fulfillment-lines td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-lines th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-fulfillment-lines td input[type="number"] {
  width: 6rem;
}

.wto-fulfillment-lines td.has-error {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--danger-bg-rgb) / var(--tw-bg-opacity));
}

.wto-fulfillment-lines td.has-error input,
    .wto-fulfillment-lines td.has-error select {
  --tw-border-opacity: 1;
  border-color: rgb(var(--danger-border-rgb) / var(--tw-border-opacity));
}

.wto-delivery-print .signatures .wto-fulfillment-editor .err {
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
  padding-top: 0.25rem;
}

.wto-fulfillment-editor .err {
  margin-top: 0.25rem;
  display: block;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-editor .remove-row {
  cursor: pointer;
  border-width: 0px;
  background-color: transparent;
  font-size: 1.125rem;
  line-height: 1.75rem;
  line-height: 1;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-editor-actions {
  margin-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Phase 2.8.2 PR-2 (SPEC §5): under 560px the create action row reverses
       to stacked full-width buttons so they don't crowd on a phone. */

@media (max-width: 559px) {
  .wto-fulfillment-editor-actions {
    flex-direction: column;
    align-items: stretch;
  }

  .wto-fulfillment-editor-actions > button {
    width: 100%;
  }
}

/* Phase 2.8.2 PR-2 (SPEC §4.4a): money cells in the create preview never
       wrap the amount across lines. */

.wto-fulfillment-lines td.wto-money-cell,
    .wto-fulfillment-lines th.wto-money-cell {
  white-space: nowrap;
  text-align: right;
}

.wto-fulfillment-editor .state-transitions {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-top: 1rem;
}

/* Phase 2.8.2 PR-2 follow-up (SPEC §5): under 560px the draft editor's
       combined Guardar + Ship + Discard row (all three live in
       `.state-transitions` now) stacks to full-width buttons, mirroring the
       create action row. */

@media (max-width: 559px) {
  .wto-fulfillment-editor .state-transitions {
    flex-direction: column;
    align-items: stretch;
  }

  .wto-fulfillment-editor .state-transitions > button {
    width: 100%;
  }
}

/* --- Fulfillment list namespace ----------------------------------- */

.wto-fulfillment-list-picker {
  margin-bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
}

.wto-fulfillment-list-picker label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-list-picker select,
    .wto-fulfillment-list-picker input[type="date"] {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-list-table {
  width: 100%;
  border-collapse: collapse;
}

.wto-fulfillment-list-table th,
    .wto-fulfillment-list-table td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-list-table th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

/* `class="err"` on the filter-error banner is LOAD-BEARING for
       `tests/integration/test_admin_fulfillment.py:234`
       (`assert 'class="err"' in body`). Keep the literal class name and
       style it via this parent selector — visually equivalent to
       `.wto-banner` per the project's danger token family. */

.wto-fulfillment-list .err {
  margin-top: 0.75rem;
  margin-bottom: 0.75rem;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--danger-border-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--danger-bg-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

.wto-fulfillment-list .actions {
  text-align: center;
}

.wto-fulfillment-list .icon-trash {
  cursor: pointer;
  border-width: 0px;
  background-color: transparent;
  padding-left: 0.25rem;
  padding-right: 0.25rem;
  font-size: 1rem;
  line-height: 1.5rem;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

/* ---------------------------------------------------------------
     * Phase 2.5.1 sub-PR 5 — Invoices + credit-memo views.
     * Restyle of `invoices_list`, `invoice_detail`, `invoice_print`
     * (STANDALONE), `credit_memo_form` + the shared `_invoice_body`
     * partial.
     *
     * NEW utility — `wto-btn-secondary`. Derived from the
     * neutral / muted token family (no raw hex per M-7 of sub-PR 4).
     * Used for the Cancel link in `credit_memo_form` and any other
     * neutral secondary action.
     *
     * Four namespace blocks:
     *   - `.wto-invoices-list`  — system-wide list view.
     *   - `.wto-invoice-detail` — chrome-wrapped detail (inside wto-card).
     *   - `.wto-invoice-print`  — STANDALONE printable document; rules
     *     here apply OUTSIDE the admin shell.
     *   - `.wto-credit-memo-form` — issue-credit-memo form.
     *
     * Shared partial `_invoice_body.html` styles its sub-elements via
     * comma-listed parent selectors (`.wto-invoice-detail tr.voided td,
     * .wto-invoice-print tr.voided td`) so it works in both contexts.
     * --------------------------------------------------------------- */

/* DDN-3: ported from the design handoff (neutral outline — the default
       action). Class name preserved. Pair with `.wto-btn` for the base. */

.wto-btn-secondary {
  background: var(--surface);
  color: var(--secondary);
  border-color: var(--divider);
}

.wto-btn-secondary:hover {
  background: var(--surface-subtle);
  border-color: var(--muted);
  color: var(--primary);
}

/* Icon-only secondary affordance — same chrome as `.wto-btn-secondary`
       but square, for row-action icon links (e.g. the POS-list dashboard
       link) so they read as a consistent button rather than a bare icon.
       Pair with `.wto-btn-secondary` so the variant's color recipe applies;
       this modifier only overrides the box. (Phase 2.7.12 ride-along.) */

.wto-btn-icon {
  padding: 9px;
  line-height: 0;
}

.wto-btn-icon svg {
  width: 18px;
  height: 18px;
}

/* --- Invoices list namespace ------------------------------------- */

.wto-invoices-list .picker {
  margin-bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
}

.wto-invoices-list .picker label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-invoices-list .picker select,
    .wto-invoices-list .picker input[type="date"] {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoices-list table {
  width: 100%;
  border-collapse: collapse;
}

.wto-invoices-list th,
    .wto-invoices-list td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoices-list th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Per-status pill (Phase 2.7.11 sub-PR 3): the `.wto-invoices-list
       .pill-*` recolor rules were RETIRED. The list template now emits
       the canonical `.pill pill-*` recipe directly, mapping the
       display-status to a color in the template. The invoice DETAIL
       page does not use `.wto-invoices-list` (it uses the canonical
       `.pill pill-*` recipe), so this removal is list-only. */

.wto-invoices-list .archived {
  margin-left: 0.25rem;
  display: inline-block;
  border-radius: 0.25rem;
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  padding-left: 0.25rem;
  padding-right: 0.25rem;
  font-size: 0.75rem;
  line-height: 1rem;
  --tw-text-opacity: 1;
  color: rgb(var(--muted-rgb) / var(--tw-text-opacity));
}

.wto-invoices-list .pagination {
  margin-top: 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* --- Invoice detail namespace ------------------------------------ */

.wto-invoice-detail dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
  -moz-column-gap: 1rem;
       column-gap: 1rem;
  row-gap: 0.25rem;
}

.wto-invoice-detail dt {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-detail dd {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  margin: 0;
}

/* SPEC §4.6b: no divider under a section heading — the bold Inter
       header (`.t-card-title` 18px/600) + whitespace is the separator;
       the table's column-header band is the structure doing the work. */

.wto-invoice-detail h2 {
  margin-top: 1.25rem;
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-detail table,
    .wto-invoice-print table {
  margin-top: 0.5rem;
  width: 100%;
  border-collapse: collapse;
}

.wto-invoice-detail th,
    .wto-invoice-detail td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-detail th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-invoice-detail tr.voided td,
    .wto-invoice-print tr.voided td {
  --tw-text-opacity: 1;
  color: rgb(var(--muted-rgb) / var(--tw-text-opacity));
  text-decoration-line: line-through;
}

.wto-invoice-detail .empty,
    .wto-invoice-print .empty {
  font-style: italic;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-detail .actions {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-top: 1rem;
}

/* Phase 2.7.11 sub-PR 4 (B1) — invoice header currency + type badges
       are now neutral; the status element is the canonical colored STATE
       pill (color-lives-in-state). The parent-scoped `.badge` base rule
       mirrors the global canonical `.badge` (neutral border + secondary
       text) so it renders consistently in both the chrome-wrapped detail
       page and the standalone print doc; the per-status `.badge.bg-*`
       colored recipes were retired (the partial no longer emits `bg-*`). */

.wto-invoice-detail .badge,
    .wto-invoice-print .badge {
  display: inline-flex;
  align-items: center;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--divider-rgb) / var(--tw-border-opacity));
  background-color: transparent;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.125rem;
  padding-bottom: 0.125rem;
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

/* Voided application row in CM detail page's Applied-to-invoices
       table — the partial keeps the class name verbatim
       (`application-row-voided`) per I-3. */

.wto-invoice-detail .application-row-voided td,
    .wto-invoice-print .application-row-voided td {
  --tw-text-opacity: 1;
  color: rgb(var(--muted-rgb) / var(--tw-text-opacity));
  text-decoration-line: line-through;
}

/* --- Invoice print (STANDALONE) namespace ------------------------- */

/* STANDALONE: `invoice_print.html` does NOT extend admin/base.html
       and is NOT wrapped in `wto-card`. The body is bare HTML and
       depends on these styles directly. Print-friendly defaults. */

.wto-invoice-print {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-print h1 {
  margin-bottom: 0.25rem;
  font-size: 1.5rem;
  line-height: 2rem;
  font-weight: 600;
}

.wto-invoice-print h2 {
  margin-top: 1.25rem;
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-bottom: 0.25rem;
  font-size: 1rem;
  line-height: 1.5rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-invoice-print dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  -moz-column-gap: 1rem;
       column-gap: 1rem;
  row-gap: 0.25rem;
}

.wto-invoice-print dt {
  font-weight: 600;
}

.wto-invoice-print dd {
  margin: 0;
}

.wto-invoice-print th,
    .wto-invoice-print td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
}

.wto-invoice-print th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-invoice-print .actions {
  margin-top: 1.5rem;
  display: flex;
  gap: 0.5rem;
}

/* @media print rule keeps the in-page actions hidden when the user
       triggers `window.print()` so the printed output is clean. */

@media print {
  .wto-invoice-print .actions {
    display: none;
  }
}

/* --- Credit-memo form namespace ---------------------------------- */

.wto-credit-memo-form table {
  margin-top: 0.5rem;
  width: 100%;
  border-collapse: collapse;
}

.wto-credit-memo-form th,
    .wto-credit-memo-form td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-credit-memo-form th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-delivery-print .signatures .wto-credit-memo-form label {
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
  padding-top: 0.25rem;
}

.wto-credit-memo-form label {
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
  display: block;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-credit-memo-form textarea {
  width: 100%;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  box-sizing: border-box;
  min-height: 4rem;
}

.wto-credit-memo-form input[type="number"] {
  width: 8rem;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  box-sizing: border-box;
}

.wto-credit-memo-form .reference {
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-credit-memo-form .actions {
  margin-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

/* SPEC §4.6b — no section-heading divider (see invoice-detail note). */

.wto-credit-memo-form h2 {
  margin-top: 1.25rem;
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* ---------------------------------------------------------------
     * Phase 2.5.1 sub-PR 6 — Delivery-note views (list / detail /
     * STANDALONE print). Restyle of `delivery_note_list.html`,
     * `delivery_note_detail.html`, `delivery_note_print.html`. List +
     * detail are chrome-wrapped (inside `wto-card`); print is
     * STANDALONE (no admin shell) and loads `app.css` directly so the
     * `.wto-delivery-print` namespace below renders the document.
     *
     * 2.0.2 bilingual MX/US branches in the print template are
     * preserved verbatim — Fluent extraction is Phase 2.5.2, not here.
     * --------------------------------------------------------------- */

/* --- Delivery-note list namespace -------------------------------- */

.wto-delivery-note-list .picker {
  margin-bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
}

.wto-delivery-note-list .picker label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-note-list .picker select,
    .wto-delivery-note-list .picker input[type="date"] {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-note-list table {
  width: 100%;
  border-collapse: collapse;
}

.wto-delivery-note-list th,
    .wto-delivery-note-list td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-note-list th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-delivery-note-list .pagination {
  margin-top: 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* --- Delivery-note detail namespace ------------------------------ */

.wto-delivery-note-detail dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
  -moz-column-gap: 1rem;
       column-gap: 1rem;
  row-gap: 0.25rem;
}

.wto-delivery-note-detail dt {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-note-detail dd {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  margin: 0;
}

/* SPEC §4.6b — no section-heading divider (see invoice-detail note). */

.wto-delivery-note-detail h2 {
  margin-top: 1.25rem;
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 600;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-note-detail table {
  margin-top: 0.5rem;
  width: 100%;
  border-collapse: collapse;
}

.wto-delivery-note-detail th,
    .wto-delivery-note-detail td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-note-detail th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-delivery-note-detail .actions {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-top: 1rem;
}

/* --- Delivery-note print (STANDALONE) namespace ------------------ */

/* STANDALONE: `delivery_note_print.html` does NOT extend
       admin/base.html. Loads `app.css` directly; the `.wto-delivery-print`
       wrapper below scopes the rules. Mirrors `.wto-invoice-print`
       STANDALONE pattern from sub-PR 5. */

.wto-delivery-print {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-print .brand {
  margin-bottom: 0.25rem;
  font-size: 1.25rem;
  line-height: 1.75rem;
  font-weight: 700;
}

.wto-delivery-print .doc-title {
  margin-top: 0.75rem;
  margin-bottom: 0.75rem;
  font-size: 1.875rem;
  line-height: 2.25rem;
  font-weight: 800;
  letter-spacing: 0.025em;
}

.wto-delivery-print .doc-number {
  font-size: 1.25rem;
  line-height: 1.75rem;
  font-weight: 700;
}

.wto-delivery-print .source-back {
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-delivery-print h2 {
  margin-top: 1rem;
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-bottom: 0.25rem;
  font-size: 0.75rem;
  line-height: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.wto-delivery-print .meta,
    .wto-delivery-print .billto {
  margin-top: 0.75rem;
  margin-bottom: 0.75rem;
}

.wto-delivery-print table {
  margin-top: 0.5rem;
  width: 100%;
  border-collapse: collapse;
}

.wto-delivery-print th,
    .wto-delivery-print td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  text-align: left;
  vertical-align: middle;
}

.wto-delivery-print th {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-delivery-print .totals {
  margin-top: 1rem;
  text-align: right;
}

.wto-delivery-print .totals .grand {
  border-top-width: 2px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
  padding-top: 0.25rem;
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 700;
}

.wto-delivery-print .signatures {
  margin-top: 3rem;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2rem;
}

.wto-delivery-print .signatures .block {
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
  padding-top: 0.25rem;
}

.wto-delivery-print .footer {
  margin-top: 3rem;
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-top: 0.5rem;
  text-align: center;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

/* ---------------------------------------------------------------
     * Phase 2.5.3.1 polish — hand-built form layout primitives.
     *
     * `.wto-field` is the macro outer-element class
     * (web/templates/admin/_form.html). Without these rules, the
     * macro's label-input-hint triple flows inline. display:grid with
     * a single column gives the vertical stack; gap-1 keeps the hint
     * hugging the input.
     *
     * 2.5.7.1 DOM shape (for/id binding — NOT wrap-in-label):
     *   <div class="wto-field">
     *     <label class="wto-field-label" for="<name>">…</label>
     *     <input | select | textarea id="<name>" name="<name>" …>
     *     <span class="wto-field-hint">…</span>?
     *     <span class="wto-field-error">…</span>?
     *   </div>
     * for/id binding keeps the input's accessible name clean (just
     * the label text — no hint pollution, no `<option>` text leak for
     * `<select>`). Load-bearing for Playwright
     * `get_by_label("Foo", exact=True)`; broken under earlier
     * wrap-in-label shapes — see `_form.html` macro comment.
     *
     * E2E locator pattern for locked / nameless inputs:
     *   `div.wto-field:has(label:has-text("…")) input`
     * --------------------------------------------------------------- */

.wto-field {
  margin-bottom: 0.75rem;
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 1fr));
  gap: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
}

.wto-field-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--secondary);
}

/* Phase 2.7.17 (C2): a label + a reactive sibling suffix (e.g. the
       payment amount's live "(MXN)") sit on one row. The suffix is a
       SIBLING of <label>, not a child, so it never leaks into the input's
       accessible name. */

.wto-field-label-row {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}

.wto-field-label-suffix {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}

.wto-field input[type="text"],
    .wto-field input[type="email"],
    .wto-field input[type="tel"],
    .wto-field input[type="number"],
    .wto-field select,
    .wto-field textarea {
  width: 100%;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  font-size: 1rem;
  line-height: 1.5rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
  box-sizing: border-box;
}

.wto-field input[disabled],
    .wto-field select[disabled] {
  cursor: not-allowed;
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-field textarea {
  min-height: 5rem;
  resize: vertical;
}

.wto-field-hint {
  font-size: 0.75rem;
  line-height: 1rem;
  line-height: 1.375;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-field-error {
  font-size: 0.75rem;
  line-height: 1rem;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

/* Red asterisk appended to required-field labels via CSS ::after.
     * Rendered as a pseudo-element (not a DOM child of <label>) so
     * Playwright's `get_by_label("Foo", exact=True)` keeps matching
     * "Foo" instead of "Foo*". Accessibility comes from the `required`
     * attribute on the input itself. */

.wto-field-label--required::after {
  content: "*";
  margin-left: 0.125rem;
  --tw-text-opacity: 1;
  color: rgb(var(--danger-text-rgb) / var(--tw-text-opacity));
}

/* Form section grouping. Stacked vertically; each section is a
     * card-within-the-card. Section heading uses a scale similar to
     * `.wto-pos-status h2`.
     *
     * `border-style: solid` is required because preflight is disabled
     * (D-5): without it the UA <fieldset> `2px groove` border style
     * leaks through on any side we don't fully override. `box-sizing:
     * border-box` + `min-inline-size: 0` are paired resets so the
     * fieldset doesn't size itself to UA `min-content`. */

.wto-form-section {
  margin-bottom: 1.5rem;
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding: 1rem;
  border-style: solid;
  box-sizing: border-box;
  min-inline-size: 0;
}

.wto-form-section:last-of-type {
  margin-bottom: 0px;
}

.wto-form-section-heading {
  margin-bottom: 0.75rem;
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-bottom: 0.25rem;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-text);
}

/* Two-column grid for sections with many short fields (Contact,
     * per DDN-2). Falls back to one column on narrow viewports.
     * gap-y-0 relies on `.wto-field`'s `mb-3` for vertical rhythm. */

.wto-form-grid-2col {
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 1fr));
  -moz-column-gap: 1.5rem;
       column-gap: 1.5rem;
  row-gap: 0px;
}

@media (min-width: 560px) {
  .wto-form-grid-2col {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Radio-button option group — stacks each `.wto-radio` label on its
     * own line. Used by pos_form.html's POS-type selector. */

.wto-radio-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Span both columns when the field is multi-line / wide
     * (Address, Notes, Invoicing information). Mirrors
     * `.wto-sales-editor-notes-cell` in the sales-report editor. */

@media (min-width: 560px) {
  .wto-form-field-wide {
    grid-column: span 2 / span 2;
  }
}

/* Form actions — Save / Cancel buttons. Card-footer styling at the
     * bottom of the form. */

.wto-form-actions {
  margin-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  border-top-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-top: 1rem;
}

/* ---------------------------------------------------------------
     * Phase 2.5.3.1 polish — pill-style status filter for hand-built
     * list pages. Replaces the bare `<select>` from
     * `_status_filter.html` on surfaces that prefer the chip pattern
     * (POS list is the pilot; 2.5.4 catalog list inherits). The
     * shared `_status_filter.html` `<select>` partial stays in place
     * for the other consumers (payments, fulfillment, invoices,
     * delivery notes) — both patterns coexist by design.
     *
     * Per DDN-3 the pills are <a> elements (JavaScript-free,
     * bookmarkable). `.wto-filter-pill-active` uses
     * `bg-accent text-on-inverse`; `.wto-filter-pill` uses
     * `bg-surface-muted text-secondary` with a subtle border.
     * --------------------------------------------------------------- */

.wto-filter-pill-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.wto-filter-pill {
  display: inline-flex;
  cursor: pointer;
  align-items: center;
  border-radius: 999px;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
  text-decoration-line: none;
}

.wto-filter-pill:hover {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-subtle-rgb) / var(--tw-bg-opacity));
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-filter-pill-active {
  display: inline-flex;
  cursor: default;
  align-items: center;
  border-radius: 999px;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--accent-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--accent-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
  text-decoration-line: none;
}

/* Filter-row container — sits below the page header card and
     * pairs the pill group with the name-search input. */

.wto-list-filters {
  margin-bottom: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-bottom: 1rem;
}

.wto-list-filters-label {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.wto-list-filters-search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.wto-list-filters-search input {
  border-radius: 0.25rem;
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.375rem;
  padding-bottom: 0.375rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

/* ---------------------------------------------------------------
     * Phase 2.5.3.1 polish — POS list table namespace. Mirrors
     * `.wto-pos-status` (table styling) and
     * `.wto-fulfillment-list-table` (list-page styling). The
     * `.wto-row-actions` token is load-bearing — see
     * tests/integration/test_admin_round17_polish.py regex
     * `<td class="wto-row-actions[^"]*">`.
     *
     * Phase 2.5.4.0 — Category list table namespace cloned from POS.
     * Per-entity class naming convention (see decisions.md
     * "Carry-forward from 2.5.3"); structurally identical body, so the
     * rules are multi-selector. The category-detail products table
     * also uses `.wto-category-list-table` for visual parity.
     * --------------------------------------------------------------- */

.wto-pos-list-table,
    .wto-category-list-table {
  width: 100%;
  border-collapse: collapse;
}

.wto-pos-list-table thead,
    .wto-category-list-table thead {
  border-bottom-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
}

.wto-pos-list-table th,
    .wto-category-list-table th {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-pos-list-table td,
    .wto-category-list-table td {
  border-width: 1px;
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  vertical-align: middle;
  font-size: 0.875rem;
  line-height: 1.25rem;
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.wto-pos-list-table tbody tr:hover,
    .wto-category-list-table tbody tr:hover {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-subtle-rgb) / var(--tw-bg-opacity));
}

.wto-pos-list-table .wto-row-actions,
    .wto-category-list-table .wto-row-actions {
  white-space: nowrap;
  padding-left: 0.75rem;
  padding-right: 0.75rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

/* Phase 2.7.11 sub-PR 3 — list-table cell links take the accent
       treatment (terracotta foreground, underline on hover) so they
       read as the brand link color, not default browser blue. Buttons
       (`.wto-btn-*`) inside row-action cells set their own color and
       must not inherit this, so the rule targets only bare `td a`
       outside `.wto-row-actions`. Lists whose cell links already carry
       inline `text-accent` classes (pos / user / payment id) are
       unaffected (same color). */

.wto-pos-list-table td:not(.wto-row-actions) a,
    .wto-fulfillment-list-table td a:not(.icon-trash),
    .wto-invoices-list td a,
    .wto-delivery-note-list td a {
  color: var(--accent-text);
  text-decoration: none;
}

.wto-pos-list-table td:not(.wto-row-actions) a:hover,
    .wto-fulfillment-list-table td a:not(.icon-trash):hover,
    .wto-invoices-list td a:hover,
    .wto-delivery-note-list td a:hover {
  text-decoration: underline;
}

/* ---------------------------------------------------------------
     * Phase 2.5.4.1 — Combined catalog list (parent + nested children).
     * Mirrors POS list visual rhythm: card-bordered, hover-highlighted
     * rows; uses <ul> instead of <table> for the parent-child shape.
     * --------------------------------------------------------------- */

/* ===================================================================
     * Phase 2.7.11 — MakerShelf design-handoff component port.
     *
     * Ported VERBATIM from the design handoff
     * (GTM/CLAUDE_DESIGN/design_handoff_makershelf/{tokens.css,components.css}),
     * raw CSS referencing the flat hex `var(--<token>)` tokens emitted in
     * the tokens layer at the top of this file. Locked namespace
     * (BRANDING.md): primitives → `wto-*`; `.pill*`/`.badge*`/`.t-*`/
     * `.num`/`.cur`/`.dash`/`.muted`/`.secondary`/`.accent-fg` stay
     * UNPREFIXED. The four `.wto-btn-*` recipes already live with the
     * button block above (DDN-3). Incumbent `.card-title`/`.wto-card*`
     * are LEFT IN PLACE — these are additive, available
     * primitives the template re-skin sub-PRs (2–4) will adopt. (The
     * `.wto-flash-*` recipes were retired in Phase 2.7.12 in favor of the
     * `.wto-banner`/`-warning`/`-success` family.)
     *
     * This is FOUNDATION only: nothing here is wired into templates yet.
     * Structural 2.7.12-owned classes (catalog/picker/prices) land dormant.
     * =================================================================== */

/* ---- Typography roles (6-role spec) ------------------------------ */

.t-page-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 500;
  font-size: 30px;
  line-height: 1.15;
  letter-spacing: -.01em;
  color: var(--primary);
  margin: 0;
}

.t-section {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 500;
  font-size: 21px;
  line-height: 1.25;
  letter-spacing: -.005em;
  color: var(--primary);
  margin: 0;
}

.t-card-title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 18px;
  line-height: 1.45;
  color: var(--primary);
  margin: 0;
}

.t-caption {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

/* ---- Pills & badges (sentence case; color-lives-in-state) -------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid transparent;
}

.pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: .7;
}

.pill-neutral {
  background: transparent;
  color: var(--secondary);
  border-color: var(--divider);
}

.pill-accent  {
  background: var(--accent-soft);
  color: var(--accent-text);
  border-color: var(--accent-soft-border);
}

.pill-info    {
  background: var(--info-bg);
  color: var(--info-text);
  border-color: var(--info-border);
}

.pill-success {
  background: var(--success-bg);
  color: var(--success-text);
  border-color: var(--success-border);
}

.pill-warning {
  background: var(--warning-bg);
  color: var(--warning-text);
  border-color: var(--warning-border);
}

.pill-muted   {
  background: transparent;
  color: var(--muted);
  border-color: var(--subtle);
  text-decoration: line-through;
}

.pill-muted::before {
  opacity: .45;
}

/* Type badges — firm / consignment / direct. Neutral by design
       (TYPE carries no hue; color lives in the STATE pill). These are the
       available neutral recipes; sub-PR 3 swaps the incumbent colored
       parent-scoped `.badge-*` rules to these. */

.badge {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1;
  padding: 4px 8px;
  border-radius: var(--r-sm);
  border: 1px solid var(--divider);
  background: transparent;
  color: var(--secondary);
}

/* Currency code suffix — quiet so the figure stays prominent */

.cur {
  color: var(--muted);
  font-weight: 500;
  font-size: .82em;
  margin-left: 3px;
  letter-spacing: .02em;
}

/* SPEC §4.4a — money never wraps: numeric/currency figures stay on one
       line ($550.00 MXN must not break across two lines). `.num` is the
       blanket marker; `*:has(.cur)` catches every `format_money_html` cell
       (the currency-code span) without per-cell classes. If width is tight,
       the text column wraps or the table scrolls (§4.4b) — never the money. */

.num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

:is(td, th, li):has(.cur) {
  white-space: nowrap;
}

/* Generic helpers */

.muted {
  color: var(--muted);
}

.secondary {
  color: var(--secondary);
}

/* ---- Inputs (NEW available primitives; box-sizing explicit because
       preflight is OFF). Incumbent `.wto-field*` form primitives stay. --- */

.field-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--secondary);
  margin-bottom: 6px;
}

.wto-input {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--primary);
  background: var(--surface);
  border: 1px solid var(--divider);
  border-radius: var(--r-sm);
  padding: 11px 13px;
  transition: border-color var(--ease), box-shadow var(--ease);
}

.wto-input::-moz-placeholder {
  color: var(--muted);
}

.wto-input::placeholder {
  color: var(--muted);
}

.wto-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.wto-select,
    .wto-textarea {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--primary);
  background: var(--surface);
  border: 1px solid var(--divider);
  border-radius: var(--r-sm);
  padding: 11px 13px;
  transition: border-color var(--ease), box-shadow var(--ease);
}

.wto-textarea {
  resize: vertical;
  min-height: 84px;
  line-height: 1.5;
}

.wto-select:focus,
    .wto-textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

/* ---- Data table ------------------------------------------------- */

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

table.data th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 11px 14px;
  border-bottom: 1px solid var(--divider);
  background: var(--surface);
  white-space: nowrap;
}

table.data td {
  padding: 14px;
  border-bottom: 1px solid var(--subtle);
  color: var(--primary);
  vertical-align: middle;
}

table.data tbody tr {
  transition: background var(--ease);
}

table.data tbody tr:hover {
  background: var(--surface-subtle);
}

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

/* ===================================================================
     * PART B — app components (shell/nav, appbar, filters, search, mobile
     * shell, cards, login, email, empty states, toasts, order detail,
     * settlement print, catalog, modals, category picker, prices).
     * Available primitives; not wired into templates in this PR. Structural
     * `wto-*` primitives renamed per the locked namespace; 2.7.12-owned
     * structural class names (catalog/picker/prices) ported as-is, dormant.
     * =================================================================== */

/* sidebar (desktop) */

.sidebar-brand {
  padding: 18px 20px;
  border-bottom: 1px solid rgba(255,255,255,.1);
  display: flex;
  align-items: center;
  gap: 10px;
}

.sidebar-brand .wordmark {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--on-inverse);
  font-size: 20px;
}

.sidebar-brand .wordmark .shelf {
  color: color-mix(in srgb, var(--accent) 60%, #fff);
}

.side-favi {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
  flex: none;
}

.nav {
  padding: 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
}

.nav-item.active {
  background: var(--accent);
  color: #fff;
}

.nav-foot {
  padding: 12px;
  border-top: 1px solid rgba(255,255,255,.1);
  display: flex;
  align-items: center;
  gap: 10px;
}

.avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 70%, #fff);
  color: var(--surface-inverse);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 13px;
  flex: none;
}

/* main */

.appbar {
  box-sizing: border-box;
  height: 60px;
  flex: none;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--subtle);
}

.appbar .crumbs {
  font-size: 13px;
  color: var(--muted);
}

.appbar .crumbs b {
  color: var(--primary);
  font-weight: 600;
}

.content {
  padding: 26px 24px;
  overflow: auto;
}

/* Page-head band — appears INSIDE wto-content (Phase 2.7.12, C1).
       The admin shell renders its own `.appbar` band inside the content
       area (not as the top-of-main bar the mockup uses); give it a
       bottom margin so the page-head clears it, and inset/round it to
       read as a header strip within the padded content column. */

.wto-content .appbar {
  border: 1px solid var(--subtle);
  border-radius: var(--r-sm);
  margin-bottom: 16px;
  height: auto;
  min-height: 44px;
  padding: 10px 16px;
}

.crumbs a {
  color: var(--muted);
  text-decoration: none;
}

.crumbs a:hover {
  color: var(--primary);
  text-decoration: underline;
}

.crumbs-sep {
  color: var(--muted);
  padding: 0 4px;
}

.page-head {
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 22px;
  flex-wrap: wrap;
}

.page-head .t-caption {
  margin-top: 4px;
}

.page-head-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

/* search input in appbar */

/* filters bar */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
  align-items: center;
}

.wto-chip.on {
  background: var(--accent-soft);
  border-color: var(--accent-soft-border);
  color: var(--accent-text);
}

.wto-chip .n {
  font-variant-numeric: tabular-nums;
  opacity: .65;
}

/* stat cards (dashboard) */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}

.stat {
  box-sizing: border-box;
  background: var(--surface);
  border: 1px solid var(--subtle);
  border-radius: var(--r-lg);
  padding: 16px 18px;
  box-shadow: var(--shadow-card);
}

.stat .k {
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
  margin-bottom: 9px;
}

.stat .v {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 28px;
  line-height: 1;
  color: var(--primary);
}

.stat .v ul, .stat .v li {
  margin: 0;
  padding: 0;
  list-style: none;
}

.stat .d {
  font-size: 12px;
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: 5px;
}

.stat .d.up {
  color: var(--success-text);
}

.stat .d.flat {
  color: var(--muted);
}

@media (max-width: 900px) {
  .stat-grid {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 560px) {
  .stat-grid {
    grid-template-columns: 1fr;
  }
}

/* two-column dashboard layout */

.dash-cols {
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  gap: 18px;
  align-items: start;
}

@media (max-width: 1000px) {
  .dash-cols {
    grid-template-columns: 1fr;
  }
}

/* activity feed */

.feed {
  display: flex;
  flex-direction: column;
}

/* progress meter for inventory */

/* mobile shell */

.m-appbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px;
  background: var(--surface);
  border-bottom: 1px solid var(--subtle);
}

.m-appbar .icon-btn {
  width: 38px;
  height: 38px;
  border-radius: var(--r-sm);
  border: 1px solid var(--subtle);
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--secondary);
  cursor: pointer;
}

.m-tab.active {
  color: var(--accent-text);
}

/* order card (mobile) */

.o-card .top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 9px;
}

.o-card .ref {
  font-weight: 600;
  font-size: 14px;
}

.o-card .store {
  font-size: 13.5px;
  color: var(--primary);
  margin-bottom: 3px;
}

.o-card .meta {
  font-size: 12px;
  color: var(--muted);
  display: flex;
  gap: 10px;
  align-items: center;
}

/* login */

.login-wrap {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px;
  background:
        radial-gradient(140% 70% at 50% -10%, var(--accent-soft), transparent 55%), var(--surface-subtle);
}

.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--subtle);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-pop);
  padding: 34px 30px;
}

.login-wm {
  text-align: center;
  margin-bottom: 8px;
}

.login-foot {
  text-align: center;
  margin-top: 26px;
  font-size: 12.5px;
  color: var(--muted);
}

/* email */

.hide {
  display: none !important;
}

/* inline banner recipe family (Phase 2.7.12 — replaces `.wto-flash-*`).
       `.wto-banner` = danger/error (red); `.wto-banner-warning` = amber
       advisory; `.wto-banner-success` = green confirmation. Documented in
       docs/BRANDING.md. */

.wto-banner {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  color: var(--danger-text);
  border-radius: var(--r-sm);
  padding: 13px 15px;
  margin-bottom: 16px;
  font-size: 13.5px;
  line-height: 1.5;
}

.wto-banner svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 1px;
}

.wto-banner b {
  font-weight: 700;
}

.wto-banner-warning {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
  color: var(--warning-text);
  border-radius: var(--r-sm);
  padding: 13px 15px;
  margin-bottom: 16px;
  font-size: 13.5px;
  line-height: 1.5;
}

.wto-banner-warning svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 1px;
}

.wto-banner-warning b {
  font-weight: 700;
}

/* in-banner CTA link (was `.wto-flash-action`): inherits the banner
       text color, underlined for affordance. */

.wto-banner-warning a {
  color: inherit;
  text-decoration: underline;
  font-weight: 600;
}

.wto-banner-success {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  color: var(--success-text);
  border-radius: var(--r-sm);
  padding: 13px 15px;
  margin-bottom: 16px;
  font-size: 13.5px;
  line-height: 1.5;
}

.wto-banner-success svg {
  width: 18px;
  height: 18px;
  flex: none;
  margin-top: 1px;
}

.wto-banner-success b {
  font-weight: 700;
}

/* empty states */

.empty {
  text-align: center;
  padding: 56px 32px;
  background: var(--surface);
  border: 1px solid var(--subtle);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}

.empty .ic-wrap {
  width: 56px;
  height: 56px;
  border-radius: 14px;
  background: var(--surface-muted);
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 18px;
}

.empty .ic-wrap svg {
  width: 26px;
  height: 26px;
}

.empty h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  color: var(--primary);
  margin: 0 0 8px;
}

.empty p {
  color: var(--secondary);
  font-size: 14px;
  margin: 0 auto 22px;
  max-width: 38ch;
}

/* toasts */

.wto-toast.out {
  animation: wto-toast-out 200ms ease forwards;
}

@keyframes wto-toast-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes wto-toast-out {
  to {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* order detail — identity metadata + reconciliation */

.meta-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px 28px;
}

@media (max-width: 760px) {
  .meta-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.meta-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.meta-k {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.meta-v {
  font-size: 14.5px;
  color: var(--primary);
  font-weight: 500;
}

/* Disclosure — collapsible section (SPEC 4.8b; design_handoff_makershelf_5).
       A section header, NOT a chip: full-width trigger, title left, thin
       chevron right (rotates 90° on open), hover tints the row. Inside-card
       variant by default; `.standalone` for top-level page sections. The
       panel toggles instantly via max-height (CSS transitions on max-height
       proved unreliable in some engines per the handoff). */

.disclosure {
  border-top: 1px solid var(--subtle);
}

.disclosure:first-child {
  border-top: 0;
}

.disclosure-trigger {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  padding: 14px 20px;
  font: inherit;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
  color: var(--primary);
  transition: background .12s;
}

.disclosure-trigger:hover {
  background: var(--surface-subtle);
}

.disclosure-trigger:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent-ring);
}

.disclosure-title {
  flex: 1;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 15px;
  color: var(--primary);
}

.disclosure-chev {
  flex: none;
  width: 18px;
  height: 18px;
  color: var(--muted);
  display: inline-flex;
}

.disclosure-chev svg {
  width: 18px;
  height: 18px;
}

.disclosure-trigger[aria-expanded="true"] .disclosure-chev {
  transform: rotate(90deg);
}

.disclosure-panel > .disclosure-inner {
  overflow: hidden;
  max-height: 0;
}

.disclosure[data-open="true"] > .disclosure-panel > .disclosure-inner {
  max-height: none;
}

.disclosure-body {
  padding: 4px 20px 18px;
  color: var(--secondary);
  font-size: 14px;
  line-height: 1.55;
}

.disclosure-body .meta-grid {
  padding-top: 8px;
}

/* Long-form fields (address, invoicing info, notes) span the full grid
       width instead of cramping into a 1/3 column; preserve their newlines. */

.disclosure-body .meta-cell--wide {
  grid-column: 1 / -1;
}

.disclosure-body .meta-v.multiline {
  white-space: pre-wrap;
}

/* standalone variant — its own bordered block, not inside a card. */

.disclosure.standalone {
  border: 1px solid var(--subtle);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.disclosure.standalone .disclosure-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
}

.disclosure.standalone .disclosure-trigger {
  padding: 16px 20px;
}

/* Inventory-movement / return line tables (return form + the 4 IM forms +
       the IM-detail Lines table). Preflight is OFF (D-5), so a bare <table>
       hugs left with no padding and a width+padding input overflows its cell.
       This primitive mirrors the billing-family inline data-table look
       (§4.6b): a muted uppercase column-header band IS the structure, with
       bottom-row ruling — NOT full per-cell borders (that recipe is reserved
       for the dense `.wto-status-table` POS grid). Phase 2.7.17 — C1(a). */

.wto-form-lines,
    .wto-detail-lines {
  width: 100%;
  border-collapse: collapse;
  box-sizing: border-box;
  margin-bottom: 0;
  /* §4.6a: card padding owns the air below */
}

/* Detail action bar (e.g. Void button) owns its top gap — the card's
       vertical rhythm comes from each child's own margins, and this div
       follows a section that has none of its own below it. */

.wto-detail-actions {
  margin-top: 1rem;
}

.wto-form-lines th, .wto-form-lines td,
    .wto-detail-lines th, .wto-detail-lines td {
  padding: 6px 8px;
  text-align: left;
  vertical-align: middle;
  color: var(--primary);
  border-bottom: 1px solid var(--subtle);
}

.wto-form-lines th,
    .wto-detail-lines th {
  background: var(--surface-muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.wto-form-lines input[type="number"] {
  box-sizing: border-box;
  width: 100%;
  max-width: 8rem;
}

table.data.recon td {
  vertical-align: middle;
}

table.data.recon tfoot td {
  border-top: 1.5px solid var(--divider);
  border-bottom: 0;
  background: var(--surface-subtle);
}

/* liquidation / settlement — printable */

.liq-table .num {
  font-variant-numeric: tabular-nums;
}

.liq-tline .num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.liq-tline.grand {
  margin-top: 6px;
  padding-top: 13px;
  border-top: 2px solid #1b1b1b;
  font-size: 17px;
  font-weight: 700;
  color: #111;
}

/* catalog — flat filterable table (2.7.12-owned structural names, dormant) */

.seg-mini {
  display: inline-flex;
  background: var(--surface-muted);
  border: 1px solid var(--subtle);
  border-radius: var(--r-sm);
  padding: 2px;
  gap: 2px;
}

.seg-mini button, .seg-mini a {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--secondary);
  background: transparent;
  border: 0;
  cursor: pointer;
  text-decoration: none;
  padding: 5px 12px;
  border-radius: 4px;
  white-space: nowrap;
  transition: background .12s, color .12s;
}

.seg-mini button:hover, .seg-mini a:hover {
  color: var(--primary);
}

.seg-mini button[aria-pressed="true"], .seg-mini a[aria-pressed="true"] {
  background: var(--surface);
  color: var(--primary);
  box-shadow: var(--shadow-card);
}

.catalog-table td {
  vertical-align: middle;
}

.catalog-table .cat-name {
  font-weight: 600;
  color: var(--primary);
}

.catalog-table tr.is-archived .cat-name,
    .catalog-table tr.is-archived td {
  color: var(--muted);
}

.catalog-table .cat-cat {
  color: var(--secondary);
}

.catalog-table .oh-low {
  color: var(--warning-text);
  font-weight: 700;
}

.catalog-table .oh-zero {
  color: var(--muted);
}

.row-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.kebab-btn {
  width: 30px;
  height: 30px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}

.kebab-btn:hover, .kebab-btn[aria-expanded="true"] {
  background: var(--surface-muted);
  color: var(--primary);
  border-color: var(--subtle);
}

.kebab-btn svg {
  width: 17px;
  height: 17px;
}

/* Canonical overflow menu (SPEC §4.10). A single floating element teleported
       to <body> on open (not nested in the row — avoids clipping/stacking). The
       Alpine component computes top/left from the trigger rect and applies it as
       an inline style; box-sizing is explicit (Tailwind preflight off). */

.menu {
  box-sizing: border-box;
  position: absolute;
  z-index: 60;
  min-width: 168px;
  background: var(--surface);
  border: 1px solid var(--subtle);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-pop);
  padding: 5px;
  display: none;
}

.menu.open {
  display: block;
  transform-origin: top right;
  animation: menu-in .13s cubic-bezier(.2,.7,.3,1) both;
}

.menu.\!open {
  display: block !important;
  transform-origin: top right !important;
  animation: menu-in .13s cubic-bezier(.2,.7,.3,1) both !important;
}

@keyframes menu-in {
  from {
    opacity: 0;
    transform: translateY(-4px) scale(.97);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.menu button {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  font: inherit;
  font-size: 13.5px;
  color: var(--secondary);
  text-align: left;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 8px 10px;
  border-radius: 5px;
  transition: background .1s, color .1s;
}

.menu button:hover {
  background: var(--surface-muted);
  color: var(--primary);
}

.menu button svg {
  width: 15px;
  height: 15px;
  flex: none;
  opacity: .8;
}

.menu button.danger {
  color: var(--danger-text);
}

.menu button.danger:hover {
  background: var(--danger-bg);
}

/* archive/restore POST forms wrap a menu button; the form must not break the
       full-width flex item or add its own box. */

.menu form {
  display: block;
  margin: 0;
}

.menu-sep {
  height: 1px;
  background: var(--subtle);
  margin: 4px 2px;
}

.add-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--accent-text);
  background: transparent;
  border: 0;
  border-top: 1px solid var(--subtle);
  cursor: pointer;
  padding: 14px 18px;
  transition: background .12s;
}

.add-row:hover {
  background: var(--surface-subtle);
}

.add-row svg {
  width: 15px;
  height: 15px;
}

.cat-card.is-archived {
  opacity: .7;
}

/* modal — scrim + sheet (category management) */

@keyframes scrim-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes modal-in {
  from {
    opacity: 0;
    transform: translateY(10px) scale(.98);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.modal-x {
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  border: 1px solid var(--subtle);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
}

.modal-x:hover {
  color: var(--primary);
  background: var(--surface-muted);
}

.modal-lede {
  margin: 6px 0 16px;
}

.modal-addrow .wto-input {
  flex: 1;
}

.cml-row.is-archived {
  color: var(--muted);
}

.cml-row.is-archived .cml-name {
  color: var(--muted);
  text-decoration: line-through;
}

/* Phase 2.7.14 (1a) — scrim for the LIVE Preline confirm modals.
       Preline injects `.hs-overlay-backdrop` at runtime; its stock utility
       classes (`bg-gray-900 bg-opacity-50`) are JIT-purged (never appear in
       templates), so the backdrop ships transparent. Styling the injected
       class directly in the component layer is purge-proof and applies to
       every `hs-overlay` modal at once. Values mirror the handoff
       `.modal-scrim` (`_3` components.css): ~52% `#1c130d` + 2px blur + the
       `scrim-in` fade. The live dialog is `z-[80]` (_confirm_modal.html), so
       the backdrop sits just below at z-index 70 (the handoff's standalone
       `.modal-scrim` uses 200 because there it CONTAINS the sheet; here it is
       a sibling under the dialog). Preline removes the div on close, so the
       scrim clears automatically. */

.hs-overlay-backdrop {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: color-mix(in srgb, #1c130d 52%, transparent);
  -webkit-backdrop-filter: blur(2px);
          backdrop-filter: blur(2px);
  animation: scrim-in .14s ease both;
}

/* Phase 2.7.17 (C1) — §4.9 alignment of the SHARED confirm modal
       (`_confirm_modal.html`). The §4.9 scrim (`.hs-overlay-backdrop`
       above) and the standalone head primitives (`.modal-head h3`,
       `.modal-x`) already existed; this block REPOINTS the confirm modal's
       sheet / title / close-× onto the same tokens and adds the only two
       genuinely-new rules: the close-× `:focus-visible` ring (the headline
       dogfooding fix) and a reduced-motion-gated dialog scale-in. */

.wto-confirm-modal-dialog {
  max-width: 440px;
}

.wto-confirm-modal-dialog.modal-wide {
  max-width: 520px;
}

.wto-confirm-modal-content {
  box-shadow: var(--shadow-pop);
  border-radius: var(--r-lg);
}

/* Title: Newsreader 600 / 19px (matches `.modal-head h3`). */

.wto-confirm-modal-header .modal-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 19px;
  color: var(--primary);
  margin: 0;
}

/* Close-× focus ring — preflight-off otherwise gives it a raw UA
       outline (the dogfooding finding). Reuses the `--accent-ring` recipe. */

.modal-x:focus-visible {
  box-shadow: 0 0 0 3px var(--accent-ring);
  outline: none;
}

/* §4.9 `.16s` scale-in on the dialog sheet, reusing `@keyframes modal-in`.
       Gated behind `prefers-reduced-motion: no-preference` so reduced-motion
       users get an instant, motion-free `opacity:1` dialog (the behavior the
       old instant-appear contract protected); the backdrop still fades. */

@media (prefers-reduced-motion: no-preference) {
  .wto-confirm-modal-content {
    animation: modal-in .16s cubic-bezier(.2,.7,.3,1) both;
  }
}

/* Phase 2.7.14 (1b) — §4.9-B line-item confirm archetype (ported from
       the `_3` handoff components.css). Used by the finalize modal body
       (`_finalize_modal_body.html`) inside the live Preline confirm modal.
       Box-sizing note: none of these recipes set an explicit width except
       `.modal-field .input` (carried below with `box-sizing:border-box`);
       the table/totals are width:100%/auto so the preflight-off overflow
       trap does not bite. */

.modal-wide {
  max-width: 520px;
}

.modal-field {
  margin-bottom: 2px;
}

.modal-field > label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--secondary);
  margin-bottom: 6px;
}

.modal-field .input {
  width: 100%;
  box-sizing: border-box;
}

.modal-lede strong {
  font-weight: 600;
  color: var(--primary);
}

.modal-consequences {
  margin: 0 0 18px;
  padding-left: 20px;
}

.modal-consequences li {
  font-size: 13.5px;
  color: var(--secondary);
  line-height: 1.55;
  margin-bottom: 7px;
}

.modal-consequences li:last-child {
  margin-bottom: 0;
}

.modal-consequences strong {
  font-weight: 600;
  color: var(--primary);
}

.modal-block-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 9px;
}

.modal-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin-bottom: 4px;
}

.modal-table th {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: left;
  padding: 0 0 7px;
  border-bottom: 1px solid var(--subtle);
}

.modal-table th.r, .modal-table td.r {
  text-align: right;
}

.modal-table th.c, .modal-table td.c {
  text-align: center;
}

.modal-table td {
  padding: 9px 0;
  color: var(--primary);
  border-bottom: 1px solid var(--subtle);
}

.modal-table td:not(:first-child), .modal-table th:not(:first-child) {
  padding-left: 14px;
}

.modal-table tbody tr:last-child td {
  border-bottom: 0;
}

.modal-table .num {
  font-variant-numeric: tabular-nums;
}

.modal-totals {
  border-top: 1.5px solid var(--divider);
  padding-top: 11px;
  margin-top: 2px;
}

.modal-tline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 4px 0;
  font-size: 13px;
  color: var(--secondary);
}

.modal-tline .num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.modal-tline.grand {
  margin-top: 4px;
  font-size: 15.5px;
  font-weight: 700;
  color: var(--primary);
}

.modal-aside {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.5;
  margin: 14px 0 0;
}

.modal-aside .num {
  font-variant-numeric: tabular-nums;
}

.modal-aside.warn {
  color: var(--warning-text);
  font-weight: 500;
}

/* a confirm table row that fails preflight (e.g. insufficient stock) */

.modal-table tr.short td {
  color: var(--warning-text);
}

.modal-table tr.short td:first-child {
  font-weight: 600;
}

/* Phase 2.7.14 (1c) — mandatory mobile / overflow behavior for the SHARED
       confirm modal (`_confirm_modal.html`), ported from the `_4` handoff
       SPEC §4.9 "Mobile / overflow behavior". Handoff class → our class:
       `.modal` → `.wto-confirm-modal-content` (the sheet/form, already
       `flex flex-col`); `.modal-head` → `.wto-confirm-modal-header`;
       `.modal-foot` → `.wto-confirm-modal-footer`. Presentation-only — every
       confirm modal benefits (tall finalize line-item modal especially).

       Cap + internal scroll: the SHEET is the single scroll container so the
       sticky head/foot pin to it. The handoff uses `100dvh` (dynamic viewport
       unit) NOT `100vh` so mobile browser chrome doesn't push the footer
       off-screen. On desktop the cap only bites when content exceeds the
       viewport, so short modals render identically to before. */

.wto-confirm-modal-content {
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

/* The SHEET (not the body) is the scroll container so the sticky header /
       footer pin against its scroll. The body must therefore keep its full
       intrinsic height — `flex-shrink: 0` stops the flex column from
       compressing it (which would clip content with no scroll). The template
       no longer puts `overflow-y-auto` on the body for this reason. */

.wto-confirm-modal-body {
  flex-shrink: 0;
}

/* Sticky head + foot: title and Cancelar/primary stay reachable while a
       tall body scrolls. Surface bg + the template's border lines keep the
       scrolling body from showing through; z-index lifts them over the body.
       `top/bottom: 0` (not the handoff's negative offsets) because our header
       and footer are flush flex children with their own padding — there is no
       sheet padding above/below them to compensate for. */

.wto-confirm-modal-header {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
}

.wto-confirm-modal-footer {
  position: sticky;
  bottom: 0;
  z-index: 2;
  background: var(--surface);
}

/* Money never wraps: keep `$X,XXX.00 MXN` on one line; the item-name
       column (first cell) wraps instead (see the 560px block below). */

.modal-table .num {
  white-space: nowrap;
}

@media (max-width: 560px) {
  /* tighten the sheet + table so the nowrap currency columns get room;
           reduce the viewport inset to 24px for narrow phones */

  .wto-confirm-modal-content {
    max-height: calc(100dvh - 24px);
  }

  /* The dialog wrapper ships `mt-7`(28px) top + `m-3`(12px) margins.
           At desktop the handoff's 48px inset budget covers them; under 560px
           the inset drops to 24px, so the 28px top margin would push a TALL
           (scrolling) sheet's footer ~4px off-screen. Pull the wrapper's
           mobile top margin back to 12px so the sheet stays fully inside the
           `100dvh - 24px` budget and the Cancelar/primary stay reachable. */

  .modal-dialog:has(> .wto-confirm-modal-content) {
    margin-top: 12px;
  }

  .modal-table {
    font-size: 12.5px;
  }

  .modal-table th:not(:first-child), .modal-table td:not(:first-child) {
    padding-left: 10px;
  }

  .modal-table td:first-child {
    word-break: break-word;
  }
}

/* category picker — trigger + desktop dropdown + mobile bottom sheet */

.menu-picker.\!open {
  display: block !important;
}

.menu-picker.open {
  display: block;
}

.sheet {
  width: 100%;
  background: var(--surface);
  border-radius: 18px 18px 0 0;
  box-shadow: var(--shadow-pop);
  padding: 8px 16px 22px;
  max-height: 76vh;
  display: flex;
  flex-direction: column;
  animation: sheet-in .24s cubic-bezier(.2,.75,.3,1) both;
}

@keyframes sheet-in {
  from {
    transform: translateY(100%);
  }

  to {
    transform: none;
  }
}

/* prices — two-currency editable grid, sticky save bar (2.7.12-owned) */

.prices-shell .content {
  flex: 1 1 auto;
  min-height: 0;
}

.price-table th.cur-col {
  text-align: left;
  width: 200px;
}

.pd-label {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-right: 9px;
}

.pd-cat {
  font-weight: 600;
  color: var(--primary);
}

.price-cell {
  position: relative;
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  width: 168px;
  background: var(--surface);
  border: 1px solid var(--divider);
  border-radius: var(--r-sm);
  transition: border-color .12s, box-shadow .12s, background .12s;
}

.price-cell:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.cur-pre {
  padding-left: 11px;
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.price-in {
  box-sizing: border-box;
  flex: 1;
  width: 100%;
  border: 0;
  background: transparent;
  outline: none;
  font: inherit;
  font-size: 14px;
  color: var(--primary);
  text-align: right;
  padding: 9px 11px 9px 4px;
  font-variant-numeric: tabular-nums;
}

.price-in::-moz-placeholder {
  color: var(--muted);
  opacity: 1;
}

.price-in::placeholder {
  color: var(--muted);
  opacity: 1;
}

.price-in.inherits {
  color: var(--muted);
}

.price-cell:has(.dirty) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.price-cell:has(.dirty) .cur-pre {
  color: var(--accent-text);
}

.price-cell:has(.dirty)::after {
  content: "";
  position: absolute;
  top: -4px;
  right: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface-subtle);
}

/* invalid cell (system standard for inline numeric/price validation,
       SPEC §4.3a) — danger sibling of the dirty state; NO layout shift.
       The error text overlays via .price-err (absolutely positioned), never
       reflowing the row. box-sizing explicit (Tailwind preflight is OFF). */

.price-cell.invalid {
  box-sizing: border-box;
  border-color: var(--danger-border);
  background: var(--danger-bg);
}

.price-cell.invalid .cur-pre,
    .price-cell.invalid .price-in {
  color: var(--danger-text);
}

.price-cell.invalid:focus-within {
  border-color: var(--danger-text);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger-text) 22%, transparent);
}

.price-cell.invalid::after {
  content: "";
  position: absolute;
  top: -4px;
  right: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger-text);
  border: 2px solid var(--surface-subtle);
}

.price-err {
  position: absolute;
  top: calc(100% + 3px);
  left: 0;
  z-index: 5;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--danger-text);
  white-space: nowrap;
  pointer-events: none;
}

.save-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 24px;
  background: var(--surface);
  border-top: 1px solid var(--subtle);
  transition: border-color .15s, box-shadow .15s;
}

.save-bar.has-changes {
  border-top-color: var(--accent-soft-border);
  box-shadow: 0 -5px 18px -10px rgba(43,30,22,.28);
}

.price-dirty-count {
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.save-bar.has-changes .price-dirty-count {
  color: var(--accent-text);
  font-weight: 600;
}

.save-bar .wto-btn[disabled] {
  opacity: .5;
  cursor: default;
  pointer-events: none;
}

.price-card {
  background: var(--surface);
  border: 1px solid var(--subtle);
  border-radius: var(--r-lg);
  padding: 13px 14px;
  box-shadow: var(--shadow-card);
  margin-bottom: 9px;
}

.price-card .pc-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 4px;
}

.pc-name {
  font-weight: 600;
  font-size: 14px;
  color: var(--primary);
}

.pc-line {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 9px;
}

.pc-cur {
  width: 36px;
  flex: none;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--muted);
}

.pc-line .price-cell {
  flex: 1;
  width: auto;
}

/* =======================================================================
       CATÁLOGO v2 (Phase 2.8.1) — grouped table: category bands carry
       baselines, products inherit; inline price editing across 1–3
       currencies. Reuses .price-cell, .save-bar, .kebab-btn, .seg-mini,
       .pill from the existing system. Ported from the design bundle's
       components.css. Tailwind preflight is OFF — new inputs/cells set
       box-sizing: border-box explicitly.
       ======================================================================= */

.cat2-table {
  table-layout: auto;
  width: 100%;
}

/* R5 header typography (SPEC §4.4 Tables): small / uppercase / muted, never
       the big dark headers. `th.cur-col`'s right-align (below) still wins for
       money headers via later-source-order + equal specificity. */

.cat2-table th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  white-space: nowrap;
  padding: 9px 12px;
  text-align: left;
  border-bottom: 1px solid var(--divider);
}

.cat2-table th.cur-col {
  text-align: right;
  width: 150px;
}

.cat2-table th.r, .cat2-table td.r {
  text-align: right;
}

.cat2-table td {
  vertical-align: middle;
}

/* R3 column-width standard (SPEC §4.4-0): SKU / Estado / Acciones shrink to
       content; the single Producto column flexes to absorb the slack. Money
       columns KEEP their deliberate fixed 150px (cur-col above) — they are NOT
       set to width:1%. */

.cat2-table th:first-child, .cat2-table td:first-child {
  width: 1%;
  white-space: nowrap;
}

/* SKU */

.cat2-table th.col-product, .cat2-table td.col-product {
  width: 100%;
  white-space: normal;
}

.cat2-table th.col-status, .cat2-table td.col-status {
  width: 1%;
  white-space: nowrap;
}

.cat2-table th.col-actions, .cat2-table td.col-actions {
  width: 1%;
  white-space: nowrap;
  padding-left: 8px;
}

/* category band — the accent-tinted baseline-price row */

tr.cat2-band > td {
  background: var(--accent-soft);
  border-top: 1px solid var(--accent-soft-border);
  border-bottom: 1px solid var(--accent-soft-border);
  padding-top: 11px;
  padding-bottom: 11px;
}

tr.cat2-band .pd-label {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-text);
}

tr.cat2-band .pd-cat {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
  color: var(--primary);
  margin-left: 4px;
}

/* baseline price inputs read as authoritative: surface fill on the tint */

tr.cat2-band .price-cell {
  width: 138px;
  background: var(--surface);
}

/* product rows under a band */

tr.cat2-prod td:first-child {
  padding-left: 28px;
}

/* indent SKU under its band */

tr.cat2-prod .cat-name {
  font-weight: 600;
  color: var(--primary);
}

tr.cat2-prod.is-archived .cat-name, tr.cat2-prod.is-archived td {
  color: var(--muted);
}

.cat2-table .price-cell {
  width: 138px;
}

/* R1 click-to-edit product name (system standard for editable list rows,
       SPEC §4.4-1). Ported from the design bundle's screens.css. */

.editable-name {
  cursor: text;
  border-radius: 4px;
  padding: 1px 4px;
  margin: -1px -4px;
  transition: background .12s, box-shadow .12s;
}

.editable-name:hover {
  background: var(--surface-muted);
  box-shadow: inset 0 -1px 0 var(--divider);
}

.editable-name.dirty-name {
  box-shadow: inset 0 -1.5px 0 var(--accent);
}

/* server name-conflict signal: a danger underline on the rejected name (the
       message renders in the adjacent overlay `.name-err`). */

.editable-name[data-name-invalid] {
  box-shadow: inset 0 -1.5px 0 var(--danger-border);
  color: var(--danger-text);
}

.name-edit-input {
  box-sizing: border-box;
  width: 100%;
  max-width: 320px;
  padding: 5px 9px;
  font-size: 14px;
  font-weight: 600;
}

/* overlaid name-conflict message — absolutely positioned, never reflows the
       row (SPEC §4.3a; same discipline as `.price-err`). The desktop cell + the
       mobile card head are the positioning context. */

.cat2-table td.col-product {
  position: relative;
}

.name-err {
  position: absolute;
  top: calc(100% - 6px);
  left: 12px;
  z-index: 5;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--danger-text);
  white-space: nowrap;
  pointer-events: none;
}

.pc-head {
  position: relative;
}

.price-card .name-err {
  position: static;
  display: block;
  margin-top: 2px;
}

/* inline add row — deliberately DISTINCT from the accent band:
       neutral surface + an accent left-marker so "new entry" ≠ "category". */

tr.cat2-inline-row td {
  background: var(--surface-subtle);
  border-bottom: 1px solid var(--divider);
  padding-top: 10px;
  padding-bottom: 10px;
}

tr.cat2-inline-row td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}

.cat2-inline-row .price-cell {
  width: 138px;
  background: var(--surface);
}

.cat2-inline-sku {
  padding-left: 28px !important;
}

.cat2-inline-input {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  padding: 7px 10px;
  border: 1px solid var(--subtle);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--primary);
}

.cat2-inline-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* inline-add trigger row (the "Add product" affordance) */

.add-inline-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--accent-text);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 11px 0 11px 28px;
  text-align: left;
  transition: color .12s;
}

.add-inline-trigger:hover {
  color: var(--accent-hover);
}

/* mobile grouped cards */

.cat2-group {
  margin-bottom: 20px;
}

/* sticky category NAME header; baseline block connects directly beneath it
       as one accent unit, while the header alone freezes as products scroll */

.cat2-mhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: var(--accent-soft);
  border: 1px solid var(--accent-soft-border);
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  position: sticky;
  top: 0;
  z-index: 3;
}

.cat2-mhead .pd-cat {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
  color: var(--primary);
}

.cat2-baseline-card {
  background: color-mix(in srgb, var(--accent-soft) 50%, var(--surface));
  border: 1px solid var(--accent-soft-border);
  border-top: 0;
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  padding: 10px 12px 12px;
  margin-bottom: 10px;
}

.cat2-baseline-card .pd-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: 4px;
}

.cat2-msku {
  font-size: 11.5px;
  color: var(--muted);
  margin: 2px 0 4px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.cat2-inline-card {
  border-color: var(--accent);
  border-left-width: 3px;
  background: var(--surface-subtle);
}

.cat2-newcat-m {
  width: 100%;
  box-sizing: border-box;
  justify-content: center;
  padding: 14px 0;
  border: 1px dashed var(--accent-soft-border);
  border-radius: var(--r-sm);
  margin-top: 4px;
}

/* desktop table vs. mobile cards — one or the other per viewport */

.cat2-mobile {
  display: none;
}

@media (max-width: 760px) {
  .cat2-desktop {
    display: none;
  }

  .cat2-mobile {
    display: block;
  }

  /* §7: the top-bar "New category" button is the DESKTOP entry point;
           the mobile entry is the add-at-end row in the card list. */

  .cat2-desktop-only {
    display: none;
  }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

.pointer-events-none {
  pointer-events: none;
}

.pointer-events-auto {
  pointer-events: auto;
}

.visible {
  visibility: visible;
}

.collapse {
  visibility: collapse;
}

.fixed {
  position: fixed;
}

.absolute {
  position: absolute;
}

.relative {
  position: relative;
}

.sticky {
  position: sticky;
}

.right-0 {
  right: 0px;
}

.start-0 {
  inset-inline-start: 0px;
}

.top-0 {
  top: 0px;
}

.z-50 {
  z-index: 50;
}

.z-\[80\] {
  z-index: 80;
}

.m-3 {
  margin: 0.75rem;
}

.mx-auto {
  margin-left: auto;
  margin-right: auto;
}

.mb-1 {
  margin-bottom: 0.25rem;
}

.mb-3 {
  margin-bottom: 0.75rem;
}

.mb-4 {
  margin-bottom: 1rem;
}

.ml-1 {
  margin-left: 0.25rem;
}

.mt-1 {
  margin-top: 0.25rem;
}

.mt-2 {
  margin-top: 0.5rem;
}

.mt-4 {
  margin-top: 1rem;
}

.mt-7 {
  margin-top: 1.75rem;
}

.mt-auto {
  margin-top: auto;
}

.block {
  display: block;
}

.inline-block {
  display: inline-block;
}

.inline {
  display: inline;
}

.flex {
  display: flex;
}

.inline-flex {
  display: inline-flex;
}

.table {
  display: table;
}

.grid {
  display: grid;
}

.hidden {
  display: none;
}

.size-full {
  width: 100%;
  height: 100%;
}

.h-5 {
  height: 1.25rem;
}

.w-20 {
  width: 5rem;
}

.w-5 {
  width: 1.25rem;
}

.w-full {
  width: 100%;
}

.min-w-0 {
  min-width: 0px;
}

.min-w-\[16rem\] {
  min-width: 16rem;
}

.max-w-7xl {
  max-width: 80rem;
}

.max-w-md {
  max-width: 28rem;
}

.flex-1 {
  flex: 1 1 0%;
}

.border-collapse {
  border-collapse: collapse;
}

.flex-col {
  flex-direction: column;
}

.flex-wrap {
  flex-wrap: wrap;
}

.items-center {
  align-items: center;
}

.justify-end {
  justify-content: flex-end;
}

.justify-center {
  justify-content: center;
}

.justify-between {
  justify-content: space-between;
}

.gap-2 {
  gap: 0.5rem;
}

.gap-2\.5 {
  gap: 0.625rem;
}

.overflow-hidden {
  overflow: hidden;
}

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

.overflow-y-auto {
  overflow-y: auto;
}

.overflow-x-hidden {
  overflow-x: hidden;
}

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.whitespace-nowrap {
  white-space: nowrap;
}

.rounded {
  border-radius: 0.25rem;
}

.border {
  border-width: 1px;
}

.border-b {
  border-bottom-width: 1px;
}

.border-t {
  border-top-width: 1px;
}

.border-subtle {
  --tw-border-opacity: 1;
  border-color: rgb(var(--subtle-rgb) / var(--tw-border-opacity));
}

.bg-accent {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--accent-rgb) / var(--tw-bg-opacity));
}

.bg-secondary {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--secondary-rgb) / var(--tw-bg-opacity));
}

.bg-surface {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-rgb) / var(--tw-bg-opacity));
}

.bg-surface-subtle {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-subtle-rgb) / var(--tw-bg-opacity));
}

.p-4 {
  padding: 1rem;
}

.px-3 {
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}

.px-4 {
  padding-left: 1rem;
  padding-right: 1rem;
}

.py-1 {
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}

.py-2 {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.py-3 {
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
}

.font-display {
  font-family: Newsreader, Georgia, Times New Roman, serif;
}

.font-sans {
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif;
}

.text-sm {
  font-size: 0.875rem;
  line-height: 1.25rem;
}

.text-xs {
  font-size: 0.75rem;
  line-height: 1rem;
}

.font-medium {
  font-weight: 500;
}

.font-semibold {
  font-weight: 600;
}

.lowercase {
  text-transform: lowercase;
}

.italic {
  font-style: italic;
}

.text-accent {
  --tw-text-opacity: 1;
  color: rgb(var(--accent-rgb) / var(--tw-text-opacity));
}

.text-muted {
  --tw-text-opacity: 1;
  color: rgb(var(--muted-rgb) / var(--tw-text-opacity));
}

.text-on-inverse {
  --tw-text-opacity: 1;
  color: rgb(var(--on-inverse-rgb) / var(--tw-text-opacity));
}

.text-on-inverse\/70 {
  color: rgb(var(--on-inverse-rgb) / 0.7);
}

.text-primary {
  --tw-text-opacity: 1;
  color: rgb(var(--primary-rgb) / var(--tw-text-opacity));
}

.text-secondary {
  --tw-text-opacity: 1;
  color: rgb(var(--secondary-rgb) / var(--tw-text-opacity));
}

.underline {
  text-decoration-line: underline;
}

.no-underline {
  text-decoration-line: none;
}

.shadow {
  --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

.shadow-lg {
  --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

.outline {
  outline-style: solid;
}

.blur {
  --tw-blur: blur(8px);
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}

.filter {
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}

/* -------------------------------------------------------------------
 * Component layer — project-specific helpers that test contracts
 * depend on. Keep narrowly scoped; do not add general purpose helpers
 * here unless they're load-bearing for a test selector.
 * ------------------------------------------------------------------- */

@media (min-width: 760px) {
  .wto-delivery-print .signatures .md\:block {
    border-top-width: 1px;
    --tw-border-opacity: 1;
    border-color: rgb(var(--primary-rgb) / var(--tw-border-opacity));
    padding-top: 0.25rem;
  }
}

.hover\:bg-accent-hover:hover {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--accent-hover-rgb) / var(--tw-bg-opacity));
}

.hover\:bg-surface-muted:hover {
  --tw-bg-opacity: 1;
  background-color: rgb(var(--surface-muted-rgb) / var(--tw-bg-opacity));
}

.hover\:text-accent-hover:hover {
  --tw-text-opacity: 1;
  color: rgb(var(--accent-hover-rgb) / var(--tw-text-opacity));
}

@media (min-width: 560px) {
  .sm\:mx-auto {
    margin-left: auto;
    margin-right: auto;
  }

  .sm\:w-full {
    width: 100%;
  }
}

@media (min-width: 760px) {
  .md\:block {
    display: block;
  }

  .md\:hidden {
    display: none;
  }
}
