/* SWCorp app family — base stylesheet (v1, 2026-10-02).
   Shared by Freight Bill, Amazon Returns, ScanShip and Rebate Validation (same
   class names: .app-frame, .header, .side-menu, .pop-out, .toast-stack), with
   the page layout (.content/.main/.foot), forms, slots and tables from Lowe's
   Invoice Reconciler. Load order: swcorp-tokens.css → swcorp.css → app.css
   (app-specific rules only). Do not edit tokens or these rules per app: change
   them here and propagate. Rules and the reasons behind them: CLAUDE.md. */

/* ---------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  /* Safe area (notch, home bar): needs viewport-fit=cover in the meta viewport. */
  --sat: env(safe-area-inset-top, 0px); --sab: env(safe-area-inset-bottom, 0px);
  --sal: env(safe-area-inset-left, 0px); --sar: env(safe-area-inset-right, 0px);
}
html, body { height: 100dvh; }
html { overflow: hidden; }
body {
  font: 14px/1.45 var(--font); color: var(--ink); background: var(--ground); font-optical-sizing: auto; -webkit-font-smoothing: antialiased;
  display: flex; flex-direction: column; overflow: hidden;
  padding: var(--sat) var(--sar) var(--sab) var(--sal);
  -webkit-tap-highlight-color: transparent; -webkit-text-size-adjust: 100%;
}
::selection { background: var(--navy-100); color: var(--ink); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: var(--focus); text-underline-offset: 2px; }
svg.i { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.sprite { position: absolute; }
[hidden] { display: none !important; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.mono { font-variant-numeric: tabular-nums; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-3); }
.hint { font-size: 12px; color: var(--ink-3); }
.c-ok { color: var(--ok); } .c-warn { color: var(--warn); } .c-block { color: var(--block); }
/* Accounting: "$" pinned left, figure right, negatives in (). */
.acct { display: flex; justify-content: space-between; align-items: baseline; gap: .5em; width: 100%; font-variant-numeric: tabular-nums; }
.acct > i { font-style: normal; color: var(--ink-3); font-weight: 400; }

.skip { position: absolute; left: 12px; top: -60px; z-index: var(--z-dialog); padding: 8px 14px; border-radius: var(--r-ctl); background: var(--navy); color: #fff; font-weight: 600; text-decoration: none; transition: top .15s ease; }
.skip:focus { top: 12px; }

/* ---------------------------------------------------------------- window
   <body> > .app-frame (header, side menu, .app-body). Any viewport larger than
   1180 × 720 — a browser tab or Acumatica's iframe (about 1650×940 on 1080p) —
   shows a floating card centred on a white ground, filling the viewport minus
   a gap that scales with the screen, up to 1480×920 (the height subtracts the
   gap, or a short viewport cuts the card). Smaller (laptop iframe ~1100×630,
   tablet, phone): full-bleed, no radius, no shadow, and the frame stretches to
   the full height (body centres it only in card mode). */
.app-frame { flex: 1; min-height: 0; width: 100%; position: relative; display: flex; flex-direction: column; background: var(--surface); }
.app-body { flex: 1; min-height: 0; position: relative; display: flex; flex-direction: column; overflow: hidden; background: var(--surface); }
@media (min-width: 1181px) and (min-height: 721px) {
  body { --gap: clamp(20px, 4vmin, 48px); padding: var(--gap); align-items: center; justify-content: center; }
  .app-frame { width: min(1480px, 100%); height: min(920px, calc(100dvh - 2 * var(--gap))); flex: none;
    border-radius: var(--r-window); overflow: hidden; box-shadow: var(--shadow-window); }
}

/* ---------------------------------------------------------------- header
   Navy 56px: .header-home (logo, goes to the app's main screen) · .header-sep ·
   .header-title (app name) · (.run-chip) · .header-grow · .menu-toggle. The
   right corner holds only the menu button. No z-index here: the menu button
   stacks above the side menu, which covers this header when open. */
.header { position: relative; flex: none; display: flex; align-items: center; gap: 20px; height: var(--h-hdr); padding: 0 24px; background: var(--navy); color: #fff; }
.header :focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.header-home { flex: none; display: block; border-radius: 4px; transition: opacity .15s ease, transform var(--dur-press) var(--ease-out); }
.header-home:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) { .header-home:hover { opacity: .85; } }
.header-logo-img { height: 26px; width: auto; display: block; flex: none; }
.header-sep { width: 1px; height: 24px; background: rgba(255,255,255,.28); flex: none; }
.header-title { font-size: 15px; font-weight: 600; letter-spacing: -.005em; color: #fff; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.header-grow { flex: 1; }
/* Inside Acumatica (html.embedded) its screen title names the app: logo + menu only. */
html.embedded .header-sep, html.embedded .header-title { display: none; }
.run-chip { margin-left: 8px; display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: var(--r-ctl); background: rgba(255,255,255,.1); font-size: 13px; color: #fff; }
.run-chip b { font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- side menu
   motion.dev "variants" in CSS. .menu-toggle: three white lines, no disc at
   rest. .side-menu is a white panel the full height of the window (over the
   header), clipped to a circle of 0px centred on the button (JS measures
   --cx/--cy and --menu-r); opening grows it until it fills the panel (640ms
   drawer curve), the rows rise in 60ms apart, the top and bottom lines bend
   into an X (CSS d) and the middle one fades. Closing: rows drop at once,
   then the circle shrinks back (420ms, after 140ms). Picking an entry closes
   it fast (body.menu-quick) while the new screen comes in. JS toggles
   .open / body.menu-open / inert. Origin: Freight Bill Processor. */
.menu-toggle { position: relative; z-index: 41; flex: none; display: grid; place-items: center; width: 36px; height: 36px; margin-right: -6px; border-radius: 50%; color: #fff; -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-press) var(--ease-out), color 160ms ease 460ms; }   /* navy until the circle has shrunk away */
body.menu-open .menu-toggle { color: var(--navy); transition: transform var(--dur-press) var(--ease-out), color 120ms ease; }
.menu-toggle:active { transform: scale(.94); }
.menu-toggle svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; overflow: visible; }
.menu-toggle .l1 { d: path("M4 6.5 L20 6.5"); }
.menu-toggle .l2 { d: path("M4 12 L20 12"); }
.menu-toggle .l3 { d: path("M4 17.5 L20 17.5"); }
.menu-toggle path { transition: d 320ms var(--ease-in-out), opacity 160ms ease; }
body.menu-open .menu-toggle .l1 { d: path("M6 18 L18 6"); }
body.menu-open .menu-toggle .l2 { opacity: 0; }
body.menu-open .menu-toggle .l3 { d: path("M6 6 L18 18"); }
body.menu-open .menu-toggle path { transition: d 320ms var(--ease-in-out), opacity 120ms ease; }
.menu-toggle:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
body.menu-open .menu-toggle:focus-visible { outline-color: var(--navy); }
.side-menu { --cx: calc(100% - 36px); --cy: 28px; position: absolute; top: 0; right: 0; bottom: 0; z-index: 40; width: min(320px, 100%); padding: 76px 14px 24px; overflow-y: auto;
  display: flex; flex-direction: column; background: var(--surface); clip-path: circle(0px at var(--cx) var(--cy));
  transition: clip-path 420ms var(--ease-in-out) 140ms; }
/* No shadow, and the scrim stops at the panel: anything translucent under the
   panel tints the window's rounded corner through the curve's soft edge. */
.side-menu.open { clip-path: circle(var(--menu-r, 1200px) at var(--cx) var(--cy)); transition: clip-path 640ms cubic-bezier(.32, .72, 0, 1); }
body.menu-quick .side-menu { transition: clip-path 220ms var(--ease-out); }
body.menu-quick .menu-anim { transition: opacity 80ms ease; transition-delay: 0ms; }
body.menu-quick .menu-scrim { transition: opacity 160ms ease, visibility 0s linear 160ms; }
body.menu-quick .menu-toggle { transition: transform var(--dur-press) var(--ease-out), color 120ms ease 120ms; }
.menu-scrim { position: absolute; top: 0; left: 0; bottom: 0; right: min(320px, 100%); z-index: 39; background: rgba(21,46,69,.18); opacity: 0; visibility: hidden; transition: opacity 240ms ease, visibility 0s linear 240ms; }
.menu-scrim.open { opacity: 1; visibility: visible; transition: opacity 240ms ease, visibility 0s; }
.menu-anim { opacity: 0; transform: translateY(24px); transition: opacity 120ms ease, transform 160ms var(--ease-out); }
.side-menu.open .menu-anim { opacity: 1; transform: none; transition: opacity 320ms var(--ease-out), transform 420ms var(--ease-out); transition-delay: calc(180ms + var(--i, 0) * 60ms); }
/* One list, entries centred in the panel's free height (views first, external
   links after, with ↗); a single soft highlight glides to the entry under the
   mouse (JS sets its translateY and height). */
.menu-body { position: relative; margin: auto 0; }
.menu-glide { position: absolute; left: 0; right: 0; top: 0; height: 0; border-radius: var(--r-box); background: var(--surface-2); opacity: 0; pointer-events: none;
  transition: transform 240ms var(--ease-out), opacity 160ms ease; }
.menu-glide.on { opacity: 1; }
.menu-list { list-style: none; display: flex; flex-direction: column; gap: 2px; }
.menu-item { position: relative; display: flex; align-items: center; gap: 12px; width: 100%; padding: 9px 10px; border: 0; border-radius: var(--r-box); background: none;
  font: inherit; text-align: left; text-decoration: none; color: var(--ink); cursor: pointer;
  transition: background-color .15s ease, transform var(--dur-press) var(--ease-out); }
.menu-item:active { transform: scale(.98); }
.menu-item:focus-visible { outline: 2px solid var(--navy); outline-offset: -2px; }
/* Each entry shows its destination's own mark (app icon, SharePoint, ...),
   painted navy through a mask (one colour); the current view inverts it. */
.menu-ic { flex: none; display: grid; place-items: center; width: 34px; height: 34px; border: 1.5px solid var(--line); border-radius: 9px; background: var(--surface); color: var(--navy);
  transition: background-color .2s ease, border-color .2s ease, color .2s ease; }
.menu-ic .i { width: 16px; height: 16px; }
.menu-logo { width: 18px; height: 18px; background: currentColor; -webkit-mask: var(--logo) center / contain no-repeat; mask: var(--logo) center / contain no-repeat; }
.menu-t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; font-size: 13px; font-weight: 600; line-height: 1.3; }
.menu-t small { font-size: 12px; font-weight: 400; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.menu-ext { flex: none; width: 14px; height: 14px; fill: none; stroke: var(--ink-3); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.menu-item[aria-current] { background: var(--navy-50); }
.menu-item[aria-current] .menu-t { color: var(--navy); }
.menu-item[aria-current] .menu-ic { background: var(--navy); border-color: var(--navy); color: #fff; }
@media (hover: hover) and (pointer: fine) {
  .menu-toggle:hover { transform: scale(1.06); }
  .menu-item:not([aria-current]):hover .menu-ic { border-color: var(--navy); }
}
/* Every layer at the window's rounded corner lets a little of the one below
   through its soft edge, so the header's navy tinted the corner under the
   white panel. While the panel covers that corner the header rounds its own
   corner more — switched only under the panel (100ms after opening, 370ms into
   closing; 90ms on a quick close), so the outline never visibly changes. */
.header { transition: border-top-right-radius 0s linear 370ms; }
body.menu-open .header { border-top-right-radius: 32px; transition-delay: 100ms; }
body.menu-quick .header { transition: border-top-right-radius 0s linear 90ms; }
@media (prefers-reduced-motion: reduce) {
  .side-menu, .side-menu.open { transition: none; }
  .menu-anim, .side-menu.open .menu-anim { transform: none; transition: opacity 150ms ease; transition-delay: 0ms; }
  .menu-toggle path, body.menu-open .menu-toggle path { transition: none; }
  .menu-toggle:hover { transform: none; }
  .menu-glide { transition: opacity 150ms ease; }
}

/* ---------------------------------------------------------------- pop-out
   Inside Acumatica only: a quiet round button bottom-right that opens the app
   in its own tab (<a class="pop-out" target="_blank" rel="noopener"
   aria-label="Open … in a new tab">). Nearly invisible at rest, full under
   the pointer or keyboard focus; on touch it rests at .6. No tooltip. */
.pop-out { display: none; }
html.embedded .pop-out { position: fixed; right: 14px; bottom: 14px; z-index: 70; display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;
  background: var(--navy); color: #fff; box-shadow: 0 4px 14px rgba(21,46,69,.18); opacity: .22; transform: scale(.92);
  transition: opacity .2s ease, transform var(--dur-press) var(--ease-out), box-shadow .2s ease; }
html.embedded .pop-out svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
html.embedded .pop-out:hover, html.embedded .pop-out:focus-visible { opacity: 1; transform: none; box-shadow: 0 6px 18px rgba(21,46,69,.26); }
html.embedded .pop-out:focus-visible { outline: 2px solid var(--navy); outline-offset: 3px; }
html.embedded .pop-out:active { transform: scale(.94); }
@media (hover: none) { html.embedded .pop-out { opacity: .6; transform: none; } }

/* ---------------------------------------------------------------- body
   .content (skip target) > .main (scrolls) + .foot (pinned). Only one inner
   region scrolls; never the page, never the iframe. */
.content { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.content:focus { outline: none; }
.main { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 28px 32px 0; overflow: auto; }
.main.fixed { overflow: hidden; }
@media (max-height: 820px) { .main { padding: 18px 24px 0; } }
.spacer { flex: 1 1 0; min-height: 12px; }

.titlebar { position: relative; z-index: var(--z-pop); display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
.titlebar h2 { font-size: 20px; font-weight: 800; line-height: 1.45; letter-spacing: -.01em; color: var(--navy); text-wrap: balance; }
.titlebar p { color: var(--ink-3); font-size: 13px; line-height: 1.65; margin-top: 4px; max-width: 80ch; text-wrap: pretty; }
.titlebar .right { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.section-title { font-size: 14px; font-weight: 700; color: var(--navy); margin: 20px 0 8px; }
.label { font-size: 10px; font-weight: 700; letter-spacing: .045em; text-transform: uppercase; color: var(--ink-3); }

/* ---------------------------------------------------------------- footer
   Pinned action bar: message left, total, primary action right. */
.foot { flex: none; display: flex; align-items: center; gap: 14px; padding: 14px 32px; border-top: 1px solid var(--line); background: var(--surface); transition: background-color 200ms ease, border-color 200ms ease; flex-wrap: wrap; }
.foot .msg { display: flex; align-items: flex-start; gap: 8px; font-size: 13px; color: var(--ink-2); min-width: 0; }
.foot .msg b { color: var(--warn); }
.foot .grow { flex: 1; }
.foot .total { font-size: 13px; color: var(--ink-2); white-space: nowrap; }
.foot .total b { font-size: 15px; color: var(--ink); font-variant-numeric: tabular-nums; margin-left: 4px; }
.foot.done { background: var(--ok-bg); border-top-color: var(--ok-line); }
.foot.done .msg, .foot.done .msg b { color: var(--ok); }
.foot.confirm { background: var(--warn-bg); }
@media (max-height: 820px) { .foot { padding: 12px 24px; } }

/* ---------------------------------------------------------------- buttons */
.btn { display: inline-flex; align-items: center; gap: 8px; height: var(--h-ctl); padding: 0 16px; border-radius: var(--r-ctl); font-weight: 600; font-size: 13px; white-space: nowrap; text-decoration: none;
  transition: transform 160ms var(--ease-out), background-color .15s ease, color .15s ease, border-color .15s ease, box-shadow .15s ease; }
.btn:active:not([aria-disabled=true]):not(:disabled) { transform: scale(.97); }
.btn.primary { background: var(--navy); color: #fff; border: 1.5px solid var(--navy); }
.btn.sec { border: 1.5px solid var(--line); color: var(--ink-2); background: var(--surface); }
.btn.danger { color: var(--block); border: 1.5px solid var(--block-line); background: var(--surface); }
.btn.small { height: var(--h-ctl-sm); padding: 0 12px; font-size: 12.5px; }
.btn[aria-disabled=true], .btn:disabled { background: var(--surface-2); color: var(--ink-3); border: 1.5px solid var(--line); cursor: not-allowed; }
.btn:focus-visible { outline: none; box-shadow: var(--ring-strong); }
.btn[aria-busy=true] { cursor: progress; }
@media (hover: hover) and (pointer: fine) {
  .btn.primary:not(:disabled):not([aria-disabled=true]):hover { background: var(--navy-dark); border-color: var(--navy-dark); }
  .btn.sec:not(:disabled):not([aria-disabled=true]):hover { border-color: var(--navy); color: var(--navy); }
  .btn.danger:not(:disabled):not([aria-disabled=true]):hover { background: var(--block-bg); }
}
.linkbtn { color: var(--navy); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
.row-actions { display: flex; gap: 10px; align-items: center; }

/* ---------------------------------------------------------------- forms */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px 18px; margin-top: 10px; }
.field label { display: block; font-size: 12px; font-weight: 600; color: var(--ink-2); margin-bottom: 5px; }
.field input, .field select, .field textarea { width: 100%; height: var(--h-ctl); border: 1.5px solid var(--line-strong); border-radius: var(--r-ctl); padding: 0 10px; background: var(--surface-2); font-size: 13px;
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease; }
.field textarea { height: auto; min-height: 80px; padding: 8px 10px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--navy); background: var(--surface); box-shadow: var(--ring); }
.field input[aria-invalid=true] { border-color: var(--block); background: var(--block-bg); }
.field .hint { margin-top: 3px; }

.search { display: flex; align-items: center; gap: 6px; border: 1.5px solid var(--line-strong); border-radius: var(--r-ctl); padding: 0 10px; height: var(--h-ctl); width: 260px; color: var(--ink-3); background: var(--surface-2);
  transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease; }
.search input { border: 0; outline: 0; font-size: 13px; flex: 1; min-width: 0; color: var(--ink); background: none; }
.search input::placeholder { color: var(--ink-3); }
.search:focus-within { border-color: var(--navy); background: var(--surface); box-shadow: var(--ring); }
/* Tucked search (LIR, family default): only its icon sits at the end of the
   row; it opens on hover or focus and stays open while it has text. Markup:
   <label class="search tuck"><svg class="i"/><input aria-label="…">
   <button type="button" class="search-x" aria-label="Clear search" hidden>…</button></label> */
.search.tuck { width: var(--h-ctl); overflow: hidden; cursor: pointer; border-color: transparent; background: transparent; color: var(--ink-2);
  transition: width 280ms var(--ease-out), border-color .15s ease, background-color .15s ease, box-shadow .15s ease; }
.search.tuck .i { flex: none; }
.search.tuck input { opacity: 0; transition: opacity 140ms ease; cursor: pointer; }
.search.tuck:hover, .search.tuck:focus-within, .search.tuck.has-q { width: 260px; border-color: var(--line-strong); background: var(--surface-2); color: var(--ink-3); cursor: text; }
.search.tuck:hover input, .search.tuck:focus-within input, .search.tuck.has-q input { opacity: 1; transition-delay: 80ms; cursor: text; }
.search.tuck:focus-within { border-color: var(--navy); background: var(--surface); box-shadow: var(--ring); }
.search-x { flex: none; display: grid; place-items: center; width: 22px; height: 22px; margin-right: -4px; padding: 0; border: 0; border-radius: 999px; background: none; color: var(--ink-3); cursor: pointer;
  transition: background-color .15s ease, color .15s ease; }
.search-x[hidden] { display: none; }
@media (hover: hover) and (pointer: fine) { .search-x:hover { background: var(--surface-3); color: var(--ink); } }
/* Just cleared with the x: back to the icon even under the pointer (JS adds .shut until pointerleave). */
.search.tuck.shut:hover { width: var(--h-ctl); border-color: transparent; background: transparent; color: var(--ink-2); cursor: pointer; }
.search.tuck.shut:hover input { opacity: 0; transition-delay: 0s; }
@media (prefers-reduced-motion: reduce) { .search.tuck, .search.tuck input { transition: none; } }

/* Checkbox: navy fill, tick draws along its path. */
.cbx { position: relative; display: inline-grid; width: 18px; height: 18px; vertical-align: middle; }
.cbx input { appearance: none; -webkit-appearance: none; margin: 0; width: 18px; height: 18px; border: 1.5px solid var(--line-strong); border-radius: 5px; background: var(--surface); cursor: pointer;
  transition: background-color 180ms var(--ease-out), border-color 180ms var(--ease-out), transform 120ms var(--ease-out); }
.cbx input:checked { background: var(--navy); border-color: var(--navy); }
.cbx input:active { transform: scale(.88); }
.cbx svg { position: absolute; inset: 0; width: 18px; height: 18px; pointer-events: none; fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 13; stroke-dashoffset: 13; transition: stroke-dashoffset 140ms var(--ease-out); }
.cbx input:checked + svg { stroke-dashoffset: 0; transition: stroke-dashoffset 220ms var(--ease-out) 40ms; }

/* ---------------------------------------------------------------- toolbar, tabs, chips */
.toolbar { display: flex; align-items: center; gap: 8px; margin: 14px 0 10px; flex-wrap: wrap; }
.toolbar .search { margin-left: auto; }
.tabs { display: flex; border: 1.5px solid var(--line); border-radius: var(--r-box); padding: 2px; background: var(--surface-2); }
.tab { padding: 5px 12px; border-radius: var(--r-ctl); font-size: 13px; color: var(--ink-2); display: flex; gap: 6px; align-items: center; border: 1px solid transparent; transition: background-color .15s ease, color .15s ease; }
.tab .c { font-variant-numeric: tabular-nums; color: var(--ink-3); }
.tab[aria-selected=true] { background: var(--surface); color: var(--navy); font-weight: 600; border-color: var(--line); box-shadow: 0 1px 2px rgba(21,46,69,.08); }
.tab[aria-selected=true] .c { color: var(--navy); }
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip { display: flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px; border: 1.5px solid var(--line); font-size: 12.5px; color: var(--ink-2); transition: background-color .15s ease, border-color .15s ease; }
.chip b { font-variant-numeric: tabular-nums; }
.chip[aria-pressed=true] { border-color: var(--navy); background: var(--navy-50); color: var(--navy); font-weight: 600; }

/* ---------------------------------------------------------------- status */
.badge { display: inline-flex; align-items: center; gap: 5px; padding: 0 9px 0 6px; border-radius: 999px; font-size: 12px; font-weight: 600; line-height: 18px; white-space: nowrap; border: 1px solid transparent; vertical-align: middle; }
.badge .i { width: 13px; height: 13px; }
.badge.ok { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.badge.warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line); }
.badge.block { background: var(--block-bg); color: var(--block); border-color: var(--block-line); }
.badge.off { background: var(--surface-2); color: var(--ink-3); border-color: var(--line); }
.tag { display: inline-block; padding: 1px 7px; border-radius: var(--r-tag); font-size: 12px; background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-2); }

/* Notice: white card, 3px left edge carries the meaning. */
.notice { display: flex; gap: 10px; align-items: flex-start; padding: 11px 14px; border-radius: var(--r-box); font-size: 13px; margin-top: 12px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--navy); box-shadow: 0 2px 10px rgba(28,61,90,.07); }
.notice .i { margin-top: 1px; color: var(--navy); }
.notice.warn { border-left-color: var(--amber); } .notice.warn .i { color: var(--amber-stroke); }
.notice.error { border-left-color: var(--block); } .notice.error .i { color: var(--block); }
.notice.ok { border-left-color: var(--ok); } .notice.ok .i { color: var(--ok); }

/* ---------------------------------------------------------------- panels */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; margin-top: 16px; }
.tile { border: 1.5px solid var(--line); border-radius: var(--r-box); padding: 18px 20px; background: var(--surface); display: flex; flex-direction: column; gap: 10px; }
.tile h3 { font-size: 14px; font-weight: 700; color: var(--navy); display: flex; align-items: center; gap: 8px; }
.tile .meta { font-size: 12.5px; color: var(--ink-2); }
.tile.ok { border-color: var(--navy); }

/* Drop zone: real <input type=file> stretched over the box. */
.drop { position: relative; display: block; border: 1.5px solid var(--line); border-radius: var(--r-box); padding: 20px; text-align: center; color: var(--ink-2); font-size: 13px; transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease; }
.drop input[type=file] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; }
.drop.over { border-color: var(--navy); background: var(--surface-2); box-shadow: 0 0 0 4px rgba(28,61,90,.08); }
.drop:has(input:focus-visible) { border-color: var(--navy); box-shadow: var(--ring-strong); }
.drop b { color: var(--navy); }
.drop .i { width: 22px; height: 22px; color: var(--navy); margin: 0 auto 6px; display: block; }

/* Input pills: one per file/source the screen needs, under the drop zone.
   70px tall, 13/16 padding, 24px ring, 10px uppercase source over an 11px
   detail. The detail SHOULD link to where the file comes from (portal,
   SharePoint, Acumatica screen) with .slot-link; plain text only when there
   is nowhere to link. States: (rest = pending; an empty pill already says
   "still needed", so no extra colour) · .loading · .done navy · .failed.
   A file the user loaded and that is not used yet gets a .slot-x to take it
   back; un-ticking retracts the check, then the navy fades. Fixed count: grid of 3; variable count: .slots.flex. */
.slots { list-style: none; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; flex: none; }
.slots.flex { display: flex; } .slots.flex > .slot { flex: 1; }
.slot { display: flex; align-items: center; gap: 12px; min-width: 0; min-height: 70px; padding: 13px 16px; border: 1.5px solid var(--line); border-radius: var(--r-box); background: var(--surface);
  transition: background-color 300ms var(--ease-out), border-color 300ms var(--ease-out), transform 300ms var(--ease-out); }
.slot-mark { position: relative; flex: none; width: 24px; height: 24px; border-radius: 50%; border: 2px solid var(--line-mid); transition: background-color 240ms var(--ease-out), border-color 240ms var(--ease-out); }
.slot-mark svg { position: absolute; inset: -2px; width: 24px; height: 24px; fill: none; stroke: var(--navy); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 16; stroke-dashoffset: 16; transition: stroke-dashoffset 280ms var(--ease-out) 90ms; }
.slot-mark svg.x { stroke: var(--block); stroke-dasharray: 20; stroke-dashoffset: 20; }
.slot-text { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.slot-top { font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3); line-height: 1.2; transition: color 240ms ease; }
.slot-main { font-size: 11px; font-weight: 500; color: var(--ink-3); line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color 240ms ease; }
a.slot-main, .slot-link { color: inherit; text-decoration: none; text-underline-offset: 2px; }
a.slot-main:hover, .slot-link:hover { text-decoration: underline; }
.slot.loading .slot-mark { border-top-color: var(--navy); animation: spin .75s linear infinite; }
.slot.done { border-color: var(--navy); background: var(--navy); }
.slot.done .slot-mark { border-color: #fff; background: #fff; }
.slot.done .slot-mark svg:not(.x) { stroke-dashoffset: 0; }
.slot.done .slot-top { color: #fff; }
.slot.done .slot-main { color: rgba(255,255,255,.72); }
.slot.done :focus-visible { outline-color: #fff; }
.slot.failed { border-color: var(--block-line); background: var(--block-bg); }
.slot.failed .slot-mark { border-color: var(--block); background: #fff; }
.slot.failed .slot-mark svg.x { stroke-dashoffset: 0; }
.slot.failed .slot-main { color: var(--block); }
.slot:not(.done) .slot-mark svg:not(.x) { transition: stroke-dashoffset 140ms var(--ease-out); }
.slot-x { flex: none; display: grid; place-items: center; width: 26px; height: 26px; margin: -4px -6px -4px 0; border-radius: 50%; color: rgba(255,255,255,.62);
  opacity: 0; transform: scale(.9); visibility: hidden; transition: opacity 120ms ease-out, transform 120ms var(--ease-out), background-color 150ms ease, color 150ms ease; }
.slot-x svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.slot-x:focus-visible { outline-color: #fff; }
.slot.done .slot-x { visibility: visible; }
.slot.done:hover .slot-x, .slot.done .slot-x:focus-visible { opacity: 1; transform: none; }
.slot.done .slot-x:hover { background: rgba(255,255,255,.14); color: #fff; }
.slot.done .slot-x:active { transform: scale(.9); }
@media (hover: none) { .slot.done .slot-x { opacity: 1; transform: none; } }
@media (max-height: 720px) { .slot { padding: 10px 12px; } }

.empty { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 40px 0; text-align: center; color: var(--ink-3); font-size: 13px; line-height: 1.6; max-width: 46ch; }
.empty .i { width: 40px; height: 40px; stroke-width: 1.2; color: var(--line-strong); }
.empty b { color: var(--ink); font-size: 14px; }

.console { font: 12px/1.6 var(--mono); background: #10202F; color: #D7E3EE; border-radius: var(--r-box); padding: 10px 12px; max-height: 180px; overflow: auto; white-space: pre-wrap; }
.console .ok { color: #7FD8A6; } .console .warn { color: #F2C14E; } .console .err { color: #F7A097; }

/* ---------------------------------------------------------------- screen change
   The new screen comes in from the left (always left to right) while it fades
   up: el.animate([{opacity:0, transform:'translateX(-28px)'}, {opacity:1,
   transform:'none'}], {duration:280, easing:'cubic-bezier(.23,1,.32,1)'}).
   transform + opacity only (composited). No view transitions, masks or
   full-window curtains: measured, they froze ~180ms on the click and ran at
   15-20fps. Reduced motion: plain swap. (JS only, no CSS needed.) */

/* ---------------------------------------------------------------- tables
   Plain <table class="grid"> for small sets; Tabulator for thousands of rows
   (overrides in tabulator.css, same look: navy header, 38px rows). */
.grid-wrap { border: 1px solid var(--line); border-radius: var(--r-box); overflow: auto; scrollbar-width: thin; scrollbar-color: #CED4DA transparent; }
table.grid { width: 100%; border-collapse: collapse; font-size: 13px; }
table.grid th { position: sticky; top: 0; background: var(--navy); color: rgba(255,255,255,.85); font-size: 10px; font-weight: 700; letter-spacing: .045em; text-transform: uppercase; padding: 10px 12px; text-align: center; line-height: 1.3; }
table.grid td { height: var(--row); padding: 9px 12px; border-bottom: 1px solid var(--line-soft); color: var(--ink-2); font-variant-numeric: tabular-nums; }
table.grid td.mono { color: var(--ink); }
table.grid tr:last-child td { border-bottom: 0; }
table.grid tr.warn td { background: var(--warn-bg); }
table.grid tr.block td { background: var(--block-bg); }
table.grid tr[aria-selected=true] td { background: var(--navy-50); }
table.grid tfoot td { background: var(--surface-2); border-top: 1px solid var(--line); font-weight: 600; }
@media (hover: hover) { table.grid tbody tr:hover td { background: var(--surface-2); } }

/* ---------------------------------------------------------------- loading */
.spin { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(28,61,90,.2); border-top-color: var(--navy); animation: spin .7s linear infinite; flex: none; }
.btn.primary .spin { border-color: rgba(255,255,255,.35); border-top-color: #fff; }
@keyframes spin { to { transform: rotate(360deg); } }
.skel { display: grid; gap: 10px; width: min(520px, 70vw); }
.skel i { display: block; height: 12px; border-radius: 6px; background: linear-gradient(90deg, var(--surface-3) 0%, #E9ECEF 40%, var(--surface-3) 80%); background-size: 200% 100%; animation: skel 1.2s linear infinite; }
.skel i:nth-child(2) { width: 86%; } .skel i:nth-child(3) { width: 64%; }
@keyframes skel { to { background-position: -200% 0; } }

/* Processing screen: navy field inside the window, morphing white loader,
   status line cross-fades. Absolute so it stays inside the card. */
.proc-screen { position: absolute; inset: 0; z-index: var(--z-overlay); display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 24px; text-align: center;
  background: var(--navy); opacity: 0; transition: opacity .42s var(--ease-settle); }
.proc-screen.show { opacity: 1; }
.loader-shape { width: 48px; height: 48px; margin: 0 auto 20px; border-radius: 50%; background: #fff; animation: loader-morph 2s linear infinite; }
@keyframes loader-morph {
  0% { border-radius: 50%; }
  12.5% { border-radius: 0 50% 50% 50%; transform: rotate(45deg); }
  25% { border-radius: 0 0 50% 50%; transform: rotate(90deg); }
  37.5% { border-radius: 0 0 0 50%; transform: rotate(135deg); }
  50% { border-radius: 0; transform: rotate(180deg); }
  62.5% { border-radius: 50% 0 0 0; transform: rotate(225deg); }
  75% { border-radius: 50% 50% 0 0; transform: rotate(270deg); }
  87.5% { border-radius: 50% 50% 50% 0; transform: rotate(315deg); }
  100% { border-radius: 50%; transform: rotate(360deg); }
}
.process-text { min-height: 22px; font-size: 15px; font-weight: 600; letter-spacing: .2px; color: rgba(255,255,255,.8); transition: opacity .3s; }

/* ---------------------------------------------------------------- toasts
   One stack per page, bottom-right, outside the card, newest at the bottom
   (phones: top, under the header). Every toast is the same card: white, 3px
   left edge carrying its meaning — navy info · .warn amber (wrong file,
   something still missing) · .error red (hard failure only) · .ok green —
   icon 16px, title 13/700 over a 12px line, × top-right, and a 2px line along
   the foot that empties in 6s. That line's animationend IS the timer (no
   sticky toasts); hover/focus pause it, and so does a hidden tab (JS toggles
   html.tab-hidden on visibilitychange). Showing the same toast again restarts
   it. When one leaves, the others slide to their place (FLIP, 240ms ease-out).
   Markup: <div class="toast-stack" aria-live="polite"> <div class="toast warn">
   <svg class="toast-icon"/><div class="toast-body"><div class="toast-title"/>
   <div class="toast-detail"/></div><button class="toast-x"/>
   <i class="toast-timer"/></div> */
.toast-stack { position: fixed; right: 16px; bottom: calc(16px + var(--sab)); z-index: var(--z-toast); display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
  max-width: min(420px, calc(100vw - 32px)); pointer-events: none; }
.toast-stack > * { pointer-events: auto; }
/* Inside Acumatica the stack rises above the pop-out bubble. */
html.embedded .toast-stack { bottom: calc(14px + 32px + 10px); }
.toast { position: relative; display: flex; align-items: flex-start; gap: 10px; min-width: 280px; padding: 12px 36px 14px 14px; border-radius: var(--r-box); overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--navy); box-shadow: var(--shadow-pop); color: var(--ink);
  --edge: var(--navy); animation: toast-in var(--dur-toast) var(--ease-out); }
.toast.warn { border-left-color: var(--amber); --edge: var(--amber-stroke); }
.toast.error { border-left-color: var(--block); --edge: var(--block); }
.toast.ok { border-left-color: var(--ok); --edge: var(--ok); }
.toast.leaving { animation: toast-out var(--dur-exit) var(--ease-out) forwards; }
.toast-icon { width: 16px; height: 16px; flex: none; margin-top: 1px; fill: none; stroke: var(--edge); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.toast-body { min-width: 0; flex: 1; }
.toast-title { font-size: 13px; font-weight: 700; color: var(--ink); }
.toast-detail { margin-top: 2px; font-size: 12px; line-height: 1.5; color: var(--ink-2); overflow-wrap: anywhere; }
/* A summary: the title carries the figure ("351 Pending Invoices"), then name ↔ count. */
.toast-list { list-style: none; margin-top: 4px; display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--ink-2); }
.toast-list b { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
.toast-x { position: absolute; top: 8px; right: 8px; display: grid; place-items: center; width: 24px; height: 24px; border-radius: 6px; color: var(--ink-3);
  transition: background-color .15s ease, color .15s ease, transform var(--dur-press) var(--ease-out); }
.toast-x:active { transform: scale(.95); }
.toast-x svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
@media (hover: hover) and (pointer: fine) { .toast-x:hover { background: var(--surface-3); color: var(--ink); } }
.toast-timer { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--edge); transform-origin: left center; animation: toast-timer 6s linear forwards; }
.toast:hover > .toast-timer, .toast:focus-within > .toast-timer, html.tab-hidden .toast-timer { animation-play-state: paused; }
@keyframes toast-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@keyframes toast-in { from { opacity: 0; transform: translateY(calc(var(--toast-dy, 1) * 16px)) scale(.95); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(calc(var(--toast-dy, 1) * 10px)); } }

/* ---------------------------------------------------------------- hover tip
   The family's one allowed tooltip (never title=""): the full value of a cell
   cut off with "…", in the app's own card. Cells opt in with data-tip (full
   text) and tabindex="0"; ONE card for the page, moved by delegation to the
   cell under the pointer or with keyboard focus, shown only when the cell is
   actually clipped (data-tip-always: the card carries information the cell
   does not show). Esc, blur, scroll and resize hide it. pointer-events: none
   so the card over the row never steals the pointer and closes itself. */
.hover-tip { position: fixed; z-index: 120; left: 0; top: 0; max-width: 380px; padding: 10px 12px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-ctl);
  box-shadow: var(--shadow-card); font-size: 12px; line-height: 1.5; color: var(--ink); text-align: left; white-space: normal; overflow-wrap: anywhere;
  opacity: 0; visibility: hidden; transition: opacity .12s ease; pointer-events: none; }
.hover-tip.visible { opacity: 1; visibility: visible; }
/* Optional second line when the cell's click does something ("Click to copy this row"). */
.hover-tip-hint { display: block; margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--surface-3); font-size: 11px; color: var(--ink-3); }
[data-tip]:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* ---------------------------------------------------------------- completed
   The rare celebration (origin ScanShip "Order Completed"): navy screen over
   the card, logo, a ring that pops (from .9) with a check that draws, title
   24/800, the reference under it, and how to go on. It leaves by itself
   (ScanShip: 3s) or on a tap/key. role="status" aria-live="assertive". */
.completed { position: absolute; inset: 0; z-index: var(--z-overlay); display: none; flex-direction: column; align-items: center; justify-content: center;
  padding: 32px 24px max(32px, var(--sab)); text-align: center; background: var(--navy); color: #fff; cursor: pointer; }
.completed.show { display: flex; animation: fade-in 200ms ease-out; }
.completed-logo { height: 30px; width: auto; opacity: .9; margin-bottom: 40px; }
.completed-ring { width: 108px; height: 108px; border-radius: 50%; background: rgba(255,255,255,.12); display: grid; place-items: center; margin-bottom: 28px; }
.completed-dot { width: 78px; height: 78px; border-radius: 50%; background: #fff; display: grid; place-items: center; }
.completed-dot svg { width: 38px; height: 38px; fill: none; stroke: var(--navy); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 24; stroke-dashoffset: 24; }
.completed.show .completed-ring { animation: done-pop 420ms var(--ease-out) both; }
.completed.show .completed-dot svg { animation: done-tick 320ms var(--ease-out) 220ms forwards; }
.completed-title { font-size: 24px; font-weight: 800; letter-spacing: -.01em; margin-bottom: 6px; }
.completed-ref { font-size: 15px; font-weight: 600; color: rgba(255,255,255,.65); font-variant-numeric: tabular-nums; }
.completed-hint { margin-top: 28px; font-size: 12px; color: rgba(255,255,255,.5); }
@keyframes done-pop { from { transform: scale(.9); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes done-tick { to { stroke-dashoffset: 0; } }

/* ---------------------------------------------------------------- dialog
   Native <dialog>: 440px card, radius 14, blurred navy backdrop, springy
   entrance, quicker exit (add .closing, remove after 140ms). */
.dialog { margin: auto; width: min(440px, calc(100% - 32px)); padding: 22px 22px 18px; border: 0; border-radius: var(--r-dialog); background: var(--surface); color: var(--ink); box-shadow: var(--shadow-dialog); }
.dialog.wide { width: min(520px, calc(100% - 32px)); }
.dialog[open] { animation: dlg-in 220ms var(--ease-out); }   /* opened tens of times a day: quick, no bounce */
.dialog::backdrop { background: rgba(21,46,69,.32); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); animation: fade-in .2s ease-out; }
.dialog.closing { animation: dlg-out .14s ease-out forwards; }
.dialog.closing::backdrop { animation: fade-out .14s ease-out forwards; }
.dialog h3 { font-size: 16px; font-weight: 800; color: var(--navy); }
.dialog p { margin-top: 6px; font-size: 13px; line-height: 1.6; color: var(--ink-2); }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; }
@keyframes dlg-in { from { opacity: 0; transform: translateY(8px) scale(.96); } }
@keyframes dlg-out { to { opacity: 0; transform: translateY(6px) scale(.95); } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes fade-out { to { opacity: 0; } }

/* ---------------------------------------------------------------- error page */
.errpage { margin: auto; max-width: 460px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 40px 0; }
.errpage .code { font: 800 13px/1 var(--mono); letter-spacing: .08em; color: var(--ink-3); }
.errpage h2 { font-size: 20px; font-weight: 800; color: var(--navy); }
.errpage p { color: var(--ink-3); font-size: 13px; line-height: 1.65; text-wrap: pretty; }

/* ---------------------------------------------------------------- responsive tiers
   Every family app works on all four tiers. Width tiers:
     phone    < 600px        full-bleed, safe-area, header logo 22 / title 14,
                             16px gutters, 16px inputs (no iOS zoom), pills 2/row
     compact  600–1180px     full-bleed (also any height ≤ 720px or iframe),
                             24px gutters
     desktop  > 1180 × 720   floating card (rules in "window" above), 32px gutters
   Height tiers (any width): ≤ 820px tighter gutters (18/24); ≤ 720px full-bleed
   and tighter blocks. Scale with clamp() inside a tier, never per-pixel queries. */
@media (max-width: 1180px) and (min-height: 821px) { .main { padding: 24px 24px 0; } .foot { padding: 14px 24px; } }
@media (max-width: 599px) {
  .header { gap: 12px; padding: 0 16px; }
  .header-logo-img { height: 22px; }
  .header-title { font-size: 14px; }
  .side-menu { width: 100%; }
  .run-chip { display: none; }
  .main { padding: 20px 16px 0; }
  .titlebar h2 { font-size: 18px; }
  .foot { padding: 12px 16px; gap: 10px; }
  .foot .btn.primary { flex: 1 1 100%; justify-content: center; height: 44px; }
  .field input, .field select, .field textarea, .search input { font-size: 16px; }
  .search { width: 100%; margin-left: 0; }
  .btn { height: 40px; }
  .slots, .slots.flex { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .slots > .slot:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .slot { min-height: 56px; padding: 10px 12px; gap: 10px; }
  .tiles { grid-template-columns: 1fr; }
  /* Phones: toasts at the top, under the header, entering from above. */
  .toast-stack { left: 12px; right: 12px; top: calc(var(--h-hdr) + 8px + var(--sat)); bottom: auto; max-width: none; align-items: stretch; }
  html.embedded .toast-stack { bottom: auto; }
  .toast { --toast-dy: -1; min-width: 0; }
  .dialog { width: calc(100% - 24px); }
}

/* ---------------------------------------------------------------- reduced motion
   Loaders keep their speed (they only say "working"); entrances become fades. */
@media (prefers-reduced-motion: reduce) {
  .toast { animation-name: fade-in; } .toast.leaving { animation-name: fade-out; }
  .dialog[open] { animation-name: fade-in; } .dialog.closing { animation-name: fade-out; }
  .completed.show .completed-ring { animation: none; } .completed.show .completed-dot svg { animation: none; stroke-dashoffset: 0; }
  .hover-tip { transition: none; }
  .dialog[open] { animation: fade-in 150ms ease-out; }
  .dialog.closing { animation: fade-out 120ms ease-out forwards; }
  .btn:active, .cbx input:active { transform: none !important; }
}
