/* Shared post-login shell: sidebar + topbar + design tokens, used by dashboard.html,
   label-process.html, history.html, and analytics.html. One file instead of repeating
   ~150 lines of tokens/layout CSS per page (same reasoning as vendoring Font Awesome
   once instead of per-page).

   Fonts load from Google Fonts (not self-hosted) — a deliberate, narrow exception to
   this project's usual "vendor it, no CDN" rule (see Font Awesome), same reasoning as
   the Google Identity Services script on /login: self-hosting two variable font
   families with many weights is disproportionate effort for a visual-match request,
   and Google Fonts serving CSS+WOFF2 carries no functional/security surface the way a
   3rd-party JS SDK would. */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');

/*
  iSellerEngine design system (2026 refresh).

  The values below come from the design-system token files shipped with the new
  design (`_ds/.../tokens/*.css`): one indigo-violet brand hue, cool blue-leaning
  neutrals, a white card system on a pale blue-grey page, and hairline borders
  doing the separation work that shadows used to do.

  The token NAMES are unchanged from the previous theme on purpose. Around thirty
  files and several hundred call sites already say `var(--accent-ink)` /
  `var(--panel-bg)` / `var(--border-soft)`; re-pointing those names at new values
  re-skins the whole app from one place, where renaming them would have meant
  editing every caller and would have been the change most likely to leave a page
  nobody re-opened rendering in the old theme.

  The design system's OWN names (--brand-600, --surface-card, --shadow-brand, …)
  are published alongside, so markup ported from the design files can use the
  names those files use without being translated first.
*/
:root {
  color-scheme: light dark;
  --font-body: 'Plus Jakarta Sans', 'Segoe UI', system-ui, -apple-system, sans-serif;
  /* One family across the product now, headings included — the new system does
     its hierarchy with weight (800/700) and tight tracking rather than with a
     second display face, so --font-heading is an alias kept only so the existing
     `var(--font-heading)` call sites keep resolving. */
  --font-heading: var(--font-body);
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;

  /* ---- brand ramp: indigo-violet ---- */
  --brand-50: #F1EFFE;  --brand-100: #E5E1FD; --brand-200: #CFC8FB; --brand-300: #AFA3F8;
  --brand-400: #8B78F5; --brand-500: #7255F2; --brand-600: #5B3DF6; --brand-700: #4A2EE0;
  --brand-800: #3B23B4; --brand-900: #2C1A85;
  --brand-gradient: linear-gradient(135deg, #6D53F8 0%, #5B3DF6 55%, #4A2EE0 100%);

  /* ---- cool neutrals ---- */
  --neutral-0: #FFFFFF;   --neutral-25: #FBFCFE;  --neutral-50: #F7F8FC;  --neutral-100: #F1F3F9;
  --neutral-150: #E9EBF3; --neutral-200: #E2E5EF; --neutral-300: #CBD0E0; --neutral-400: #9AA1B8;
  --neutral-500: #6B7392; --neutral-600: #4F566F; --neutral-700: #343A50; --neutral-800: #232838;
  --neutral-900: #141827;

  /* ---- marketplace accents: logo tiles and donut segments only ----
     Each marketplace is a tile colour plus the ink that sits on it. Flipkart is
     the one that is not white-on-colour — its mark is blue on yellow — so it gets
     a distinct -ink token and the other three take white. */
  --mp-meesho: #F43397;   --mp-meesho-ink: #FFFFFF;
  --mp-flipkart: #FFD200; --mp-flipkart-ink: #2874F0;
  --mp-amazon: #131921;   --mp-amazon-ink: #FFFFFF;  --mp-amazon-accent: #FF9900;
  --mp-snapdeal: #E40046; --mp-snapdeal-ink: #FFFFFF;

  --page-bg: #F6F7FB;
  --panel-bg: #FFFFFF;
  /* Body ink is neutral-800, not the 900 reserved for headings and metrics: one
     step of extra darkness plus the weight jump is what separates a heading from
     the copy under it in this system. */
  --text: var(--neutral-800);
  --text-heading: var(--neutral-900);
  --muted: var(--neutral-500);
  --subtle: var(--neutral-400);
  --border: #E4E7F0;
  --border-soft: #EFF1F7;
  --border-strong: var(--neutral-300);

  /*
    --accent is the accent as a SURFACE: a filled button, the active rail item,
    the avatar. White sits on it, so it has to stay dark enough in both themes.

    --accent-ink is the accent as TEXT: a sorted column header, "View all
    records", an active tab. That one has to inverse-track the theme — the same
    indigo that reads well on a white card is far too dim on a dark one, which is
    what made every sortable header and link in dark mode all but invisible.

    One token cannot do both, hence the split rather than a tweak. (Same reasoning
    as the theme this replaces; only the hues moved.)
  */
  --accent: var(--brand-600);
  --accent-hover: var(--brand-700);
  --accent-bg: var(--brand-50);
  --accent-ink: var(--brand-600);
  --accent-ink-hover: var(--brand-700);
  /* The one gradient in the system, and the coloured shadow that sits under it.
     Used at most once or twice per screen — the primary button, the active nav
     pill, avatars, the current pagination page — never as a page background. */
  --accent-gradient: var(--brand-gradient);
  --accent-glow: 0 6px 16px rgba(91, 61, 246, 0.28);
  --focus-ring: 0 0 0 3px rgba(91, 61, 246, 0.18);
  /* Controls — buttons, chips, pills. In light mode these are exactly what the
     buttons already used (the shared hairline, a transparent or card-white face),
     so nothing here changes the light theme. See the dark blocks for why they
     exist at all. */
  --control-face: transparent;
  --control-edge: var(--border);
  --control-hover: var(--surface-hover);
  --control-ink-muted: var(--muted);
  --control-selected: var(--brand-50);
  --control-selected-ink: var(--brand-600);
  --control-danger-edge: var(--danger-border);
  /* Selected text — see ::selection. */
  --selection-bg: var(--brand-100);
  --selection-ink: var(--brand-900);

  /* Kept because a handful of marketing/CTA surfaces still reference them;
     re-pointed at the brand ramp so nothing renders in the old amber. */
  --cta-1: var(--brand-500);
  --cta-2: var(--brand-700);
  --brand-1: var(--brand-400);
  --brand-2: var(--brand-700);

  /* Same ink-vs-surface split as the accent above, but these are ink in almost
     every use (the house pattern is `background: var(--X-bg); color: var(--X)`),
     so the solid token itself is the one that lightens in dark mode.
     --danger-solid is the exception kept for the few FILLED red buttons, which
     put white on the colour and so need it to stay dark in both themes. */
  --success: #12833C;
  --success-dot: #16A34A;
  --success-bg: #E9F9F0;
  --success-border: #D3F3E1;
  --danger: #D32F2F;
  --danger-solid: #EF4444;
  --danger-bg: #FDECEC;
  --danger-border: #FBD9D9;
  --warn: #D07E05;
  --warn-bg: #FEF4E6;
  --warn-border: #FDE8CB;
  --info: #2563EB;
  --info-bg: #E8F1FE;
  --info-border: #D3E4FD;

  /* The rail is white in this theme (it was a dark navy gradient before), so
     these now read as "the sidebar's own surface tokens" rather than "the dark
     ones". Still tokens, not inlined values, because admin mode re-points them
     (see html.admin-shell) to tell the two areas apart at a glance. */
  --sidebar-1: #FFFFFF;
  --sidebar-2: #FFFFFF;
  --sidebar-text: var(--neutral-800);
  --sidebar-text-active: #FFFFFF;
  --sidebar-border: #EFF1F7;
  --sidebar-hover: var(--neutral-100);
  --sidebar-active-bg: var(--brand-gradient);
  --sidebar-section: var(--neutral-400);

  /* Inter-state borders on the India choropleth (india-map.js). Tokens rather
     than a second `prefers-color-scheme` block down beside .in-state, so the
     light/dark/system resolution below is stated exactly once in this file. */
  --map-stroke: rgba(52, 58, 80, 0.5);
  --map-stroke-strong: rgba(20, 24, 39, 0.85);
  --map-hover-fill: #16A34A;

  /* 12px cards, 10px tiles, 8px controls — the design system's card/tile/control
     radii, under the size-ordered names the app already calls them by. */
  --radius-lg: 12px;
  --radius-md: 10px;
  --radius-sm: 8px;
  /* Tags and inline code chips only — the one radius smaller than a control. */
  --radius-xs: 4px;
  --radius-pill: 999px;
  /* Depth comes from the hairline border, not the shadow, so the card shadow is
     deliberately almost invisible. Popovers are the one thing that lifts. */
  --shadow-card: 0 1px 2px rgba(20, 24, 39, 0.04), 0 4px 14px rgba(20, 24, 39, 0.04);
  --shadow-popover: 0 10px 30px rgba(20, 24, 39, 0.12);
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 22px;

  /* Restrained and purely functional: colour/border/shadow at 140ms, surfaces at
     200ms, the sidebar collapse at 320ms, all on one easing curve. */
  --ease: cubic-bezier(.2, 0, .2, 1);
  --dur-fast: 140ms;
  --dur-base: 200ms;
  --dur-slow: 320ms;

  /* Design-system aliases, so markup ported from the design files resolves
     without first being translated into this file's names. */
  --surface-card: var(--panel-bg);
  --surface-sunken: var(--neutral-50);
  --surface-hover: var(--neutral-100);
  --surface-selected: var(--brand-50);
  --text-body: var(--text);
  --text-muted: var(--muted);
  --border-subtle: var(--border-soft);
  --border-default: var(--border);
  --shadow-brand: var(--accent-glow);
}

/*
   Dark tokens, declared once and selected three ways — this is the pattern every
   dark block in the app follows (ui-alerts.css, analytics-shared.css too).

     :root[data-theme="dark"]                        — chose Dark explicitly
     prefers-color-scheme + :not([data-theme=light])  — chose System, OS is dark
     (neither)                                        — chose Light, or System on
                                                        a light OS: the :root above

   theme.js stamps that attribute in <head> before first paint. The two dark
   selectors can't share one rule list (only one of them belongs inside the media
   query), so the token list is written out twice and the two copies must stay
   identical — a token added to only one of them silently works in "Dark" but not
   in "System on a dark OS", or the reverse.

   `color-scheme` rides along so form controls, scrollbars and the browser's own
   canvas follow the CHOICE, not the OS. Without it, forcing Light on a dark-mode
   laptop leaves native <select> popups and scrollbars black against the page. */
:root[data-theme="light"] { color-scheme: light; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --neutral-0: #171C2C;   --neutral-25: #171C2C;  --neutral-50: #1E2436;  --neutral-100: #232A3F;
    --neutral-150: #2A3149; --neutral-200: #2E3550; --neutral-300: #3A4260; --neutral-400: #6F778F;
    --neutral-500: #9AA1B8; --neutral-600: #B5BBD0; --neutral-700: #CFD3E0; --neutral-800: #E1E4EE;
    --neutral-900: #F3F4F8;
    --brand-50: #262347; --brand-100: #33306A; --brand-200: #3E3A80;
    --brand-600: #7C63FF; --brand-700: #A899FF;
    --page-bg: #0F1320;
    --panel-bg: #171C2C;
    --border: #2E3550;
    --border-soft: #252B3F;
    /* One step deeper than the #7C63FF this used to be: white on that was
       4.13:1, under AA, on every filled button in dark mode. #6D53F8 is 4.93:1,
       and hover goes a step deeper again, as it already did. */
    --accent: #6D53F8;
    --accent-hover: #5B45D6;
    /* A control sits a step ABOVE the card it is on, with an edge that shows.
       The shared hairline (#2E3550) is 1.41:1 against a #171C2C card — right
       for dividers, near-invisible as the outline of a button. #4A5373 is
       2.25:1: plainly a button, still quieter than the content. */
    --control-face: #232A3F;
    --control-edge: #4A5373;
    --control-hover: #2E3752;
    /* Icon buttons draw their glyph in --muted; #9AA1B8 is right for secondary
       text but leaves an icon-only button looking disabled. */
    --control-ink-muted: #C3C8D9;
    /* The SELECTED control — a toggle's active side, an active chip. --accent-bg
       is #262347 in dark mode, which is no brighter than a resting control's
       face, so once controls got a face of their own the selected one stopped
       standing out. A clearer indigo, with lighter ink on it (5.8:1). */
    --control-selected: #3B3585;
    --control-selected-ink: #C4BAFF;
    /* A destructive button's red outline. --danger-border (#4A2226) is a card
       tint, 1.25:1 as an outline; #8C3A42 is 2.26:1, level with --control-edge. */
    --control-danger-edge: #8C3A42;
    --accent-ink: #A899FF;
    --accent-ink-hover: #C4BAFF;
    --accent-glow: 0 6px 16px rgba(124, 99, 255, 0.3);
    --focus-ring: 0 0 0 3px rgba(139, 116, 255, 0.28);
    /* White on a violet the dark cards can still show as a highlight: 6.4:1 for the
       text, 2.6:1 for the highlight against #171C2C. */
    --selection-bg: #5B45D6;
    --selection-ink: #FFFFFF;
    --success: #4ADE80; --success-dot: #4ADE80; --success-bg: #123424; --success-border: #1B4A32;
    --danger: #F87171;  --danger-bg: #3A1B1F;   --danger-border: #4A2226;
    --warn: #FBBF24;    --warn-bg: #3A2A12;     --warn-border: #4A361A;
    --info: #60A5FA;    --info-bg: #14243D;     --info-border: #1E3559;
    --sidebar-1: #171C2C;
    --sidebar-2: #171C2C;
    --sidebar-border: #252B3F;
    --map-stroke: rgba(11, 14, 26, 0.65);
    --map-stroke-strong: rgba(5, 7, 14, 0.9);
    --map-hover-fill: #4ADE80;
    --shadow-popover: 0 10px 30px rgba(0, 0, 0, 0.5);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --neutral-0: #171C2C;   --neutral-25: #171C2C;  --neutral-50: #1E2436;  --neutral-100: #232A3F;
  --neutral-150: #2A3149; --neutral-200: #2E3550; --neutral-300: #3A4260; --neutral-400: #6F778F;
  --neutral-500: #9AA1B8; --neutral-600: #B5BBD0; --neutral-700: #CFD3E0; --neutral-800: #E1E4EE;
  --neutral-900: #F3F4F8;
  --brand-50: #262347; --brand-100: #33306A; --brand-200: #3E3A80;
  --brand-600: #7C63FF; --brand-700: #A899FF;
  --page-bg: #0F1320;
  --panel-bg: #171C2C;
  --border: #2E3550;
  --border-soft: #252B3F;
  --accent: #6D53F8;
  --accent-hover: #5B45D6;
  --control-face: #232A3F;
  --control-edge: #4A5373;
  --control-hover: #2E3752;
  --control-ink-muted: #C3C8D9;
  --control-selected: #3B3585;
  --control-selected-ink: #C4BAFF;
  --control-danger-edge: #8C3A42;
  --accent-ink: #A899FF;
  --accent-ink-hover: #C4BAFF;
  --accent-glow: 0 6px 16px rgba(124, 99, 255, 0.3);
  --focus-ring: 0 0 0 3px rgba(139, 116, 255, 0.28);
  --selection-bg: #5B45D6;
  --selection-ink: #FFFFFF;
  --success: #4ADE80; --success-dot: #4ADE80; --success-bg: #123424; --success-border: #1B4A32;
  --danger: #F87171;  --danger-bg: #3A1B1F;   --danger-border: #4A2226;
  --warn: #FBBF24;    --warn-bg: #3A2A12;     --warn-border: #4A361A;
  --info: #60A5FA;    --info-bg: #14243D;     --info-border: #1E3559;
  --sidebar-1: #171C2C;
  --sidebar-2: #171C2C;
  --sidebar-border: #252B3F;
  --map-stroke: rgba(11, 14, 26, 0.65);
  --map-stroke-strong: rgba(5, 7, 14, 0.9);
  --map-hover-fill: #4ADE80;
  --shadow-popover: 0 10px 30px rgba(0, 0, 0, 0.5);
}

/*
  DARK-MODE CONTROLS — every button, chip and pill, without editing any of them.

  Reported: "in dark mode, grid's buttons are not visible". Measured across seventeen
  pages, it was nearly every secondary, outline and icon button in the app, not one
  grid: they all draw their outline with the shared --border and their face with
  --panel-bg (or nothing), and in dark mode those are the card's own colour and a
  1.41:1 hairline. The buttons were there; they had no edge.

  Rather than restate some forty button rules across a dozen pages, the TOKENS are
  re-pointed inside controls. A custom property resolves on the element that uses it,
  so `border: 1px solid var(--border)` written in any page's stylesheet now finds the
  control edge when it is on a button, and the hairline everywhere else — cards,
  tables and dividers are untouched. :where() keeps this at zero specificity; it
  declares variables, not styles, so it never competes with the rules that use them.

  Filled buttons are unaffected (they paint with --accent or a gradient), and so is
  anything drawn deliberately without a border or a face (tabs, the search clear ×).

  EVERY selector here is wrapped whole in :where(). Written as
  `:root:not([data-theme=light]) :where(button)`, the theme test outside it still
  counts — (0,2,0) — and quietly outranks page rules like `.row-actions button`
  (0,1,1), repainting buttons that were styled on purpose.
*/
@media (prefers-color-scheme: dark) {
  :where(:root:not([data-theme="light"])) :where(button, [role="button"], a.sc-btn, a[class*="-btn"], .marketplace-pill, .chip) {
    --border: var(--control-edge);
    --panel-bg: var(--control-face);
    --surface-hover: var(--control-hover);
    --muted: var(--control-ink-muted);
    --accent-bg: var(--control-selected);
    --accent-ink: var(--control-selected-ink);
    --danger-border: var(--control-danger-edge);
  }
  /* A button nothing on the page has styled falls back to the browser's own — a flat
     grey slab (buttonface) with white on it. Only that fallback is replaced: zero
     specificity, so any page rule for the same property still wins. */
  :where(:root:not([data-theme="light"]) button) {
    background-color: var(--control-face); color: var(--text);
    border: 1px solid var(--control-edge); border-radius: var(--radius-sm);
  }
}
:where(:root[data-theme="dark"]) :where(button, [role="button"], a.sc-btn, a[class*="-btn"], .marketplace-pill, .chip) {
  --border: var(--control-edge);
  --panel-bg: var(--control-face);
  --surface-hover: var(--control-hover);
  --muted: var(--control-ink-muted);
  --accent-bg: var(--control-selected);
  --accent-ink: var(--control-selected-ink);
  --danger-border: var(--control-danger-edge);
}
:where(:root[data-theme="dark"] button) {
  background-color: var(--control-face); color: var(--text);
  border: 1px solid var(--control-edge); border-radius: var(--radius-sm);
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font-family: var(--font-body);
  font-size: 13px;
  background: var(--page-bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}
/*
  Wrapped in :where() so both rules carry ZERO specificity.

  This is the fix for a bug that kept coming back one button at a time: a plain
  `a:hover` is (0,1,1) and out-specifies any `.sc-btn-primary { color: #fff }` at
  (0,1,0), so EVERY anchor styled as a filled button repainted its white label in
  the link colour on hover — indigo text on an indigo button, around 1.3:1, i.e.
  the label vanished under the cursor. It had already been patched three separate
  times (history.html's "Open Orders", ui-alerts.css's a.ui-btn, the topbar's
  Label Process button), each time for one button, each time with a comment
  explaining the same specificity trap.

  At zero specificity any class on the anchor now wins by default, so a button
  that looks like a button keeps its own colour without having to out-specify the
  base link style, and a bare <a> still gets the link colour because nothing else
  applies to it.
*/
:where(a) { color: var(--accent-ink); text-decoration: none; }
:where(a:hover) { color: var(--accent-ink-hover); }
h1, h2, h3, h4, h5, h6 { font-family: var(--font-heading); color: var(--text-heading); letter-spacing: -0.02em; }
h1 { font-weight: 800; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 8px; border: 2px solid transparent; background-clip: content-box; }
input, select, button, textarea { font-family: var(--font-body); }
/* A 3px indigo ring at 18%, never removed — the system's one focus treatment, set
   here so every control in the app gets it without each page restating it. */
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
/*
  Selected text. Its own pair of tokens, set per theme below, because the two brand
  steps it used to borrow do not move together: dark mode remaps --brand-100 to a dark
  indigo (#33306A) but leaves --brand-900 at its light-mode #2C1A85 — so selecting
  anything in dark mode painted dark indigo on dark indigo, and the text vanished the
  moment it was highlighted. Reported with a screenshot of the Seller Accounts cards.
*/
::selection { background: var(--selection-bg); color: var(--selection-ink); }

.shell { display: flex; height: 100vh; overflow: hidden; }

/* ---------- sidebar ---------- */
/* White rail with a hairline right border, not the dark navy gradient this
   replaces. The rail and the top bar are now the same surface as the cards, and
   the page behind them is the pale blue-grey — so what separates the chrome from
   the content is a 1px line, the same way everything else in this system is
   separated. */
.sidebar {
  width: 258px; flex-shrink: 0; background: var(--sidebar-1);
  border-right: 1px solid var(--sidebar-border);
  display: flex; flex-direction: column; color: var(--sidebar-text);
  overflow-y: auto; overflow-x: hidden; padding: 0 14px 18px;
  transition: width var(--dur-slow) var(--ease);
}
/* Collapsed state (desktop only — see the max-width:880px block below for the
   separate mobile drawer behavior, which ignores this entirely so a stale
   collapsed flag from a desktop session never leaves the mobile drawer stuck
   icon-only). The class lands on <html> (not <body>), stamped by a one-line
   addition to each page's own <head> auth-guard script, BEFORE first paint —
   that's what avoids a flash of a wide sidebar snapping narrow right after load;
   toggling it here on click (see shell.js initSidebarToggle) is what animates it
   back. */
@media (min-width: 881px) {
  html.sidebar-collapsed .sidebar { width: 76px; padding: 0 10px 18px; }
  html.sidebar-collapsed .sidebar-brand { justify-content: center; }
  html.sidebar-collapsed .sidebar-brand a { justify-content: center; }
  html.sidebar-collapsed .sidebar-brand-text,
  html.sidebar-collapsed .nav-label,
  html.sidebar-collapsed .nav-badge,
  html.sidebar-collapsed .sidebar-collapse-btn,
  html.sidebar-collapsed .sidebar-mp-title,
  html.sidebar-collapsed .sidebar-mp-name,
  html.sidebar-collapsed .sidebar-mp-count { display: none; }
  /* The group's label has nowhere to go at 76px; its rule is what still reads. */
  html.sidebar-collapsed .nav-group span { display: none; }
  html.sidebar-collapsed .nav-group { padding: 0 10px; margin: 12px 0 6px; }
  html.sidebar-collapsed .sidebar-nav a,
  html.sidebar-collapsed .sidebar-mp-row { justify-content: center; padding-left: 0; padding-right: 0; }
  html.sidebar-collapsed .sidebar-expand-btn { display: flex; }
}
/* The expand affordance only exists in the collapsed rail; in the expanded rail
   the collapse button sits beside the wordmark instead. Two buttons rather than
   one that moves, because at 76px there is no wordmark row left to sit in. */
.sidebar-expand-btn { display: none; }

/* Mobile off-canvas drawer — sidebar slides in over the content instead of
   pushing it, since there's no spare width to share below this point. Starts
   translated fully off-screen; shell.js's initSidebarToggle adds `sidebar-open`
   on <html> (deliberately not persisted — every page load is a real navigation,
   so starting closed each time is the expected mobile-nav behavior) to slide it
   in and show the backdrop. */
@media (max-width: 880px) {
  .sidebar {
    position: fixed; top: 0; left: 0; z-index: 200; height: 100vh; width: 258px;
    transform: translateX(-100%); transition: transform 0.22s var(--ease);
  }
  html.sidebar-open .sidebar { transform: translateX(0); box-shadow: var(--shadow-popover); }
  .sidebar-collapse-btn { display: none; }
  .sidebar-backdrop { display: none; }
  html.sidebar-open .sidebar-backdrop {
    display: block; position: fixed; inset: 0; z-index: 190; background: rgba(20, 24, 39, 0.45);
  }
  .shell { position: relative; }
}
.sidebar-brand { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 18px 0 14px; }
.sidebar-brand a { display: flex; align-items: center; gap: 10px; color: inherit; min-width: 0; }
.sidebar-brand a:hover { text-decoration: none; }
.sidebar-logo { width: 36px; height: 36px; object-fit: contain; border-radius: 8px; flex-shrink: 0; }
.sidebar-brand-name { display: block; font-weight: 800; font-size: 16px; letter-spacing: -0.02em; color: var(--text-heading); }
.sidebar-brand-sub { font-size: 10px; letter-spacing: 0.08em; color: var(--subtle); font-weight: 700; }
/* The collapse toggle moved off the top bar and into the rail it controls (the
   top bar keeps its own toggle for the mobile drawer, which is a different
   behaviour — see initSidebarToggle). */
.sidebar-collapse-btn,
.sidebar-expand-btn {
  align-items: center; justify-content: center; flex: none;
  width: 30px; height: 30px; border: 1px solid var(--border); background: var(--panel-bg);
  color: var(--muted); border-radius: var(--radius-sm); cursor: pointer;
  transition: var(--dur-fast) var(--ease); transition-property: background-color, color, border-color;
}
.sidebar-collapse-btn { display: flex; }
.sidebar-expand-btn { width: 40px; height: 32px; margin: 0 auto 10px; }
.sidebar-collapse-btn:hover,
.sidebar-expand-btn:hover { background: var(--surface-hover); color: var(--text); }
.sidebar-nav { display: flex; flex-direction: column; gap: 3px; }
/* ---------------------------------------------------------------- nav groups ----
   A labelled section in the rail (Returns). Quiet uppercase: it names a group, it is
   not competing with the items under it.

   Collapsed to 76px there is no room for a word, so the label goes and the rule stays
   — the separation is the part that still works at that width. */
.nav-group {
  display: flex; align-items: center; gap: 8px;
  margin: 14px 0 4px; padding: 0 14px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--subtle);
}
.nav-group::after { content: ""; flex: 1; height: 1px; background: var(--border-soft); }
.nav-group-end { height: 1px; margin: 10px 14px 4px; background: var(--border-soft); }

.sidebar-nav a {
  display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-radius: var(--radius-md);
  cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--sidebar-text);
  transition: background-color var(--dur-fast) var(--ease); text-decoration: none;
}
.sidebar-nav a:hover { background: var(--sidebar-hover); color: var(--sidebar-text); text-decoration: none; }
/* The one gradient in the system, with its coloured shadow — this and the primary
   button are the only two places it appears on most screens. */
.sidebar-nav a.active {
  background: var(--sidebar-active-bg); color: var(--sidebar-text-active); font-weight: 700;
  box-shadow: var(--accent-glow);
}
.sidebar-nav a.active:hover { color: var(--sidebar-text-active); }
.sidebar-nav a .nav-icon { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; flex: none; }
.sidebar-nav a .nav-icon svg { width: 18px; height: 18px; display: block; }
.sidebar-nav a .nav-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-nav a .nav-badge {
  margin-left: auto; font-size: 11px; font-weight: 700; background: var(--accent-bg);
  color: var(--accent-ink); padding: 2px 7px; border-radius: var(--radius-pill);
}
.sidebar-nav a.active .nav-badge { background: rgba(255, 255, 255, 0.22); color: #fff; }
.sidebar-mp { margin-top: 22px; border-top: 1px solid var(--sidebar-border); padding-top: 18px; }
.sidebar-mp-title {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700;
  color: var(--sidebar-section); padding: 0 6px 10px;
}
.sidebar-mp-row {
  display: flex; align-items: center; gap: 12px; padding: 8px 14px; border-radius: var(--radius-md);
  font-size: 13px; font-weight: 500; color: var(--sidebar-text); text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease);
}
.sidebar-mp-row:hover { background: var(--sidebar-hover); color: var(--sidebar-text); text-decoration: none; }
/* ---------- marketplace mark ----------
   The lettered colour tile: a marketplace's own colour with its initial on it.
   The real Meesho/Flipkart/Amazon/Snapdeal logos are third-party trademarks and
   are not bundled, so this is the placeholder the design system ships — and it is
   the ONE way a marketplace is drawn anywhere in the portal, so a Meesho row on
   Seller Accounts, in Recent Jobs, in the rail and in an Orders table all carry
   the same mark instead of four separately-invented tinted glyphs.

   Sized by one custom property, so every call site scales the radius and the
   letter together in the proportions the design uses (0.28 and 0.58 of the box).
   Lowercase on purpose: the design's marks read `m f a s`, not `M F A S`. */
.mp-mark {
  --mp-size: 22px;
  width: var(--mp-size); height: var(--mp-size); flex: none;
  border-radius: calc(var(--mp-size) * 0.28);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-heading); font-weight: 800; letter-spacing: -0.02em;
  font-size: calc(var(--mp-size) * 0.58); line-height: 1;
  background: var(--neutral-200); color: var(--neutral-600);
  text-transform: lowercase; user-select: none;
}
.mp-mark[data-mp="meesho"] { background: var(--mp-meesho); color: var(--mp-meesho-ink); }
.mp-mark[data-mp="flipkart"] { background: var(--mp-flipkart); color: var(--mp-flipkart-ink); }
.mp-mark[data-mp="amazon"] { background: var(--mp-amazon); color: var(--mp-amazon-ink); }
.mp-mark[data-mp="snapdeal"] { background: var(--mp-snapdeal); color: var(--mp-snapdeal-ink); }
/* Amazon's is #131921 — near-black, which disappears against a dark rail or a
   dark card. Its own accent orange is the other half of that brand and stays
   legible in both themes, so the tile swaps to it rather than being lightened
   into a grey that belongs to nobody. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .mp-mark[data-mp="amazon"] { background: var(--mp-amazon-accent); color: #141827; }
}
:root[data-theme="dark"] .mp-mark[data-mp="amazon"] { background: var(--mp-amazon-accent); color: #141827; }

/* The mark with its name beside it — the design's `withLabel` variant, for the
   places a tile alone would not say enough (a table cell, a filter row). */
.mp-mark-label { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--text); white-space: nowrap; text-transform: capitalize; }

/* The rail's row keeps its own class for layout; the mark inside it is shared. */
.sidebar-mp-row.is-idle .mp-mark { filter: saturate(0.25); opacity: 0.6; }
.sidebar-mp-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-mp-count { font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }
/* #apiKey is now a hidden input (see shell.js's sidebarHtml) — every page's
   script still reads/writes it by id for the dev-only X-Api-Key header, it's
   just no longer a visible sidebar-footer field, so no visual styling needed. */

/* Hover tooltip for the collapsed rail: at 76px the label is gone, so the name
   (and the badge count, where there is one) has to come back on hover or the
   icons are a guessing game. Positioned by shell.js against the rail's right
   edge; pointer-events off so it can never eat the click it's describing. */
.sidebar-tip {
  position: fixed; z-index: 60; display: flex; align-items: center; gap: 10px;
  padding: 9px 14px 9px 11px; background: var(--panel-bg); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  font-size: 13px; font-weight: 600; color: var(--text); white-space: nowrap; pointer-events: none;
}
.sidebar-tip .sidebar-tip-icon { display: flex; flex: none; width: 20px; justify-content: center; color: var(--accent-ink); }
.sidebar-tip .sidebar-tip-icon svg { width: 17px; height: 17px; }
.sidebar-tip .sidebar-tip-count { font-family: var(--font-mono); font-size: 11.5px; font-weight: 500; color: var(--muted); }

/* ---------- topbar + main ---------- */
.shell-main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
.topbar {
  flex-shrink: 0; height: 70px; display: flex; align-items: center; gap: 12px; padding: 0 20px;
  background: var(--panel-bg); border-bottom: 1px solid var(--border-soft);
}
/* Outline icon button — the top bar's utility shape (feedback, theme, settings)
   and the mobile nav toggle. 32px square, hairline border, ghost fill. */
.topbar-toggle {
  background: var(--panel-bg); border: 1px solid var(--border); cursor: pointer; color: var(--muted);
  width: 32px; height: 32px; border-radius: var(--radius-sm); display: flex; align-items: center;
  justify-content: center; flex-shrink: 0;
  transition: var(--dur-fast) var(--ease); transition-property: background-color, color, border-color;
}
.topbar-toggle:hover { background: var(--surface-hover); color: var(--text); }
/* The rail carries its own collapse control now, so this button's only remaining
   job is the mobile drawer. Hidden on desktop rather than removed, so
   initSidebarToggle keeps one element to bind and the drawer still opens. */
@media (min-width: 881px) { .topbar-toggle { display: none; } }
@media (max-width: 880px) {
  .topbar { padding: 0 16px; gap: 10px; }
  /* Every shell page's own content starts with `<div style="padding:20px 28px
     32px">` (label-process.html is the one exception, with its own layout) —
     targeting that generically here, rather than editing all 8 files' inline
     styles, keeps the "change it once" goal intact for this too. !important
     because it's beating an inline style, not a specificity accident. */
  .shell-content > div:first-child { padding: 16px 16px 24px !important; }
}
@media (max-width: 560px) {
  .topbar-search { max-width: none; }
}
.topbar-search { flex: 1 1 200px; min-width: 0; max-width: 520px; position: relative; }
.topbar-search input {
  width: 100%; height: 40px; padding: 0 14px 0 38px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); font-size: 13px; background: var(--surface-sunken); color: inherit;
  transition: var(--transition-control, all var(--dur-fast) var(--ease));
}
.topbar-search input:focus { outline: none; background: var(--panel-bg); border-color: var(--brand-500); box-shadow: var(--focus-ring); }
.topbar-search input::placeholder { color: var(--subtle); }
.topbar-search .topbar-search-icon {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--subtle); font-size: 13px; pointer-events: none;
}
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }
/* The primary button: gradient fill, coloured shadow, 36px tall. One per screen
   is the rule, and in the shell this is it. */
.topbar-label-process-btn {
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; flex-shrink: 0;
  font-size: 13px; font-weight: 700; color: #fff; background: var(--accent-gradient);
  height: 36px; padding: 0 16px; border-radius: var(--radius-sm); box-shadow: var(--accent-glow);
  transition: var(--dur-fast) var(--ease); transition-property: filter, box-shadow, transform;
}
.topbar-label-process-btn:hover { filter: brightness(1.06); color: #fff; text-decoration: none; }
.topbar-label-process-btn:active { transform: scale(0.98); box-shadow: none; }
@media (max-width: 640px) { .topbar-label-process-btn span { display: none; } .topbar-label-process-btn { padding: 0 12px; } }
.topbar-admin-tag {
  display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 700;
  color: var(--accent-ink); background: var(--accent-bg); padding: 7px 12px; border-radius: var(--radius-pill);
}
/* overflow-x explicit, not left to default — per the CSS overflow spec, when one
   axis is non-visible the other computes away from `visible` to `auto` too, so
   `overflow-y: auto` alone silently made this ALSO horizontally scrollable. That
   let any one wide element inside it (a data table with many columns, say) drag
   the WHOLE content area into horizontal scroll together — scrolling right to see
   a table's later columns also scrolled everything above/around it off-screen to
   the left, not just the table. Real pages should contain their own overflow in a
   dedicated wrapper (e.g. .table-scroll) instead of relying on this. */
.shell-content { flex: 1; overflow-y: auto; overflow-x: hidden; }

/* The top bar's outline icon button — 32px square, hairline border, ghost fill.
   Shared by the Settings shortcut here and, via the same tokens, by the feedback
   and theme controls that sit beside it, so the four read as one row of controls
   rather than four separately-invented ones. */
.topbar-icon-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 32px; height: 32px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-bg); color: var(--muted); cursor: pointer;
  transition: var(--dur-fast) var(--ease); transition-property: background-color, color, border-color;
}
.topbar-icon-btn:hover { background: var(--surface-hover); color: var(--text); text-decoration: none; }

/* account pill (topbar), reused from account.js but restyled for the shell */
.account-pill {
  display: inline-flex; align-items: center; gap: 9px; white-space: nowrap; cursor: default;
  font-size: 13px; color: var(--text); padding: 4px 10px 4px 4px; border-radius: var(--radius-md); border: 1px solid var(--border);
}
/* One of the handful of places the brand gradient is allowed to appear. */
.account-pill .avatar {
  width: 30px; height: 30px; border-radius: 50%; background: var(--accent-gradient);
  color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 800; flex-shrink: 0;
}
.account-pill .account-id { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.25; min-width: 0; }
.account-pill .account-name { font-size: 13px; font-weight: 700; color: var(--text-heading); max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-pill .account-role { font-size: 11px; font-weight: 500; color: var(--muted); }
/* The top bar drops the search field at 560px and is already tight well before
   that; the role line is the next thing that can go without losing anything —
   the name above it still says whose account this is. */
@media (max-width: 900px) { .account-pill .account-role { display: none; } }
@media (max-width: 760px) { .account-pill .account-id { display: none; } .account-pill { padding: 4px; } }
.account-pill .signout { color: var(--muted); font-size: 13px; line-height: 1; padding: 2px; }
.account-pill .signout:hover { color: var(--danger); }
.account-pill.signed-out { border-style: dashed; padding: 4px 12px; }
.account-pill.signed-out a { color: var(--accent-ink); font-weight: 600; }

/* account dropdown (My Profile / Sign out) */
#accountIndicator { position: relative; display: inline-block; }
button.account-pill {
  cursor: pointer; background: var(--panel-bg); font-family: inherit;
}
button.account-pill:hover { border-color: var(--accent); }
.account-pill .caret { display: inline-flex; color: var(--subtle); transition: transform var(--dur-fast) var(--ease); }
button.account-pill.open .caret { transform: rotate(180deg); }
.account-menu {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 240px; z-index: 60;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-popover); padding: 6px;
}
.account-menu a {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px;
  font-size: 13px; color: var(--text); text-decoration: none;
}
.account-menu a { font-weight: 500; }
.account-menu a:hover { background: var(--surface-hover); text-decoration: none; }
.account-menu a i { width: 16px; text-align: center; color: var(--muted); }
/* The glyph column. Fixed width so the labels line up whether the row's mark is a
   wide one (the shield) or a narrow one (the chevron). */
.account-menu .menu-glyph,
.account-sub-toggle .menu-glyph { display: inline-flex; flex: none; width: 18px; justify-content: center; color: var(--muted); }
.account-menu a.danger .menu-glyph { color: var(--danger); }
.account-menu a.danger, .account-menu a.danger i { color: var(--danger); font-weight: 600; }
.account-menu a.danger:hover { background: var(--danger-bg); }
.account-menu .sep { height: 1px; background: var(--border-soft); margin: 6px 4px; }

/* ---------- admin submenu (account.js) ----------
   The admin sections, folded away inside the account menu. Ten entries would otherwise
   turn a four-item menu into a wall for the seller who is not an admin — and for the
   one who is, most visits are not admin visits. */
.account-sub-toggle {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 12px;
  border: none; border-radius: 8px; background: none; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--text); text-align: left;
}
.account-sub-toggle:hover { background: var(--surface-hover); }
.account-sub-toggle i { width: 16px; text-align: center; color: var(--muted); }
.account-sub-toggle .menu-label { flex: 1; }
.account-sub-toggle .sub-caret { display: inline-flex; margin-left: auto; color: var(--subtle); transition: transform var(--dur-fast) var(--ease); }
.account-sub-toggle.open .sub-caret { transform: rotate(180deg); }
/* Indented and hung off a rule, so an open submenu reads as contained by the row above
   rather than as more of the same list. */
.account-sub { margin: 2px 0 4px 18px; padding-left: 8px; border-left: 2px solid var(--border-soft); }
.account-sub[hidden] { display: none; }
.account-sub a { padding: 7px 10px; font-size: 12.5px; }

/* ---------- colour-theme control (theme.js) ---------- */
/* Sits in the topbar beside the account pill rather than inside its menu: it's a
   display setting, not an account one, and someone comparing the two themes wants
   it reachable in one click. Deliberately borrows .account-menu's popover
   vocabulary (same radius, padding, row height) so the two neighbours don't read
   as coming from different apps. */
/* Every rule here is written under #themeSwitch. That's not stylistic — an id
   puts these above anything a page's own stylesheet can say with element or
   class selectors, and label-process.html is a live example of a page that
   styles bare `button` and `button:hover` for its whole surface. Without the id
   the theme menu inherits that page's accent-blue fill and white label. */
#themeSwitch { position: relative; display: inline-block; flex-shrink: 0; }
#themeSwitch .theme-btn {
  background: var(--panel-bg); border: 1px solid var(--border); cursor: pointer; font-size: 14px; color: var(--muted);
  width: 32px; height: 32px; padding: 0; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center;
  transition: var(--dur-fast) var(--ease); transition-property: background-color, color, border-color;
}
#themeSwitch .theme-btn:hover { background: var(--surface-hover); color: var(--text); }
#themeSwitch .theme-btn.open { color: var(--accent-ink); border-color: var(--brand-200); background: var(--accent-bg); }
#themeSwitch .theme-menu {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 180px; z-index: 60;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-popover); padding: 6px;
}
#themeSwitch .theme-opt {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 12px; border: 0; border-radius: 8px;
  background: none; font: inherit; font-size: 13px; color: var(--text); text-align: left; cursor: pointer;
}
#themeSwitch .theme-opt:hover { background: var(--surface-hover); color: var(--text); }
#themeSwitch .theme-opt > i:first-child { width: 16px; text-align: center; color: var(--muted); font-size: 13px; }
#themeSwitch .theme-opt-glyph { display: inline-flex; flex: none; width: 18px; justify-content: center; color: var(--muted); }
#themeSwitch .theme-opt > span:not(.theme-opt-glyph):not(.theme-tick) { flex: 1; }
/* The tick marks the active option; hidden (not removed) so the three rows keep
   equal width and the labels don't shuffle sideways as the selection moves. */
#themeSwitch .theme-opt .theme-tick { display: inline-flex; flex: none; color: var(--accent-ink); visibility: hidden; }
#themeSwitch .theme-opt[aria-checked="true"],
#themeSwitch .theme-opt[aria-checked="true"]:hover { color: var(--accent-ink); font-weight: 600; }
#themeSwitch .theme-opt[aria-checked="true"] > i:first-child,
#themeSwitch .theme-opt[aria-checked="true"] .theme-opt-glyph { color: var(--accent-ink); }
#themeSwitch .theme-opt[aria-checked="true"] .theme-tick { visibility: visible; }

/* ---------- form controls ----------
   A baseline for every text control in the shell: 36px tall, 8px radius, hairline
   border, and the system's focus treatment (a --brand-500 border plus the 3px
   indigo ring at 18%).

   Wrapped in :where() so the whole block has ZERO specificity. That matters:
   about a dozen pages already style their own inputs (`.filters input`,
   `.toolbar input`, `.edit-field input`, label-process's whole control surface),
   and those rules come after this file in source order anyway. At zero
   specificity this can only reach controls no page has an opinion about — it
   raises the floor without overriding a single existing decision, which is the
   only way to add a global input rule to a codebase this size without breaking a
   page nobody re-opens.

   Checkboxes and radios are deliberately excluded: they are not text fields, and
   giving them a height and padding is what turns them into rectangles. */
:where(
  input[type="text"], input[type="search"], input[type="email"], input[type="password"],
  input[type="number"], input[type="tel"], input[type="url"], input[type="date"],
  input:not([type]), select, textarea
) {
  height: var(--control-h, 36px);
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  transition: var(--dur-fast) var(--ease);
  transition-property: border-color, box-shadow, background-color;
}
:where(textarea) { height: auto; padding: 10px 12px; line-height: 1.5; resize: vertical; }
:where(input, select, textarea)::placeholder { color: var(--subtle); }
:where(input, select, textarea):focus {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: var(--focus-ring);
}
:where(input, select, textarea):disabled { opacity: 0.5; cursor: not-allowed; }
/* Checkboxes and radios keep the accent, and nothing else. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }

/* Field label above its control — the pattern repeated a dozen times in the
   History filter grid and again in every edit form. */
.sc-field { display: flex; flex-direction: column; gap: 5px; }
.sc-field > label { font-size: 12px; font-weight: 500; color: var(--muted); }

/* ---------- shared card/table/badge vocabulary ---------- */
/* Cards use the SUBTLE hairline, not the default one: at 12px radius on a pale
   page the stronger border read as an outline drawn around the card rather than
   as its edge. The stronger one is kept for nested cards (a store card inside the
   Seller Accounts panel), which need to read against a white parent. */
.sc-card { background: var(--panel-bg); border: 1px solid var(--border-soft); border-radius: var(--radius-lg); box-shadow: var(--shadow-card); }
.sc-card-nested { border-color: var(--border); }
.sc-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.sc-table th {
  text-align: left; padding: 11px 12px; font-size: 12px; letter-spacing: 0; font-weight: 600;
  color: var(--muted); border-bottom: 1px solid var(--border); background: var(--surface-sunken);
}
.sc-table td { padding: 13px 12px; border-bottom: 1px solid var(--border-soft); }
/* Row hover tints to the neutral step, not the brand one: rows highlight on
   nearly every table in the app, and an indigo wash that often stops reading as
   "this row" and starts reading as "these rows are selected". Brand tint is
   reserved for actual selection. */
.sc-table tbody tr:hover td { background: var(--surface-hover); }
.sc-table tbody tr.is-selected td { background: var(--surface-selected); }
/* Shared across every .sc-table on every page (History, Admin, Products) — same
   ▲/▼ click-to-sort convention as label-process.html's Order Data table. */
.sc-table th.sortable-th { cursor: pointer; user-select: none; }
.sc-table th.sortable-th:hover { color: var(--accent-ink); }
.sc-table th.sortable-th.sorted { color: var(--accent-ink); }
.sc-table th .sort-indicator { font-size: 10px; margin-left: 3px; opacity: 0.8; }
.sc-pill {
  font-size: 11.5px; font-weight: 600; padding: 3px 10px; border-radius: var(--radius-pill); background: var(--surface-hover);
  color: var(--text); text-transform: capitalize; display: inline-block;
}
/* Semantic colour appears only as a 50-level tint behind a 500/600-level label,
   never as a large fill — the rule the whole system follows. */
.sc-pill-success { background: var(--success-bg); color: var(--success); }
.sc-pill-warn { background: var(--warn-bg); color: var(--warn); }
.sc-pill-danger { background: var(--danger-bg); color: var(--danger); }
.sc-pill-info { background: var(--info-bg); color: var(--info); }
.sc-pill-brand { background: var(--accent-bg); color: var(--accent-ink); }
.sc-status-done { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--success); }
.sc-status-done .sc-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--success-dot); }
.sc-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: none; font-size: 13px; font-weight: 700; height: 36px; padding: 0 16px;
  border-radius: var(--radius-sm); cursor: pointer; white-space: nowrap;
  transition: var(--dur-fast) var(--ease);
  transition-property: background-color, border-color, box-shadow, color, filter;
}
/* Press is scale + losing the coloured shadow, never a colour inversion. */
.sc-btn:active { transform: scale(0.98); }
.sc-btn:disabled { opacity: 0.5; box-shadow: none; cursor: not-allowed; }
/* --cta and --primary are the same gradient now. They were two different fills
   (amber CTA, solid accent) in the old theme; this system has exactly one primary
   treatment, so the two class names are kept as aliases rather than re-pointed at
   two invented variants — every existing caller keeps working and they all land
   on the one correct button. */
.sc-btn-cta,
.sc-btn-primary { background: var(--accent-gradient); color: #fff; box-shadow: var(--accent-glow); }
.sc-btn-cta:hover,
.sc-btn-primary:hover { filter: brightness(1.06); color: #fff; }
.sc-btn-cta:active,
.sc-btn-primary:active { box-shadow: none; }
.sc-btn-secondary { border: 1px solid var(--border); background: var(--panel-bg); color: var(--text); }
.sc-btn-secondary:hover { border-color: var(--border-strong); background: var(--surface-hover); }
.sc-btn-ghost { background: transparent; color: var(--text); }
.sc-btn-ghost:hover { background: var(--surface-hover); }
.sc-btn-danger { background: var(--danger-solid); color: #fff; }
.sc-btn-danger:hover { filter: brightness(0.94); color: #fff; }
.sc-mono { font-family: var(--font-mono); }

/* ---------- page header ----------
   Bare-noun title, one explanatory sentence under it ending in a period, actions
   on the right.

   `.page-head` was defined identically in nine separate page <style> blocks (all
   nine said `font-size: 21px; font-weight: 700`), which is why the retheme to
   28px/800 could not be done in one place: each copy came after shell.css in
   source order and won. The three shared rules now live here and those copies are
   gone; the pages that genuinely differ (support.html caps its paragraph width)
   keep only the line that differs. */
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
.page-head h1 { margin: 0; font-size: 28px; font-weight: 800; line-height: 1.15; letter-spacing: -0.02em; }
.page-head p { margin: 6px 0 0; font-size: 14px; color: var(--muted); }
.page-head-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.page-head a.back-link { font-size: 13px; font-weight: 600; color: var(--accent-ink); text-decoration: none; display: inline-flex; align-items: center; gap: 6px; }
.page-head a.back-link:hover { text-decoration: underline; }

/* ---------- stat card ----------
   Label, metric, signed comparative delta, and a tinted glyph tile. The tile tint
   is a 50-level semantic colour behind its matching 500-level glyph — the only
   way semantic colour is allowed to appear at this size. */
.sc-stat { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px; }
.sc-stat-label { font-size: 12px; font-weight: 500; color: var(--muted); }
.sc-stat-value { margin-top: 6px; font-size: 24px; font-weight: 800; line-height: 1.15; letter-spacing: -0.02em; color: var(--text-heading); }
.sc-stat-delta { margin-top: 6px; font-size: 12px; font-weight: 600; color: var(--success); }
.sc-stat-delta.is-down { color: var(--danger); }
.sc-stat-delta.is-flat { color: var(--muted); }
.sc-stat-tile {
  width: 38px; height: 38px; flex: none; border-radius: var(--radius-md); display: inline-flex;
  align-items: center; justify-content: center; background: var(--accent-bg); color: var(--accent-ink);
}
.sc-stat-tile.is-success { background: var(--success-bg); color: var(--success-dot); }
.sc-stat-tile.is-warn { background: var(--warn-bg); color: var(--warn); }
.sc-stat-tile.is-danger { background: var(--danger-bg); color: var(--danger); }
.sc-stat-tile.is-info { background: var(--info-bg); color: var(--info); }

/* ---------- India state-wise choropleth (india-map.js) ---------- */
/* Shared by analytics.html and dashboard.html. Sequential single-hue ramp —
   set as `color` on each .in-state group and picked up via fill="currentColor"
   in the vendored SVG (assets/maps/india-states.svg) — never a rainbow, per
   the dataviz skill's sequential-encoding rule. */
.in-map-wrap { position: relative; max-width: 460px; margin: 0 auto; }
.in-map-wrap svg { width: 100%; height: auto; display: block; }
.in-state { cursor: pointer; color: var(--in-state-fill); transition: color 0.12s ease; }
/* A visible ink-toned border between states (not a pale surface-color gap —
   at this map's scale, especially the small northeastern cluster, a
   panel-bg stroke reads as "shape falling apart" rather than "distinct
   states"). Stronger still on hover so the highlighted state's outline
   reads clearly against its neighbors. */
.in-state path { stroke: var(--map-stroke); stroke-width: 1.1; stroke-linejoin: round; }

/* Hover recolours the state green and changes NOTHING else — no extra outline, no
   change to the resting border, no change to geometry or paint order.

   It works by swapping the fill colour only. india-map.js sets each state's own
   colour into --in-state-fill rather than straight onto `color`, so this rule can
   win on hover without !important — an inline style would otherwise always beat a
   class rule, which is why the colour is handed over through a custom property.

   Green on purpose, and deliberately NOT the --success token: the choropleth is a
   sequential INDIGO ramp now (it was blue; see india-map.js), so a green still
   reads as "the one under the cursor" rather than as another value on the scale —
   and if anything more clearly, since no step of the ramp is near it. Reusing a
   status colour for a pointer highlight would overload a reserved meaning. */
.in-state:hover, .in-state.in-state-hover, .in-state:focus-visible { color: var(--map-hover-fill); outline: none; }
.in-map-tooltip {
  position: fixed; transform: translate(-50%, calc(-100% - 8px));
  background: var(--text); color: var(--panel-bg); font-size: 11.5px; line-height: 1.5;
  padding: 7px 10px; border-radius: 8px; white-space: nowrap; pointer-events: none;
  opacity: 0; visibility: hidden; transition: opacity 0.1s ease; z-index: 100;
  box-shadow: 0 8px 20px -6px rgba(0,0,0,0.35);
}
.in-map-tooltip.show { opacity: 1; visibility: visible; }
/* Body sans, not the mono figure font used elsewhere (.viz-tooltip etc.) —
   at this tooltip's small size JetBrains Mono's 0 and 8 are nearly
   identical (both closed, similarly rounded), which reads as a genuine
   ambiguous number rather than a style choice. Instrument Sans' 8 is
   clearly two-lobed against 0's single oval, and this value is a single
   floating figure (not a column), so there's no tabular-alignment need to
   trade away. */
.in-map-tooltip strong { font-family: var(--font-body); font-weight: 700; }
.in-map-legend { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 14px; font-size: 11px; color: var(--muted); }
.in-map-legend-ramp { display: inline-flex; border-radius: 4px; overflow: hidden; }
.in-map-legend-ramp span { width: 18px; height: 10px; }
.in-map-credit { text-align: center; font-size: 10px; color: var(--muted); margin-top: 8px; }
.in-map-credit a { color: var(--muted); text-decoration: underline; }
