/* =========================================================================
   Tailor Ji — brand design tokens + component system
   Dark is the DEFAULT. Light is applied via <html data-theme="light">.

   Rule: components reference tokens ONLY. No hardcoded colour value lives
   outside the two blocks at the top of this file — not in a view, not in an
   inline style, not further down this file.

   Layout of this file:
     1. Tokens (dark)          5. App shell — sidebar, topbar, page
     2. Tokens (light)         6. Components — tiles, tables, pills, meters
     3. Base elements          7. Charts
     4. Controls               8. Responsive
   ========================================================================= */

/* =======================================================================
   1. TOKENS — DARK (default)
   ======================================================================= */
:root {
  /* ---- Brand constants (identical across themes) ---- */
  --brand-navy: #0B2368;   /* primary  */
  --brand-gold: #E9B65E;
  --brand-cyan: #45DFD0;

  /* ---- Typography ---- */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;

  --fs-xs:   0.75rem;
  --fs-sm:   0.8125rem;
  --fs-base: 0.9375rem;
  --fs-md:   1rem;
  --fs-lg:   1.125rem;
  --fs-xl:   1.375rem;
  --fs-2xl:  1.75rem;
  --fs-3xl:  2.25rem;

  /* ---- Spacing scale. Every margin/padding/gap comes from here, so
          rhythm is a choice made once rather than per-view. ---- */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  20px;
  --sp-6:  24px;
  --sp-7:  32px;
  --sp-8:  48px;

  /* ---- Radius / elevation ---- */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-pill: 999px;

  --shadow:      0 2px 8px rgba(0, 0, 0, .45);
  --shadow-lift: 0 8px 24px rgba(0, 0, 0, .55);

  --sidebar-w: 244px;

  /* ---- Semantic colour ---- */
  --color-primary: var(--brand-navy);
  --color-accent:  var(--brand-cyan);
  --color-gold:    var(--brand-gold);

  --bg:        #060B1A;   /* app background (deep navy-black)   */
  --bg-elev:   #0B1430;   /* raised surfaces / inputs           */
  --card:      #0E1A3A;   /* cards / panels                     */
  --card-2:    #12224A;   /* nested surface inside a card       */
  --sidebar:   #080F26;   /* the rail — one step below the app  */
  --text:      #EAF0FF;   /* primary text                       */
  --muted:     #8DA0C7;   /* secondary text                     */
  --faint:     #61729B;   /* tertiary — axis ticks, meta        */
  --border:    #1E2C55;   /* hairlines / dividers               */
  --border-strong: #2C3E70;

  --primary-contrast: #FFFFFF;  /* text on primary/navy fills   */
  --gold-contrast:    #0B2368;  /* text on gold fills           */

  /* ---- Status. Reserved meaning: never reused as a series colour. ---- */
  --color-ok:   #3FBF7F;
  --color-warn: #E9B65E;
  --color-bad:  #FF6B6B;

  /* ---- Tinted washes. Backgrounds for pills/active states; the alpha
          keeps them tied to whatever surface they sit on. ---- */
  --wash-gold:   rgba(233, 182, 94, .14);
  --wash-accent: rgba(69, 223, 208, .13);
  --wash-ok:     rgba(63, 191, 127, .14);
  --wash-warn:   rgba(233, 182, 94, .14);
  --wash-bad:    rgba(255, 107, 107, .14);
  --wash-hover:  rgba(255, 255, 255, .04);

  --focus-ring: 0 0 0 3px rgba(69, 223, 208, .35);

  /* ---- Ordinal ramp: the six stitching stages, in pipeline order.
          ONE hue (teal, the brand accent family), monotone lightness,
          computed in OKLCH and checked with the dataviz validator
          (monotone L, adjacent dL >= 0.06, light end 2.94:1 on --card,
          hue spread 1 degree). It is ordinal, not categorical: the colour
          carries the ORDER of the pipeline, which a set of unrelated hues
          could not. Later stage = brighter, because on a dark surface
          "more" reads as lighter. ---- */
  --stage-1: #207070;   /* placed    */
  --stage-2: #2b8888;   /* cutting   */
  --stage-3: #36a1a1;   /* stitching */
  --stage-4: #49b8b8;   /* finishing */
  --stage-5: #5bd0cf;   /* ready     */
  --stage-6: #73e7e6;   /* delivered */

  /* Chart chrome — one step off the surface, recessive by design. */
  --grid: #16224a;
}

/* =======================================================================
   2. TOKENS — LIGHT
   ======================================================================= */
[data-theme="light"] {
  --color-primary: var(--brand-navy);
  --color-accent:  #0E7A88;      /* teal (brief)                */
  --color-gold:    #B08324;      /* muted gold for light bg     */

  --bg:        #F5F7FB;
  --bg-elev:   #FFFFFF;
  --card:      #FFFFFF;
  --card-2:    #F7F9FD;
  --sidebar:   #FFFFFF;
  --text:      #0F1A33;
  --muted:     #5C6B87;
  --faint:     #8492AC;
  --border:    #E2E8F2;
  --border-strong: #C9D4E6;

  --primary-contrast: #FFFFFF;
  --gold-contrast:    #FFFFFF;

  --shadow:      0 1px 3px rgba(15, 26, 51, .08);
  --shadow-lift: 0 6px 20px rgba(15, 26, 51, .12);

  --color-ok:   #1E7F4F;   /* darker: these sit on a light background */
  --color-warn: #9A6B12;
  --color-bad:  #C0392B;

  --wash-gold:   rgba(176, 131, 36, .12);
  --wash-accent: rgba(14, 122, 136, .10);
  --wash-ok:     rgba(30, 127, 79, .12);
  --wash-warn:   rgba(154, 107, 18, .12);
  --wash-bad:    rgba(192, 57, 43, .10);
  --wash-hover:  rgba(11, 35, 104, .05);

  --focus-ring: 0 0 0 3px rgba(14, 122, 136, .30);

  /* Same ramp re-stepped for a white surface: later stage = DARKER, since
     on light "more" reads as darker. Re-validated in light mode (light end
     2.06:1 on #FFFFFF) — a theme flip of the dark steps would fail. */
  --stage-1: #6bc3c2;
  --stage-2: #45aead;
  --stage-3: #039999;
  --stage-4: #008384;
  --stage-5: #006e6f;
  --stage-6: #005859;

  --grid: #E8EDF6;
}

/* =======================================================================
   3. BASE ELEMENTS
   ======================================================================= */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { line-height: 1.25; margin: 0; font-weight: 600; }
h1 { font-size: var(--fs-xl); }
h2 { font-size: var(--fs-lg); }
h3 { font-size: var(--fs-md); }

p { margin: 0 0 var(--sp-3); }

a { color: var(--color-accent); }

:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

/* Numbers, money, and measurements are ALWAYS monospaced and tabular — a
   project-wide rule, so a column of rupees lines up on the decimal wherever
   it appears. It is deliberately applied to headline figures too: the brand
   reads as a ledger, and consistency wins over the extra tightness that
   proportional figures would give one number. */
.num,
.money,
.measure,
[data-numeric] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

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

/* Visually hidden but reachable by assistive tech. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Dashed "stitch" seam divider — the brand's signature separator. */
.seam {
  height: 0;
  border: 0;
  border-top: 2px dashed var(--color-gold);
  opacity: .7;
  margin: var(--sp-5) 0;
}

/* Horizontal stitch as a reusable background (for card edges, etc.). */
.seam-x {
  background-image: repeating-linear-gradient(
    90deg,
    var(--color-gold) 0 6px,
    transparent 6px 12px
  );
  height: 2px;
}

/* =======================================================================
   4. CONTROLS
   ======================================================================= */
.btn {
  font: inherit;
  font-size: var(--fs-base);
  font-weight: 500;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  white-space: nowrap;
  text-decoration: none;
  transition: background-color .12s ease, border-color .12s ease;
}

.btn-primary { background: var(--color-primary); color: var(--primary-contrast); }
.btn-gold    { background: var(--color-gold);    color: var(--gold-contrast); font-weight: 600; }
.btn-ghost   { background: transparent; color: var(--text); border-color: var(--border-strong); }

.btn-gold:hover  { filter: brightness(1.07); }
.btn-ghost:hover { background: var(--wash-hover); border-color: var(--color-gold); }

.btn-sm { padding: var(--sp-1) var(--sp-3); font-size: var(--fs-sm); }
.btn-block { width: 100%; justify-content: center; margin-top: var(--sp-4); }

/* Anchors used as buttons must not inherit the link colour. */
a.btn { text-decoration: none; display: inline-flex; }
a.btn-gold  { color: var(--gold-contrast); }
a.btn-ghost { color: var(--text); }

/* A ghost button acting as the CURRENT choice in a filter row. */
.btn-ghost[aria-pressed="true"],
.btn-ghost.is-on {
  border-color: var(--color-gold);
  background: var(--wash-gold);
  color: var(--text);
}

.field {
  font: inherit;
  font-size: var(--fs-base);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--bg-elev);
  color: var(--text);
}
.field::placeholder { color: var(--faint); }
.field:focus-visible { border-color: var(--color-accent); }

.field-sm { padding: var(--sp-1) var(--sp-2); font-size: var(--fs-sm); }
.field-inline { width: auto; }
/* An amount box: wide enough for "12345.00" and no wider, so a money row reads
   as a money row rather than a full-width text field. */
.field-amount { width: 110px; }
/* A search box grows into its bar but never past it. */
.field-search { width: min(280px, 100%); }
/* The sentence under a field that says what it expects — "leave blank to get a
   code", "at least 8 characters". Quiet, but it sits with the input rather than
   waiting to become an error message after the fact. */
.field-hint { margin: var(--sp-1) 0 0; font-size: var(--fs-sm); color: var(--muted); }

.label {
  display: block;
  font-size: var(--fs-sm);
  color: var(--muted);
  margin-bottom: var(--sp-1);
}
/* "required" said in words rather than an asterisk nobody has a legend for. */
.req { color: var(--color-gold); font-size: var(--fs-xs); margin-left: var(--sp-1); }

/* Form grid: pairs on a desk, one column on a phone. .form-span takes the full
   width for the fields that deserve it (name, notes). */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--sp-4); }
.form-span { grid-column: 1 / -1; }
.flash-list { margin: 0; padding-left: var(--sp-5); }

/* Inline icon: inherits text colour and cap height, never its own colour. */
.icon {
  width: 1.15em; height: 1.15em;
  flex: 0 0 auto;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-lg { width: 20px; height: 20px; }

/* =======================================================================
   5. APP SHELL — sidebar rail + main column
   ======================================================================= */
.shell {
  display: flex;
  min-height: 100vh;
  align-items: stretch;
}

.shell-main {
  flex: 1 1 auto;
  min-width: 0;          /* lets wide tables scroll instead of pushing the rail */
  display: flex;
  flex-direction: column;
}

.page {
  flex: 1 1 auto;
  width: 100%;
  max-width: 1320px;
  margin: 0 auto;
  padding: var(--sp-6) var(--sp-6) var(--sp-8);
}

/* ---- The rail ---- */
.sidebar {
  flex: 0 0 var(--sidebar-w);
  width: var(--sidebar-w);
  background: var(--sidebar);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-5) var(--sp-3) var(--sp-4);
  position: sticky;
  top: 0;
  align-self: flex-start;
  max-height: 100vh;
  overflow-y: auto;
}

/* The brand block stacks: the badge, then the SHOP'S name under it. The badge
   already says "TailorJi", so the wordmark is not also set in type beside it —
   the only line of text here is the one belonging to the person using the app. */
.sidebar-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-2) var(--sp-4);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
}

/* Sized in CSS, not only by the width/height attributes, so the rail can be
   narrowed without the logo overflowing it. The attributes stay on the tag to
   reserve the space and stop the nav jumping while the image loads. */
.brand-logo {
  width: 104px;
  max-width: 100%;
  height: auto;
  display: block;
}

/* The mobile top bar has room for the roundel only — see the note in base.php. */
.brand-mark-img {
  width: 28px; height: 28px;
  flex: 0 0 auto;
  display: block;
}

/* Login / verify / onboarding / billing / 403, on the centred auth shell. */
/* ⚠ NOT `max-width: <percentage>` here. .auth-shell centres its children, so
   .app-header is shrink-to-fit and a percentage max-width resolves against a
   width that this image is itself deciding — the browser settles that loop by
   collapsing the logo to about half the size asked for. Found by screenshotting
   the page, not by reading the rule. A fixed cap has no such circularity. */
.auth-logo {
  width: 128px;
  max-width: 100%;
  height: auto;
  display: block;
}

.app-brand {
  color: var(--color-gold);
  font-weight: 700;
  font-size: var(--fs-md);
  letter-spacing: .04em;
  text-decoration: none;
  white-space: nowrap;
}

.brand-sub {
  font-size: 10px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: center;
  /* A long shop name must wrap under the badge rather than widen the rail. */
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* ---- Nav list ---- */
.app-nav { display: flex; flex-direction: column; gap: 2px; }

.app-nav a {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--muted);
  text-decoration: none;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-size: var(--fs-base);
  white-space: nowrap;
}

.app-nav a:hover { color: var(--text); background: var(--wash-hover); }

/* Current section: gold wash + gold rule, the brand's stitch idiom. */
.app-nav a[aria-current="page"] {
  color: var(--text);
  background: var(--wash-gold);
  border-color: var(--color-gold);
  font-weight: 600;
}
.app-nav a[aria-current="page"] .icon { color: var(--color-gold); }

.sidebar-foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--sp-3); }

/* ---- Plan card ---- */
.plan-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.plan-card .plan-kicker {
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--color-gold);
}
.plan-card .plan-name { font-weight: 600; }
.plan-card a { font-size: var(--fs-xs); color: var(--muted); }
.plan-card a:hover { color: var(--color-gold); }

/* ---- User card ---- */
.user-card {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2);
  border-top: 1px solid var(--border);
  padding-top: var(--sp-3);
}
.user-card .user-meta { min-width: 0; flex: 1 1 auto; }
.user-card .user-name {
  font-size: var(--fs-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-card .user-role { font-size: var(--fs-xs); color: var(--faint); text-transform: capitalize; }

.signout {
  color: var(--muted);
  display: inline-flex;
  padding: var(--sp-2);
  border-radius: var(--radius-md);
  text-decoration: none;
}
.signout:hover { color: var(--color-bad); background: var(--wash-hover); }

/* ---- Initials monogram. We store no photos, so identity is the initials
        of the real name — never a stock avatar. ---- */
.avatar {
  width: 34px; height: 34px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--wash-accent);
  color: var(--color-accent);
  border: 1px solid var(--border-strong);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
}
.avatar-sm { width: 26px; height: 26px; font-size: var(--fs-xs); }

/* ---- Mobile top bar + the collapse toggle ---- */
.topbar {
  display: none;              /* desktop: the rail IS the navigation */
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--sidebar);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}

/* The toggle is a checkbox so the rail collapses with NO JavaScript at all:
   on shop-floor Android the nav can never be stranded by a script that
   failed to load. Trade-off accepted: assistive tech announces "Menu,
   checkbox" rather than a button with aria-expanded. */
.nav-toggle { position: absolute; opacity: 0; pointer-events: none; }

.nav-burger {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--text);
  cursor: pointer;
  font-size: var(--fs-sm);
}
.nav-burger:hover { background: var(--wash-hover); }
.nav-toggle:focus-visible + .shell .nav-burger { box-shadow: var(--focus-ring); }

/* ---- Auth shell (login / verify): no tenant, so no rail. ---- */
.auth-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-5);
  padding: var(--sp-6);
}
.auth-shell .app-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  background: none;
  border: 0;
  box-shadow: none;
}
.auth-card { width: 100%; max-width: 400px; padding: var(--sp-6); margin: 0 auto; }
/* Billing also renders on this shell (no tenant is resolved yet) and needs room
   for three plans side by side. */
.auth-card-wide { max-width: 760px; }
.plan-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--sp-4); margin: var(--sp-5) 0; }
.plan-tile { text-align: center; align-items: center; gap: var(--sp-1); }
.plan-tile form { width: 100%; }
.brand-rule { width: 96px; }
.page-flush { padding-top: 0; }
.auth-alt { margin: var(--sp-4) 0 0; font-size: var(--fs-sm); }
/* The divider between the two ways in. Both buttons submit ONE form (the server
   decides which path ran), so this is a rule with a word in it — not a tab, not
   an accordion, nothing that needs a script to reveal the other half. */
.auth-or {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-4) 0;
  color: var(--faint);
  font-size: var(--fs-sm);
}
.auth-or::before,
.auth-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
/* One-time code: wide tracking so six digits are easy to check by eye. */
.code-input { letter-spacing: .5em; text-align: center; font-size: var(--fs-lg); }

/* Bare brand bar for pages that have no tenant but are not auth screens
   (onboarding, billing). Kept as a class the old views already reference. */
.app-header {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  border-radius: 0;
}

/* =======================================================================
   6. COMPONENTS
   ======================================================================= */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

/* Panel = a card with a titled head. The dashboard's unit of composition. */
.panel { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow); }
.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.panel-title { font-size: var(--fs-md); font-weight: 600; margin: 0; }
/* A heading INSIDE a panel body (the Top / Bottom blocks of a set measurement).
   Gold, because it names a part of the garment, not a section of the app. */
.section-heading { font-size: var(--fs-sm); font-weight: 600; color: var(--color-gold); margin: 0; text-transform: uppercase; letter-spacing: .06em; }
.panel-sub { font-size: var(--fs-sm); color: var(--muted); margin: 2px 0 0; }
.panel-body { padding: var(--sp-5); }
.panel-body-flush { padding: 0; }

/* One figure parked in a panel head (a chart's total). Right-aligned and
   quieter than the title so the head still reads title-first. */
.panel-figure { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.2; }
.panel-figure-label { font-size: var(--fs-xs); color: var(--faint); }
.panel-figure .money { font-weight: 600; }
.panel-figure .unit { color: var(--muted); margin-right: 1px; }

/* ---- Page header ---- */
.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-5);
}
.page-title { font-size: var(--fs-2xl); color: var(--color-gold); margin: 0; }
.page-sub { color: var(--muted); font-size: var(--fs-base); margin: var(--sp-1) 0 0; }
.page-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* ---- Grids ---- */
.grid { display: grid; gap: var(--sp-4); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* A main column + a narrower rail (order detail, customer detail). */
.grid-split { display: grid; gap: var(--sp-4); grid-template-columns: minmax(0, 1fr) 340px; align-items: start; }

.stack { display: flex; flex-direction: column; gap: var(--sp-4); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }
.cluster { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.cluster-top { margin-top: var(--sp-4); }

/* ---- Stat tiles: icon + label + value + optional delta ---- */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-4);
}

/* Six headline figures across the top of the dashboard. They are a summary
   ROW, not six cards to study, so they sit narrower and closer than the
   default tile grid — otherwise they wrap to three lines and stop reading as
   one glance. */
.tile-grid-tight { grid-template-columns: repeat(auto-fit, minmax(172px, 1fr)); gap: var(--sp-3); }

.tile {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: var(--sp-4) var(--sp-5);
  text-decoration: none;
  color: var(--text);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

a.tile { transition: border-color .12s ease, transform .12s ease; }
a.tile:hover { border-color: var(--color-gold); box-shadow: var(--shadow-lift); }

.tile-top { display: flex; align-items: center; gap: var(--sp-3); }

/* The tile's icon chip. Tinted by role, not decorative: gold = money owed,
   bad = something late, accent = ordinary volume. */
.tile-icon {
  width: 38px; height: 38px;
  flex: 0 0 auto;
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--wash-accent);
  color: var(--color-accent);
}
.tile-icon-gold { background: var(--wash-gold); color: var(--color-gold); }
.tile-icon-bad  { background: var(--wash-bad);  color: var(--color-bad); }
.tile-icon-ok   { background: var(--wash-ok);   color: var(--color-ok); }

.tile-label { color: var(--muted); font-size: var(--fs-sm); }
.tile-value { font-size: var(--fs-2xl); font-weight: 600; line-height: 1.1; }
.tile-value .unit { color: var(--muted); font-size: var(--fs-md); margin-right: 1px; }

.tile-delta { font-size: var(--fs-sm); display: flex; align-items: center; gap: var(--sp-1); }
.tile-delta-up   { color: var(--color-ok); }
.tile-delta-down { color: var(--color-bad); }
.tile-delta-flat { color: var(--faint); }
.tile-delta .vs { color: var(--faint); }
/* The arrow inherits the delta's tone, so direction is never carried by colour
   alone — a red-green-blind owner still sees which way it points. */
.delta-arrow { width: 13px; height: 13px; }

/* A tile whose figure demands attention (overdue work, money owed). */
.tile-alert { border-color: var(--color-gold); }
.tile-alert .tile-value { color: var(--color-gold); }

/* ---- Pills / badges ---- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 2px var(--sp-2);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  font-size: var(--fs-xs);
  font-weight: 500;
  white-space: nowrap;
  line-height: 1.6;
}
.pill-ok      { color: var(--color-ok);   border-color: var(--color-ok);   background: var(--wash-ok); }
.pill-warn    { color: var(--color-warn); border-color: var(--color-warn); background: var(--wash-warn); }
.pill-bad     { color: var(--color-bad);  border-color: var(--color-bad);  background: var(--wash-bad); }
.pill-neutral { color: var(--muted); }

/* Stage pill: a dot in the ordinal ramp colour + the name in text ink.
   Text never wears the data colour — the dot carries identity. */
.pill-stage { color: var(--text); }
.dot {
  width: 8px; height: 8px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  background: var(--faint);
}
.dot-1 { background: var(--stage-1); }
.dot-2 { background: var(--stage-2); }
.dot-3 { background: var(--stage-3); }
.dot-4 { background: var(--stage-4); }
.dot-5 { background: var(--stage-5); }
.dot-6 { background: var(--stage-6); }

/* ---- Tables ---- */
.table-wrap { overflow-x: auto; }

.table { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }

.table th {
  text-align: left;
  font-weight: 500;
  font-size: var(--fs-xs);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--faint);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--wash-hover); }

.table .right { text-align: right; }
.table .nowrap { white-space: nowrap; }

/* Two-line cell: a strong first line with quiet meta under it. */
.cell-stack { display: flex; flex-direction: column; line-height: 1.35; }
.cell-meta { font-size: var(--fs-xs); color: var(--faint); }
.cell-id { display: flex; align-items: center; gap: var(--sp-3); }

.table a { color: var(--text); text-decoration: none; font-weight: 500; }
.table a:hover { color: var(--color-accent); text-decoration: underline; }

/* A retired row: still readable (it is on old bills), visibly out of service. */
.is-retired td { opacity: .62; }

/* Tone for a due-date cell. Late is the only thing allowed to shout. */
.due-overdue { color: var(--color-bad); }
.due-soon    { color: var(--color-warn); }
.due-none    { color: var(--faint); }

/* ---- Definition list, used for record detail ---- */
.dl { display: grid; grid-template-columns: minmax(90px, auto) 1fr; gap: var(--sp-3) var(--sp-5); margin: 0; }
/* A long list of short pairs (a measurement set) reads better in columns than as
   one tall ribbon. Each pair is a <div> (legal inside <dl> in HTML5) so a pair
   can never be split across a column boundary. */
.dl-cols { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-4); }
.dl-cols > div { display: flex; flex-direction: column; gap: 2px; }
.dl dt { color: var(--muted); font-size: var(--fs-sm); }
.dl dd { margin: 0; }

/* ---- Timeline: an ordered list of what has already happened to a record.
        The rail is drawn on the LI, so it stops at the last step instead of
        trailing into empty space. ---- */
.timeline { list-style: none; margin: 0; padding: var(--sp-4) var(--sp-5); display: flex; flex-direction: column; }
.timeline-step {
  display: grid;
  grid-template-columns: 8px 1fr;
  align-items: start;
  gap: var(--sp-4);
  padding-bottom: var(--sp-4);
  position: relative;
}
.timeline-step:last-child { padding-bottom: 0; }
.timeline-step::before {
  content: "";
  position: absolute;
  left: 3.5px; top: 12px; bottom: 0;
  width: 1px;
  background: var(--border);
}
.timeline-step:last-child::before { display: none; }
.timeline-step .dot { margin-top: 5px; }

/* Money summary: label left, figure right-aligned in a column. */
.dl-money { display: grid; grid-template-columns: 1fr auto; gap: var(--sp-2) var(--sp-5); margin: 0; }
.dl-money dt { color: var(--muted); }
.dl-money dd { margin: 0; text-align: right; }
.dl-money .total { color: var(--color-gold); font-weight: 600; }
.dl-money .total-row { border-top: 1px solid var(--border); padding-top: var(--sp-2); }

/* ---- Empty state. A new shop sees this first, so it has to read as
        "nothing here YET" and point at the next action — never as a
        broken page. ---- */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sp-3);
  padding: var(--sp-7) var(--sp-5);
  color: var(--muted);
}
.empty-mark {
  width: 52px; height: 52px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--wash-gold);
  color: var(--color-gold);
}
.empty-title { color: var(--text); font-weight: 600; font-size: var(--fs-md); }
.empty-body { max-width: 42ch; margin: 0; font-size: var(--fs-sm); }

/* ---- Flash messages ---- */
.flash {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  margin-bottom: var(--sp-4);
  font-size: var(--fs-base);
}
.flash-ok  { border-color: var(--color-ok);  background: var(--wash-ok);  color: var(--text); }
.flash-bad { border-color: var(--color-bad); background: var(--wash-bad); color: var(--text); }

/* ---- Filter bar: ONE row above everything it scopes. ---- */
.filters {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
}
/* The same bar used as a FOOTER (result count, clear-filters): the rule moves to
   the top edge so the panel does not end on a floating hairline. */
.filters-foot { border-bottom: 0; border-top: 1px solid var(--border); color: var(--muted); }
.filters-scroll {
  display: flex;
  gap: var(--sp-2);
  overflow-x: auto;
  padding-bottom: var(--sp-1);
}

/* =======================================================================
   7. CHARTS
   Inline SVG + CSS only, no charting library: every dataset here is tiny
   and already server-side, so this costs zero JavaScript, survives JS-off,
   prints, and adds no third-party origin. Every value drawn is ALSO
   readable as text nearby, so nothing is gated behind a hover.
   ======================================================================= */

/* ---- Pipeline: one horizontal bar per stage, ordinal ramp ---- */
.pipeline { display: flex; flex-direction: column; gap: var(--sp-1); }

.pipeline-row {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr) 44px;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-5);
  text-decoration: none;
  color: var(--text);
  border-radius: var(--radius-sm);
}
.pipeline-row:hover { background: var(--wash-hover); }
.pipeline-name { font-size: var(--fs-sm); display: flex; align-items: center; gap: var(--sp-2); }
.pipeline-count { text-align: right; font-size: var(--fs-sm); }

.pipeline-track { height: 10px; border-radius: var(--radius-pill); background: var(--grid); overflow: hidden; }
.pipeline-fill  { height: 100%; border-radius: var(--radius-pill); min-width: 2px; }
.fill-1 { background: var(--stage-1); }
.fill-2 { background: var(--stage-2); }
.fill-3 { background: var(--stage-3); }
.fill-4 { background: var(--stage-4); }
.fill-5 { background: var(--stage-5); }
.fill-6 { background: var(--stage-6); }

/* ---- Bar list: rank + label + magnitude bar + value (top garments) ---- */
.barlist { display: flex; flex-direction: column; }
.barlist-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 120px 92px 48px;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-5);
}
.barlist-row:hover { background: var(--wash-hover); }
.barlist-label { display: flex; align-items: baseline; gap: var(--sp-2); min-width: 0; }
.barlist-code { font-weight: 600; }
.barlist-name {
  color: var(--faint); font-size: var(--fs-xs);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.barlist-track { height: 8px; border-radius: var(--radius-pill); background: var(--grid); overflow: hidden; }
/* Nominal categories: ONE colour for every bar. Colouring each bar by its
   own value would re-encode bar length as hue and spend the identity
   channel on information the length already carries. */
.barlist-fill { height: 100%; border-radius: var(--radius-pill); background: var(--color-accent); min-width: 2px; }
.barlist-value { text-align: right; font-size: var(--fs-sm); }
.barlist-share { text-align: right; font-size: var(--fs-sm); color: var(--faint); }

/* ---- Meter: a single ratio against a total (2px surface gaps, no strokes) ---- */
.meter { display: flex; gap: 2px; height: 8px; border-radius: var(--radius-pill); overflow: hidden; background: var(--grid); }
.meter-seg { height: 100%; min-width: 2px; }
.meter-seg-accent { background: var(--color-accent); }
.meter-seg-gold   { background: var(--color-gold); }
/* Legend dots: the same two fills the meter uses, so a row's label and its slice
   are matched by colour rather than by reading order. */
.dot-key-accent { background: var(--color-accent); }
.dot-key-gold   { background: var(--color-gold); }
.meter-key { display: flex; gap: var(--sp-4); flex-wrap: wrap; font-size: var(--fs-xs); color: var(--muted); }
.meter-key .cluster { gap: var(--sp-1); }

/* ---- Line chart ---- */
.chart { width: 100%; display: block; }
.chart-grid { stroke: var(--grid); stroke-width: 1; }         /* solid hairline, never dashed */
.chart-line { stroke: var(--color-accent); stroke-width: 2; fill: none; stroke-linejoin: round; stroke-linecap: round; }
.chart-area { fill: var(--color-accent); opacity: .10; }      /* a wash, not a block */
.chart-dot  { fill: var(--color-accent); stroke: var(--card); stroke-width: 2; }  /* surface ring */
.chart-tick { fill: var(--faint); font-size: 10px; font-family: var(--font-mono); }
.chart-label { fill: var(--text); font-size: 11px; font-family: var(--font-mono); }

/* ---- Delivery calendar: a month of due dates, drawn as a month ------------
   Seven columns, always. The grid is filled out to whole weeks with the
   neighbouring months' days (.is-out) so the shape stays rectangular; they are
   dimmed rather than blank, because an empty cell reads as "no deliveries" and
   these days simply are not this month's business.

   Load is shown as three bands against the BUSIEST day of the month, not an
   absolute count — five orders is a heavy day in one shop and a quiet one in
   another. Three bands, because a continuous ramp across 31 cells is a gradient
   nobody can read a value off; three is the most a glance resolves. Colour is
   never the only signal: the count is printed in the cell. */
.cal {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 3px;
}
.cal-head {
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--faint);
  padding-bottom: var(--sp-1);
}
.cal-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-height: 40px;
  padding: var(--sp-1) 2px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--grid);
  color: var(--muted);
  font-size: var(--fs-sm);
  text-decoration: none;
}
a.cal-day:hover { border-color: var(--color-gold); color: var(--text); }
.cal-day.is-out { background: none; color: var(--faint); opacity: .5; }
/* Today is outlined, not filled: the fill is already carrying delivery load. */
.cal-day.is-today { border-color: var(--color-gold); }
.cal-date { line-height: 1; }
.cal-count {
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1;
  color: var(--text);
}
.cal-day.load-1 { background: var(--wash-accent); }
.cal-day.load-2 { background: var(--wash-warn); }
.cal-day.load-3 { background: var(--wash-bad); }
.cal-day.load-3 .cal-count { color: var(--color-bad); }

/* The line above a row of tiles saying which question it answers — "in the
   selected period" vs "right now". Cheap, and it is what keeps a balance from
   being read as a period total when the selector moves. */
.tile-kicker {
  margin: 0 0 calc(-1 * var(--sp-2));
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--faint);
}

/* The quiet "and there is more where this came from" line under a screen. */
.dash-more { margin: 0; font-size: var(--fs-sm); }
.dash-more a { color: var(--muted); }
.dash-more a:hover { color: var(--color-gold); }

/* =======================================================================
   8. RESPONSIVE
   ======================================================================= */
@media (max-width: 1080px) {
  .grid-split { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 860px) {
  .topbar { display: flex; }

  .page { padding: var(--sp-4) var(--sp-4) var(--sp-7); }

  .shell { display: block; }

  /* Collapsed by default on small screens; the checkbox reveals it. The rail
     becomes a normal block in the flow rather than an overlay, so it can
     never trap focus or cover content it pushed off-screen. */
  .sidebar {
    display: none;
    width: 100%;
    flex-basis: auto;
    position: static;
    max-height: none;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
  .nav-toggle:checked ~ .shell .sidebar { display: flex; }

  .pipeline-row  { grid-template-columns: 96px minmax(0, 1fr) 38px; padding-inline: var(--sp-4); }
  .barlist-row   { grid-template-columns: minmax(0, 1fr) 84px 44px; padding-inline: var(--sp-4); }
  .barlist-share { display: none; }

  .tile-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-3); }
  .tile { padding: var(--sp-3) var(--sp-4); }
  .tile-value { font-size: var(--fs-xl); }
  .tile-icon { width: 32px; height: 32px; }

  .table th, .table td { padding: var(--sp-2) var(--sp-3); }
  .page-title { font-size: var(--fs-xl); }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* Print: the rail and controls are screen furniture. */
@media print {
  .sidebar, .topbar, .page-actions, .filters, .btn { display: none !important; }
  .page { max-width: none; padding: 0; }
}
