/* ncs-ui.css — the shared NCS interface layer.  v0.4.2
 *
 * ── What this is ───────────────────────────────────────────────────────────────
 *
 * One stylesheet for every NCS internal tool, so a change to the house look is a
 * version bump and not nineteen hand-edits.
 *
 * ── Changelog ─────────────────────────────────────────────────────────────────
 *
 * v0.4.2  <button> now inherits the page font. Form controls do not inherit
 *         font-family by default, and the input/select/textarea rule had missed
 *         button — so unstyled buttons rendered in the browser default (Arial)
 *         inside an otherwise-Montserrat page. Three apps had each worked around
 *         it locally before it was noticed centrally.
 * v0.4.1  Checkboxes and radios no longer inherit the text-field rule, which had
 *         been stretching every one of them to width:100%. Also worth knowing, and
 *         now documented in ADOPTION.md: `.ncs label { display: block }` is (0,1,1)
 *         and silently beats an app's own `.field { display: flex }` at (0,1,0).
 * v0.4.0  Nav now styles <a> as well as <button>. Tools with real pages navigate
 *         with links, and the Wiki had hand-rolled `.ncs nav a` in all four of its
 *         documents to get the pill look back — the second adopter needing it was
 *         Ticket Review, so it moved here. Adopters can delete their local copies.
 * v0.3.1  Hardened the shell for a flex-column <body>. Several tools use one as a
 *         full-height app frame; in that context a flex item with `margin: 0 auto`
 *         shrink-to-fits rather than stretching, so main collapsed to its content
 *         width, and header/nav were squeezed by a tall page. Neither happens in
 *         normal block flow, which is why it went unnoticed until the Wiki grew a
 *         stat-tile row whose intrinsic width was small.
 * v0.3.0  Added .work, the content-plus-rail page layout. It came out of restyling
 *         the Wiki: the shape was already needed by several tools and each was
 *         hand-rolling it, which is how the Wiki ended up with a full-height
 *         bordered sidebar where the design called for a card.
 * v0.2.0  Added the radius / spacing / motion scale, which was a real gap: v0.1.0
 *         had only --radius and --radius-sm, while launchpad had already grown a
 *         full scale. Values are launchpad's, unchanged, so it adopts without
 *         re-tuning. Added --text and --signal-tint aliases.
 * v0.1.0  First cut. Tokens, three-state theming, component layer.
 *
 * ── House decisions this file deliberately does NOT provide ───────────────────
 *
 * NO GRID BACKGROUND. Six apps carried a 44px graph-paper texture inherited from
 * Stack Atlas by copy-paste; billing reconciliation, which this system is drawn
 * from, never had it. Swept out of the estate 2026-09-01. It is not a token, not a
 * utility, and should not come back — if a page needs texture, that is a decision
 * for that page to argue for, not a default to inherit.
 *
 * LIGHT-FIRST. The bare :root is the light palette; dark is the override. An app
 * that wants to be dark-only stamps data-theme="dark" rather than redefining tokens.
 *
 * ── Why the token names look familiar ──────────────────────────────────────────
 *
 * A survey of the 19 projects with UI found that all 19 already use CSS custom
 * properties, and that two vocabularies had grown up side by side:
 *
 *   --ground / --surface / --ink / --ink-soft / --muted / --faint / --line /
 *   --signal / --signal-deep / --signal-ink / --font-sans      12–16 projects
 *
 *   --bg / --accent / --accent-text / --border / --st-bad|warn|good           1
 *   (ncs-vendor-tracker, the billing reconciliation tool)
 *
 * The values were near-identical — --signal and --accent are both #4CB81A, exactly;
 * --signal-deep and --accent-dim are both #3A8F14; --line #DCE2DB against --border
 * #dce1d6. Two names for one system.
 *
 * So this file keeps the MAJORITY names. Renaming twelve projects to match one would
 * have been churn with no visual payoff. What it takes from the billing tool is the
 * part that was genuinely new: Montserrat, the severity wash/edge pairs, three-state
 * theming, and the component layer.
 *
 * ── The one idea worth keeping from the billing tool ───────────────────────────
 *
 * NCS green cannot carry white text: #FFF on #4CB81A is 2.57:1. Every earlier attempt
 * either darkened the brand green into something nobody specified, or used it only as
 * a hairline.
 *
 * The fix is to invert it — the bright brand green is a FILL carrying near-black text:
 *
 *   #0F120F on #4CB81A     7.35:1     buttons, active nav, any solid green
 *   #2A6B0F on #FFFFFF     6.55:1     green TEXT on light
 *   #4CB81A on #0C110E     6.89:1     green TEXT on dark
 *
 * Which is what the brand guide asks for anyway: green is the accent, black is
 * structural, don't fill large areas with green. So --signal is one colour in both
 * themes (it is a fill), --signal-on is what sits on it, and only --signal-ink — green
 * used as TEXT — shifts between themes.
 *
 * ── How to adopt ──────────────────────────────────────────────────────────────
 *
 *   <link rel="stylesheet" href="https://ui.techietools.ai/ncs-ui.css">
 *   <body class="ncs">
 *
 * Tokens are global on :root, so a project that already references --ink / --surface /
 * --line / --signal picks up the new palette the moment it deletes its own :root block.
 * Every COMPONENT is scoped under .ncs, so adding the stylesheet cannot restyle
 * anything you did not opt in. Adopt tokens first, components when you get to them.
 *
 * ── Hosting note (decide before anyone adopts) ────────────────────────────────
 *
 * Several of these apps sit behind Cloudflare Access. A stylesheet served from another
 * host behind Access does not load — the CSS request is answered with the login page,
 * and the app renders unstyled. This file must be served from a PUBLIC, cacheable path.
 * It carries no secrets, so that is safe; it just has to be a deliberate choice.
 */

@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;800;900&display=swap");

/* ═══════════════════════════════════════════════════════════════════════════════
   TOKENS — global, safe to drop in
   ═══════════════════════════════════════════════════════════════════════════════ */

:root {
  /* Montserrat is what carries the weight of this look; Verdana, the NCS brand font,
     has no 900. Fallback runs brand font first, then system, so a blocked webfont
     degrades to the brand standard rather than to Times. */
  --font-sans: "Montserrat", Verdana, Tahoma, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "Cascadia Code", "Segoe UI Mono", Consolas, Menlo, monospace;

  /* Layered surfaces, faintly green-biased. A pure #f5f5f5 under a green accent reads
     as unconsidered; these are picked to relate to it. */
  --ground:     #EDF0EA;
  --surface:    #FFFFFF;
  --surface-2:  #F6F8F4;
  --surface-3:  #EAEEE7;
  --raise:      #FFFFFF;

  --ink:        #141614;
  --ink-soft:   #48524B;
  --muted:      #616861;
  --faint:      #7C847C;

  --line:        #DCE1D6;
  --line-soft:   #E7EBE5;
  --line-strong: #C3CABD;

  /* The accent. --signal is the FILL and is identical in both themes; --signal-on is
     the text that sits on it; --signal-ink is green used as text, and must darken on
     a light ground. */
  --signal:      #4CB81A;
  --signal-deep: #3A8F14;
  --signal-on:   #0F120F;
  --signal-ink:  #2A6B0F;
  --signal-wash: rgba(76, 184, 26, .13);
  --signal-edge: rgba(76, 184, 26, .34);

  /* Status. Keeps the --s-* names already used across the estate, and adds the
     wash/edge pair each one was missing. These are NOT brand colours: they mean
     "wrong" and "decide", and a reader must never have to work out whether a colour
     is decoration or a verdict. */
  --s-live:      #2A6B0F; --s-live-wash:     rgba(76, 184, 26, .13);  --s-live-edge:     rgba(76, 184, 26, .30);
  --s-pending:   #8A5300; --s-pending-wash:  rgba(138, 83, 0, .10);   --s-pending-edge:  rgba(138, 83, 0, .26);
  --s-broken:    #B3261E; --s-broken-wash:   rgba(179, 38, 30, .10);  --s-broken-edge:   rgba(179, 38, 30, .28);
  --s-info:      #2A5580; --s-info-wash:     rgba(42, 85, 128, .10);  --s-info-edge:     rgba(42, 85, 128, .26);
  --s-inactive:  #6B726B; --s-inactive-wash: rgba(107,114,107,.10);   --s-inactive-edge: rgba(107,114,107,.24);
  --s-external:  #5B7089;

  /* Component radii — the card corner and the control corner. Deliberately NOT
     folded into the step scale below: those are sizing steps, these are two
     specific decisions, and collapsing them would change every card in the estate. */
  --radius:    16px;
  --radius-sm: 10px;

  /* Radius, spacing and motion steps. Taken verbatim from launchpad, which had
     already grown a considered scale while this file had only two radii — so this
     is a gap being closed, not a compatibility shim. Keeping its exact values means
     launchpad adopts without re-tuning a single measurement. */
  --r-xs:   5px;
  --r-sm:   7px;
  --r-md:   9px;
  --r-lg:  12px;
  --r-xl:  14px;
  --r-pill: 20px;

  --sp-1:  6px;
  --sp-2:  9px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 22px;

  --t-fast: .15s;
  --ease:   cubic-bezier(.2, .7, .3, 1);
  --shadow:    0 1px 2px rgba(20, 22, 20, .05), 0 6px 20px rgba(20, 22, 20, .06);
  --shadow-lg: 0 24px 64px rgba(20, 22, 20, .22);
}

/* Dark from the system, unless an explicit light choice overrides it. Three states,
   not two: the default "system" setting stamps no attribute at all, so the light
   palette must live on bare :root and only the overrides go in here. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:    #0C0E0C;
    --surface:   #171917;
    --surface-2: #1F221F;
    --surface-3: #262A26;
    --raise:     #1F221F;

    --ink:      #F4F6F4;
    --ink-soft: #AAB5AC;
    --muted:    #9AA09A;
    --faint:    #858C85;

    --line:        #303430;
    --line-soft:   #262A26;
    --line-strong: #40463F;

    /* --signal and --signal-on do NOT change: the fill and its text are one pair that
       already clears 7.35:1 on any ground. Only green-as-text moves. */
    --signal-ink:  #4CB81A;
    --signal-wash: rgba(76, 184, 26, .14);
    --signal-edge: rgba(76, 184, 26, .38);

    --s-live:     #5ED67A; --s-live-wash:     rgba(94, 214, 122, .13); --s-live-edge:     rgba(94, 214, 122, .32);
    --s-pending:  #F0B445; --s-pending-wash:  rgba(240, 180, 69, .13); --s-pending-edge:  rgba(240, 180, 69, .32);
    --s-broken:   #FF6B62; --s-broken-wash:   rgba(255, 107, 98, .13); --s-broken-edge:   rgba(255, 107, 98, .32);
    --s-info:     #6BA8F5; --s-info-wash:     rgba(107, 168, 245, .13);--s-info-edge:     rgba(107, 168, 245, .32);
    --s-inactive: #9AA09A; --s-inactive-wash: rgba(154, 160, 154, .12);--s-inactive-edge: rgba(154, 160, 154, .26);
    --s-external: #8296AC;

    --shadow:    0 2px 6px rgba(0, 0, 0, .4), 0 12px 32px rgba(0, 0, 0, .32);
    --shadow-lg: 0 30px 80px rgba(0, 0, 0, .62);
  }
}

/* Repeated so an explicit toggle wins over a light OS too. */
:root[data-theme="dark"] {
  --ground:    #0C0E0C;
  --surface:   #171917;
  --surface-2: #1F221F;
  --surface-3: #262A26;
  --raise:     #1F221F;

  --ink:      #F4F6F4;
  --ink-soft: #AAB5AC;
  --muted:    #9AA09A;
  --faint:    #858C85;

  --line:        #303430;
  --line-soft:   #262A26;
  --line-strong: #40463F;

  --signal-ink:  #4CB81A;
  --signal-wash: rgba(76, 184, 26, .14);
  --signal-edge: rgba(76, 184, 26, .38);

  --s-live:     #5ED67A; --s-live-wash:     rgba(94, 214, 122, .13); --s-live-edge:     rgba(94, 214, 122, .32);
  --s-pending:  #F0B445; --s-pending-wash:  rgba(240, 180, 69, .13); --s-pending-edge:  rgba(240, 180, 69, .32);
  --s-broken:   #FF6B62; --s-broken-wash:   rgba(255, 107, 98, .13); --s-broken-edge:   rgba(255, 107, 98, .32);
  --s-info:     #6BA8F5; --s-info-wash:     rgba(107, 168, 245, .13);--s-info-edge:     rgba(107, 168, 245, .32);
  --s-inactive: #9AA09A; --s-inactive-wash: rgba(154, 160, 154, .12);--s-inactive-edge: rgba(154, 160, 154, .26);
  --s-external: #8296AC;

  --shadow:    0 2px 6px rgba(0, 0, 0, .4), 0 12px 32px rgba(0, 0, 0, .32);
  --shadow-lg: 0 30px 80px rgba(0, 0, 0, .62);
}

/* ── Compatibility aliases ─────────────────────────────────────────────────────
 *
 * So ncs-vendor-tracker — and anything else on the --bg / --accent vocabulary — can
 * link this file and keep working while it migrates, rather than needing a big-bang
 * rename. Delete a project's aliases once it is on the house names; delete this whole
 * block once nothing references it.
 */
:root {
  --bg:           var(--ground);
  --border:       var(--line);
  --border-strong: var(--line-strong);
  --ink-2:        var(--ink-soft);
  --ink-3:        var(--muted);
  --accent:       var(--signal);
  --accent-dim:   var(--signal-deep);
  --accent-on:    var(--signal-on);
  --accent-text:  var(--signal-ink);
  --accent-wash:  var(--signal-wash);
  --accent-edge:  var(--signal-edge);
  --font:         var(--font-sans);
  /* --text is what launchpad, ncs-slides and the Wiki call --ink. */
  --text:         var(--ink);
  /* launchpad's --signal-tint was a SOLID #E8F5E0 / #1C2A15; --signal-wash is the
     same colour as a 13% alpha over the surface. Visually equivalent everywhere it
     is used (chip and row backgrounds sitting on --surface), and the alpha version
     composites correctly over any surface instead of assuming one. */
  --signal-tint:  var(--signal-wash);
  --st-good: var(--s-live);     --st-good-bg: var(--s-live-wash);     --st-good-bd: var(--s-live-edge);
  --st-warn: var(--s-pending);  --st-warn-bg: var(--s-pending-wash);  --st-warn-bd: var(--s-pending-edge);
  --st-bad:  var(--s-broken);   --st-bad-bg:  var(--s-broken-wash);   --st-bad-bd:  var(--s-broken-edge);
  --st-info: var(--s-info);     --st-info-bg: var(--s-info-wash);     --st-info-bd: var(--s-info-edge);
  --st-mute: var(--s-inactive); --st-mute-bg: var(--s-inactive-wash); --st-mute-bd: var(--s-inactive-edge);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   COMPONENTS — all scoped under .ncs, so linking this file restyles nothing
   you did not opt into. Put class="ncs" on <body>.
   ═══════════════════════════════════════════════════════════════════════════════ */

.ncs *, .ncs *::before, .ncs *::after { box-sizing: border-box; }

body.ncs {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 13.5px;
  line-height: 1.5;
  /* Explicit, from a token. A transparent body borrows whatever is painted behind it. */
  background: var(--ground);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* Digits line up in columns throughout these apps. Set once, not remembered per table. */
.ncs td, .ncs th, .ncs .v, .ncs .num, .ncs input, .ncs select { font-variant-numeric: tabular-nums; }

/* The wide-tracked uppercase micro-label is the most characteristic thing in this
   system, so it is defined once and reused by every label-shaped thing. */
.ncs .k,
.ncs th,
.ncs label,
.ncs .lbl,
.ncs header h1 .sub { font-size: 10.5px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase; }

/* ── Shell ─────────────────────────────────────────────────────────────────── */

.ncs header {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding: 0 22px; min-height: 62px;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
.ncs header img.logo { height: 30px; width: auto; display: block; border-radius: 7px; }
.ncs header h1 { font-size: 16px; margin: 0; font-weight: 900; letter-spacing: -.01em; line-height: 1.15; color: var(--ink); }
.ncs header h1 .sub { display: block; color: var(--signal-ink); margin-top: 2px; letter-spacing: .18em; }
.ncs header .who { margin-left: auto; font-size: 11.5px; color: var(--muted); font-weight: 600; }

/* Sticky, because losing the tab bar halfway down a long table is the most irritating
   thing about a long table. */
.ncs header, .ncs nav { position: sticky; z-index: 30; background: var(--surface); }
.ncs header { top: 0; }
.ncs nav { top: 62px; }

/* Nav as a pill GROUP rather than a bar of underlines: a bordered container of
   buttons, the active one a solid accent fill. */
.ncs nav {
  padding: 10px 22px 12px;
  border-bottom: 1px solid var(--line);
  display: flex; gap: 3px; overflow-x: auto; scrollbar-width: thin;
}
.ncs nav button,
.ncs nav a {
  border: 0; background: transparent; color: var(--ink-soft);
  font-family: var(--font-sans); font-weight: 700; font-size: 12.5px;
  padding: 8px 15px; border-radius: 9px; cursor: pointer; white-space: nowrap;
  transition: background .13s, color .13s;
  /* The link half needs these two; the button half is unaffected by them. */
  text-decoration: none; line-height: 1.2;
}
.ncs nav button:hover,
.ncs nav a:hover { color: var(--ink); background: var(--surface-3); }
.ncs nav button.active,
.ncs nav a.active { background: var(--signal); color: var(--signal-on); font-weight: 900; }
.ncs nav button:focus-visible,
.ncs nav a:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

.ncs main { padding: 22px; max-width: 1440px; margin: 0 auto; width: 100%; }

/* Shell hardening for a flex-column <body>.
 *
 * `margin: 0 auto` centres a block, but on a FLEX ITEM the auto side-margins also
 * make it shrink-to-fit, so main collapses to whatever its content happens to be
 * wide. `width: 100%` above restores the intent in both layouts.
 *
 * Likewise a tall page squeezes a flex-column's header and nav unless they are told
 * not to shrink. Both are no-ops in normal block flow, so this costs nothing. */
.ncs header, .ncs nav { flex-shrink: 0; }

/* ── Page layout: content + rail ────────────────────────────────────────────
 *
 * The shape most of these tools want: a main column with a narrower companion
 * beside it — sources, filters, a detail pane, a preview.
 *
 * `align-items: start` is the entire trick, and it is the difference between the
 * two things people mean by "sidebar". Without it the rail stretches to the row
 * height and reads as a full-height chrome panel bolted to the window edge. With
 * it, the rail is a CARD that sits beside the content and ends where its content
 * ends — which is what the house design actually calls for.
 *
 * Collapses to one column early (980px), because a 320px rail beside a squeezed
 * main column is worse than stacking. */
.ncs .work {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 16px;
  align-items: start;
}
/* For a rail that holds chips or short facts rather than a list of sources. */
.ncs .work.rail-sm { grid-template-columns: minmax(0, 1fr) 260px; }
/* Rail first, for filter-led pages. */
.ncs .work.rail-first { grid-template-columns: 320px minmax(0, 1fr); }
@media (max-width: 980px) {
  .ncs .work,
  .ncs .work.rail-sm,
  .ncs .work.rail-first { grid-template-columns: minmax(0, 1fr); }
}
.ncs h2 { font-size: 15px; margin: 0 0 16px; font-weight: 800; letter-spacing: -.01em; color: var(--ink); }
.ncs .row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.ncs .spacer { flex: 1; }

/* ── Stat tiles ────────────────────────────────────────────────────────────── */

.ncs .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 13px; margin-bottom: 20px; }
.ncs .card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; box-shadow: var(--shadow); position: relative; overflow: hidden;
}
.ncs .card .k { color: var(--ink-soft); }
.ncs .card .v { font-size: 30px; font-weight: 900; letter-spacing: -1.2px; line-height: 1; margin-top: 7px; color: var(--ink); }
.ncs .card .foot { font-size: 11px; color: var(--muted); font-weight: 600; margin-top: 6px; }

.ncs .card.accent::before,
.ncs .card.alert::before,
.ncs .card.warn::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }
/* The stripe AND the number take the same colour — that is what makes these read as a
   set rather than as decorated boxes. */
.ncs .card.accent::before { background: var(--signal); }
.ncs .card.accent .v      { color: var(--signal-ink); }
.ncs .card.alert::before  { background: var(--s-broken); }
.ncs .card.alert .v       { color: var(--s-broken); }
.ncs .card.warn::before   { background: var(--s-pending); }
.ncs .card.warn .v        { color: var(--s-pending); }

/* ── Panels and tables ─────────────────────────────────────────────────────── */

.ncs .panel {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); max-width: 100%;
}
.ncs .panel-head { padding: 14px 18px; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 10px; }
.ncs .panel-head .lbl { color: var(--muted); }
.ncs .panel-body { padding: 18px; }

/* Wide content scrolls inside its own container so the page body never scrolls sideways.
   A height cap is what makes the sticky `th` below work at all: `overflow:auto` makes
   this the containing block for the sticky header, and with no height limit the
   scrollport never scrolls, so the header never sticks. max-height, not height —
   short tables stay short. */
.ncs .tablewrap { overflow: auto; max-height: calc(100vh - 210px); }
.ncs table { width: 100%; border-collapse: separate; border-spacing: 0; }
.ncs th, .ncs td { text-align: left; padding: 10px 13px; font-size: 12.5px; vertical-align: top; border-bottom: 1px solid var(--line); }
.ncs th {
  background: var(--surface-2); color: var(--muted); white-space: nowrap;
  position: sticky; top: 0; z-index: 2; border-bottom: 1px solid var(--line-strong);
}
.ncs tbody tr { transition: background .1s; }
.ncs tbody tr:hover td { background: var(--surface-2); }
.ncs tr:last-child td { border-bottom: 0; }
.ncs td.num, .ncs th.num { text-align: right; }

/* A row that behaves like a link has to look like one. A <tr> is not focusable on its
   own, so it carries tabindex and a keydown handler — without a visible focus style a
   keyboard user would be moving through invisible targets. */
.ncs tr.clickable { cursor: pointer; }
.ncs tr.clickable:hover td { background: var(--signal-wash); }
.ncs tr.clickable:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
.ncs tr.clickable:focus-visible td { background: var(--signal-wash); }
/* The destination label: quiet at rest, so eight rows are not eight shouted calls to
   action, and accent-coloured the moment the row is the one under the cursor. */
.ncs td.go { color: var(--muted); font-weight: 700; white-space: nowrap; }
.ncs tr.clickable:hover td.go,
.ncs tr.clickable:focus-visible td.go { color: var(--signal-ink); }
.ncs td.go .chev { display: inline-block; margin-left: 3px; transition: transform .12s; }
.ncs tr.clickable:hover td.go .chev { transform: translateX(2px); }

/* ── Buttons ───────────────────────────────────────────────────────────────── */

.ncs .btn {
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 800; cursor: pointer;
  background: var(--signal); color: var(--signal-on); border: 1px solid var(--signal);
  padding: 9px 15px; border-radius: var(--radius-sm); white-space: nowrap;
  transition: background .13s, border-color .13s, color .13s;
}
.ncs .btn:hover:not(:disabled) { background: var(--signal-deep); border-color: var(--signal-deep); }
.ncs .btn:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.ncs .btn:disabled { opacity: .45; cursor: default; }
/* Secondary is a surface, not an outlined accent — it has to recede from the fill. */
.ncs .btn.sec { background: var(--surface-2); color: var(--ink); border-color: var(--line); font-weight: 700; }
.ncs .btn.sec:hover:not(:disabled) { background: var(--surface-3); border-color: var(--line-strong); }
.ncs .btn.ghost { background: transparent; color: var(--signal-ink); border-color: transparent; padding: 6px 10px; }
.ncs .btn.ghost:hover:not(:disabled) { background: var(--signal-wash); }
.ncs .btn.danger { background: transparent; color: var(--s-broken); border-color: var(--s-broken-edge); padding: 6px 10px; }
.ncs .btn.danger:hover:not(:disabled) { background: var(--s-broken-wash); }
.ncs .btn.sm { font-size: 11.5px; padding: 6px 11px; }

.ncs .theme-toggle {
  background: var(--surface-2); color: var(--ink-soft); border: 1px solid var(--line);
  border-radius: 9px; width: 34px; height: 30px; cursor: pointer; font-size: 14px;
  line-height: 1; display: grid; place-items: center; padding: 0; font-family: var(--font-sans);
}
.ncs .theme-toggle:hover { background: var(--surface-3); color: var(--ink); }
.ncs .theme-toggle:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* ── Chips: the wash/edge pattern ──────────────────────────────────────────── */

/* One shape shared by every badge: a ~13%-alpha tint with a matching ~30%-alpha border
   of the same hue. It is what makes a dozen different states look like one family. */
.ncs .chip, .ncs .status, .ncs .flag {
  display: inline-block; border-radius: 7px; padding: 3px 8px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em;
  border: 1px solid transparent; white-space: nowrap;
  background: var(--s-inactive-wash); border-color: var(--s-inactive-edge); color: var(--ink-soft);
}
.ncs .status { border-radius: 99px; padding: 3px 10px; }

/* Severity is the only thing the colour encodes. */
.ncs .chip.live,    .ncs .status.live,    .ncs .flag.live    { background: var(--s-live-wash);     border-color: var(--s-live-edge);     color: var(--s-live); }
.ncs .chip.pending, .ncs .status.pending, .ncs .flag.pending { background: var(--s-pending-wash);  border-color: var(--s-pending-edge);  color: var(--s-pending); }
.ncs .chip.broken,  .ncs .status.broken,  .ncs .flag.broken  { background: var(--s-broken-wash);   border-color: var(--s-broken-edge);   color: var(--s-broken); }
.ncs .chip.info,    .ncs .status.info,    .ncs .flag.info    { background: var(--s-info-wash);     border-color: var(--s-info-edge);     color: var(--s-info); }
.ncs .chip.accent,  .ncs .status.accent,  .ncs .flag.accent  { background: var(--signal-wash);     border-color: var(--signal-edge);     color: var(--signal-ink); }

/* ── Forms ─────────────────────────────────────────────────────────────────── */

.ncs label { display: block; color: var(--muted); margin: 14px 0 5px; }
.ncs input, .ncs select, .ncs textarea {
  width: 100%; padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-family: var(--font-sans); font-size: 12.5px; color: var(--ink); background: var(--surface-2);
}
.ncs input:focus, .ncs select:focus, .ncs textarea:focus {
  outline: none; border-color: var(--signal); box-shadow: 0 0 0 3px var(--signal-wash);
}
.ncs textarea { min-height: 60px; resize: vertical; }

/* Form controls do not inherit font-family — that is a browser default, not an
   oversight anyone notices until they look. The rule above covers input, select
   and textarea; <button> was missed, so every unstyled button in an adopting
   tool fell back to the browser default and rendered in Arial while the page
   around it was Montserrat.
   Found on the Stack Atlas icon buttons, and by then All-Stars and the travel
   calculator had each hand-rolled the same reset locally — three apps working
   around one missing line.
   .ncs .btn and .ncs nav button already set the face explicitly, so this only
   catches the ones nothing else styles. */
.ncs button { font-family: inherit; }

/* A checkbox is not a text field. The rule above was stretching every one in the
   estate to width:100% and giving it a text field's padding, border and
   background — which reads as a wide empty box, and pushes the label text onto a
   second line when the two share a flex row.
   Two adopters had already hand-rolled this (social approvals, and Ticket Review
   needed it next); two more — billing reconciliation and the Wiki — were
   silently stretched and nobody had noticed. Hence it belongs here.
   The [type] attribute puts this at (0,2,1), above the (0,1,1) rule it corrects. */
.ncs input[type="checkbox"],
.ncs input[type="radio"] {
  width: 16px; height: 16px; flex: none;
  padding: 0; border: 0; background: none; border-radius: 0;
  accent-color: var(--signal); cursor: pointer;
}

/* ── Modal ─────────────────────────────────────────────────────────────────── */

.ncs .modal-bg {
  position: fixed; inset: 0; background: rgba(6, 8, 6, .62);
  display: none; align-items: flex-start; justify-content: center;
  padding: 44px 16px; overflow: auto; z-index: 50;
}
.ncs .modal-bg.open { display: flex; }
.ncs .modal {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  width: 660px; max-width: 100%; padding: 24px; box-shadow: var(--shadow-lg);
  border-top: 3px solid var(--signal);
}
.ncs .modal h3 { margin: 0 0 18px; font-size: 16px; font-weight: 900; letter-spacing: -.01em; }
.ncs .modal-actions { display: flex; gap: 10px; margin-top: 24px; justify-content: flex-end; }

/* ── Utilities ─────────────────────────────────────────────────────────────── */

.ncs .muted { color: var(--muted); }
.ncs .pos   { color: var(--s-live);    font-weight: 800; }
.ncs .neg   { color: var(--s-broken);  font-weight: 800; }
.ncs .warnc { color: var(--s-pending); font-weight: 800; }
.ncs .empty { padding: 44px; text-align: center; color: var(--muted); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .ncs *, .ncs *::before, .ncs *::after { transition: none !important; animation: none !important; }
}
