/* ============================================================
   ProjectTelemetry — DESIGN SYSTEM  (the single source of truth)
   assets/design-system.css

   ★★ THIS FILE IS THE UI STANDARD. It loads LAST, after every module
   stylesheet, so it is the final word on control sizing, spacing and
   containment. If a module needs a different look, it changes a TOKEN
   here — it does not re-declare .btn-xs with its own pixels.

   Why this exists: control metrics were hard-coded in 58 stylesheets and
   in <style> blocks injected from JS, so the same class rendered at a
   different size depending which page you were on (.btn-xs alone existed
   in five different sizes), and edits to one module silently changed the
   look of another. Everything below is driven by tokens so that can't
   recur.

   THE FIVE RULES THIS FILE ENFORCES
     1. Controls come in exactly three sizes (xs / sm / default). One set
        of metrics, used everywhere.
     2. Nothing touches the chrome or the viewport edge — the page shell
        always keeps a gutter.
     3. Outlined containers never collide — panels/cards carry their own
        bottom spacing and inner padding.
     4. Modals never overlap themselves — the body scrolls, controls keep
        a minimum height, and nothing is clipped behind the header/footer.
     5. Tabs, inputs and buttons share one height so rows line up.

   CHECKLIST AFTER ANY PAGE/MODAL EDIT (do this every time):
     □ buttons use .btn-* (no inline font-size/padding on a control)
     □ page content sits inside the standard gutter (no edge-to-edge)
     □ panels/cards separated by --stack, not touching
     □ modal body scrolls; no input clipped by the header/footer
     □ inputs and buttons on the same row share a height
   ============================================================ */

/* ── How precedence is guaranteed (no build step) ─────────────
   1. This file is UNLAYERED and loaded LAST. Unlayered declarations
      out-rank layered ones, and at equal specificity the later sheet
      wins — so these primitives are the final word.
   2. Module stylesheets may not re-declare a shared primitive at all.
      That rule is machine-checked: tools/check-ui.sh fails the build
      if any module file defines .btn*, .field, .cp-modal, etc.
   Together those two mean an edit to one page cannot change another. */

:root {
  /* ── space scale ─────────────────────────────────────────── */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-7: 32px; --sp-8: 40px;

  /* ── page shell: the gutter nothing may cross ────────────── */
  --page-x: 22px;          /* left/right breathing room from chrome + edge */
  --page-y: 18px;          /* top/bottom */
  --stack: 16px;           /* vertical gap between sibling panels/cards */

  /* ── control metrics: THE three sizes ────────────────────── */
  --ctl-h:      34px;      /* default control height (buttons, inputs, selects) */
  --ctl-h-sm:   30px;
  --ctl-h-xs:   26px;
  --ctl-fs:     13px;
  --ctl-fs-sm:  12px;
  --ctl-fs-xs:  11.5px;
  --ctl-px:     16px;      /* horizontal padding */
  --ctl-px-sm:  12px;
  --ctl-px-xs:  9px;
  --ctl-radius: 7px;
  --ctl-gap:    8px;       /* gap between controls in a row */

  /* ── containers ──────────────────────────────────────────── */
  --panel-pad: 14px 16px;
  --panel-radius: 12px;
}

/* ════════════════════════════════════════════════════════════
   1. CONTROLS — one set of metrics, everywhere
   Colour/variant styling still comes from app.css; this governs SIZE,
   shape and alignment only, so module variants keep their identity.
   ════════════════════════════════════════════════════════════ */
.btn, .btn-accent, .btn-dark, .btn-ghost, .btn-light, .btn-outline,
.btn-danger, .btn-primary, .btn-secondary {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  min-height: var(--ctl-h);
  padding: 0 var(--ctl-px);
  font-size: var(--ctl-fs);
  font-family: inherit;
  line-height: 1;
  border-radius: var(--ctl-radius);
  white-space: nowrap;
  cursor: pointer;
  box-sizing: border-box;
}

/* Size modifiers — these are the ONLY sanctioned sizes. */
.btn-sm, .btn-accent.btn-sm, .btn-dark.btn-sm, .btn-ghost.btn-sm, .btn-light.btn-sm {
  min-height: var(--ctl-h-sm);
  padding: 0 var(--ctl-px-sm);
  font-size: var(--ctl-fs-sm);
  border-radius: var(--ctl-radius);   /* app.css set 5px here, 7px elsewhere */
}
.btn-xs, .btn-accent.btn-xs, .btn-dark.btn-xs, .btn-ghost.btn-xs, .btn-light.btn-xs {
  min-height: var(--ctl-h-xs);
  padding: 0 var(--ctl-px-xs);
  font-size: var(--ctl-fs-xs);
  border-radius: 6px;
}
.btn-lg, .btn-dark-lg, .btn-outline-lg {
  min-height: 42px; padding: 0 24px; font-size: 14px;
}

/* Square icon buttons — always square, always centred, never squashed. */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--ctl-h); height: var(--ctl-h);
  padding: 0; flex: 0 0 auto;
  border-radius: var(--ctl-radius);
  font-size: 15px; line-height: 1; cursor: pointer;
  box-sizing: border-box;
}
/* The -sm/-xs variants must repeat the radius: used standalone (without
   .btn-icon) they'd otherwise render square. Found by the browser harness. */
.btn-icon-sm, .btn-icon.sm {
  width: var(--ctl-h-sm); height: var(--ctl-h-sm); font-size: 13.5px;
  border-radius: var(--ctl-radius);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; flex: 0 0 auto; box-sizing: border-box; cursor: pointer;
}
.btn-icon-xs, .btn-icon.xs {
  width: var(--ctl-h-xs); height: var(--ctl-h-xs); font-size: 12.5px;
  border-radius: var(--ctl-radius);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; flex: 0 0 auto; box-sizing: border-box; cursor: pointer;
}

/* Module control classes that predate the system, normalised to it so a
   toolbar doesn't mix a 19px-tall button with a 26px one. Colour/border
   intent stays in the module. */
.vmb-btn-sm, .sv-tgl {
  min-height: var(--ctl-h-xs);
  padding: 0 var(--ctl-px-xs);
  font-size: var(--ctl-fs-xs);
  display: inline-flex; align-items: center; gap: 4px;
  line-height: 1; box-sizing: border-box;
}

/* ── Missing variants ─────────────────────────────────────────
   .btn, .btn-light and .btn-primary were used 66 times across the app but
   never had a background or border defined anywhere — they rendered as
   bare text (found by rendering the control set in a real browser; no
   static check catches an unstyled class). Defined here once. */
.btn {
  background: var(--bg2); border: 1px solid var(--bd); color: var(--ink);
  font-weight: 500;
}
.btn:hover:not(:disabled) { background: var(--bg); border-color: var(--ink-faint); }

.btn-light {
  background: var(--white); border: 1px solid var(--bd); color: var(--ink);
  font-weight: 500;
}
.btn-light:hover:not(:disabled) { background: var(--bg2); border-color: var(--ink-faint); }

.btn-primary {
  background: var(--accent); border: 1px solid var(--accent); color: var(--accent-ink);
  font-weight: 600;
}
.btn-primary:hover:not(:disabled) { opacity: .9; }

/* Icon buttons are bare until hovered — give them a visible affordance. */
.btn-icon { background: none; border: none; color: var(--ink-soft); }
.btn-icon:hover:not(:disabled) { background: var(--bg2); color: var(--ink); }

/* A disabled control must LOOK disabled, everywhere. */
.btn:disabled, .btn-accent:disabled, .btn-dark:disabled, .btn-ghost:disabled,
.btn-light:disabled, .btn-icon:disabled, .btn-primary:disabled {
  opacity: .45; cursor: not-allowed;
}

/* Icons inside controls never dictate the control's height. */
.btn i, .btn-accent i, .btn-dark i, .btn-ghost i, .btn-light i,
.btn-sm i, .btn-xs i, .btn-icon i { line-height: 1; flex: 0 0 auto; }

/* ════════════════════════════════════════════════════════════
   2. FORM CONTROLS — same height as buttons so rows line up
   ════════════════════════════════════════════════════════════ */
.field input:not([type="checkbox"]):not([type="radio"]),
.field select, .field textarea {
  box-sizing: border-box;
  font-family: inherit;
  border-radius: var(--ctl-radius);
}
/* Inputs must be the SAME height as buttons or every filter row is ragged.
   Height comes from the token; the font-size stays whatever the type scale
   says (16px on Comfortable, which also stops iOS Safari zooming on focus),
   so we set the box and let the text sit inside it. */
.field input:not([type="checkbox"]):not([type="radio"]),
.field select {
  height: var(--ctl-h);
  min-height: var(--ctl-h);
  padding-top: 0; padding-bottom: 0;
  line-height: normal;
}
.field textarea { padding: 9px 12px; min-height: 80px; }

/* Module inputs adopt the same box so they line up with buttons beside them. */
.sv-in:not(textarea), .sv-select {
  height: var(--ctl-h-xs);
  min-height: var(--ctl-h-xs);
  padding-top: 0; padding-bottom: 0;
  box-sizing: border-box;
}
/* Two-up field rows collapse instead of overflowing their modal. */
.field-row { gap: var(--sp-3); }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }

/* ════════════════════════════════════════════════════════════
   3. PAGE SHELL — nothing touches the chrome or the viewport edge
   ════════════════════════════════════════════════════════════ */
.page-hdr { margin-bottom: var(--sp-5); }

/* The shell gutter. #pt-main already pads itself in app.css; this guarantees
   a minimum so a module can never leave content flush against the sidebar or
   the window edge. */
#pt-main { padding: 28px; }
@media (max-width: 1024px) { #pt-main { padding: 18px; } }
@media (max-width: 600px)  { #pt-main { padding: 14px; } }

/* Full-bleed surfaces (curve editor, plan viewer, big canvases) opt out with
   a CLASS — never by writing inline style onto the shell. Inline styles on
   #pt-main leak: if the restore is missed on an error or a fast route change,
   every later page renders with no gutter. A class always comes off cleanly. */
#pt-main.is-fullbleed {
  padding: 0; overflow: hidden;
  display: flex; flex-direction: column;
}

/* A page's first element never collides with the sticky header. */
#pt-main > :first-child { margin-top: 0; }

/* ════════════════════════════════════════════════════════════
   4. CONTAINERS — outlined boxes never butt against each other
   Any bordered/elevated container gets inner padding and a bottom gap.
   :where() keeps specificity at 0 so modules can still override freely.
   ════════════════════════════════════════════════════════════ */
:where(.pt-card, .card, .panel, .pt-panel, .cd-panel, .sv-panel,
       .mod-panel, .box, .tile) {
  border-radius: var(--panel-radius);
}
:where(.pt-card, .card, .panel, .pt-panel) { padding: var(--panel-pad); }

/* Consecutive panels always keep air between them. */
:where(.sv-panel, .cd-panel, .pt-card, .card, .panel) +
:where(.sv-panel, .cd-panel, .pt-card, .card, .panel) { margin-top: var(--stack); }

/* Toolbars: consistent gap so buttons never touch each other or the edge. */
.mod-tb, .pt-toolbar, .sv-panel-tools, .td-filters, .oc-toolbar {
  display: flex; align-items: center; gap: var(--ctl-gap); flex-wrap: wrap;
}

/* Grids of cards keep a real gutter rather than hairline-to-hairline. */
:where(.pt-grid, .card-grid, .tile-grid) { gap: var(--stack); }

/* ════════════════════════════════════════════════════════════
   5. MODALS — never overlap, never clip, never exceed the viewport
   ════════════════════════════════════════════════════════════ */
.modal-overlay { padding: var(--sp-5); overflow-y: auto; }
.cp-modal, .pt-modal {
  display: flex; flex-direction: column;
  max-height: calc(100vh - 2 * var(--sp-5));
  max-width: min(96vw, 640px);
  border-radius: 12px;
  overflow: hidden;                 /* header/footer stay put… */
}
.cp-modal .cp-body, .pt-modal .pt-modal-body {
  overflow-y: auto;                 /* …only the body scrolls */
  flex: 1 1 auto;
  min-height: 0;                    /* the fix that stops clipped inputs */
}
.cp-header, .pt-modal-header { flex: 0 0 auto; gap: var(--sp-3); }
/* A modal's action row never sits on top of the last field. */
.cp-body > :last-child { margin-bottom: 0; }
.tc-actions, .pt-modal-actions {
  display: flex; align-items: center; gap: var(--ctl-gap);
  flex-wrap: wrap; margin-top: var(--sp-4);
}
/* Long content inside a modal wraps instead of forcing horizontal scroll. */
.cp-body { word-break: break-word; }
.cp-body table { width: 100%; }

/* ── 5b. UNIVERSAL MODAL SAFETY ───────────────────────────────
   The app grew twelve independent modal implementations (bl-, cb-, co-,
   cp-, es-, forms-, hr-, msg-, pdc-, tc-, plus drawers). Most never got
   max-height or a scrolling body, which is why tall forms clipped their
   last input or ran a button under the edge. Rather than patch twelve
   stylesheets — the very habit that caused the drift — every family is
   brought under one rule here.
   ★ New modal? Prefer .cp-modal/.cp-header/.cp-body. If you must invent
     one, add its classes to the lists below so it inherits the safety. */

/* Backdrops: cover the viewport, centre the card, scroll if it overflows. */
.modal-overlay, .cb-modal-bg, .msg-modal-back, .pdc-modal,
.bl-modal, .co-modal,
.cn-drawer-overlay, .dir-drawer-ov, .pl-overlay {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* Cards: never taller than the viewport, always a flex column so the body
   is the only part that scrolls. */
.cp-modal, .bl-modal-box, .cb-modal, .cb-modal-wide, .co-modal-box,
.es-modal, .forms-modal, .hr-modal, .msg-modal, .pdc-modal-card, .tc-modal {
  display: flex; flex-direction: column;
  max-height: calc(100vh - 2 * var(--sp-5));
  max-width: min(96vw, 720px);
  box-sizing: border-box;
  overflow: hidden;
}

/* Bodies scroll; min-height:0 is what actually stops the clipping inside
   a flex column (without it the body refuses to shrink and overflows). */
.cp-body, .bl-modal-bd, .cb-modal-p, .co-modal-bd, .forms-modal-bd,
.hr-modal-bd, .msg-modal-note, .pdc-modal-bd {
  overflow-y: auto; flex: 1 1 auto; min-height: 0;
}

/* Headers and footers hold their size so they can't be pushed off-screen
   or overlap the body. */
.cp-header, .bl-modal-hd, .cb-modal-hd, .co-modal-hd, .forms-modal-hd,
.hr-modal-hd, .msg-modal-hd, .pdc-modal-hd,
.bl-modal-ft, .co-modal-ft, .forms-modal-ft, .hr-modal-ft, .msg-modal-ft,
.pdc-modal-ft, .cb-modal-act {
  flex: 0 0 auto;
}
/* Footers keep their buttons off the last field. */
.bl-modal-ft, .co-modal-ft, .forms-modal-ft, .hr-modal-ft, .msg-modal-ft,
.pdc-modal-ft, .cb-modal-act, .flt-modal-actions {
  display: flex; align-items: center; gap: var(--ctl-gap); flex-wrap: wrap;
}

/* Side drawers: full height, own scroll, never wider than the viewport. */
.cn-drawer, .dir-drawer, .pl-drawer, .mtg-idrawer, .wip-sheet {
  max-width: 96vw; max-height: 100vh;
  display: flex; flex-direction: column;
  box-sizing: border-box;
}
.dir-drawer-body, .pl-drawer-list { overflow-y: auto; flex: 1 1 auto; min-height: 0; }

/* ════════════════════════════════════════════════════════════
   6. TABS — one height, one rhythm, no collisions
   ════════════════════════════════════════════════════════════ */
.ld-tabs, .tc-tabs, .pt-tabs, .oc-viewtoggle, .pth-toggle {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.ld-tab, .pt-tab, .tc-tab {
  min-height: var(--ctl-h-sm);
  padding: 0 var(--ctl-px-sm);
  font-size: var(--ctl-fs-sm);
  display: inline-flex; align-items: center; gap: 5px;
  border-radius: var(--ctl-radius);
  cursor: pointer; white-space: nowrap;
  box-sizing: border-box;
}

/* ════════════════════════════════════════════════════════════
   6b. MANPOWER CURVE EDITOR — its toolbar geometry lives HERE
   This module injects its own <style> at runtime, which lands after every
   stylesheet and therefore always used to win. Its control METRICS were
   moved here so the toolbar can no longer drift between releases (it had
   changed size repeatedly); the module keeps only colour/border intent.
   ★ Do not put width/height/font-size/padding for .ce-* controls back
     into curve-editor.js — tools/check-ui.sh fails the build if you do.
   ════════════════════════════════════════════════════════════ */
.ce-icon-btn {
  width: 22px; height: 22px; font-size: 12px;
  border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 auto; box-sizing: border-box;
}
/* Inline text-links inside the curve editor (override / reset). These used to
   carry ~9 inline style properties each on every render. */
.ce-linkbtn, .ce-hlinkbtn {
  font-weight: 600; text-transform: none; letter-spacing: 0;
  background: none; border: none; cursor: pointer;
  text-decoration: underline; padding: 0;
  color: var(--ink-soft); font-family: inherit;
}
.ce-linkbtn  { font-size: 10px; }
.ce-hlinkbtn { font-size: 9.5px; }
.ce-linkbtn.is-reset, .ce-hlinkbtn.is-reset { color: var(--accent); }

.ce-seg    { padding: 4px 10px; font-size: 11px; line-height: 1; box-sizing: border-box; }
.ce-seg-sm { padding: 2px 7px;  font-size: 10px; line-height: 1; box-sizing: border-box; }
.ce-tab-btn{ padding: 7px 14px; font-size: 12px; line-height: 1; box-sizing: border-box; }

/* ════════════════════════════════════════════════════════════
   7. TABLES — headers and cells never crush their content
   ════════════════════════════════════════════════════════════ */
:where(table) { border-collapse: collapse; }
/* Any wide table scrolls inside its own container instead of pushing the
   page sideways (a recurring cause of content running under the chrome). */
:where(.pt-tblwrap, .sv-tblwrap, .table-wrap) { overflow-x: auto; max-width: 100%; }
/* Controls sitting inside table cells use the compact size automatically. */
:where(td) > .btn { min-height: var(--ctl-h-sm); font-size: var(--ctl-fs-sm); padding: 0 var(--ctl-px-sm); }

/* ════════════════════════════════════════════════════════════
   8. GLOBAL SAFETY — the "never again" rules
   ════════════════════════════════════════════════════════════ */
/* Nothing may force the page to scroll sideways. */
html, body { max-width: 100%; }
/* Media and embeds stay inside their box. */
:where(img, svg, video, canvas) { max-width: 100%; }
/* Absolutely-positioned overlays never escape their container. */
:where(.pt-page, .sv-panel, .cd-panel) { position: relative; }

/* Touch: keep controls tappable WITHOUT inflating dense toolbars — the
   mistake that repeatedly broke the manpower curve editor. Only reach the
   minimum by growing the tap area, never the visual box. */
@media (hover: none) and (pointer: coarse) {
  .btn, .btn-accent, .btn-dark, .btn-ghost, .btn-light, .btn-icon,
  .btn-sm, .btn-xs, .ld-tab, .pt-tab {
    touch-action: manipulation;
  }
}

/* Print: don't carry interactive chrome onto paper. */
@media print {
  .btn, .btn-accent, .btn-dark, .btn-ghost, .btn-icon, .mod-tb, .pt-toolbar { display: none !important; }
}
