/* ============================================================================
   Seller Hub — shared stylesheet for every page mockup.

   Load order on every page:
     1. Bootstrap 5.3 CSS  (CDN)
     2. this file

   Bootstrap supplies the reboot, the grid, the utility classes, the offcanvas
   drawer and the modal. This file re-asserts the mockup's own design language
   on top of it and owns the whole density scale, so the pages stay compact
   without any component losing its shape.

   Density: base 13px / 1.45, 4px spacing rhythm, 8px controls, 10–14px panels.
   Aesthetic: a calm, precise product UI — neutral ink and zinc greys, one
   indigo accent that always means "interactive", Geist for text and Geist
   Mono for codes, hairline borders lifted by soft shadows, and one easing
   curve for everything that moves. Colour is reserved for meaning: green,
   red and amber are figures, never decoration.
   ========================================================================= */

/* The whole app renders at 90% — one step below the browser's 100% — so
   more of every table fits without each person setting browser zoom on
   their own machine.

   `zoom`, not `transform: scale()`. Zoom participates in layout, so the page
   genuinely has more room and sticky/fixed elements keep working; a transform
   would shrink the picture while the layout still believed it had the old
   width, which gives you a scaled page with a scrollbar round it.

   One number, here, because it is the kind of thing that gets tuned. */
:root {
  --zoom: 0.9;
  zoom: var(--zoom);
  /* Viewport units do NOT come along for the ride: `100vh` still measures the
     real viewport, but the element carrying it is scaled, so it renders at the
     zoom's share of the height it asks for — a sidebar that stops short and a nav that
     scrolls when it did not need to. Anything sized against the viewport uses
     these instead, which pre-divide the scale back out. */
  --vh100: calc(100vh / var(--zoom));
  --vw100: calc(100vw / var(--zoom));
}

:root {
  /* Surfaces, darkest-to-lightest is page → card. Neutral zinc rather than
     the slate the mockup started with: a blue-tinted grey next to an indigo
     accent made every panel read faintly tinted. */
  --bg: #f7f7f8;
  --surface: #ffffff;
  --surface-2: #fafafa;    /* quiet fills: table heads, code, a selected row */
  --hover: #f4f4f5;        /* the one hover wash, everywhere */
  --border: #e7e7ea;
  --border-strong: #d6d6db;

  /* Text. Each passes AA on --surface except --faint, which is reserved for
     the "true, and not the point" layer: meta lines, em dashes, timestamps. */
  --text: #0b0b0e;
  --muted: #61616b;
  --faint: #9b9ba5;

  /* Ink for the primary action; indigo for anything that is a link, a focus
     or a selection. Two colours with two jobs, never swapped. */
  --primary: #111114;
  --primary-hover: #2a2a30;
  --accent: #3e63dd;
  --accent-hover: #3358d4;
  --accent-fill: #3e63dd;  /* a solid fill that carries white text */
  --accent-soft: #eef2fe;  /* a selected row, an info note */
  --accent-line: #c9d5fa;

  /* Figures. Darkened from the -600s so each reads as text at AA: the amber
     a deduction prints in was 3.2:1 on white, which is a minus sign nobody
     could quite read. */
  --green: #1a7f4b;
  --red: #d1343b;
  --amber: #b25e09;        /* deductions render in amber with a leading minus */
  --violet: #6550b9;

  --sidebar-bg: #0c0c0f;
  --sidebar-text: #a9a9b3;
  --sidebar-active: rgba(255, 255, 255, 0.07);
  --sidebar-line: rgba(255, 255, 255, 0.06);

  --radius: 10px;          /* cards, tiles, dialogs' inner panels */
  --radius-sm: 7px;        /* controls: buttons, inputs, menu items */
  --radius-lg: 14px;       /* dialogs */
  --sidebar-w: 206px;
  --appbar-h: 52px;

  /* Depth. A hairline border does the separating; the shadow only says
     "this sits above the page", so it stays small and never smudges. */
  --shadow-xs: 0 1px 2px rgba(16, 16, 20, 0.05);
  --shadow-sm: 0 1px 2px rgba(16, 16, 20, 0.04), 0 1px 3px rgba(16, 16, 20, 0.05);
  --shadow-md: 0 6px 16px -4px rgba(16, 16, 20, 0.10), 0 2px 4px -2px rgba(16, 16, 20, 0.05);
  --shadow-lg: 0 24px 48px -12px rgba(16, 16, 20, 0.20), 0 4px 10px -2px rgba(16, 16, 20, 0.06);
  /* The focus halo. A soft ring beside a 1px accent border reads as "this is
     where you are" without the hard 2px outline jumping the layout. */
  --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent);

  /* One curve for everything that moves: fast out, long settle. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --fast: 140ms;
  --base: 200ms;

  --font-sans: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* hand the tokens to Bootstrap so its own components inherit the mockup */
  --bs-body-font-family: var(--font-sans);
  --bs-body-font-size: 13px;
  --bs-body-line-height: 1.45;
  --bs-body-color: var(--text);
  --bs-body-bg: var(--bg);
  --bs-border-color: var(--border);
  --bs-border-radius: var(--radius-sm);
  --bs-link-color: var(--accent);
  --bs-link-hover-color: var(--accent-hover);
  --bs-emphasis-color: var(--text);
  --bs-secondary-color: var(--muted);
  --bs-font-monospace: var(--font-mono);
}

/* ---------- Reset ----------
   Element-scoped rather than a bare `*` so Bootstrap's own component internals
   (offcanvas padding, .btn-close hit area, modal spacing) survive intact. */
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, h4, h5, h6, p, figure, blockquote,
dl, dd, dt, ol, ul, pre, fieldset, legend, table, form { margin: 0; padding: 0; }

body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  line-height: 1.45;
  letter-spacing: -0.003em;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Headings tighten as they grow — the type is drawn for text sizes, and at
   display sizes its default spacing reads loose. */
h1, h2, h3 { letter-spacing: -0.015em; }

a { color: var(--accent); text-decoration: none; transition: color var(--fast) ease; }
a:hover { color: var(--accent-hover); text-decoration: underline; text-underline-offset: 2px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); }

/* ---------- App shell ---------- */
.shell { display: flex; min-height: calc(var(--vh100) - var(--appbar-h)); }

/* ---------- Brand ----------
   Appears in the rail and in the drawer header. Deliberately NOT a
   `.sidebar a`, so it never picks up the nav item's padding or hover. */
.brand {
  display: inline-flex; align-items: center; gap: 8px;
  color: #fff; text-decoration: none; line-height: 1;
  border-radius: var(--radius-sm);
}
.brand:hover { color: #fff; text-decoration: none; }
.brand:hover .brand-mark { transform: translateY(-1px); }
.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.brand-mark {
  width: 22px; height: 22px; flex-shrink: 0;
  border-radius: 6px; display: grid; place-items: center;
  background: linear-gradient(180deg, #5876e8 0%, #3e63dd 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.30), inset 0 0 0 1px rgba(255, 255, 255, 0.06),
              0 1px 2px rgba(0, 0, 0, 0.30);
  transition: transform var(--base) var(--ease);
}
.brand-mark svg { width: 13px; height: 13px; display: block; }
.brand-name {
  font-size: 14.5px; font-weight: 650; letter-spacing: -0.025em; white-space: nowrap;
}
.brand-name > span { color: #a9bbfb; font-weight: 500; }   /* two-tone wordmark */
@media (prefers-reduced-motion: reduce) { .brand-mark { transition: none; } }
.sidebar .brand { padding: 2px 10px 12px; }            /* mark aligns with nav text */
.appbar .brand { flex-shrink: 0; color: var(--text); }
.appbar .brand:hover { color: var(--text); }
.appbar .brand-name > span { color: var(--accent); }
.sidebar .offcanvas-header .brand { padding: 0; }

/* Sidebar. Below lg it is a Bootstrap offcanvas drawer; from lg up it is the
   static rail it always was, sticky so it survives a long page. */
.sidebar {
  --bs-offcanvas-width: 236px;
  --bs-offcanvas-bg: var(--sidebar-bg);
  --bs-offcanvas-color: var(--sidebar-text);
  --bs-offcanvas-border-width: 0;
  --bs-offcanvas-padding-x: 0;
  --bs-offcanvas-padding-y: 0;
  color: var(--sidebar-text);
}
.sidebar .offcanvas-header {
  padding: 9px 11px; border-bottom: 1px solid var(--sidebar-line);
  /* Never declare `display` here. Bootstrap hides this header from lg up with
     `.offcanvas-lg .offcanvas-header { display: none }` — same specificity as
     this rule, so declaring display:flex would win and show a second brand
     next to the rail one. Its base rule already supplies flex below lg. */
  align-items: center; justify-content: space-between; gap: 10px;
}
/* Bootstrap's .btn-close is a 1em box; give it a real 30px hit area. */
.sidebar .btn-close {
  --bs-btn-close-focus-shadow: none;
  width: 30px; height: 30px; padding: 8px; background-size: 12px 12px;
  background-position: center; box-sizing: border-box; flex-shrink: 0;
}

/* Two classes deep on purpose: Bootstrap's own `.offcanvas-lg .offcanvas-body`
   is a two-class selector, and a bare `.sidebar-nav` would lose to it. */
.sidebar .sidebar-nav {
  display: flex; flex-direction: column; gap: 1px;
  padding: 10px 8px;
  flex: 1 1 auto; min-height: 0;    /* min-height:0 lets it shrink and scroll */
  overflow-y: auto;
}
/* Narrower than the page's: it runs beside the nav labels. */
.sidebar .sidebar-nav::-webkit-scrollbar { width: 5px; }

/* Direct children only — the brand link lives inside .offcanvas-header or as a
   .brand child, so it is excluded from every nav-item rule below. */
.sidebar .sidebar-nav > a:not(.brand) {
  position: relative;
  display: flex; align-items: center; gap: 9px;
  color: var(--sidebar-text); text-decoration: none;
  padding: 6px 10px; border-radius: var(--radius-sm); font-size: 13px; font-weight: 450;
  transition: background-color var(--fast) ease, color var(--fast) ease;
}
.sidebar .sidebar-nav > a:not(.brand):hover {
  background: rgba(255, 255, 255, 0.045); color: #e4e4e9; text-decoration: none;
}
/* The current page: a lifted row and a short indigo tick at its left edge.
   The tick is what the eye finds when scanning down the rail; the fill alone
   is too quiet against near-black to do it. */
.sidebar .sidebar-nav > a:not(.brand).active {
  background: var(--sidebar-active); color: #fff; font-weight: 550;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
}
.sidebar .sidebar-nav > a:not(.brand).active::before {
  content: ""; position: absolute; left: -8px; top: 7px; bottom: 7px; width: 3px;
  border-radius: 0 3px 3px 0; background: #7d96f2;
}

/* Roomier rows in the drawer, where the nav is touched rather than clicked. */
@media (max-width: 991.98px) {
  .sidebar .sidebar-nav > a:not(.brand) { padding: 8px 10px; font-size: 13px; }
  .sidebar .sidebar-nav { gap: 2px; }
}

/* Stroke icons, inheriting the link colour so they follow hover and active. */
.nav-icon {
  width: 15px; height: 15px; flex-shrink: 0; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.6; transition: opacity var(--fast) ease, color var(--fast) ease;
}
.sidebar-nav > a:hover .nav-icon { opacity: 0.9; }
.sidebar-nav > a.active .nav-icon { opacity: 1; color: #93a9f5; }
@media (prefers-reduced-motion: reduce) { .nav-icon { transition: none; } }

/* A boosted navigation says it is working.
 
   `hx-boost` keeps the reader on the page it is leaving until the next one has
   rendered, which is the right behaviour and was also the whole complaint: the
   heaviest page in the app takes the better part of a second to build, and for
   that second nothing anywhere moved. A click that produces no response reads
   as a click that was missed, so it gets made again — which queues a second
   render behind the first and makes the wait longer.
 
   htmx puts `.htmx-request` on the link it is fetching for, so the answer is
   just to style it: the destination lights up the way the active row does, and
   its icon spins. The page is already committed to that row by the time this
   shows, so highlighting it early is honest rather than optimistic. */
.sidebar .sidebar-nav > a:not(.brand).htmx-request {
  background: var(--sidebar-active); color: #fff; cursor: progress;
}
.sidebar-nav > a.htmx-request .nav-icon {
  opacity: 1; color: #93a9f5;
  animation: sh-spin .7s linear infinite;
}
/* The rest of the nav dims, so the one row that is loading is the only one
   drawing the eye. `:has()` and not a class on the nav itself: htmx marks the
   link it is fetching for and nothing above it, which is verifiable in the
   DOM and was worth checking rather than assuming. */
.sidebar-nav:has(> a.htmx-request) > a:not(.brand):not(.htmx-request) { opacity: .45; }
@media (prefers-reduced-motion: reduce) {
  .sidebar-nav > a.htmx-request .nav-icon { animation: none; }
}
.sidebar .section-label {
  font-size: 10px; font-weight: 550; text-transform: uppercase; letter-spacing: 0.09em;
  color: #6c6c78; padding: 16px 10px 5px;
}

@media (min-width: 992px) {
  /* Bootstrap zeroes a responsive offcanvas background at its breakpoint with
     !important, so the rail's own colour has to answer in kind. */
  .shell > .sidebar {
    background-color: var(--sidebar-bg) !important;
    position: sticky; top: var(--appbar-h); align-self: flex-start;
    width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
    height: calc(var(--vh100) - var(--appbar-h));
    /* Bootstrap only sets display:flex on an offcanvas *below* its breakpoint,
       so the rail has to become a column itself for the nav to own the scroll. */
    display: flex; flex-direction: column;
  }
  /* Bootstrap resets .offcanvas-body to flex-grow:0 / overflow-y:visible /
     padding:0 at the breakpoint — restate what the rail actually needs. */
  .shell > .sidebar > .sidebar-nav {
    flex: 1 1 auto; min-height: 0;
    padding: 10px 8px;
    overflow-y: auto;
  }
}

/* ---------- App bar ----------
   One bar at every width: sidebar toggle, inline breadcrumb, then search,
   notifications, theme and profile. Named .appbar, not .navbar, which is a
   Bootstrap component. */
.content { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.appbar {
  position: sticky; top: 0; z-index: 1030;
  display: flex; align-items: center; gap: 10px;
  height: var(--appbar-h); flex-shrink: 0; padding: 0 14px;
  background: var(--bg); border-bottom: 1px solid var(--border);
  color: var(--text);
}
/* Translucent where the browser can blur behind it, so a scrolled table
   passes under the bar rather than being cut off by a wall. Functional, not
   decoration: it is what tells the reader there is more page above. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .appbar {
    background: color-mix(in srgb, var(--bg) 80%, transparent);
    -webkit-backdrop-filter: saturate(1.6) blur(12px);
    backdrop-filter: saturate(1.6) blur(12px);
  }
}
.appbar-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0; padding: 0;
  border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--muted); cursor: pointer;
}
.appbar-toggle { transition: background-color var(--fast) ease, color var(--fast) ease; }
.appbar-toggle:hover { background: var(--hover); color: var(--text); }
.appbar-toggle svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
/* The navbar segment above the sidebar carries the rail's colour, so the two
   read as one block in the top-left corner. */
.appbar-brandzone {
  display: flex; align-items: center; gap: 10px;
  align-self: stretch; flex-shrink: 0;
  width: var(--sidebar-w);
  /* Bleed left, and over the bar's border. Two pixels, not one: the app
     renders under a root zoom, where the 1px border draws 1.11px tall and a
     1px overlap left a hairline of it across the top of the rail. */
  margin: 0 0 -2px -14px;
  padding: 0 12px 2px 14px;
  background: var(--sidebar-bg);
}
.appbar-brandzone .appbar-toggle { color: var(--sidebar-text); }
.appbar-brandzone .appbar-toggle:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }
.appbar-brandzone .brand { color: #fff; }
.appbar-brandzone .brand:hover { color: #fff; }
.appbar-brandzone .brand-name > span { color: #a9bbfb; }
@media (max-width: 991.98px) { .appbar-brandzone { width: auto; } }

.appbar-right { display: flex; align-items: center; gap: 1px; margin-left: auto; }
.appbar-divider { width: 1px; height: 20px; flex-shrink: 0; background: var(--border); margin: 0 8px 0 6px; }

.appbar-icon {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0; padding: 0;
  border: 0; border-radius: 999px;
  background: none; color: var(--muted); cursor: pointer; text-decoration: none;
  transition: background-color var(--fast) ease, color var(--fast) ease;
}
.appbar-icon:hover { background: var(--hover); color: var(--text); }
.appbar-icon svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.appbar-badge {
  position: absolute; top: 2px; right: 2px;
  /* padding stays under the min-width so a single digit renders as a true
     circle; two digits outgrow it and the pill stretches on its own. */
  min-width: 16px; height: 16px; padding: 0 3px;
  border-radius: 999px; background: var(--red); color: #fff;
  font-size: 9.5px; font-weight: 700; line-height: 16px; text-align: center;
  border: 2px solid var(--bg); letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.appbar-badge.blue { background: var(--accent); }
/* Matches the amber dot the routine items carry in the list below, so the
   count and the things it counts agree on how serious they are. */
.appbar-badge.amber { background: var(--amber); }

/* search reveals inline rather than sitting there taking space */
.appbar-find { display: flex; align-items: center; }
.appbar-quick {
  width: 0; opacity: 0; padding: 0; border-width: 0; margin-right: 0;
  transition: width .16s ease, opacity .16s ease, padding .16s ease, margin .16s ease;
}
.appbar-find.open .appbar-quick {
  width: 190px; opacity: 1; padding: 4px 10px; border-width: 1px; margin-right: 4px;
}
/* two classes deep: the generic .input rule is declared later in this file */
.appbar .appbar-quick { background: var(--surface); border-color: var(--border); color: var(--text); }
.appbar .appbar-quick::placeholder { color: var(--faint); }
.appbar .appbar-quick:focus { border-color: var(--accent); outline-color: var(--accent); }
@media (max-width: 575.98px) { .appbar-find.open .appbar-quick { width: 130px; } }
.ico-sun { display: none; }

.appbar-user {
  display: inline-flex; align-items: center; gap: 9px;
  height: 38px; padding: 0 6px 0 10px; margin-left: 0;
  border: 0; border-radius: 999px; background: none; cursor: pointer; color: var(--text);
  transition: background-color var(--fast) ease;
}
.appbar-user:hover { background: var(--hover); }
.appbar-user .who { display: grid; text-align: right; line-height: 1.2; }
.appbar-user .who b { font-size: 12.5px; font-weight: 600; color: var(--text); letter-spacing: -0.015em; }
.appbar-user .who small { font-size: 10px; color: var(--muted); font-weight: 400; margin-top: 1px; }
.appbar-user > svg { width: 11px; height: 11px; fill: none; stroke: var(--faint); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.avatar-round, .avatar {
  width: 30px; height: 30px; border-radius: 999px; flex-shrink: 0;
  background: linear-gradient(180deg, #3b3b44 0%, #232329 100%); color: #fff;
  font-size: 10.5px; font-weight: 600; display: grid; place-items: center; letter-spacing: .04em;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 0 0 1px rgba(16, 16, 20, 0.08);
}
@media (max-width: 767.98px) { .appbar-user .who { display: none; } .appbar-user > svg { display: none; } }

.appbar .dropdown-menu { --bs-dropdown-min-width: 236px; margin-top: 8px; }
.dropdown-head {
  padding: 7px 11px 5px; font-size: 10px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint);
}
.notif {
  display: grid; grid-template-columns: 6px 1fr; gap: 8px; align-items: start;
  padding: 7px 11px; font-size: 11.5px; color: var(--text); text-decoration: none;
}
.notif { border-radius: var(--radius-sm); transition: background-color var(--fast) ease; }
.notif:hover { background: var(--hover); text-decoration: none; color: var(--text); }
.notif i { width: 6px; height: 6px; border-radius: 999px; background: var(--red); margin-top: 4px; }
.notif i.amber { background: var(--amber); }
.notif i.blue { background: var(--accent); }
.notif .when { display: block; color: var(--faint); font-size: 10px; margin-top: 1px; }
/* Unread carries the weight; read items stay legible rather than greyed out.
   A dropdown that dims what you have already read makes the list harder to
   scan for the one you came back for. */
.notif.unread { font-weight: 600; }
.notif.unread i { box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 12%, transparent); }
.notif-more { padding: 4px 11px 7px; font-size: 10.5px; color: var(--faint); }

/* ---------- Scrollbars ---------- */
/* One treatment for every scroll surface, the page included. The default
   Windows bar is 17px of solid grey with arrow buttons at both ends, which on
   a dense admin page is a permanent vertical rule down the right-hand side
   louder than any content next to it.

   Thin, round, no track, no buttons — and now shown only while the pointer is
   over the thing that scrolls.

   Hover-only rather than always-thin, because on these pages the bar was still
   a line drawn beside everything: eleven-column tables, the nav rail, a dialog
   of prose, dropdowns of forty ASINs, and the page itself. It was also not
   telling anyone anything. A bar that is always there says nothing about
   whether there is anywhere to scroll to; the moment it is worth seeing is the
   moment somebody reaches for it.

   The track keeps its width in both states and only the thumb's colour
   changes, so revealing the bar never reflows the content beside it. That is
   why this is done with colour and not with `scrollbar-width: none`.

   Two guards, and neither is optional:

   `@media (hover: hover)` wraps the HIDING, not the showing. A touch device
   never enters a hover state, so inside that query it would keep an invisible
   bar for ever. Outside it, the bar simply stays visible there — which is what
   the platform's own overlay scrollbars do anyway.

   `:focus-within` sits beside every `:hover`. A keyboard reader tabbing into a
   scrollable region never triggers hover, and a scroll position they cannot
   see is a worse outcome for them than a grey line ever was.

   `scrollbar-color` is an inherited property and `:hover` matches every
   ancestor of the pointer, so the elements that light up are exactly the
   scroll containers the pointer is currently inside — which is the behaviour
   wanted, and is why this needs no list of container selectors.

   The transparent border plus `background-clip` is what insets the thumb from
   the edge; a plain narrow bar sits flush and reads as a border. */
:root {
  --thumb: #d4d4d8;
  --thumb-grabbed: #a1a1aa;
}
[data-theme="dark"] {
  --thumb: #34343c;
  --thumb-grabbed: #4a4a54;
}

* { scrollbar-width: thin; scrollbar-color: var(--thumb) transparent; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background-color: var(--thumb); border-radius: 999px;
  border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--thumb-grabbed); }
/* The little square where the two bars meet, which otherwise stays default. */
::-webkit-scrollbar-corner { background: transparent; }
/* The arrow buttons at each end. Windows draws them on an unstyled bar and
   they are the loudest part of it — two grey blocks with triangles, clickable
   targets nobody uses when a wheel and a trackpad exist. */
::-webkit-scrollbar-button { display: none; width: 0; height: 0; }

@media (hover: hover) {
  /* Firefox, and Chrome, through the standard property. */
  * { scrollbar-color: transparent transparent; }
  *:hover, *:focus-within { scrollbar-color: var(--thumb) transparent; }

  /* WebKit ignores that property and paints the pseudo-element instead, so
     the same two states have to be said again in its own vocabulary. */
  ::-webkit-scrollbar-thumb { background-color: transparent; }
  *:hover::-webkit-scrollbar-thumb,
  *:focus-within::-webkit-scrollbar-thumb { background-color: var(--thumb); }
  *:hover::-webkit-scrollbar-thumb:hover,
  *:focus-within::-webkit-scrollbar-thumb:hover {
    background-color: var(--thumb-grabbed);
  }
}

/* The dark rail keeps its own thumb at both themes, or the light one would
   glare on it. Custom properties, so the rules above pick these up in every
   state rather than each state needing its own override. */
.sidebar { --thumb: #2c2c33; --thumb-grabbed: #44444d; }

/* Reserve the gutter so a page that gains a scrollbar mid-render does not
   shift sideways under the reader. */
html { scrollbar-gutter: stable; }


/* ---------- Skeletons ---------- */
/* Laid OVER the region being replaced rather than swapped into it. Two things
   fall out of that and both matter: the old content is still there if the
   request fails, so a dropped connection leaves the page it had instead of a
   frozen skeleton; and the veil inherits the region's current height, so
   nothing jumps when the real content lands. */
/* The veil is a sibling of the region, not a child: htmx replaces the
   region's innerHTML, and a veil inside it would be destroyed by the very
   swap it is covering. The parent carries the positioning context. */
.skel-host { position: relative; }
.skel-veil {
  position: absolute; z-index: 3; background: var(--bg);
  padding: 0; overflow: hidden; border-radius: var(--radius);
}
.skel {
  /* Flat first, for anything without color-mix: a plain grey bar is a fine
     skeleton, and the shimmer is decoration on top of it. */
  background: var(--border);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--border) 55%, transparent) 25%,
    color-mix(in srgb, var(--border) 90%, transparent) 37%,
    color-mix(in srgb, var(--border) 55%, transparent) 63%);
  background-size: 400% 100%;
  animation: sh-skel 1.3s ease-in-out infinite;
  border-radius: 5px;
}
@keyframes sh-skel { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
/* The animation is the only thing saying "still working". Without it the veil
   has to say so in a way that does not move. */
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; opacity: .7; }
  td { transition: none; }
}
.skel-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(156px, 1fr));
              gap: 8px; margin-bottom: 12px; }
.skel-tile { background: var(--surface); border: 1px solid var(--border);
             border-radius: var(--radius); padding: 11px; }
.skel-card { background: var(--surface); border: 1px solid var(--border);
             border-radius: var(--radius); padding: 12px; }
.skel-row { display: flex; gap: 10px; align-items: center; padding: 7px 0;
            border-bottom: 1px solid var(--border); }
.skel-row:last-child { border-bottom: 0; }
/* `data-skeleton="stats"`: a row of figures inside a card. It fills the veil
   with the card's own surface, so the placeholder reads as part of the card
   rather than as a grey box laid on it. */
.skel-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
              gap: 10px 18px; min-height: 100%; padding-top: 2px; background: var(--surface); }
/* The dropdown head carries an action now, so it is a row rather than a label. */
.dropdown-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.dropdown-head form { margin: 0; }
/* A button that has to read as a link: it posts, so it cannot be an <a>. */
.linkish {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-weight: 600; letter-spacing: inherit;
  text-transform: none; color: var(--accent);
}
.linkish:hover { text-decoration: underline; }

/* ---------- Collapsed rail ----------
   The rail opens over the page on hover, so the labels are a glance away
   without pinning the sidebar back open.

   **The page never moves.** The rail is laid out at full width the whole
   time and occupies only `--rail-w` of the row (the negative margin), then
   clipped back to the rail. Hovering animates the clip, not a width: no
   layout runs while it opens, so a page with a 100-row table beside it
   stays smooth, and the content underneath is not reflowed twice per peek.

   **The icons do not move either.** They sit at the rail's centre in both
   states, and each row is capped to an icon-sized pill until the rail opens,
   so the only things in motion are the edge and the labels fading in.

   **Deliberate in, quick out.** A short delay before opening keeps a cursor
   crossing to the page from flashing the panel open; closing starts at once
   and runs faster. Fine pointers only — a touch "hover" is a tap. */
@media (min-width: 992px) {
  :root { --rail-w: 58px; }
  .nav-collapsed .shell > .sidebar {
    width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
    margin-right: calc(var(--rail-w) - var(--sidebar-w));
    z-index: 30;
    clip-path: inset(0 calc(var(--sidebar-w) - var(--rail-w)) 0 0);
    transition: clip-path 160ms var(--ease), box-shadow 160ms ease;
  }
  /* 11px + the row's 10px puts a 15px icon's centre on the rail's. */
  .nav-collapsed .shell > .sidebar > .sidebar-nav { padding: 10px 8px 10px 11px; }
  .nav-collapsed .sidebar-nav > a:not(.brand) {
    padding: 7px 10px; max-width: 36px; overflow: hidden;
    transition: max-width 160ms var(--ease),
                background-color var(--fast) ease, color var(--fast) ease;
  }
  .nav-collapsed .sidebar-nav > a.active::before { left: -11px; }
  .nav-collapsed .nav-label { opacity: 0; white-space: nowrap; transition: opacity 90ms ease; }
  /* Section labels stay hidden open as well: showing them would push every
     row down mid-reveal, and the rows are what the eye is tracking. */
  .nav-collapsed .section-label { display: none; }
  /* The navy block above the rail is clipped the same way, laid out at the
     full sidebar width and holding only the rail's width of the bar — so
     when the panel opens, the block opens with it and the two read as one
     column from the top of the window, the wordmark coming in with them.
     Opened alone, the panel jutted out under the white bar, a notch in the
     corner where the dark column should be. */
  .nav-collapsed .appbar-brandzone {
    position: relative; z-index: 2;
    margin-right: calc(var(--rail-w) - var(--sidebar-w));
    clip-path: inset(0 calc(var(--sidebar-w) - var(--rail-w)) 0 0);
    transition: clip-path 160ms var(--ease), box-shadow 160ms ease;
  }
  .nav-collapsed .appbar-brandzone .brand { opacity: 0; transition: opacity 90ms ease; }
}
/* One condition opens everything: the rail or the block above it hovered,
   or keyboard focus inside either. Moving up from the panel to the toggle
   therefore keeps it open, and the block and the panel cannot disagree. */
@media (min-width: 992px) and (hover: hover) and (pointer: fine) {
  .nav-collapsed:has(.shell > .sidebar:hover, .shell > .sidebar :focus-visible,
                     .appbar-brandzone:hover, .appbar-brandzone :focus-visible)
    :is(.shell > .sidebar, .appbar-brandzone) {
    /* The negative inset lets the shadow show past the panel's edge. */
    clip-path: inset(0 -32px 0 0);
    box-shadow: 10px 0 28px -14px rgba(0, 0, 0, 0.45);
    transition: clip-path 220ms var(--ease) 100ms, box-shadow 220ms ease 100ms;
  }
  .nav-collapsed:has(.shell > .sidebar:hover, .shell > .sidebar :focus-visible,
                     .appbar-brandzone:hover, .appbar-brandzone :focus-visible)
    .sidebar-nav > a:not(.brand) {
    max-width: 100%;
    transition: max-width 220ms var(--ease) 100ms,
                background-color var(--fast) ease, color var(--fast) ease;
  }
  .nav-collapsed:has(.shell > .sidebar:hover, .shell > .sidebar :focus-visible,
                     .appbar-brandzone:hover, .appbar-brandzone :focus-visible)
    :is(.nav-label, .appbar-brandzone .brand) {
    opacity: 1; transition: opacity 160ms ease 160ms;
  }
}
/* Reduced motion: the panel still opens, just without the sweep. The labels
   keep their short fade, which carries meaning and does not move. */
@media (min-width: 992px) and (prefers-reduced-motion: reduce) {
  .nav-collapsed .shell > .sidebar,
  .nav-collapsed .appbar-brandzone,
  .nav-collapsed .sidebar-nav > a:not(.brand),
  .nav-collapsed:has(.shell > .sidebar:hover, .shell > .sidebar :focus-visible,
                     .appbar-brandzone:hover, .appbar-brandzone :focus-visible)
    :is(.shell > .sidebar, .appbar-brandzone, .sidebar-nav > a:not(.brand)) {
    transition: none;
  }
}

.main { flex: 1 1 auto; min-width: 0; width: 100%; padding: 20px 24px 28px; max-width: 1600px; }
@media (max-width: 1199.98px) { .main { padding: 18px; } }
@media (max-width: 767.98px)  { .main { padding: 14px 14px 24px; } }

/* ---------- Breadcrumb (lives in the app bar) ---------- */
.crumbs { min-width: 0; overflow: hidden; margin-left: 10px; }
@media (max-width: 767.98px) { .crumbs { margin-left: 0; } }
.crumbs ol {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 7px;
  list-style: none; font-size: 13px; color: var(--muted); white-space: nowrap;
}
.crumbs li { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
.crumbs li + li::before { content: "\203A"; color: var(--faint); opacity: .85; font-size: 12px; line-height: 1; }
.crumbs a { color: var(--muted); text-decoration: none; border-radius: 3px; }
.crumbs a:hover { color: var(--text); text-decoration: none; }
.crumbs [aria-current="page"] { color: var(--text); font-weight: 550; }
.crumbs .crumb-tab { color: var(--muted); font-weight: 500; }
@media (max-width: 575.98px) {
  .crumbs .crumb-hide { display: none; }
  /* the hidden crumbs still count as siblings, so drop the orphaned separator */
  .crumbs .crumb-hide + li::before { content: none; }
}

.page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; flex-wrap: wrap; gap: 10px; }
.page-header h1 { font-size: 20px; font-weight: 650; line-height: 1.2; letter-spacing: -0.025em; }
.page-header .sub { color: var(--muted); font-size: 12px; margin-top: 3px; }
.page-header.is-stacked { flex-direction: column; align-items: stretch; }
.page-header.is-stacked .controls { justify-content: flex-end; }
@media (max-width: 767.98px) {
  .page-header { flex-direction: column; align-items: stretch; }
  .page-header .controls { width: 100%; }
}

/* ---------- Controls ---------- */
.controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* Pinned to the right corner, also once a long marketplace row makes the
   controls wrap under the page title (the Orders page). */
.controls.is-end { margin-left: auto; justify-content: flex-end; }
/* Three tiers — ink, outline, ghost — and one height. The press feedback is a
   1% scale on :active: small enough to feel like a key going down, never so
   large the label moves under the cursor. */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border); background: var(--surface);
  border-radius: var(--radius-sm); padding: 5px 11px; font-size: 12.5px; font-weight: 500;
  color: var(--text); cursor: pointer; line-height: 1.4; letter-spacing: -0.005em;
  text-decoration: none; white-space: nowrap;
  box-shadow: var(--shadow-xs);
  transition: background-color var(--fast) ease, border-color var(--fast) ease,
              color var(--fast) ease, box-shadow var(--fast) ease, transform var(--fast) var(--ease);
}
.btn:hover { background: var(--hover); border-color: var(--border-strong); color: var(--text); text-decoration: none; }
.btn:active { transform: scale(0.98); }
.btn:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
.btn.primary {
  background: var(--primary); color: #fff; border-color: var(--primary);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10), 0 1px 2px rgba(16, 16, 20, 0.20);
}
.btn.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
.btn.primary:focus-visible { box-shadow: var(--ring); }
.btn.danger { color: var(--red); border-color: color-mix(in srgb, var(--red) 28%, var(--border)); background: var(--surface); }
.btn.danger:hover { background: color-mix(in srgb, var(--red) 6%, var(--surface)); border-color: color-mix(in srgb, var(--red) 45%, var(--border)); color: var(--red); }
.btn.ghost { border-color: transparent; background: transparent; box-shadow: none; }
.btn.ghost:hover { background: var(--hover); border-color: transparent; }
.btn:disabled, .btn.disabled { opacity: 0.45; pointer-events: none; box-shadow: none; }
.btn.sm { padding: 2px 8px; font-size: 11.5px; gap: 4px; }
@media (prefers-reduced-motion: reduce) { .btn { transition: none; } .btn:active { transform: none; } }
/* Button icons: stroked, sized off the label, and never a hit target of their own. */
.btn > svg {
  width: 13px; height: 13px; flex-shrink: 0; pointer-events: none;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.btn.sm > svg { width: 11px; height: 11px; }
/* The incoming page eases in. There is deliberately no fade *out*: emptying
   the page first is what read as a flicker, because the content area
   collapsed while the request was still in flight. */
#pageBody { transition: opacity .1s ease-out; }
#pageBody.htmx-settling { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  #pageBody, #pageBody.htmx-settling { transition: none; opacity: 1; }
}

/* The same easing for a REGION swap, which is most of what this app does.
   A filter click, a sort, a page turn and a tab change all replace a region's
   innerHTML, and until now every one of them landed as an instant hard cut —
   the new rows simply appeared in place of the old ones, which reads as a
   flash rather than as a change.

   Shorter than the page fade (70ms against 100ms) and on the SETTLE only, for
   the reason the comment above gives: emptying first is what reads as a
   flicker. htmx adds `.htmx-settling` to the swap target for one frame after
   the content lands, so this is one repaint's worth of ease-in on content
   that is already there — never a delay before it appears.

   Scoped to the regions this app actually swaps rather than `*`: a blanket
   rule would also catch the out-of-band filter bar and the toast layer, which
   have their own motion and must not acquire a second one. */
#orRegion.htmx-settling, #orTable.htmx-settling,
#prRegion.htmx-settling, #prTable.htmx-settling,
#invRegion.htmx-settling, #invTable.htmx-settling,
#lhRegion.htmx-settling, #lhTable.htmx-settling,
#adRegion.htmx-settling, #adTable.htmx-settling, #adTiles.htmx-settling,
#pbRegion.htmx-settling, #baRegion.htmx-settling {
  opacity: 0;
}
#orRegion, #orTable, #prRegion, #prTable, #invRegion, #invTable,
#lhRegion, #lhTable, #adRegion, #adTable, #adTiles, #pbRegion, #baRegion {
  transition: opacity .07s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  #orRegion, #orTable, #prRegion, #prTable, #invRegion, #invTable,
  #lhRegion, #lhTable, #adRegion, #adTable, #adTiles, #pbRegion, #baRegion,
  #orRegion.htmx-settling, #orTable.htmx-settling,
  #prRegion.htmx-settling, #prTable.htmx-settling,
  #invRegion.htmx-settling, #invTable.htmx-settling,
  #lhRegion.htmx-settling, #lhTable.htmx-settling,
  #adRegion.htmx-settling, #adTable.htmx-settling, #adTiles.htmx-settling,
  #pbRegion.htmx-settling, #baRegion.htmx-settling {
    transition: none; opacity: 1;
  }
}

/* htmx marks the element it is posting from; the icon spins while it waits. */
form.htmx-request .btn, .btn.htmx-request { opacity: 0.6; pointer-events: none; }
form.htmx-request .btn > svg, .btn.htmx-request > svg { animation: sh-spin .7s linear infinite; }
@keyframes sh-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  form.htmx-request .btn > svg, .btn.htmx-request > svg { animation: none; }
}

.select, .input {
  border: 1px solid var(--border); background: var(--surface);
  border-radius: var(--radius-sm); padding: 5px 10px; font-size: 12.5px; color: var(--text);
  max-width: 100%; font-family: inherit; line-height: 1.4;
  box-shadow: var(--shadow-xs);
  transition: border-color var(--fast) ease, box-shadow var(--fast) ease;
}
.select:hover, .input:hover { border-color: var(--border-strong); }
/* A native <select> drawn as the app's own picker: the platform arrow is
   replaced by the same 12px chevron the dropdown pickers carry, so a filter
   bar mixing the two reads as one row of one kind of control. */
select.select {
  -webkit-appearance: none; appearance: none;
  padding-right: 28px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2361616b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 12px 12px;
}
[data-theme="dark"] select.select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23a1a1aa' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
}
.input::placeholder { color: var(--faint); }
.select:focus, .input:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
textarea.input { resize: vertical; }

@media (max-width: 991.98px) {
  .btn, .select, .input { min-height: 32px; }   /* comfortable touch targets */
}
@media (max-width: 575.98px) {
  /* wins over the inline widths the mockups set on their search inputs */
  .controls > .select, .controls > .input { flex: 1 1 100%; width: 100% !important; min-width: 0; }
  .controls > .btn { flex: 1 1 auto; justify-content: center; }
}

/* ---------- Tabs ---------- */
.tabs {
  display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 16px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  padding: 7px 10px 8px; font-size: 13px; color: var(--muted);
  text-decoration: none; border-bottom: 2px solid transparent; white-space: nowrap;
  flex-shrink: 0; font-weight: 500;
  transition: color var(--fast) ease, border-color var(--fast) ease;
}
.tabs a:hover { color: var(--text); text-decoration: none; }
.tabs a.active { color: var(--text); font-weight: 550; border-bottom-color: var(--text); }
/* the tab you just clicked, while its page is on the way */
.tabs a.htmx-request { opacity: .55; cursor: progress; }

/* A tab that switches something in place rather than fetching a page is a
   button, not a link — but it has to look identical, or the same control reads
   as two different ones. The browser's default button chrome is what has to be
   undone here; everything else is inherited from the rule above. */
.tabs button.tab {
  padding: 7px 10px 8px; font-size: 13px; color: var(--muted);
  border: 0; border-bottom: 2px solid transparent; background: none;
  white-space: nowrap; flex-shrink: 0; cursor: pointer; line-height: 1.4;
  font-family: inherit; font-weight: 500;
  transition: color var(--fast) ease, border-color var(--fast) ease;
}
.tabs button.tab:hover { color: var(--text); }
.tabs button.tab.active { color: var(--text); font-weight: 550; border-bottom-color: var(--text); }
.tabs button.tab:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 4px 4px 0 0; }
.tabs button.tab.htmx-request { opacity: .55; cursor: progress; }

/* A page's own tabs, sitting under the section tabs. Same control, quieter, so
   the two rows read as a hierarchy rather than as a repetition. */
/* Drawn as a segmented control: a recessed track with the chosen segment
   lifted out of it. Reads as "pick one of these views", which is what it is. */
.tabs-sub {
  display: inline-flex; border-bottom: 0; gap: 2px; margin: -4px 0 16px; padding: 3px;
  background: color-mix(in srgb, var(--text) 5%, var(--bg)); border-radius: 9px;
  max-width: 100%;
}
.tabs-sub a {
  padding: 4px 11px; margin: 0; font-size: 12px; border-bottom: 0; border-radius: 6px;
  font-weight: 500;
  transition: background-color var(--fast) ease, color var(--fast) ease, box-shadow var(--fast) ease;
}
.tabs-sub a:hover { color: var(--text); }
.tabs-sub a.active {
  background: var(--surface); color: var(--text); font-weight: 550; border-bottom-color: transparent;
  box-shadow: 0 1px 2px rgba(16, 16, 20, 0.08), 0 0 0 1px rgba(16, 16, 20, 0.04);
}

/* ---------- Tiles ---------- */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin-bottom: 16px; }
/* The Dashboard's tiles and the marketplace cards under them share one grid —
   four equal columns, one gap — so each card sits exactly beneath a tile. */
#dashTiles > .tiles, #dashTiles.tiles, .mkt-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 1199.98px) {
  #dashTiles > .tiles, #dashTiles.tiles, .mkt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 575.98px) {
  #dashTiles > .tiles, #dashTiles.tiles, .mkt-grid { grid-template-columns: minmax(0, 1fr); }
}
/* A KPI reads number-first: the figure is the largest thing on the tile, set
   in tabular figures so a row of tiles lines its digits up, and everything
   else steps down from it in size and colour. */
.tile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 14px; min-width: 0;
  box-shadow: var(--shadow-xs);
}
.tile .label { font-size: 11.5px; color: var(--muted); font-weight: 500; letter-spacing: -0.005em; }
.tile .value {
  font-size: 21px; font-weight: 600; margin-top: 5px; line-height: 1.15;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}
.tile .delta { font-size: 11.5px; margin-top: 5px; font-weight: 550; font-variant-numeric: tabular-nums; }
.tile .delta.up { color: var(--green); }
.tile .delta.down { color: var(--red); }
.tile .meta { font-size: 11px; color: var(--faint); margin-top: 3px; line-height: 1.45; }
/* Every tile is the same column — label, figure, change, the two meta lines —
   and its badge row is pushed to the foot, so across a row of six the badges
   sit on one line however much text a tile above them carries. The grid
   already stretches the tiles to one height; this is what lines up inside. */
.tile { display: flex; flex-direction: column; }
.tile .label { display: flex; align-items: center; gap: 6px; min-height: 18px; }
.tile .tile-line, .tile .delta { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tile-others { color: var(--muted) !important; }
.tile-foot { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: auto; padding-top: 8px; min-height: 26px; }

/* Sales by marketplace (Dashboard → Overview): one card per marketplace,
   between the period tiles and the chart. Same surface as a tile, one step
   richer — a country mark, its currency, and a strip of the figures behind
   the headline. */
.mkt { margin: 2px 0 16px; }
.mkt-head { display: flex; align-items: center; gap: 8px; margin: 0 2px 10px; flex-wrap: wrap; }
.mkt-head h2 { font-size: 14px; font-weight: 600; letter-spacing: -0.02em; }
.mkt-hint { margin-left: auto; font-size: 11.5px; color: var(--faint); }
.mkt-grid { display: grid; }
.mkt-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px 14px; box-shadow: var(--shadow-xs); min-width: 0;
  transition: box-shadow var(--base) var(--ease), border-color var(--fast) ease;
}
.mkt-card:hover { box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.mkt-top { display: flex; align-items: center; gap: 10px; }
/* The channel's mark on its own brand ground — Amazon's navy, TikTok's black —
   so the cards are told apart by shape before they are read. The country
   tag sits on the Amazon mark's corner, cut out of the card by a ring of the
   card's own colour. */
.mkt-logo {
  position: relative; width: 36px; height: 36px; flex-shrink: 0; border-radius: 10px;
  display: grid; place-items: center;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10), 0 1px 2px rgba(16, 16, 20, 0.18);
}
.mkt-logo svg { display: block; }
.mkt-logo.is-amazon { background: #131921; }
.mkt-logo.is-amazon svg { width: 30px; height: 30px; }
.mkt-logo.is-tiktok { background: #000; }
.mkt-logo.is-tiktok svg { width: 21px; height: 21px; }
.mkt-logo.is-shopify { background: #5e8e3e; }
.mkt-logo.is-shopify svg { width: 19px; height: 19px; }
.mkt-flag {
  position: absolute; right: -6px; bottom: -5px;
  padding: 0 3px; border-radius: 4px; background: var(--surface); color: var(--text);
  font: 700 8.5px/1.45 var(--font-mono); letter-spacing: 0.03em;
  box-shadow: 0 0 0 2px var(--surface), inset 0 0 0 1px var(--border-strong);
}
.mkt-who { min-width: 0; }
.mkt-name { display: block; font-size: 13.5px; font-weight: 600; color: var(--text); letter-spacing: -0.01em; }
a.mkt-name:hover { color: var(--accent); text-decoration: none; }
.mkt-code { font-size: 11px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mkt-cur {
  margin-left: auto; align-self: flex-start; padding: 1px 6px; border-radius: 6px;
  font: 600 10.5px/1.55 var(--font-mono); color: var(--muted);
  background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--border);
}
.mkt-value {
  margin-top: 14px; font-size: 22px; font-weight: 600; line-height: 1.15;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}
.mkt-delta { margin-top: 4px; font-size: 12px; font-weight: 550; font-variant-numeric: tabular-nums; }
.mkt-delta.up { color: var(--green); }
.mkt-delta.down { color: var(--red); }
.mkt-delta span { color: var(--faint); font-weight: 450; }
.mkt-stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; margin: 14px 0 0; padding-top: 12px;
  border-top: 1px solid color-mix(in srgb, var(--border) 70%, var(--surface));
}
.mkt-stats dt { font-size: 10.5px; font-weight: 550; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.mkt-stats dd { margin: 2px 0 0; font-size: 13px; font-weight: 550; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mkt-stats dd.pos { color: var(--green); }
.mkt-stats dd.neg { color: var(--red); }
/* The Orders page's Returns & refunds cards: the same stat language as the
   market cards, laid out as a row across a full-width card. */
.ret-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px 18px;
  margin: 0 0 14px; padding-bottom: 12px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 70%, var(--surface));
}
.ret-stats dt { font-size: 10.5px; font-weight: 550; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.ret-stats dd { margin: 3px 0 0; font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ret-stats dd.pos { color: var(--green); }
.ret-stats dd.neg { color: var(--red); }
.ret-stats dd .hint { font-weight: 450; }
.ret-stats .ret-reasons { grid-column: span 2; }
.ret-stats .ret-reasons dd { display: flex; flex-wrap: wrap; gap: 2px 12px; font-size: 12px; font-weight: 500; white-space: normal; }
@media (max-width: 560px) { .ret-stats .ret-reasons { grid-column: auto; } }
.mkt-margin { color: var(--faint); font-weight: 450; font-size: 12px; }
.mkt-flags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; padding-top: 12px; min-height: 32px; }
.mkt-card.is-quiet .mkt-value { color: var(--faint); }
.mkt-value.is-none { font-size: 18px; font-weight: 550; letter-spacing: -0.02em; line-height: 25px; }
.mkt-why { font-size: 12px; font-weight: 500; cursor: help; }
.mkt-top .live-badge { margin-left: auto; align-self: flex-start; }
@media (max-width: 575.98px) { .mkt-hint { margin-left: 0; width: 100%; } }
.tile .delta .delta-note { color: var(--faint); font-weight: 450; margin-left: 2px; }
/* A figure read as it happens rather than from a closed day: a small green
   "Live" chip with a breathing dot, so it is never mistaken for a settled
   total. The breathing stops for anyone who has asked for less motion. */
.live-badge {
  display: inline-flex; align-items: center; gap: 4px; margin-left: auto;
  padding: 0 6px; border-radius: 999px; font-size: 10px; font-weight: 600;
  letter-spacing: 0.02em; line-height: 17px; color: var(--green);
  background: color-mix(in srgb, var(--green) 9%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--green) 22%, transparent);
}
.live-badge i {
  width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--green) 45%, transparent);
  animation: sh-live 2s ease-out infinite;
}
@keyframes sh-live { 70%, 100% { box-shadow: 0 0 0 5px transparent; } }
@media (prefers-reduced-motion: reduce) { .live-badge i { animation: none; } }
@media (max-width: 575.98px) {
  .tiles { grid-template-columns: repeat(auto-fit, minmax(136px, 1fr)); gap: 8px; }
  .tile { padding: 10px 11px; }
  .tile .value { font-size: 18px; }
}

/* ---------- Cards ---------- */
.card {
  display: block;                 /* Bootstrap otherwise makes .card a flex column */
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px; margin-bottom: 12px;
  min-width: 0; box-shadow: var(--shadow-xs);
}
.card h2 { font-size: 14px; font-weight: 600; margin-bottom: 10px; letter-spacing: -0.02em; }
.card h3 { font-size: 12.5px; font-weight: 600; margin: 10px 0 6px; }
/* A card heading that owns an action: title on the left, control in the corner.
   Opt-in, so the headings that merely carry a badge keep their inline flow. */
.card h2.card-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.card h2.card-head > .btn { margin-left: auto; }
/* Several controls in that corner instead of one. Each is its own <form>, so
   the group is the flex item and the forms inside it are the buttons' only
   wrapper — no display:contents needed. */
.card h2.card-head > .h2-actions { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.card h2.card-head > .h2-actions form { margin: 0; }
@media (max-width: 767.98px) { .card { padding: 12px; } }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: start; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; align-items: start; }
@media (max-width: 1399.98px) { .grid-3 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 1199.98px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } }

/* ---------- Tables ---------- */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Slimmer than the page's own, and its colour and its hover behaviour come
   from the global rule under Scrollbars. A horizontal bar sits directly under
   the last row of data, so it gets less room than a vertical one. */
.table-wrap::-webkit-scrollbar { height: 7px; }

table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
th {
  text-align: left; color: var(--muted); font-weight: 550; font-size: 10.5px;
  padding: 8px 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
  text-transform: uppercase; letter-spacing: 0.055em;
}
/* Row rules a shade lighter than the head's: the head rule closes a section,
   the row rules only keep a long row on its line. */
td {
  padding: 7px 10px; white-space: nowrap;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 62%, var(--surface));
  transition: background-color var(--fast) ease;
}
tr:hover td { background: color-mix(in srgb, var(--text) 2.5%, var(--surface)); }
tbody tr:last-child td { border-bottom-color: transparent; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.pos { color: var(--green); }
td.neg { color: var(--red); }
td.deduction { color: var(--amber); }        /* always shown with leading minus */
tfoot td { font-weight: 650; border-top: 1px solid var(--border-strong); border-bottom: 0; }
.row-link { cursor: pointer; }
.row-link:hover td { background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }

/* ---------- Badges ---------- */
/* A status, not a button: a tinted chip with a hairline of its own colour,
   squared off slightly so a column of them reads as labels rather than as a
   row of pills asking to be pressed. */
.badge {
  display: inline-block; padding: 1px 7px; border-radius: 6px;
  font-size: 10.5px; font-weight: 550; line-height: 1.5; letter-spacing: 0.005em;
  color: var(--text); background: transparent; white-space: nowrap;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 16%, transparent);
}
.badge.green  { background: #ebf7f0; color: #1a7f4b; }
.badge.red    { background: #fdeeee; color: #c4262e; }
.badge.amber  { background: #fdf4e4; color: #a15207; }
.badge.blue   { background: #eef2fe; color: #3451c4; }
.badge.violet { background: #f3f0fd; color: #5b47ad; }
.badge.gray   { background: #f4f4f5; color: #52525b; }

/* ---------- Fake charts (pure CSS) ---------- */
.chart {
  position: relative; height: 176px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface); overflow: hidden;
  /* Faint horizontal guides at the quarters, drawn by the background rather
     than by markup, so every chart in the app gets them for free. */
  background-image: linear-gradient(to bottom, transparent calc(100% - 1px),
                    color-mix(in srgb, var(--border) 55%, transparent) 0);
  background-size: 100% 25%;
}
.chart .bars { display: flex; align-items: flex-end; gap: 3px; height: 100%; padding: 10px 10px 18px; }
.chart .bar {
  flex: 1; min-width: 3px; border-radius: 3px 3px 1px 1px;
  background: color-mix(in srgb, var(--accent) 30%, var(--surface));
  transition: background-color var(--fast) ease;
}
.chart .bar:hover { background: color-mix(in srgb, var(--accent) 55%, var(--surface)); }
.chart .bar.hot { background: var(--accent); }
.chart .bar.alt { background: color-mix(in srgb, var(--violet) 28%, var(--surface)); }

/* ---------- The P&L tab (Dashboard) ----------
   Three layers: four headline figures on the Overview's four-column grid,
   the allocation bar, and the statement. The bar's colours are the validated
   categorical palette in fixed order — light and dark both pass the CVD and
   normal-vision checks; three light hues sit under 3:1 against the surface,
   so every segment is also labelled in the legend and the statement is the
   bar's table view. */
:root {
  --seg-ads: #2a78d6; --seg-cogs: #eb6834; --seg-promotions: #1baf7a;
  --seg-refunds: #eda100; --seg-fees: #e87ba4; --seg-profit: #008300;
}
[data-theme="dark"] {
  --seg-ads: #3987e5; --seg-cogs: #d95926; --seg-promotions: #199e70;
  --seg-refunds: #c98500; --seg-fees: #d55181; --seg-profit: #008300;
}
.seg-ads { background: var(--seg-ads); }
.seg-cogs { background: var(--seg-cogs); }
.seg-promotions { background: var(--seg-promotions); }
.seg-refunds { background: var(--seg-refunds); }
.seg-fees { background: var(--seg-fees); }
.seg-profit { background: var(--seg-profit); }

.pl-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
@media (max-width: 1199.98px) { .pl-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 575.98px) { .pl-kpis { grid-template-columns: minmax(0, 1fr); } }
.pl-kpi {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; box-shadow: var(--shadow-xs); min-width: 0;
}
.pl-kpi.is-hero {
  border-color: color-mix(in srgb, var(--green) 28%, var(--border));
  background: linear-gradient(180deg, color-mix(in srgb, var(--green) 6%, var(--surface)), var(--surface) 70%);
}
.pl-kpi-label { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; color: var(--muted); }
.pl-kpi-label .badge { margin-left: auto; }
.pl-kpi-value { margin-top: 6px; font-size: 24px; font-weight: 600; letter-spacing: -0.03em; line-height: 1.15; white-space: nowrap; }
.pl-kpi-value.neg { color: var(--red); }
.pl-kpi-sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 7px; font-size: 12px; color: var(--muted); }

.pl-chg {
  display: inline-block; padding: 0 6px; border-radius: 999px;
  font-size: 11px; font-weight: 600; line-height: 1.6; font-variant-numeric: tabular-nums;
}
.pl-chg.good { color: var(--green); background: color-mix(in srgb, var(--green) 10%, var(--surface)); }
.pl-chg.bad  { color: var(--red);   background: color-mix(in srgb, var(--red) 9%, var(--surface)); }

.pl-info {
  display: inline-grid; place-items: center; width: 16px; height: 16px; flex-shrink: 0;
  border-radius: 50%; color: var(--faint); cursor: help; vertical-align: -3px;
  transition: color var(--fast) ease, background-color var(--fast) ease;
}
.pl-info .man-ico { width: 13px; height: 13px; }
.pl-info:hover, .pl-info:focus-visible { color: var(--accent); background: var(--accent-soft); outline: none; }

.pl-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.pl-head h2 { margin: 0; }
.pl-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }

.pl-bar { display: flex; gap: 2px; height: 20px; border-radius: 6px; overflow: hidden; margin: 4px 0 16px; }
.pl-seg { height: 100%; min-width: 3px; transition: opacity var(--fast) ease; }
.pl-bar:hover .pl-seg { opacity: 0.45; }
.pl-bar .pl-seg:hover { opacity: 1; }
.pl-legend {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 190px)); gap: 12px 28px;
}
.pl-legend li { display: grid; grid-template-columns: 10px 1fr auto; column-gap: 8px; align-items: center; min-width: 0; }
.pl-legend i { width: 10px; height: 10px; border-radius: 3px; }
.pl-legend-name { font-size: 12.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pl-legend b { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.pl-legend em { grid-column: 2 / span 2; font-style: normal; font-size: 12px; color: var(--faint); font-variant-numeric: tabular-nums; }
.pl-legend li.is-profit .pl-legend-name { color: var(--text); font-weight: 600; }
.pl-legend li.is-profit b { color: var(--green); }

.pl-table thead th { padding-top: 6px; }
.pl-table th.num, .pl-table td.num { width: 1%; white-space: nowrap; padding-left: 22px; }
.pl-group-head th {
  padding: 18px 10px 6px; border-bottom: 0; background: none;
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.09em; color: var(--faint);
}
.pl-group:first-of-type .pl-group-head th { padding-top: 10px; }
.pl-row th.pl-line {
  padding: 9px 10px; text-transform: none; letter-spacing: -0.005em;
  font-size: 13px; font-weight: 450; color: var(--text); white-space: nowrap;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 62%, var(--surface));
}
.pl-group tr:last-child th.pl-line { border-bottom-color: transparent; }
.pl-line .pl-name { font-weight: 550; }
.pl-table thead .badge { margin-left: 6px; text-transform: none; letter-spacing: 0; vertical-align: 1px; }
.pl-line .pl-hint { margin-left: 6px; font-size: 11.5px; color: var(--faint); font-weight: 400; }
.pl-line .pl-info { margin-left: 4px; }
.pl-line .badge { margin-left: 6px; vertical-align: 1px; }
.pl-row td { padding-top: 9px; padding-bottom: 9px; }
.pl-row .pl-now { font-weight: 550; }
.pl-row .pl-share { color: var(--faint); }
.pl-row .pl-prior { color: var(--muted); }
.pl-row.is-count .pl-name, .pl-row.is-ratio .pl-name, .pl-row.is-credit .pl-name { font-weight: 450; color: var(--muted); }
.pl-row.is-count .pl-now, .pl-row.is-ratio .pl-now { font-weight: 450; }
.pl-credit { color: var(--green); }
.pl-row.is-subtotal th, .pl-row.is-subtotal td { border-top: 1px solid var(--border-strong); }
.pl-row.is-subtotal .pl-name, .pl-row.is-subtotal .pl-now { font-weight: 650; }
.pl-row.is-total th, .pl-row.is-total td {
  background: color-mix(in srgb, var(--green) 6%, var(--surface));
  border-top: 1px solid color-mix(in srgb, var(--green) 25%, var(--border));
  border-bottom: 1px solid color-mix(in srgb, var(--green) 25%, var(--border));
}
.pl-row.is-total .pl-name { font-weight: 650; }
.pl-row.is-total .pl-now { font-size: 14px; font-weight: 650; }
.pl-row.is-total:hover th, .pl-row.is-total:hover td { background: color-mix(in srgb, var(--green) 9%, var(--surface)); }
.pl-table tr:hover th.pl-line { background: color-mix(in srgb, var(--text) 2.5%, var(--surface)); }

.pl-notes { margin-top: 14px; display: grid; gap: 10px; }
.pl-notes p { display: flex; gap: 8px; align-items: flex-start; margin: 0; font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.pl-notes p .man-ico { width: 15px; height: 15px; flex-shrink: 0; margin-top: 2px; color: var(--faint); }
.pl-unsourced { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); padding: 10px 14px; }
.pl-unsourced summary { font-size: 12.5px; font-weight: 600; color: var(--text); cursor: pointer; }
.pl-unsourced summary span { font-weight: 400; color: var(--muted); }
.pl-unsourced ul { list-style: none; margin: 10px 0 2px; padding: 0; display: grid; gap: 8px; }
.pl-unsourced li { display: grid; grid-template-columns: 160px 1fr; gap: 12px; font-size: 12.5px; line-height: 1.55; }
.pl-unsourced li span { color: var(--muted); }
@media (max-width: 767.98px) { .pl-unsourced li { grid-template-columns: 1fr; gap: 2px; } }

/* ---------- The Channels tab (Dashboard) ----------
   Every channel on one row: its mark, its sales and share of sales (within
   its currency), and the figures behind them, with a slim margin meter. */
.ch-table th, .ch-table td { padding-top: 11px; padding-bottom: 11px; }
.ch-table thead th { padding-top: 6px; }
.ch-table th.num, .ch-table td.num { padding-left: 18px; white-space: nowrap; }
.ch-channel { display: flex; align-items: center; gap: 12px; min-width: 220px; }
.ch-logo.mkt-logo { width: 30px; height: 30px; border-radius: 8px; }
.ch-logo.mkt-logo.is-amazon svg { width: 25px; height: 25px; }
.ch-logo.mkt-logo.is-tiktok svg { width: 17px; height: 17px; }
.ch-logo.mkt-logo.is-shopify svg { width: 15px; height: 15px; }
.ch-logo .mkt-flag { right: -6px; bottom: -5px; font-size: 8px; }
.ch-who { display: grid; gap: 1px; min-width: 0; }
.ch-name { font-size: 13px; font-weight: 600; color: var(--text); }
a.ch-name:hover { color: var(--accent); text-decoration: none; }
.ch-code { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--faint); }
.ch-sales { font-weight: 600; }
.ch-sharecol { width: 22%; min-width: 150px; }
.ch-share { display: flex; align-items: center; gap: 10px; }
.ch-share-bar { flex: 1; height: 6px; border-radius: 999px; background: var(--hover); overflow: hidden; }
.ch-share-bar i { display: block; height: 100%; border-radius: 999px; background: var(--accent); }
.ch-table tr.is-tiktok .ch-share-bar i { background: #111114; }
[data-theme="dark"] .ch-table tr.is-tiktok .ch-share-bar i { background: #e4e4e9; }
.ch-table tr.is-shopify .ch-share-bar i { background: #5e8e3e; }
.ch-share b { width: 36px; text-align: right; font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
.ch-why { font-size: 12px; font-weight: 500; color: var(--faint); cursor: help; border-bottom: 1px dotted var(--border-strong); }
.ch-contribution { font-weight: 600; }
.ch-margincol { width: 120px; }
.ch-margin { display: flex; align-items: center; gap: 8px; }
.ch-margin b { width: 38px; text-align: right; font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.ch-margin b.neg { color: var(--red); }
.ch-meter { flex: 1; height: 6px; min-width: 48px; border-radius: 999px; background: var(--hover); overflow: hidden; }
.ch-meter i { display: block; height: 100%; border-radius: 999px; background: color-mix(in srgb, var(--green) 70%, var(--surface)); }
.ch-table td .badge { margin-left: 6px; }
.ch-table tfoot td {
  border-top: 1px solid var(--border-strong); background: var(--surface-2);
  font-weight: 650; padding-top: 12px; padding-bottom: 12px;
}
.ch-channel-total { display: grid !important; gap: 1px; }
.ch-total { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; }
.ch-total .mkt-cur { margin: 0; }
.ch-notes { list-style: none; margin: 14px 0 0; padding: 12px 0 0; display: grid; gap: 6px;
  border-top: 1px solid color-mix(in srgb, var(--border) 60%, var(--surface)); }
.ch-notes li { display: flex; gap: 8px; align-items: flex-start; font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.ch-notes .man-ico { width: 15px; height: 15px; flex-shrink: 0; margin-top: 2px; color: var(--faint); }

/* ---------- The sales chart (Dashboard → Overview) ----------
   Built to the data-viz rules this app follows: one series, so no legend box
   (the title names it); bars capped at 24px with a 4px rounded data-end on
   one baseline and a 2px surface gap between them; solid hairline gridlines
   at round values; the whole column — not just the painted bar — is the
   hover target, and the table view carries every value without hovering. */
.vchart-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px 24px; flex-wrap: wrap; margin-bottom: 16px; }
.vchart-head h2 { display: flex; align-items: center; gap: 8px; margin: 0; }
.vchart-head h2 .live-badge { margin-left: 0; }
.vchart-sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.vchart-sub span { border-bottom: 1px dotted var(--border-strong); cursor: help; }
.vchart-kpis { display: flex; gap: 28px; margin: 0; }
.vchart-kpis dt { font-size: 10.5px; font-weight: 550; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
.vchart-kpis dd { margin: 3px 0 0; font-size: 16px; font-weight: 600; letter-spacing: -0.02em; white-space: nowrap; }
.vchart-kpis dd span { font-size: 11.5px; font-weight: 450; color: var(--faint); letter-spacing: 0; margin-left: 2px; }

.vchart { display: grid; grid-template-columns: 54px minmax(0, 1fr); grid-template-rows: 230px auto; column-gap: 10px; }
.vchart-y { position: relative; }
.vchart-y span {
  position: absolute; right: 0; transform: translateY(50%);
  font-size: 10.5px; font-weight: 500; color: var(--faint); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.vchart-plot { position: relative; }
.vchart-grid { position: absolute; left: 0; right: 0; height: 1px; background: color-mix(in srgb, var(--border) 65%, var(--surface)); }
.vchart-grid:first-child { background: var(--border-strong); }
.vchart-avg { position: absolute; left: 0; right: 0; height: 0; z-index: 2; pointer-events: none;
  border-top: 1.5px solid color-mix(in srgb, var(--accent) 55%, transparent); }
.vchart-avg b {
  position: absolute; right: 0; bottom: 3px; padding: 0 5px; border-radius: 4px;
  font-size: 9.5px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--accent); background: var(--surface);
}
.vchart-cols { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 2px; }
.vchart-col {
  position: relative; display: flex; align-items: flex-end; justify-content: center;
  border-radius: 6px 6px 0 0; transition: background-color var(--fast) ease;
}
.vchart-col:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
.vchart-bar {
  width: min(24px, 72%); min-height: 2px; border-radius: 4px 4px 0 0;
  background: color-mix(in srgb, var(--accent) 34%, var(--surface));
  transition: background-color var(--fast) ease;
}
.vchart-col.is-best .vchart-bar { background: color-mix(in srgb, var(--accent) 62%, var(--surface)); }
.vchart-col:hover .vchart-bar { background: var(--accent); }
/* No report yet: the slot is held, quietly, full height — never a short bar,
   which would read as a bad day. */
.vchart-col.is-missing { background: color-mix(in srgb, var(--text) 3.5%, transparent); }
.vchart-col.is-missing:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }

/* "All" as a graph: one small chart per marketplace, each on its own scale and
   in its own currency -- never one axis for unlike money. Same bar colours as
   the big chart. */
.vmini-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.vmini { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 12px 10px; background: var(--surface); min-width: 0; }
.vmini-head { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 550; min-width: 0; }
.vmini-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vmini-total { margin-left: auto; font-size: 14px; font-weight: 650; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vmini-sub { font-size: 11px; color: var(--faint); margin-top: 2px; text-align: right; }
.vmini-plot { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 1px; height: 72px; margin-top: 8px;
  border-bottom: 1px solid var(--border-strong); }
.vmini-col { display: flex; align-items: flex-end; justify-content: center; border-radius: 3px 3px 0 0; }
.vmini-col:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
.vmini-col i { display: block; width: min(14px, 80%); min-height: 2px; border-radius: 2px 2px 0 0;
  background: color-mix(in srgb, var(--accent) 34%, var(--surface)); }
.vmini-col.is-best i { background: color-mix(in srgb, var(--accent) 62%, var(--surface)); }
.vmini-col:hover i { background: var(--accent); }
.vmini-col.is-missing { background: color-mix(in srgb, var(--text) 3.5%, transparent); }
.vmini-x { display: flex; justify-content: space-between; font-size: 10.5px; color: var(--faint); margin-top: 4px; }
.vmini-empty { height: 72px; margin-top: 8px; display: grid; place-items: center; font-size: 12px; color: var(--faint);
  border: 1px dashed var(--border); border-radius: var(--radius-sm); }
.vchart-x { grid-column: 2; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 2px; margin-top: 7px; }
.vchart-x span { font-size: 10.5px; color: var(--faint); text-align: center; white-space: nowrap; overflow: visible; }

.vchart-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-top: 14px; padding-top: 12px;
  border-top: 1px solid color-mix(in srgb, var(--border) 60%, var(--surface)); font-size: 11.5px; color: var(--muted);
}
.vchart-key { display: inline-flex; align-items: center; gap: 6px; }
.vchart-key i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; }
.vchart-key i.is-missing { background: color-mix(in srgb, var(--text) 7%, var(--surface)); }
.vchart-key i.is-avg { width: 14px; height: 0; border-radius: 0; border-top: 1.5px solid color-mix(in srgb, var(--accent) 55%, transparent); }
.vchart-note { color: var(--faint); }
/* Graph / Table: a segmented control, the chosen side lifted out of a
   recessed track — the same shape as the page's sub-tabs. */
.vchart-head-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  /* Pinned to the right corner, also when the marketplace row is too long to
     sit beside the title and wraps onto a line of its own. */
  margin-left: auto; justify-content: flex-end; }
.view-toggle {
  display: inline-flex; gap: 2px; padding: 3px; border-radius: 9px; flex-shrink: 0;
  background: color-mix(in srgb, var(--text) 5%, var(--bg));
}
.view-toggle button {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border: 0; border-radius: 6px; background: transparent;
  font: 500 12px/1.3 var(--font-sans); color: var(--muted); cursor: pointer;
  transition: background-color var(--fast) ease, color var(--fast) ease, box-shadow var(--fast) ease;
}
.view-toggle button .man-ico { width: 14px; height: 14px; }
.view-toggle button:hover { color: var(--text); }
.view-toggle button[aria-selected="true"] {
  background: var(--surface); color: var(--text); font-weight: 550;
  box-shadow: 0 1px 2px rgba(16, 16, 20, 0.08), 0 0 0 1px rgba(16, 16, 20, 0.04);
}
.view-toggle button:focus-visible { outline: none; box-shadow: var(--ring); }
[data-theme="dark"] .view-toggle { background: #18181c; }
[data-theme="dark"] .view-toggle button[aria-selected="true"] { background: #26262c; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); }

/* The marketplace switch: the same segmented control, each side carrying a
   miniature of the channel's mark on its brand ground, so US, Canada, Mexico
   and TikTok are told apart before the label is read. */
.market-toggle button { padding-left: 6px; gap: 7px; }
/* Nineteen buttons: where the row is wider than the space it has, it scrolls
   sideways rather than pushing the page wider. No effect when it fits. */
.market-toggle { max-width: 100%; overflow-x: auto; scrollbar-width: thin; }
.mt-logo {
  width: 18px; height: 18px; flex-shrink: 0; border-radius: 5px;
  display: grid; place-items: center; opacity: 0.72;
  transition: opacity var(--fast) ease;
}
.mt-logo svg { display: block; }
.mt-logo.is-amazon { background: #131921; }
.mt-logo.is-amazon svg { width: 15px; height: 15px; }
.mt-logo.is-tiktok { background: #000; }
.mt-logo.is-tiktok svg { width: 11px; height: 11px; }
.mt-logo.is-walmart { background: #0071ce; }
.mt-logo.is-walmart svg { width: 14px; height: 14px; }
.mt-logo.is-shopify { background: #5e8e3e; }
.mt-logo.is-shopify svg { width: 13px; height: 13px; }
.mt-logo.is-noon { background: #feee00; }
.mt-logo.is-noon svg { width: 14px; height: 14px; }
/* A marketplace nothing sells in yet: still a button, but a quieter one. */
.market-toggle button.is-empty { color: var(--faint); }
.market-toggle button.is-empty .mt-logo { opacity: 0.4; }
.market-toggle button.is-empty[aria-selected="true"] { color: var(--text); }
.market-toggle button.is-empty[aria-selected="true"] .mt-logo { opacity: 1; }
.market-toggle button:hover .mt-logo,
.market-toggle button[aria-selected="true"] .mt-logo { opacity: 1; }
.market-toggle button span:last-child { font-variant-numeric: tabular-nums; }
[data-theme="dark"] .mt-logo { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14); }
/* A hairline between the Amazon marketplaces and another channel. */
.market-toggle .mt-sep { width: 1px; margin: 5px 3px; background: color-mix(in srgb, var(--text) 14%, transparent); }
/* The channel badge on a TikTok order row, carrying the channel's mark. */
.or-tt-badge { display: inline-flex; align-items: center; gap: 5px; }
.or-tt-badge .mt-logo { width: 14px; height: 14px; border-radius: 4px; opacity: 1; }
.or-tt-badge .mt-logo svg { width: 9px; height: 9px; }

/* Whose series this is, and its figures: a quiet strip above the plot. */
.vchart-market {
  --strip: color-mix(in srgb, var(--text) 2.5%, var(--surface));
  display: flex; align-items: center; justify-content: space-between; gap: 14px 28px; flex-wrap: wrap;
  margin-bottom: 18px; padding: 12px 16px; border-radius: 12px;
  background: var(--strip); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border) 70%, transparent);
}
.vchart-market .mkt-flag { background: var(--strip); box-shadow: 0 0 0 2px var(--strip), inset 0 0 0 1px var(--border-strong); }
.vchart-who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.vchart-who-name { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; color: var(--text); }
.vchart-who-sub { display: flex; align-items: center; gap: 6px; margin-top: 2px; font-size: 11px; color: var(--faint); }
.vchart-who-sub .mkt-cur { margin-left: 0; align-self: auto; }
.vchart-market .vchart-kpis > div + div { padding-left: 28px; border-left: 1px solid color-mix(in srgb, var(--border) 80%, transparent); }
.vchart-market .vchart-kpis { gap: 28px; }

/* The table view: the same days, with a slim inline bar so the shape of
   the period is still visible in a table. */
.vtable-wrap { max-height: 420px; overflow-y: auto; }
.vtable thead th { position: sticky; top: 0; z-index: 1; background: var(--surface); }
.vtable-barcol { width: 28%; min-width: 120px; padding-left: 4px !important; }
.vtable-bar { display: block; height: 8px; border-radius: 0 4px 4px 0; background: color-mix(in srgb, var(--accent) 34%, var(--surface)); }
.vtable tr.is-best .vtable-bar { background: color-mix(in srgb, var(--accent) 62%, var(--surface)); }
.vtable tr.is-missing td:first-child { color: var(--faint); }
.vtable tfoot td { font-weight: 650; }
.vtable .badge { margin-left: 4px; }
@media (max-width: 767.98px) {
  .vchart { grid-template-columns: 42px minmax(0, 1fr); grid-template-rows: 180px auto; column-gap: 6px; }
  .vchart-kpis { gap: 16px; flex-wrap: wrap; }
  .vchart-market .vchart-kpis > div + div { padding-left: 0; border-left: 0; }
  .vchart-head-right { width: 100%; }
  .market-toggle { overflow-x: auto; max-width: 100%; }
}

/* A day the report never landed on. NOT a short bar: a bar's height encodes a
   value, and drawing an unknown day as a 2%-high sliver made it read as "we
   sold almost nothing" — or, at that size, as nothing at all, so the legend
   promised a mark the chart never showed.

   A full-height dashed outline with no fill is the honest shape. It occupies
   the day's slot, it is clearly not a measurement, and it cannot be misread as
   a small number. The height is forced here rather than in the template so the
   inline `height:N%` a real bar carries is overridden for exactly these. */
.chart .bar.nodata {
  height: 100% !important;
  background: transparent;
  border: 1px dashed var(--border);
  border-bottom: 0;
  border-radius: 2px 2px 0 0;
  opacity: .85;
}
[data-theme="dark"] .chart .bar.nodata { border-color: #2e2e36; }
.chart .bar.pos { background: color-mix(in srgb, var(--green) 45%, var(--surface)); }
.chart .bar.neg { background: color-mix(in srgb, var(--red) 40%, var(--surface)); }
.chart .x-label {
  position: absolute; bottom: 3px; left: 0; right: 0;
  display: flex; justify-content: space-between; padding: 0 8px;
  font-size: 9.5px; color: var(--faint);
}
.legend { display: flex; gap: 10px; font-size: 11px; color: var(--muted); margin: 6px 2px; flex-wrap: wrap; }
.legend .dot { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 4px; }
@media (max-width: 575.98px) {
  .chart { height: 136px; }
  .chart .bars { gap: 1.5px; padding: 7px 6px 16px; }
}

/* sparkline-ish inline bar */
.spark { display: inline-flex; align-items: flex-end; gap: 1px; height: 14px; vertical-align: middle; }
.spark i { width: 3px; background: color-mix(in srgb, var(--accent) 50%, var(--surface)); border-radius: 1px; display: block; }

/* ---------- Misc ---------- */
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 2px 12px; font-size: 12px; }
.kv dt { color: var(--muted); }
.kv dd { font-weight: 500; }
@media (max-width: 575.98px) {
  .kv { grid-template-columns: 1fr; gap: 0; }
  .kv dd { margin-bottom: 6px; }
}

/* Notes explain; they should not shout. The info note is a whisper of the
   accent rather than the sky-blue panel it was, which on the Dashboard was the
   loudest thing on the page while saying the least urgent thing on it. The
   warning and success notes keep their stronger tints because they DO carry
   urgency. */
.note {
  background: color-mix(in srgb, var(--accent) 4%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 16%, var(--border));
  color: color-mix(in srgb, var(--accent) 38%, var(--text));
  border-radius: var(--radius-sm); padding: 9px 12px; font-size: 12px; line-height: 1.55;
  margin: 10px 0 0;
}
.warn-note { background: #fdf6ea; border-color: #f1d9ab; color: #864805; }
.ok-note   { background: #eef8f2; border-color: #c3e5d1; color: #1b6a41; }
.note + .note { margin-top: 8px; }
.mono { font-family: var(--font-mono); font-size: 11px; letter-spacing: -0.01em; }

/* ---------- Text roles ----------

   Three class names carry the app's most important visual convention, and
   until now none of them had a rule: 135 `.nodata`, 151 `.hint` and 108
   `span.deduction` elements rendered in full body colour.

   `.nodata` is the one that matters. Every module writes an absent figure as
   `<span class="nodata">&mdash;</span>` on a rule stated in half a dozen
   docstrings: a number we do not have must never read as a measurement.
   Unstyled, that em dash sat at the same weight and colour as a real value, so
   the discipline existed in the code and was invisible on the page. `--faint`
   is the token the tile `.meta` already uses for "true, and not the point".

   Deliberately NOT `opacity`: these sit inside `td.pos`/`td.neg`/`.deduction`
   cells, and a faded green dash still reads as a positive number. A flat
   colour on the span beats the cell's own, which is exactly right — a dash has
   no sign. */
.nodata { color: var(--faint); font-weight: 400; }
/* A `title` on one of these is an explanation of WHY the figure is missing,
   which is the most useful thing on the row. Say so with the cursor. */
.nodata[title] { cursor: help; }

/* Secondary explanatory text: "12 of 900 rows match", "as at 4 Mar 2026",
   the sentence under a control. `.form-row .hint` below stays smaller and
   fainter and still wins on specificity — a field's helper text is quieter
   than a line of page copy. */
.hint { font-size: 11px; color: var(--muted); line-height: 1.5; }
.muted { color: var(--muted); }

/* Money coming OUT, always rendered with a leading minus. `td.deduction`
   already covered the cell form; this covers the span form the Orders and
   Inventory tables use inside a cell that also holds a badge. */
.deduction { color: var(--amber); }

/* meter reused across the newer pages */
.meter { height: 6px; background: var(--hover); border-radius: 999px; overflow: hidden; min-width: 54px; }
.meter > span { display: block; height: 100%; background: #7d96f2; border-radius: 999px; }
.meter > span.warn { background: #eeaa3c; }
.meter > span.bad  { background: #e8646a; }
.meter > span.good { background: #3db178; }

details > summary { cursor: pointer; font-size: 12px; }
details > summary::-webkit-details-marker { color: var(--faint); }

/* ---------- Prose (.doc-body) ----------
   Rendered markdown, wherever it lands: the playbook's rulebooks and the
   briefing dialog's answer both come out of the same renderer, so they share
   one scale. It lived in the playbook page's own <style>, which is why the
   briefing rendered at browser defaults — 32px headings and 16px paragraphs
   inside a 12px dialog.

   The scale separates sections rather than decorating them: headings get
   space above and little below, so each reads as attached to what follows. */
.doc-body { overflow-wrap: anywhere; }
.doc-body h1, .doc-body h2 { font-size: 13.5px; font-weight: 650; margin: 20px 0 7px;
                             letter-spacing: -0.01em; }
.doc-body h1:first-child, .doc-body h2:first-child, .doc-body h3:first-child { margin-top: 0; }
.doc-body h3 { font-size: 12.5px; font-weight: 650; margin: 16px 0 5px; }
.doc-body h4 { font-size: 12px; font-weight: 600; margin: 13px 0 4px; color: var(--muted); }
.doc-body p { font-size: 12px; line-height: 1.7; margin: 0 0 9px; }
.doc-body li { font-size: 12px; line-height: 1.7; margin-bottom: 3px; }
.doc-body ul, .doc-body ol { padding-left: 19px; margin: 7px 0 11px; }
.doc-body li > ul, .doc-body li > ol { margin: 3px 0 2px; }
.doc-body > :last-child { margin-bottom: 0; }
.doc-body strong { font-weight: 650; }
.doc-body a { text-decoration: none;
              border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
.doc-body a:hover { border-bottom-color: var(--accent); }

/* Inline code is a word inside a sentence, so it stays on the line: no
   border, minimal padding, and a size close enough to the prose that a
   paragraph containing three of them does not go lumpy. */
.doc-body code { font-size: 11px; background: var(--hover); padding: 1px 4px;
                 border-radius: 3px; font-variant-ligatures: none; }
[data-theme="dark"] .doc-body code { background: #1e1e24; }

/* A block is a different thing: it gets a panel, and it scrolls rather than
   wraps, because a wrapped command is a command that does not run. */
.doc-body pre { background: var(--surface-2); border: 1px solid var(--border);
                border-radius: var(--radius-sm); padding: 9px 11px;
                margin: 8px 0 12px; font-size: 11px; line-height: 1.6; }
.doc-body pre code { background: none; padding: 0; border-radius: 0; font-size: inherit;
                     white-space: pre; overflow-wrap: normal; }
[data-theme="dark"] .doc-body pre { background: #0f0f12; }

.doc-body blockquote { margin: 9px 0 12px; padding: 2px 0 2px 11px;
                       border-left: 2px solid var(--border); color: var(--muted); }
.doc-body blockquote p:last-child { margin-bottom: 0; }
.doc-body hr { border: 0; border-top: 1px solid var(--border); margin: 16px 0; }

.doc-body table { font-size: 11.5px; border-collapse: collapse; margin: 8px 0 12px; }
.doc-body th, .doc-body td { padding: 5px 9px; border: 1px solid var(--border);
                             text-align: left; vertical-align: top; }
.doc-body th { background: var(--surface-2); font-weight: 650; }
[data-theme="dark"] .doc-body th { background: #18181c; }

/* Code and tables are the two things that must NOT wrap — a broken line of
   code is a lie about the code — so they scroll inside themselves instead.
   Their bar is the shared one, under Scrollbars. */
.doc-body pre, .doc-body table { display: block; max-width: 100%; overflow-x: auto; }

.doc-body img { max-width: 100%; height: auto; }
.doc-body .hl { background: #fef08a; border-radius: 2px; }
[data-theme="dark"] .doc-body .hl { background: #78350f; color: #fde68a; }

/* ---------- Bootstrap components, dressed as the mockup ---------- */
.modal { --bs-modal-border-radius: var(--radius-lg); --bs-modal-border-color: var(--border); }
/* The grey wash behind a dialog is gone. It was not doing the job it looks
   like it was doing.
 
   `:root` carries `zoom: var(--zoom)`, and Bootstrap sizes both the modal container
   and its backdrop off the viewport — `width: 100%; height: 100%` on the one,
   `100vw`/`100vh` on the other. A fixed element under a root zoom resolves
   those against a viewport that has already been scaled, so both came out at
   the zoom's share of the real one: the dim covered the top-left four fifths of the screen
   and stopped, with a hard vertical edge down the right and a horizontal one
   across the bottom. On a wide monitor that read as a grey rectangle laid over
   part of the page — which is exactly what it was.
 
   The fix is the sizing, not the dim: both are sized with `--vw100`/
   `--vh100`, which divide the zoom back out, so the backdrop now covers the
   real viewport and the scrim below is drawn on it again. The container is
   also what scroll-centres a tall dialog, which at the zoom's share of the
   height centred against the wrong number and could clip. */
.modal, .modal-backdrop { width: var(--vw100); height: var(--vh100); }
/* A soft scrim is back now that the backdrop is sized off `--vw100`/`--vh100`
   (which divide the zoom back out) and so reaches every edge of the screen.
   Ink, not black, and light: enough to push the page back a plane, not
   enough to make it unreadable — the row you opened is still visible
   behind the dialog it opened. */
.modal-backdrop { --bs-backdrop-bg: #0b0b0e; --bs-backdrop-opacity: 0.28; }
[data-theme="dark"] .modal-backdrop { --bs-backdrop-opacity: 0.55; }
.modal-content {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
[data-theme="dark"] .modal-content {
  box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.04);
}
/* The entrance. Bootstrap slides a dialog 50px down over 300ms on a linear-ish
   curve, which reads as a panel falling into place. A 2% grow on the app's own
   curve reads as one appearing where it was always going to be. */
.modal.fade .modal-dialog {
  transform: translateY(6px) scale(0.98);
  transition: transform var(--base) var(--ease), opacity var(--base) var(--ease);
  opacity: 0;
}
.modal.show .modal-dialog { transform: none; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .modal.fade .modal-dialog { transform: none; transition: opacity var(--fast) linear; }
}
.modal-header { padding: 14px 16px 12px; border-bottom: 1px solid var(--border); align-items: flex-start; }
.modal-title { font-size: 14.5px; font-weight: 600; line-height: 1.3; letter-spacing: -0.02em; }
.modal-title .sub { display: block; font-size: 11.5px; font-weight: 400; color: var(--muted); margin-top: 3px; letter-spacing: 0; }
.modal-body { padding: 14px 16px; font-size: 12.5px; }
.modal-body > :first-child { margin-top: 0; }
.modal-body h3 { font-size: 12.5px; font-weight: 600; margin: 12px 0 6px; }
.modal-footer {
  padding: 12px 16px; border-top: 1px solid var(--border); gap: 8px;
  background: var(--surface-2);
  border-bottom-left-radius: var(--radius-lg); border-bottom-right-radius: var(--radius-lg);
}
/* Dialogs are deliberately NOT .modal-dialog-scrollable. That class caps
   .modal-content with max-height + overflow:hidden, which is the one thing
   able to cut the footer off when the body is tall. Without it the dialog
   grows to its content and .modal (overflow-y:auto) scrolls the whole thing,
   so the buttons can never be clipped. */
.modal { --bs-modal-margin: 0.75rem; }
@media (min-width: 576px) { .modal { --bs-modal-margin: 1.75rem; } }
/* nothing between the dialog and the viewport may clip it */
.modal-content { max-height: none; overflow: visible; }
.modal-body { overflow: visible; }
.modal-footer > * { margin: 0; }
.modal-footer .spacer { margin-right: auto; font-size: 11px; color: var(--muted); }
/* Bootstrap sizes .btn-close off the inherited font-size, which left a glyph
   larger than the title it sits beside and no real hit area. Give it the same
   treatment the sidebar close already had: a 28px target, a small mark, and a
   hover state that matches the app's other icon buttons. */
.modal .btn-close {
  --bs-btn-close-focus-shadow: none;
  --bs-btn-close-opacity: 0.5;
  --bs-btn-close-hover-opacity: 1;
  width: 28px; height: 28px; padding: 0; flex-shrink: 0;
  box-sizing: border-box; border-radius: var(--radius-sm);
  background-size: 11px 11px; background-position: center;
  /* margin-left:auto is what pushes it into the corner — Bootstrap sets it,
     so the shorthand has to keep it rather than reset it to 0. */
  margin: -1px -4px 0 auto;
  transition: background-color .12s ease, opacity .12s ease;
}
.modal .btn-close:hover { background-color: var(--hover); }
.modal .btn-close:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; opacity: 1;
}
@media (prefers-reduced-motion: reduce) { .modal .btn-close { transition: none; } }
.modal-body .kv { font-size: 11.5px; }

.form-row { display: grid; gap: 5px; margin-bottom: 12px; }
.form-row > label { font-size: 11.5px; color: var(--text); font-weight: 550; }
.form-row .hint { font-size: 11px; color: var(--faint); }
.form-row .input, .form-row .select { width: 100%; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
/* Rows in a two-column grid are as tall as the taller cell, and a .form-row is
   itself a grid — left alone, the shorter side stretches its input to fill,
   which is why a field beside one with a three-line hint came out taller and
   sitting lower than its neighbour. */
.form-grid > .form-row { align-content: start; }
@media (max-width: 575.98px) { .form-grid { grid-template-columns: 1fr; } }

.form-check { min-height: auto; margin-bottom: 4px; }
.form-check + .form-check { margin-top: 6px; }
.modal-body .form-grid + .form-check { margin-top: 12px; }
.form-check-label { cursor: pointer; }
/* Bootstrap's 2.5em gutter lands exactly on the 30px track, leaving the label
   touching it — give the text its own breathing room. */
.form-switch { padding-left: 40px; }
.form-switch .form-check-input {
  width: 30px; height: 17px; margin-top: 0.05rem; margin-left: -40px;
  background-color: var(--border-strong); border-color: var(--border-strong);
  transition: background-position var(--base) var(--ease), background-color var(--fast) ease;
}
.form-check-input:checked { background-color: var(--green); border-color: var(--green); }
.form-check-input:focus { border-color: var(--accent); box-shadow: var(--ring); }
.form-check-label { font-size: 12px; }

.dropdown-menu {
  --bs-dropdown-font-size: 12.5px;
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-border-radius: var(--radius);
  --bs-dropdown-link-hover-bg: var(--hover);
  --bs-dropdown-link-active-bg: var(--hover);
  --bs-dropdown-link-active-color: var(--text);
  --bs-dropdown-padding-y: 4px;
  --bs-dropdown-padding-x: 4px;
  --bs-dropdown-item-padding-x: 9px;
  --bs-dropdown-item-padding-y: 5px;
  box-shadow: var(--shadow-md);
}
/* Menus fade up into place rather than blinking on. Bootstrap toggles them
   with display, so this is an animation on show, not a transition. */
.dropdown-menu.show { animation: sh-menu-in var(--base) var(--ease); }
@keyframes sh-menu-in { from { opacity: 0; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .dropdown-menu.show { animation: none; } }
.dropdown-item { border-radius: 6px; transition: background-color var(--fast) ease; }
.dropdown-menu .dropdown-item:hover { text-decoration: none; }
/* The chosen row. Bootstrap's own .active paints a solid blue block, which is
   the system-menu look these dropdowns replaced. A tick and a weight say the
   same thing without shouting. */
.dropdown-menu .dropdown-item.active,
.dropdown-menu .dropdown-item:active {
  background: var(--bs-dropdown-link-hover-bg); color: var(--text); font-weight: 600;
}
.dropdown-menu .dropdown-item.active::after {
  content: '\2713'; float: right; color: var(--accent); font-weight: 700;
}
/* A long list scrolls rather than running off the viewport. */
.dropdown-menu.scrolls { max-height: 300px; overflow-y: auto; }

/* The filter box pins to the top of the menu it filters, so it stays reachable
   once the list below it is scrolled. */
.dropdown-search {
  position: sticky; top: 0; z-index: 1; padding: 6px 8px 7px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.dropdown-search .input { width: 100%; height: 28px; font-size: 12px; box-shadow: none; }
.dropdown-empty { padding: 9px 11px; font-size: 11px; color: var(--faint); }

/* A two-line item: what it is, then what identifies it. The second line is
   where an ASIN belongs once a product has a name — dropped into the first it
   makes every row read as a code. */
.dropdown-menu .dropdown-item .dd-main { display: block; }
.dropdown-menu .dropdown-item .dd-meta {
  display: block; font-size: 10px; color: var(--faint); margin-top: 1px;
}
.dropdown-menu .dropdown-item.active .dd-meta { color: var(--muted); }
/* The tick sits against the first line rather than centred over both. */
.dropdown-menu .dropdown-item.active::after { float: right; margin-top: 1px; }

/* A picker reads as a field, not as a button: it shows a current value, so it
   gets the value on the left and the chevron pinned right, the way a select
   did. Sized to the widest label so switching period does not resize it. */
.btn.picker { justify-content: space-between; gap: 8px; min-width: 132px; font-weight: 500; }
.btn.picker > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn.picker > svg { width: 12px; height: 12px; flex-shrink: 0; opacity: .65; }
.ba-controls { display: contents; }

/* ---------- Prototype behaviour (app.js) ---------- */
[data-panel][hidden] { display: none !important; }

th.sortable { cursor: pointer; user-select: none; position: relative; padding-right: 15px; }
th.sortable:hover { color: var(--text); }
th.sortable::after {
  content: "↕"; position: absolute; right: 4px; opacity: 0.28; font-size: 9px; top: 50%;
  transform: translateY(-50%);
}
th.sortable[data-dir="asc"]::after  { content: "↑"; opacity: 1; color: var(--accent); }
th.sortable[data-dir="desc"]::after { content: "↓"; opacity: 1; color: var(--accent); }
/* A server-sorted header is a real link, so it carries the whole cell as its
   hit area and inherits the heading's own colour rather than the link blue. */
th.sortable > a {
  display: block; color: inherit; text-decoration: none;
  font: inherit; letter-spacing: inherit;
}
th.sortable > a:hover { color: var(--accent); text-decoration: none; }
th.sortable > a:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
th.num.sortable > a { text-align: right; }
/* The server-sorted headers on Products, Inventory and Listings are plain
   `th > a`, without `.sortable`, and were painting in link blue — a row of
   headers half blue and half grey reads as two different kinds of column. A
   header that sorts looks like a header until the pointer finds it. */
th > a { color: inherit; text-decoration: none; }
th > a:hover { color: var(--text); text-decoration: none; }

tr.is-off td { opacity: 0.5; }

.toast-host {
  position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%);
  z-index: 1080; display: flex; flex-direction: column; gap: 6px;
  align-items: center; pointer-events: none; width: max-content; max-width: calc(var(--vw100) - 24px);
}
.toast-item {
  background: #111114; color: #fff; border-radius: 999px;
  padding: 7px 15px; font-size: 12px; font-weight: 500;
  box-shadow: 0 12px 28px -6px rgba(16, 16, 20, 0.35), inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  opacity: 0; transform: translateY(8px) scale(0.98);
  transition: opacity var(--base) var(--ease), transform var(--base) var(--ease);
  max-width: 100%; text-align: center;
}
.toast-item.in { opacity: 1; transform: none; }
.toast-item { cursor: pointer; }
.toast-item.ok   { background: var(--green); }
.toast-item.warn { background: var(--amber); }
.toast-item.err  { background: var(--red); }
@media (prefers-reduced-motion: reduce) { .toast-item { transition: none; } }

/* ---------- Utility ---------- */
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wrap-cell { white-space: normal; min-width: 180px; }

/* A product thumbnail in a table cell.
   Fixed box, `contain`, and a neutral ground: Amazon serves these at whatever
   aspect the photographer shot, so `cover` would crop a tall bottle to a
   square and `contain` alone would let each row set its own height and make
   the column ragged. The ground is what a transparent PNG sits on.
   `.thumb-none` is the same box with nothing in it, so a row whose catalog
   read has not run yet keeps the column aligned instead of collapsing it. */
.thumb, .thumb-none {
  width: 40px; height: 40px; flex: 0 0 40px; border-radius: 5px;
  border: 1px solid var(--border); box-sizing: border-box;
}
/* White, and hardcoded rather than `var(--surface)`, in both themes. Amazon
   shoots catalog images on white and serves many of them as transparent PNGs,
   so a dark ground puts a black-on-transparent product label against black.
   The frame is what separates it from the page in dark mode. */
.thumb { object-fit: contain; display: block; background: #fff; }
.thumb-none {
  display: grid; place-items: center; background: var(--bg);
  color: var(--faint); font-size: 15px; line-height: 1;
}

/* ---------- Settings: people, departments, roles ---------- */
.flash { display: grid; gap: 6px; margin-bottom: 10px; }
.flash .note { margin: 0; }
.err-note { background: #fdf0f0; border-color: #f3c6c8; color: #b0262d; }

.person { display: flex; align-items: center; gap: 8px; min-width: 0; }
.person .avatar { width: 26px; height: 26px; font-size: 9.5px; }
.person .who-name { font-weight: 600; }
.person .who-mail { font-size: 10.5px; color: var(--faint); }

/* Two controls that answer one question, kept on one line so they read as a
   single field rather than two unrelated ones. */
.field-pair { display: flex; gap: 6px; align-items: center; }
.field-pair > .select { flex: 0 0 auto; width: auto; }
.field-pair > .input { flex: 1 1 auto; min-width: 0; }

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

/* ---- One height for every control in a filter bar ----
   `.btn`, `.input` and `.btn.picker` each end up 27px tall from the same
   padding, font size and line height — which is agreement by coincidence, not
   by rule. Any one of them changing its padding tilted the row, and two of
   them already disagreed: `.btn.sm` (the Clear link) is built at 11px/1px and
   sat visibly shorter than its neighbours, and a native `<select>` adds its
   own platform metrics on top.

   So the height is stated once, here, and every control in the bar inherits
   it. 30px because that is what the tallest of them already measured; the
   padding stays for the horizontal rhythm and stops mattering vertically. */
.query-filters { --control-h: 30px; }
.query-filters > .btn,
.query-filters > .input,
.query-filters > .select,
.query-filters > .dropdown > .btn {
  min-height: var(--control-h);
}
/* The Clear link keeps its quieter type — it is not a peer of the pickers —
   but not its smaller box, which was the part that read as misalignment. */
.query-filters > .btn.sm { padding-top: 0; padding-bottom: 0; }

/* A picker sized to its label, not to a fixed 132px.
   `space-between` inside a min-width that nothing fills pushed the chevron to
   the far edge and left "All products" floating in a box half again its width.
   The cap keeps a long option ("Incomplete packing data") from stretching the
   row, and `.btn.picker > span` already ellipsises past it. */
.query-filters > .dropdown > .btn.picker {
  min-width: 0; justify-content: flex-start; max-width: 210px;
}
/* `min-width: 0` is what makes the ellipsis on the label actually fire. A flex
   item refuses to shrink below its own content by default (`min-width: auto`),
   so at the 210px clamp the text would have pushed the button wider instead of
   truncating — the `text-overflow` on `.btn.picker > span` never got a chance.
   With this, the chevron stays put and the label ellipsises under it. */
.query-filters > .dropdown > .btn.picker > span { min-width: 0; }
.filters .input { min-width: 210px; flex: 1 1 210px; }
/* A filter whose input has a known length — an ASIN is ten characters — sized
   to it rather than stretched across the card. The default above grows to fill
   the row, which for a free-text search over a wide table is right and for a
   fixed-width code just makes a very long empty box. */
.filters .input.narrow { flex: 0 0 auto; width: 220px; min-width: 0; }
/* Sits beside the Clear button and only while a term is applied — the count is
   the answer to "did that match anything", so it has nothing to say otherwise. */
.filters .filter-count { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
@media (max-width: 575.98px) {
  .filters > * { flex: 1 1 100%; }
  .filters .input.narrow { width: auto; flex: 1 1 100%; }
}

.empty { text-align: center; padding: 32px 12px; color: var(--muted); font-size: 12.5px; }

/* Pagination.
   Numbered, because the tables it sits under run to a dozen pages and a lone
   Next button neither says how far the end is nor lets you get there. Both
   arrows are always drawn — disabled at the ends rather than removed — so the
   row keeps its width and the numbers do not jump sideways as you move. */
.pager { display: flex; align-items: center; gap: 4px; margin-top: 12px; flex-wrap: wrap; }
.pager .pager-at { font-size: 11.5px; color: var(--muted); margin-left: auto;
                   font-variant-numeric: tabular-nums; white-space: nowrap; }

.pager-nums { display: flex; align-items: center; gap: 2px; list-style: none; margin: 0; padding: 0; }

.pager-step, .pager-num {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 28px; height: 28px; padding: 0 7px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
  font-size: 12px; font-variant-numeric: tabular-nums; text-decoration: none;
  transition: background-color var(--fast) ease, border-color var(--fast) ease, color var(--fast) ease;
}
.pager-step svg { width: 12px; height: 12px; fill: none; stroke: currentColor;
                  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* Literal colours with a dark override rather than a token: there is no
   --surface-2 in the palette, and inventing one here would leave it defined
   for the light theme only. Same pair as .pick a:hover. */
a.pager-step:hover, a.pager-num:hover { background: var(--hover); border-color: var(--border-strong); text-decoration: none; }
a.pager-step:focus-visible, a.pager-num:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The current page is a label, not a link — nothing happens if you click it,
   so it should not invite the click. */
.pager-num.is-here { background: var(--primary); border-color: var(--primary);
                     color: #fff; font-weight: 600; cursor: default; }

.pager-step.is-off { opacity: .35; cursor: default; }

.pager-gap { display: inline-flex; align-items: center; justify-content: center;
             min-width: 18px; height: 28px; color: var(--faint); font-size: 12px; }

@media (max-width: 575.98px) {
  /* On a phone the count wraps to its own line rather than squeezing the
     controls, which are the part you came to touch. */
  .pager .pager-at { margin-left: 0; width: 100%; }
}

/* segmented none / view / edit picker */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.seg label { position: relative; margin: 0; display: block; }
.seg input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; cursor: pointer; }
.seg span { display: block; padding: 2px 9px; font-size: 11px; color: var(--muted); white-space: nowrap; border-left: 1px solid var(--border); }
.seg label:first-child span { border-left: 0; }
.seg input:checked + span { color: #fff; font-weight: 600; }
.seg input:checked + span.l-none { background: #a1a1aa; }
.seg input:checked + span.l-inherit { background: #71717a; }
.seg input:checked + span.l-view { background: var(--accent-fill); }
.seg input:checked + span.l-edit { background: var(--green); }
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: -2px; }
.seg input:disabled + span { opacity: .55; }

.matrix td { padding: 6px 8px; }
.matrix .page-name { font-weight: 500; }
.matrix .group-row td { background: var(--surface-2); font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--faint); }
[data-theme="dark"] .matrix .group-row td { background: #101013; }

.split { display: grid; grid-template-columns: 250px 1fr; gap: 10px; align-items: start; }
@media (max-width: 991.98px) { .split { grid-template-columns: 1fr; } }

.pick { display: grid; gap: 2px; }
.pick a { display: block; padding: 8px 10px; border-radius: var(--radius-sm); color: var(--text); text-decoration: none; border: 1px solid transparent; transition: background-color var(--fast) ease; }
.pick a:hover { background: var(--hover); text-decoration: none; }
.pick a.on { background: var(--accent-soft); border-color: var(--accent-line); }
.pick .t { font-size: 12.5px; font-weight: 600; }

/* A row that reveals its own delete control on hover. */
.pick-row { position: relative; }
.pick-row > a { padding-right: 32px; }
.pick-row:hover > a { background: var(--hover); }
.pick-row:hover > a.on { background: var(--accent-soft); }
.pick-del {
  position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; display: grid; place-items: center;
  border: 0; background: none; border-radius: var(--radius-sm);
  color: var(--red); cursor: pointer; padding: 0;
  opacity: 0; transition: opacity .12s ease;
}
.pick-del svg {
  width: 14px; height: 14px; fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.pick-row:hover .pick-del, .pick-del:focus-visible { opacity: 1; }
.pick-del:hover { background: color-mix(in srgb, var(--red) 8%, var(--surface)); color: var(--red); }
[data-theme="dark"] .pick-del { color: #f87171; }
[data-theme="dark"] .pick-del:hover { background: #2a1416; color: #fca5a5; }
/* Touch has no hover — keep the control reachable there. */
@media (hover: none) { .pick-del { opacity: 1; } }
.pick .s { font-size: 10.5px; color: var(--faint); }

.profile-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.profile-head .avatar { width: 52px; height: 52px; font-size: 17px; }
.profile-head h2 { font-size: 16px; margin: 0; }
.profile-head .meta { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.field-error { font-size: 10.5px; color: var(--red); margin-top: 2px; }
.form-actions { display: flex; gap: 6px; align-items: center; margin-top: 4px; }

/* ============================ Dark theme ============================
   Driven by the app bar's moon toggle, which sets data-theme on <html>.
   Almost everything is token-based, so this is mostly a palette swap; the
   rules after it repaint the few surfaces that carry literal colours. */
[data-theme="dark"] {
  --bg: #09090b;
  --surface: #121215;
  --surface-2: #161619;
  --hover: #1c1c21;
  --border: #25252b;
  --border-strong: #34343c;
  --text: #ececef;
  --muted: #a1a1aa;
  --faint: #6e6e79;
  /* On a dark ground the ink button would vanish, so the primary action
     becomes the accent fill; links and focus take a lighter indigo that reads
     on near-black. */
  --primary: #3e63dd;
  --primary-hover: #3358d4;
  --accent: #8ea4f4;
  --accent-hover: #a9b9f7;
  --accent-fill: #3e63dd;
  --accent-soft: #171c2e;
  --accent-line: #2b3664;
  --green: #4cc38a;
  --red: #f1666c;
  --amber: #f0a944;
  --violet: #a796ef;
  --sidebar-bg: #060608;
  --sidebar-active: rgba(255, 255, 255, 0.07);
  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 8px 20px -6px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.03);
  --bs-body-bg: var(--bg);
  --bs-body-color: var(--text);
  --bs-border-color: var(--border);
  --bs-emphasis-color: var(--text);
  color-scheme: dark;
}
[data-theme="dark"] .appbar-toggle:hover,
[data-theme="dark"] .appbar-icon:hover,
[data-theme="dark"] .appbar-user:hover,
[data-theme="dark"] .notif:hover,
[data-theme="dark"] .btn:hover { background: var(--hover); color: var(--text); }
[data-theme="dark"] .ico-moon { display: none; }
[data-theme="dark"] .ico-sun { display: block; }

[data-theme="dark"] tr:hover td { background: rgba(255, 255, 255, 0.025); }
[data-theme="dark"] .row-link:hover td { background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
[data-theme="dark"] .btn { background: var(--surface); border-color: var(--border); color: var(--text); }
[data-theme="dark"] .btn:hover { border-color: var(--border-strong); }
[data-theme="dark"] .btn.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
[data-theme="dark"] .btn.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
[data-theme="dark"] .select, [data-theme="dark"] .input { background-color: var(--surface); color: var(--text); border-color: var(--border); }
[data-theme="dark"] .chart { background-color: var(--surface); }
[data-theme="dark"] .tmpl,
[data-theme="dark"] .doc-row:hover,
[data-theme="dark"] .ch-row:hover { background: var(--hover); }
[data-theme="dark"] .doc-row.open, [data-theme="dark"] .ch-row.open { background: var(--accent-soft); }
[data-theme="dark"] .note      { background: #12151f; border-color: #262d47; color: #b9c6f5; }
[data-theme="dark"] .warn-note { background: #1d160a; border-color: #4d3410; color: #f3c77a; }
[data-theme="dark"] .ok-note   { background: #0d1a13; border-color: #1d4630; color: #93dcb5; }
[data-theme="dark"] .badge.green  { background: #10251a; color: #6fd3a0; }
[data-theme="dark"] .badge.red    { background: #2a1416; color: #f8a2a5; }
[data-theme="dark"] .badge.amber  { background: #2a1d0a; color: #f3c069; }
[data-theme="dark"] .badge.blue   { background: #161c33; color: #a9b9f7; }
[data-theme="dark"] .badge.violet { background: #1d1830; color: #c3b6f6; }
[data-theme="dark"] .badge.gray   { background: #1c1c21; color: #c4c4cc; }
[data-theme="dark"] .dropdown-menu {
  --bs-dropdown-bg: var(--surface); --bs-dropdown-color: var(--text);
  --bs-dropdown-link-color: var(--text);
  --bs-dropdown-link-hover-bg: var(--hover); --bs-dropdown-link-hover-color: #fff;
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-divider-bg: var(--border);
}
[data-theme="dark"] .modal-content { background: var(--surface); }
[data-theme="dark"] .modal { --bs-modal-bg: var(--surface); }
[data-theme="dark"] .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }
[data-theme="dark"] .modal .btn-close:hover { background-color: rgba(255, 255, 255, 0.10); }
[data-theme="dark"] .task-card, [data-theme="dark"] .cal-day, [data-theme="dark"] .day { background: var(--surface); }
[data-theme="dark"] .day.out { background: #0e0e11; }
[data-theme="dark"] .new-doc, [data-theme="dark"] .doc-banner { border-color: var(--border); }

[data-theme="dark"] .appbar-divider { background: var(--border); }
/* On a near-black page the near-black rail needs an edge of its own; in the
   light theme the contrast between the two does that job. */
[data-theme="dark"] .shell > .sidebar { box-shadow: inset -1px 0 0 var(--sidebar-line); }
/* The collapsed rail's edge is the clip, not the box, so the line above sits
   out of sight; draw it at the rail instead, and hand over to the open
   panel's own edge and shadow on hover (`Collapsed rail`). */
@media (min-width: 992px) {
  [data-theme="dark"].nav-collapsed .shell > .sidebar {
    box-shadow: inset -1px 0 0 var(--sidebar-line);
    background-image: linear-gradient(var(--sidebar-line), var(--sidebar-line));
    background-size: 1px 100%; background-repeat: no-repeat;
    background-position: calc(var(--rail-w) - 1px) 0;
  }
}
@media (min-width: 992px) and (hover: hover) and (pointer: fine) {
  [data-theme="dark"].nav-collapsed:has(.shell > .sidebar:hover, .shell > .sidebar :focus-visible,
                     .appbar-brandzone:hover, .appbar-brandzone :focus-visible)
    .shell > .sidebar {
    background-image: none;
    box-shadow: inset -1px 0 0 var(--sidebar-line), 10px 0 28px -14px rgba(0, 0, 0, 0.7);
  }
}
[data-theme="dark"] .appbar-brandzone { box-shadow: inset -1px 0 0 var(--sidebar-line); }
[data-theme="dark"] .avatar-round, [data-theme="dark"] .avatar {
  background: linear-gradient(180deg, #44444d 0%, #2c2c33 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.06);
}

[data-theme="dark"] .err-note { background: #1f1012; border-color: #4f1d21; color: #f8a2a5; }
[data-theme="dark"] .seg { background: var(--surface); }
[data-theme="dark"] .filters .input, [data-theme="dark"] .matrix td { color: var(--text); }

[data-theme="dark"] .tabs-sub { background: #18181c; }
[data-theme="dark"] .tabs-sub a.active { background: #26262c; color: var(--text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); }


/* A KPI tile that filters the table it sits above. Only the tiles that stand
   for a set of rows get this — the ones carrying a statistic stay inert, so
   the cursor is the honest signal about which of them will do something. */
.tile-link { cursor: pointer; transition: border-color var(--fast) ease, box-shadow var(--base) var(--ease), transform var(--base) var(--ease); }
.tile-link:hover { border-color: var(--accent-line); box-shadow: var(--shadow-md); transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .tile-link:hover { transform: none; } }
.tile-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }


/* ============================ User manual ============================
   Every page's manual, opened from the page header — after the filters
   (partials/manual-button.html,
   static/asset/js/manual.js). A wide drawer from the right: the manual's own
   header, a search, numbered contents, and section cards illustrated with
   screenshots of the page they describe. The same sections print as a PDF
   from /manual/<page>/print/. */

/* Sized to the filter controls it sits after (`.btn` and `.btn.picker` are
   30px), and set apart from them by colour: the filters are neutral, this one
   is the accent, because it opens help rather than changing the figures. */
.man-open {
  display: inline-flex; align-items: center; gap: 7px;
  height: 30px; padding: 0 12px 0 10px; flex-shrink: 0;
  border: 1px solid var(--accent-line); border-radius: 9px;
  background: var(--surface); color: var(--accent);
  font: 600 12.5px/1 var(--font-sans); letter-spacing: -0.01em; cursor: pointer;
  box-shadow: var(--shadow-xs), inset 0 0 0 1px color-mix(in srgb, var(--accent) 6%, transparent);
  transition: background-color var(--fast) ease, border-color var(--fast) ease,
              box-shadow var(--fast) ease, transform var(--fast) var(--ease);
}
.man-open .man-ico { width: 15px; height: 15px; }
.man-open:hover { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.man-open:active { transform: scale(0.97); }
.man-open:focus-visible { outline: none; box-shadow: var(--ring); }
@media (max-width: 767.98px) { .man-open { justify-content: center; } }

html.man-locked, html.man-locked body { overflow: hidden; }

/* Sized off --vw100/--vh100 like the dialogs: a fixed element under the root
   zoom otherwise covers only the zoom's share of the screen. */
.man-drawer { position: fixed; top: 0; left: 0; width: var(--vw100); height: var(--vh100); z-index: 1090; }
.man-scrim {
  position: absolute; inset: 0; background: rgba(11, 11, 14, 0.34);
  opacity: 0; transition: opacity 240ms ease;
}
.man-panel {
  position: absolute; top: 0; right: 0; height: 100%; width: min(1240px, 94%);
  display: flex; flex-direction: column; overflow: hidden; outline: none;
  background: var(--surface); border-left: 1px solid var(--border);
  border-radius: 18px 0 0 18px;
  box-shadow: -28px 0 60px -18px rgba(16, 16, 20, 0.30);
  opacity: 0; transform: translateX(40px);
  transition: transform 300ms var(--ease), opacity 220ms var(--ease);
}
.man-drawer.is-open .man-scrim { opacity: 1; }
.man-drawer.is-open .man-panel { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .man-scrim, .man-panel { transition: opacity 120ms linear; transform: none; }
}
@media (max-width: 991.98px) { .man-panel { width: 100%; border-radius: 0; border-left: 0; } }

.man-loading { display: grid; gap: 12px; padding: 32px; }
.man-loading .skel { display: block; }

.man { display: flex; flex-direction: column; height: 100%; min-height: 0; }

/* The manual's own header: an accent-lit band, the book on a solid tile, the
   page's name, and what the page is for in one paragraph. */
.man-head {
  display: grid; grid-template-columns: auto 1fr auto; gap: 18px; align-items: start;
  padding: 24px 28px 22px;
  background:
    radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%),
    var(--surface);
  border-bottom: 1px solid var(--border);
}
.man-head-icon {
  width: 48px; height: 48px; border-radius: 13px; display: grid; place-items: center;
  background: linear-gradient(180deg, #5876e8 0%, var(--accent-fill) 100%); color: #fff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 6px 16px -6px color-mix(in srgb, var(--accent-fill) 70%, transparent);
}
.man-head-icon .man-ico { width: 24px; height: 24px; }
.man-head-text { min-width: 0; }
.man-head h1 { font-size: 23px; font-weight: 650; letter-spacing: -0.035em; line-height: 1.2; margin: 1px 0 0; }
.man-head h1 span { color: var(--muted); font-weight: 500; }
.man-head p { font-size: 13.5px; line-height: 1.6; color: var(--muted); margin: 6px 0 0; max-width: 92ch; }
.man-head-actions { display: flex; align-items: center; gap: 8px; }
.man-dl { height: 36px; padding: 0 12px; font-size: 13px; gap: 7px; }
.man-dl .man-ico { width: 15px; height: 15px; }
.man-caret { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; opacity: .6; }
.man-dl + .dropdown-menu { min-width: 280px; }
.man-close {
  width: 36px; height: 36px; display: grid; place-items: center; padding: 0;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  color: var(--muted); cursor: pointer; box-shadow: var(--shadow-xs);
  transition: background-color var(--fast) ease, color var(--fast) ease, border-color var(--fast) ease;
}
.man-close:hover { background: var(--hover); color: var(--text); border-color: var(--border-strong); }
.man-close:focus-visible { outline: none; box-shadow: var(--ring); }
.man-close svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
@media (max-width: 767.98px) {
  .man-head { grid-template-columns: 1fr auto; padding: 16px; gap: 12px; }
  .man-head-icon { display: none; }
  .man-head h1 { font-size: 19px; }
  .man-dl span { display: none; }
}

.man-search {
  display: flex; align-items: center; gap: 10px;
  margin: 16px 28px; height: 46px; padding: 0 14px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--surface-2);
  transition: border-color var(--fast) ease, box-shadow var(--fast) ease, background-color var(--fast) ease;
}
.man-search:focus-within { border-color: var(--accent); box-shadow: var(--ring); background: var(--surface); }
.man-search > .man-ico { width: 17px; height: 17px; color: var(--faint); flex-shrink: 0; }
.man-search input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  font: 400 14px var(--font-sans); color: var(--text);
}
.man-search input::placeholder { color: var(--faint); }
.man-search input::-webkit-search-cancel-button { cursor: pointer; }
.man-search-count { font-size: 12px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.man-kbd, .man-sec kbd {
  display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 5px;
  border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 5px;
  background: var(--surface); color: var(--muted);
  font: 500 11px/1 var(--font-mono);
}
@media (max-width: 767.98px) { .man-search { margin: 12px 16px; } .man-kbd { display: none; } }

.man-body {
  flex: 1; min-height: 0; display: grid; grid-template-columns: 290px minmax(0, 1fr);
  border-top: 1px solid var(--border);
}
@media (max-width: 859.98px) { .man-body { grid-template-columns: 1fr; } .man .man-toc { display: none; } }

.man-toc-label {
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint); padding: 0 10px; margin-bottom: 10px;
}
.man-toc ol, .man-doc-toc ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.man .man-toc { position: relative; overflow-y: auto; padding: 20px 14px 28px 22px; border-right: 1px solid var(--border); }
.man-toc a, .man-doc-toc a {
  position: relative; display: grid; grid-template-columns: 28px 1fr; gap: 10px; align-items: center;
  padding: 8px 10px; border-radius: 10px; color: var(--text); text-decoration: none;
  font-size: 13px; line-height: 1.35; font-weight: 450;
  transition: background-color var(--fast) ease, color var(--fast) ease;
}
.man-toc a .num, .man-doc-toc a .num {
  display: grid; place-items: center; height: 24px; border-radius: 7px;
  background: var(--hover); color: var(--muted);
  font: 600 11px/1 var(--font-mono); font-variant-numeric: tabular-nums;
  transition: background-color var(--fast) ease, color var(--fast) ease;
}
.man-toc a:hover, .man-doc-toc a:hover { background: var(--hover); text-decoration: none; color: var(--text); }
.man-toc a.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.man-toc a.active .num { background: var(--accent-fill); color: #fff; }
.man-toc a.active::before {
  content: ""; position: absolute; left: -12px; top: 9px; bottom: 9px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--accent-fill);
}

/* Positioned so a section's offsetTop is measured from here — manual.js
   scrolls by it, and from the panel it would include the header's height. */
.man .man-content { position: relative; overflow-y: auto; padding: 24px 30px 48px; background: var(--surface-2); outline: none; }
@media (max-width: 767.98px) { .man .man-content { padding: 16px 14px 32px; } }

/* A section is a card: its number, an icon tile, the title, then prose that
   is set for reading rather than for a dashboard — 14px at 1.7. */
.man-sec {
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  padding: 24px 28px 26px; margin-bottom: 18px; box-shadow: var(--shadow-xs);
  scroll-margin-top: 16px;
}
.man-sec-head { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.man-sec-icon {
  width: 42px; height: 42px; flex-shrink: 0; border-radius: 12px; display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent-line);
}
.man-sec-icon .man-ico { width: 20px; height: 20px; }
.man-eyebrow { font-size: 10.5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--faint); }
.man-sec h2 { font-size: 20px; font-weight: 650; letter-spacing: -0.03em; line-height: 1.25; margin-top: 1px; }
.man-sec h3 { font-size: 15px; font-weight: 600; letter-spacing: -0.02em; margin: 22px 0 8px; }
.man-sec-body { font-size: 14px; line-height: 1.7; color: var(--text); }
.man-sec-body > p { margin: 0 0 12px; max-width: 84ch; }
.man-sec-body > :last-child { margin-bottom: 0; }
.man-sec-body ul, .man-sec-body ol:not(.man-steps) { margin: 0 0 14px; padding-left: 20px; max-width: 84ch; }
.man-sec-body li { margin: 5px 0; }
.man-sec-body li::marker { color: var(--faint); }
.man-sec-body strong, .man-sec-body b { font-weight: 600; }
.man-sec-body a { text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--accent) 35%, transparent); text-underline-offset: 2px; }

/* The name of a control, set the way it reads on screen. */
.man-ui {
  display: inline-block; padding: 0 6px; border-radius: 6px;
  background: var(--surface-2); border: 1px solid var(--border);
  font-weight: 600; font-size: 0.92em; line-height: 1.55; color: var(--text); white-space: nowrap;
}

/* Numbered steps: the number is a disc, so a procedure reads as one even
   when a step runs to three lines. */
.man-steps { list-style: none; counter-reset: man-step; margin: 4px 0 16px; padding: 0; display: grid; gap: 12px; max-width: 84ch; }
/* The number is positioned, not a grid column: as a grid, every inline
   element in a step (a bold word, a button chip, a key) became its own cell
   and the sentence broke into a column of fragments. */
.man-steps > li { counter-increment: man-step; position: relative; display: block; min-height: 28px; padding-left: 40px; margin: 0; }
.man-steps > li::before {
  content: counter(man-step); position: absolute; left: 0; top: 0;
  display: grid; place-items: center; width: 26px; height: 26px; margin-top: 1px;
  border-radius: 50%; background: var(--primary); color: #fff; font: 600 12px/1 var(--font-sans);
}

/* Screenshots: shown at the size they have on screen (captured at 2x), in a
   lifted frame, and opened full size on click. */
.man-fig { margin: 18px 0 20px; text-align: center; }
.man-fig-frame {
  display: inline-block; max-width: 100%; vertical-align: top;
  border: 1px solid var(--border); border-radius: 12px; overflow: hidden;
  background: var(--surface); box-shadow: var(--shadow-md); cursor: zoom-in;
  transition: box-shadow var(--base) var(--ease), transform var(--base) var(--ease);
}
.man-fig-frame:hover { box-shadow: 0 16px 36px -12px rgba(16, 16, 20, 0.22), 0 0 0 1px var(--accent-line); transform: translateY(-1px); }
.man-fig img { display: block; width: var(--w); max-width: 100%; height: auto; }
.man-fig.is-wide .man-fig-frame { display: block; }
.man-fig.is-wide img { width: 100%; }
.man-fig.is-small img { max-width: min(100%, 520px); }
.man-fig figcaption { font-size: 12.5px; line-height: 1.5; color: var(--muted); margin: 10px auto 0; max-width: 80ch; }
.man-fig-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  width: 100%; height: 170px; border-style: dashed; box-shadow: none; cursor: default;
  color: var(--faint); font-size: 12.5px; background: var(--surface-2);
}
.man-fig-empty .man-ico { width: 22px; height: 22px; }
@media (prefers-reduced-motion: reduce) { .man-fig-frame:hover { transform: none; } }

/* A two-column reference table: the thing, then what it means. Prose-sized
   and wrapping, unlike the app's data tables. */
.man-table {
  margin: 14px 0 18px; border: 1px solid var(--border); border-radius: 12px;
  overflow: hidden; background: var(--surface);
}
.man-table table { font-size: 13.5px; }
.man-table th { padding: 10px 16px; background: var(--surface-2); font-size: 10.5px; }
.man-table td { padding: 11px 16px; white-space: normal; vertical-align: top; line-height: 1.6; }
.man-table td:first-child { font-weight: 600; width: 28%; min-width: 150px; }
.man-table tr:hover td { background: transparent; }

/* Callouts: a tip, a note, a caution or a reassurance, each with its glyph. */
/* The glyph is positioned, not a grid column: a callout's content is loose
   prose with inline <b>s and links, and in a grid every text run between
   them became a cell of its own. */
.man-callout {
  position: relative; display: block;
  margin: 14px 0 16px; padding: 12px 16px 12px 44px; border-radius: 12px; border: 1px solid;
  font-size: 13.5px; line-height: 1.6; max-width: 92ch;
}
.man-callout::before {
  content: ""; position: absolute; left: 16px; top: 14px; width: 18px; height: 18px;
  background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat; mask: var(--glyph) center / contain no-repeat;
}
.man-callout > :first-child { margin-top: 0; }
.man-callout > :last-child { margin-bottom: 0; }
.man-callout b:first-child { margin-right: 4px; }
.man-callout.tip  { --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 12.5h4M6.6 14.3h2.8M8 1.8a4.3 4.3 0 0 0-2.6 7.7c.5.4.8 1 .8 1.6v.4h3.6v-.4c0-.6.3-1.2.8-1.6A4.3 4.3 0 0 0 8 1.8z'/%3E%3C/svg%3E");
  background: color-mix(in srgb, var(--accent) 5%, var(--surface)); border-color: var(--accent-line); color: color-mix(in srgb, var(--accent) 45%, var(--text)); }
.man-callout.note { --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.2'/%3E%3Cpath d='M8 7.3v3.8M8 5h.01'/%3E%3C/svg%3E");
  background: var(--surface-2); border-color: var(--border); color: var(--muted); }
.man-callout.note b { color: var(--text); }
.man-callout.warn { --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.1 2.6a1 1 0 0 1 1.8 0l5.3 9.6a1 1 0 0 1-.9 1.5H2.7a1 1 0 0 1-.9-1.5z'/%3E%3Cpath d='M8 6.2v3.2M8 11.6h.01'/%3E%3C/svg%3E");
  background: #fdf6ea; border-color: #f1d9ab; color: #864805; }
.man-callout.ok   { --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='8' cy='8' r='6.2'/%3E%3Cpath d='m5.4 8.2 1.8 1.8 3.4-3.6'/%3E%3C/svg%3E");
  background: #eef8f2; border-color: #c3e5d1; color: #1b6a41; }
[data-theme="dark"] .man-callout.warn { background: #1d160a; border-color: #4d3410; color: #f3c77a; }
[data-theme="dark"] .man-callout.ok   { background: #0d1a13; border-color: #1d4630; color: #93dcb5; }

/* Two or three short cards side by side: "what it is / when to use it". */
.man-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin: 14px 0 18px; }
.man-card { border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; background: var(--surface); }
.man-card h4 { font-size: 13.5px; font-weight: 600; margin: 0 0 4px; letter-spacing: -0.01em; }
.man-card p { font-size: 13px; line-height: 1.6; color: var(--muted); margin: 0; }

mark.man-hit {
  background: color-mix(in srgb, #f5b13d 38%, transparent); color: inherit;
  border-radius: 3px; padding: 0 1px; box-shadow: 0 0 0 1px color-mix(in srgb, #f5b13d 30%, transparent);
}

.man-noresult {
  display: grid; justify-items: center; gap: 6px; text-align: center;
  padding: 56px 16px; color: var(--muted); font-size: 13.5px;
}
.man-noresult .man-ico { width: 28px; height: 28px; color: var(--faint); margin-bottom: 4px; }
.man-noresult b { color: var(--text); font-weight: 600; }

.man-foot {
  display: flex; align-items: center; gap: 10px; margin-top: 8px; padding: 16px 4px 0;
  color: var(--muted); font-size: 13px; border-top: 1px dashed var(--border);
}
.man-foot .man-ico { width: 16px; height: 16px; color: var(--faint); flex-shrink: 0; }

.man-lightbox {
  position: absolute; inset: 0; z-index: 5; display: grid; place-items: center; gap: 10px;
  padding: 36px; background: rgba(9, 9, 11, 0.86); cursor: zoom-out;
  animation: sh-menu-in var(--base) var(--ease);
}
.man-lightbox[hidden] { display: none; }
.man-lightbox img { max-width: 100%; max-height: calc(100% - 30px); border-radius: 10px; box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5); background: #fff; }
.man-lightbox span { color: rgba(255, 255, 255, 0.7); font-size: 12px; }

[data-theme="dark"] .man-panel { box-shadow: -28px 0 60px -18px rgba(0, 0, 0, 0.7); }
[data-theme="dark"] .man-scrim { background: rgba(0, 0, 0, 0.6); }

/* ---- The printable copy (/manual/<page>/print/) ---- */
html.man-print-root { --zoom: 1; }
.man-print { background: var(--bg); }
.man-doc { max-width: 980px; margin: 0 auto; padding: 40px 28px 72px; }
.man-cover {
  position: relative; padding: 36px 40px; margin-bottom: 18px; border-radius: 18px;
  border: 1px solid var(--border); box-shadow: var(--shadow-sm);
  background:
    radial-gradient(110% 130% at 0% 0%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 60%),
    var(--surface);
}
.man-cover-brand { display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 28px; }
.man-cover .man-head-icon { margin-bottom: 16px; }
.man-cover h1 { font-size: 34px; font-weight: 650; letter-spacing: -0.04em; line-height: 1.1; }
.man-cover p { font-size: 15px; line-height: 1.65; color: var(--muted); margin: 10px 0 0; max-width: 70ch; }
.man-cover-meta { margin-top: 18px; font-size: 12px; color: var(--faint); }
.man-cover-actions { position: absolute; top: 30px; right: 32px; }
.man-cover-actions .btn { gap: 7px; height: 36px; padding: 0 14px; }
.man-cover-actions .man-ico { width: 15px; height: 15px; }
.man-doc-toc {
  padding: 22px 26px; margin-bottom: 18px; border-radius: 16px;
  border: 1px solid var(--border); background: var(--surface); box-shadow: var(--shadow-xs);
}
.man-doc-toc ol { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); column-gap: 14px; }
.man-doc .man-content { padding: 0; }
@media print {
  @page { size: A4; margin: 14mm 12mm; }
  html.man-print-root { --zoom: 1; }
  .man-print { background: #fff; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .man-doc { padding: 0; max-width: none; }
  .man-cover-actions { display: none; }
  .man-cover, .man-doc-toc, .man-sec { box-shadow: none; }
  .man-doc-toc { break-after: page; }
  .man-sec { break-inside: auto; }
  .man-sec-head, .man-sec h3 { break-after: avoid; }
  .man-fig, .man-table, .man-callout, .man-steps > li { break-inside: avoid; }
  .man-fig-frame { box-shadow: none; }
  .man-fig img { max-width: 100%; }
}


/* ============================ Tooltips ============================
   One floating tip for the whole app (initTips in app.js), drawn from
   `data-tip`, a `title` — which it takes over, so the browser's own grey
   box never shows as well — or an icon button's `aria-label`. */
.sh-tip {
  position: fixed; top: 0; left: 0; z-index: 2000;
  max-width: 300px; padding: 7px 10px; border-radius: 8px;
  background: #18181b; color: #fafafa;
  font: 500 11.5px/1.45 var(--font-sans); letter-spacing: 0; text-align: left;
  white-space: pre-line; overflow-wrap: break-word; pointer-events: none;
  box-shadow: 0 10px 24px -6px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(255, 255, 255, 0.07);
  opacity: 0; transform: translateY(3px) scale(0.98); transform-origin: var(--tip-x, 50%) 100%;
  transition: opacity 120ms ease, transform 180ms var(--ease);
}
.sh-tip.is-below { transform: translateY(-3px) scale(0.98); transform-origin: var(--tip-x, 50%) 0; }
.sh-tip.is-on { opacity: 1; transform: none; }
.sh-tip::after {
  content: ""; position: absolute; left: var(--tip-x, 50%); bottom: -4px;
  width: 8px; height: 8px; background: inherit; border-radius: 1.5px;
  transform: translateX(-50%) rotate(45deg);
}
.sh-tip.is-below::after { top: -4px; bottom: auto; }
.sh-tip kbd {
  display: inline-block; margin-left: 4px; padding: 0 4px; border-radius: 4px;
  background: rgba(255, 255, 255, 0.14); font: 500 10.5px/1.5 var(--font-mono); color: #fff;
}
[data-theme="dark"] .sh-tip {
  background: #2a2a31; color: #f4f4f5;
  box-shadow: 0 10px 24px -6px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(255, 255, 255, 0.09);
}
@media (prefers-reduced-motion: reduce) {
  .sh-tip, .sh-tip.is-below { transform: none; transition: opacity 80ms linear; }
}

/* ---------- Ask AI (Orders) ----------
   A question box over the figures on the page. Product-UI quiet: one accent,
   used only where something can be done (send, the primary suggestion, the
   mark); greys for hierarchy; 10–14px radii, the app's own; no gradient, no
   glass. Fixed header and composer with the thread scrolling between them —
   scoped to this dialog, since every other dialog here grows to its content
   on purpose (see `.modal-content` above). */
.ai-modal .modal-dialog { max-width: 760px; }
.ai-modal .modal-content {
  display: flex; flex-direction: column; overflow: hidden; background: var(--surface);
  max-height: calc(var(--vh100) * 0.85);
}
.ai-head {
  flex-shrink: 0; display: flex; align-items: flex-start; gap: 12px;
  padding: 16px 18px 14px; border-bottom: 1px solid var(--border);
}
.ai-mark {
  width: 34px; height: 34px; flex-shrink: 0; border-radius: 10px;
  display: grid; place-items: center; color: var(--accent);
  background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent-line);
}
.ai-mark svg { width: 17px; height: 17px; fill: currentColor; stroke: none; }
.ai-mark.is-sm { width: 22px; height: 22px; border-radius: 7px; }
.ai-mark.is-sm svg { width: 12px; height: 12px; }
.ai-head-text { flex: 1 1 auto; min-width: 0; }
.ai-title { margin: 1px 0 0; font-size: 15px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.3; }
.ai-scope { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.ai-scope-chip {
  display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 550; color: var(--text);
  background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--border);
}
.ai-scope-chip.is-quiet { color: var(--faint); font-weight: 450; background: transparent; box-shadow: none; padding-left: 2px; }
/* The marketplace and date chips are switches: the same pill, with a caret. */
.ai-scope .dropdown { display: inline-flex; }
button.ai-scope-chip {
  gap: 5px; border: 0; font-family: inherit; line-height: inherit; cursor: pointer;
  transition: background-color var(--fast) ease, box-shadow var(--fast) ease;
}
button.ai-scope-chip svg { width: 10px; height: 10px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; color: var(--muted); }
button.ai-scope-chip:hover:not(:disabled),
button.ai-scope-chip[aria-expanded="true"] { background: var(--hover); box-shadow: inset 0 0 0 1px var(--border-strong); }
button.ai-scope-chip:focus-visible { outline: none; box-shadow: var(--ring); }
button.ai-scope-chip:disabled { cursor: default; opacity: 1; }
button.ai-scope-chip:disabled svg { display: none; }
.ai-range-menu { --bs-dropdown-min-width: 264px; }
.ai-range-custom { display: grid; gap: 8px; padding: 4px 7px 5px; }
.ai-range-dates { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ai-range-dates label { display: grid; gap: 3px; font-size: 11px; color: var(--muted); }
.ai-range-dates .input { height: 30px; padding: 0 7px; font-size: 12px; }
.ai-icon-btn {
  width: 28px; height: 28px; flex-shrink: 0; border: 0; border-radius: 8px;
  display: grid; place-items: center; color: var(--muted); background: transparent; cursor: pointer;
  transition: background-color var(--fast) ease, color var(--fast) ease;
}
.ai-icon-btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ai-icon-btn:hover { color: var(--text); background: var(--hover); }
.ai-icon-btn:focus-visible { outline: none; box-shadow: var(--ring); }
.ai-head .btn-close { margin: 0 -4px 0 0; }

.ai-body { flex: 1 1 auto; min-height: 260px; overflow-y: auto; padding: 22px 22px 8px; scroll-behavior: smooth; }
.ai-empty h3 { margin: 0 0 6px; font-size: 16px; font-weight: 600; letter-spacing: -0.02em; }
.ai-empty > p { margin: 0 0 18px; max-width: 58ch; font-size: 12.5px; line-height: 1.65; color: var(--muted); }
.ai-suggest { display: flex; flex-wrap: wrap; gap: 8px; }
.ai-chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font: 500 12.5px/1.3 var(--font-sans); cursor: pointer;
  transition: border-color var(--fast) ease, background-color var(--fast) ease, transform var(--base) var(--ease), box-shadow var(--fast) ease;
}
.ai-chip:hover { border-color: var(--border-strong); background: var(--surface-2); transform: translateY(-1px); box-shadow: 0 2px 6px -2px rgba(16, 16, 20, 0.10); }
.ai-chip:active { transform: none; }
.ai-chip:focus-visible { outline: none; box-shadow: var(--ring); }
.ai-chip svg { width: 13px; height: 13px; fill: currentColor; stroke: none; }
.ai-chip.is-primary { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); font-weight: 600; }
.ai-chip.is-primary:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--accent-line)); }
.ai-tiktok-note {
  margin: -6px 0 16px; padding: 10px 12px; border-radius: 10px; font-size: 12px; line-height: 1.55;
  color: var(--text); background: color-mix(in srgb, var(--amber) 9%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--amber) 26%, transparent);
}
.ai-modal.is-blocked .ai-suggest { opacity: 0.45; pointer-events: none; }

/* The thread: the question on the right as the reader's own words, the
   answer on the left as a document — prose, not a chat bubble. */
.ai-turn { padding-bottom: 22px; animation: ai-in 280ms var(--ease) both; }
.ai-turn + .ai-turn { padding-top: 20px; border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent); }
@keyframes ai-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.ai-q { display: flex; justify-content: flex-end; margin-bottom: 14px; }
.ai-q-bubble {
  max-width: 78%; padding: 8px 13px; border-radius: 14px 14px 4px 14px;
  font-size: 12.5px; line-height: 1.55; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere;
  background: color-mix(in srgb, var(--text) 5.5%, var(--surface));
}
.ai-q.is-audit .ai-q-bubble { color: var(--accent); background: var(--accent-soft); font-weight: 550; }
.ai-a-head { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; font-size: 12px; }
.ai-a-head b { font-weight: 600; }
.ai-status { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; font-size: 11px; color: var(--faint); }
.ai-status i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: ai-pulse 1.1s ease-in-out infinite; }
.ai-status.is-error { color: var(--red); }
@keyframes ai-pulse { 0%, 100% { opacity: 0.25; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1); } }
.ai-a-body { padding-left: 30px; }
.ai-a-body .doc-body p, .ai-a-body .doc-body li { font-size: 12.75px; line-height: 1.72; }
.ai-a-body .doc-body.writing > :last-child::after {
  content: ''; display: inline-block; width: 7px; height: 13px; margin-left: 2px; vertical-align: -2px;
  border-radius: 2px; background: var(--accent); animation: ai-caret 1s steps(1) infinite;
}
@keyframes ai-caret { 50% { opacity: 0; } }
.ai-wait { padding-top: 3px; max-width: 520px; }
.ai-muted { color: var(--faint); font-size: 12px; margin: 0; }
.ai-error {
  padding: 10px 12px; border-radius: 10px; font-size: 12px; line-height: 1.55;
  background: color-mix(in srgb, var(--red) 7%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red) 22%, transparent);
}
.ai-error .btn { margin-top: 8px; }
.ai-tools { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 10px; margin: 10px 0 0 30px; }
.ai-tool {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 8px; border: 0; border-radius: 7px;
  font: 500 11.5px/1.4 var(--font-sans); color: var(--muted); background: transparent; cursor: pointer;
}
.ai-tool:hover { color: var(--text); background: var(--hover); }
.ai-tool svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.4; }
.ai-tool[aria-expanded="true"] { color: var(--text); background: var(--hover); }
.ai-brief {
  margin: 8px 0 0 30px; padding: 10px 12px; max-height: 220px; overflow: auto; border-radius: 10px;
  font: 11px/1.55 var(--font-mono); white-space: pre-wrap; color: var(--muted); background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* The composer: one rounded field, the send button inside it. */
.ai-foot { flex-shrink: 0; padding: 12px 18px 12px; border-top: 1px solid var(--border); background: var(--surface); }
.ai-composer {
  position: relative; display: flex; align-items: flex-end; gap: 8px;
  padding: 8px 8px 8px 14px; border-radius: 14px; border: 1px solid var(--border-strong); background: var(--surface);
  transition: border-color var(--fast) ease, box-shadow var(--fast) ease;
}
.ai-composer:focus-within { border-color: color-mix(in srgb, var(--accent) 55%, var(--border-strong)); box-shadow: var(--ring); }
.ai-composer textarea {
  flex: 1 1 auto; min-width: 0; min-height: 22px; max-height: 132px; padding: 4px 0; border: 0; outline: 0; resize: none;
  font: 400 13px/1.55 var(--font-sans); color: var(--text); background: transparent;
}
.ai-composer textarea::placeholder { color: var(--faint); }
.ai-composer textarea:disabled { cursor: not-allowed; }
.ai-count { align-self: center; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; }
.ai-send, .ai-stop {
  width: 32px; height: 32px; flex-shrink: 0; border: 0; border-radius: 10px; display: grid; place-items: center; cursor: pointer;
  transition: background-color var(--fast) ease, opacity var(--fast) ease, transform var(--base) var(--ease);
}
/* Ink, like the Ask AI button that opened this: the app keeps ink for the
   primary action and indigo for links, focus and selection. */
.ai-send { color: #fff; background: var(--primary); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.10), 0 1px 2px rgba(16, 16, 20, 0.20); }
.ai-send:hover:not(:disabled) { background: var(--primary-hover); transform: translateY(-1px); }
.ai-send:disabled { opacity: 0.35; cursor: not-allowed; }
.ai-send svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ai-stop { color: var(--text); background: color-mix(in srgb, var(--text) 8%, var(--surface)); }
.ai-stop:hover { background: color-mix(in srgb, var(--text) 13%, var(--surface)); }
.ai-stop svg { width: 14px; height: 14px; fill: currentColor; stroke: none; }
.ai-send:focus-visible, .ai-stop:focus-visible { outline: none; box-shadow: var(--ring); }
.ai-foot-note { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 4px 12px; margin-top: 8px; font-size: 11px; color: var(--faint); }
.ai-foot-note kbd {
  padding: 0 4px; border-radius: 4px; font: 500 10px/1.6 var(--font-sans); color: var(--muted);
  background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--border);
}
[data-theme="dark"] .ai-chip:hover { box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.5); }
@media (max-width: 575.98px) {
  .ai-body { padding: 18px 16px 6px; }
  .ai-foot { padding: 10px 12px; }
  .ai-a-body, .ai-tools, .ai-brief { padding-left: 0; margin-left: 0; }
  .ai-q-bubble { max-width: 90%; }
}
@media (prefers-reduced-motion: reduce) {
  .ai-turn, .ai-status i, .ai-a-body .doc-body.writing > :last-child::after { animation: none; }
  .ai-chip:hover, .ai-send:hover:not(:disabled) { transform: none; }
  .ai-body { scroll-behavior: auto; }
}

/* The sales chart's skeleton (dash-region.html): the real card's shape —
   title row, marketplace strip, a row of bars on one baseline — in the app's
   shimmer, so the chart lands in space already reserved. */
.vchart-skel .vchart-head { margin-bottom: 16px; }
.vchart-skel-kpis { display: flex; gap: 28px; }
.vchart-skel-plot {
  position: relative; display: flex; align-items: flex-end; gap: 2px; height: 254px;
  margin-left: 64px; padding: 0 2px; border-bottom: 1px solid var(--border-strong);
  background-image: repeating-linear-gradient(to top, transparent 0, transparent 56.5px,
    color-mix(in srgb, var(--border) 65%, var(--surface)) 56.5px, color-mix(in srgb, var(--border) 65%, var(--surface)) 57.5px);
}
.vchart-skel-plot i {
  flex: 1 1 0; max-width: 24px; margin: 0 auto; border-radius: 4px 4px 0 0;
  /* The app's own shimmer (`.skel`), so every placeholder breathes alike. */
  background: var(--border);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--border) 55%, transparent) 25%,
    color-mix(in srgb, var(--border) 90%, transparent) 37%,
    color-mix(in srgb, var(--border) 55%, transparent) 63%);
  background-size: 400% 100%; animation: sh-skel 1.3s ease-in-out infinite;
}
.vchart-skel-x { display: flex; justify-content: space-between; margin: 9px 0 0 64px; }
@media (max-width: 767.98px) {
  .vchart-skel-plot { height: 180px; margin-left: 48px; }
  .vchart-skel-x { margin-left: 48px; }
  .vchart-skel-kpis { gap: 16px; flex-wrap: wrap; }
  .vchart-skel .vchart-head-right { display: none; }
}
@media (prefers-reduced-motion: reduce) { .vchart-skel-plot i { animation: none; opacity: .7; } }
