/* The shared shell — tokens, banner, rail, breadcrumbs, pagination (slice 4, R1/R2/R3/R5).
 *
 * Extracted from tools/powerpack/.../theme.css, where slices 1-3 built and proved it. Every
 * tool and the hub load THIS file for the token layer and the shell; each then loads its own
 * stylesheet for what is specific to it.
 *
 * THE LINE: a rule belongs here if it would mean the same thing on any of the three apps —
 * tokens, base/reset, focus treatment, banner, rail, breadcrumbs, chips, the in-page filter
 * box, pagination, empty states. A rule about PowerPacks (cards, the DA table, thresholds)
 * belongs in the tool. When unsure, leave it in the tool: a shared layer earns rules by having
 * two real consumers, which is the same discipline that kept this file in powerpack until now.
 *
 * PATHS: this stylesheet is served from each tool's own /static mount (nginx routes /explorer/
 * and /powerpack/ to different processes, so there is no shared origin). url() inside it
 * resolves against THIS file's location, so font srcs stay relative and work under any mount
 * prefix without knowing it.
 */

/* PowerPack Explorer theme — the light enterprise palette (slice 1, R1/R3/R5).
 *
 * Ported from docs/design/powerpack-ux/DESIGN-SPEC.md § Design tokens. This file replaces the
 * inline <style> block that used to live in base.html; slices 2 and 3 consume these same tokens
 * for the pack and DA pages, so the token layer is the contract — change values here, not in
 * per-page markup.
 *
 * NOTE ON PATHS: this stylesheet is served from {root_path}/static/theme.css. Any url() inside
 * it resolves against THIS file's location, not the page's — so font srcs are written relative
 * ("fonts/X.woff2") and work under the /powerpack mount prefix without knowing it.
 */

/* ---------------------------------------------------------------- fonts (Unit 2) */
/* Vendored, no CDN. Relative src per the note above. */
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400 700;              /* variable: one file covers 400/500/600/700 */
  font-display: swap;
  src: url("fonts/PublicSans-Variable.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 600;              /* variable: covers 400/500/600 */
  font-display: swap;
  src: url("fonts/JetBrainsMono-Variable.woff2") format("woff2");
}

:root {
  /* --- color: surfaces --- */
  --bg-app: #f6f7f8;
  --bg-surface: #ffffff;
  --bg-fill: #f2f4f6;
  --border: #dde1e6;
  --border-subtle: #eef1f3;
  --border-row: #f4f6f7;

  /* --- color: ink --- */
  --text: #1a1d21;
  --text-secondary: #3d434a;
  --text-muted: #697077;             /* 5.0:1 on white */
  --text-disabled: #c1c7cd;

  /* --- color: accent --- */
  --accent: #b3261e;
  --accent-tint: #fbeae9;
  --accent-row: #fdf6f5;
  --accent-border: #f0c9c6;
  --link: #2a78d6;

  /* --- color: semantic --- */
  --teal: #0e6b60;                   /* provenance / lineage */
  --teal-border: #a8d5cd;
  --amber: #9a6b00;                  /* "no Dynamic Applications" */
  --warn-bar: #c8912f;

  /* --- kind chips: background / text pairs --- */
  --chip-performance-bg: #e7f0e9;  --chip-performance-fg: #1f6b3a;
  --chip-config-bg:      #e8eef7;  --chip-config-fg:      #2d5c9e;
  --chip-state-bg:       #f6ecdd;  --chip-state-fg:       #8a5a00;
  --chip-other-bg:       #f2f4f6;  --chip-other-fg:       #697077;

  /* --- geometry --- */
  --banner-h: 52px;
  --rail-w: 212px;
  --radius-card: 8px;
  --radius-input: 6px;
  --radius-page: 5px;
  --radius-tag: 4px;
  --radius-pill: 999px;

  /* --- type --- */
  --font-ui: "Public Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---------------------------------------------------------------- base */
* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg-app);
  color: var(--text);
  font: 400 13.5px/1.5 var(--font-ui);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }

/* R5: one global focus treatment, never the browser default. Token-layer concern —
   do NOT redeclare per component. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* filter.js toggles [hidden] to narrow the rendered page. */
[hidden] { display: none !important; }

.muted { color: var(--text-muted); }
.mono { font-family: var(--font-mono); }

/* ---------------------------------------------------------------- shell: banner (Unit 3) */
.banner {
  height: var(--banner-h);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 20px;
}

.banner-brand { display: flex; align-items: center; gap: 10px; }
.banner-mark { width: 24px; height: 24px; flex: none; }
.banner-name { font-weight: 700; font-size: 14px; letter-spacing: .2px; }
.banner-tag {
  font-size: 11px; color: var(--text-muted);
  border: 1px solid var(--border); border-radius: var(--radius-tag); padding: 1px 6px;
}
.hublink {
  font-size: 12.5px; color: var(--text-muted);
  padding-right: 12px; border-right: 1px solid var(--border);
}
.hublink:hover { color: var(--accent); }

/* centered global search, 420px per spec */
.banner-search { flex: 1; display: flex; justify-content: center; }
.banner-search form { position: relative; width: 420px; }
.banner-search input {
  width: 100%; font: inherit; font-size: 13px;
  background: var(--bg-fill); border: 1px solid var(--border);
  border-radius: var(--radius-input); padding: 6px 34px 6px 32px;
  color: var(--text); outline: none;
  transition: border-color .12s, background .12s;
}
.banner-search input:focus { background: var(--bg-surface); border-color: var(--accent-border); }
.banner-search input::placeholder { color: var(--text-muted); }
.banner-search .search-icon {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 14px; height: 14px; color: var(--text-muted); pointer-events: none;
}
/* the mono "/" keyboard hint, right-aligned inside the field */
.banner-search .slash-hint {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  font-family: var(--font-mono); font-size: 10px; color: var(--text-muted);
  border: 1px solid var(--border); border-radius: 3px; padding: 0 5px;
  background: var(--bg-surface); pointer-events: none;
}

.banner-identity {
  display: flex; align-items: center; gap: 14px;
  font-size: 12.5px; color: var(--text-muted);
}
.banner-identity .signout { color: var(--accent); font-weight: 600; }
.banner-identity .signout:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- shell: rail + layout */
.shell { display: flex; align-items: flex-start; }

.rail {
  width: var(--rail-w); flex: none;
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
  padding: 14px 0;
  min-height: calc(100vh - var(--banner-h));
  position: sticky; top: 0;
}
.rail-section {
  font-size: 10.5px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  color: var(--text-muted); padding: 14px 18px 6px;
}
.rail-section:first-child { padding-top: 0; }
.rail-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 18px; font-size: 13px; color: var(--text-secondary);
  border-right: 3px solid transparent;
  transition: background .12s, color .12s;
}
.rail-item:hover { background: var(--bg-fill); color: var(--text); }
.rail-item.active {
  background: var(--accent-tint); color: var(--accent); font-weight: 600;
  border-right-color: var(--accent);
}
.rail-item .rail-count {
  margin-left: auto; font-family: var(--font-mono); font-size: 11.5px;
  color: var(--text-muted);
}
.rail-item.active .rail-count { color: var(--accent); }
.rail-item .rail-glyph { width: 13px; height: 13px; flex: none; }
/* R8/R9: a zero-count entry reads as deliberate, not broken. */
.rail-item.is-empty { color: var(--text-disabled); }
.rail-item.is-empty .rail-count { color: var(--text-disabled); }
/* A zero-count rail entry is a <span>, not a link — nothing to navigate to. Kill the hover
   affordance so it doesn't invite a click it can't answer. */
.rail-item.is-empty:hover { background: none; color: var(--text-disabled); }
/* Dynamic Apps stays a real link even at 0 (it's the default view, and 28% of packs have
   none), so it can be active AND empty at once. Active wins — a greyed-out highlighted row
   reads as broken. */
.rail-item.is-empty.active,
.rail-item.is-empty.active:hover { color: var(--accent); background: var(--accent-tint); }
.rail-item.is-empty.active .rail-count { color: var(--accent); }

/* --- pack rail (slice 2, R10): back link + pack identity above PACK CONTENTS --- */
.rail-back {
  display: flex; align-items: center; gap: 8px;
  padding: 0 18px 12px; font-size: 12.5px; color: var(--text-muted);
}
/* .rail-glyph is sized under .rail-item; the back link is not one, so size it here too. */
.rail-back .rail-glyph { width: 13px; height: 13px; flex: none; }
.rail-back:hover { color: var(--accent); }
.rail-ident {
  padding: 0 18px 12px; margin-bottom: 4px;
  border-bottom: 1px solid var(--border-subtle);
}
.rail-ident-name {
  font-size: 13.5px; font-weight: 700; color: var(--text); line-height: 1.35;
  margin-bottom: 3px;
}
.rail-ident-meta { font-size: 11.5px; color: var(--text-muted); }

.content { flex: 1; min-width: 0; padding: 16px 24px 24px; }

/* ---------------------------------------------------------------- breadcrumbs */
.crumbs {
  font-size: 12px; color: var(--text-muted);
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.crumbs a:hover { color: var(--accent); }
.crumbs .sep { color: var(--text-disabled); }
.crumbs .current { color: var(--text-secondary); }

/* ---------------------------------------------------------------- page title row */
.title-row { display: flex; align-items: baseline; gap: 10px; margin: 0 0 16px; }
.page-title { font-size: 22px; font-weight: 700; letter-spacing: -.2px; margin: 0; }
.title-count { font-size: 12.5px; color: var(--text-muted); font-family: var(--font-mono); }
/* Explains a scope the user didn't choose (the default landing view), with the way out. Sits
   right of the count and pushes to the row's end so it reads as an aside, not a heading. */
.scope-note { margin-left: auto; font-size: 12px; color: var(--text-muted); }
.scope-note a { color: var(--link); }
.scope-note a:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- grid: sections + cards */
.cat-section { margin: 0 0 26px; }
.cat-label {
  font-size: 10.5px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
  color: var(--text-muted); margin: 0 0 10px;
}
.cat-label .cat-count { font-weight: 400; margin-left: 8px; font-family: var(--font-mono); }

.vendor-group { margin: 0 0 18px; }
.vendor-name {
  font-size: 12.5px; font-weight: 600; color: var(--text-secondary); margin: 0 0 9px;
}

/* The mock draws a rigid repeat(3, 1fr), which it can because its sample vendors all carry
   several packs. The real corpus does not: 168 of 233 vendors have exactly ONE pack, and 55%
   of the vendor groups on page 1 are single-card. A fixed 3-column track leaves those groups
   two-thirds empty, so cards auto-fill the row instead — visually identical wherever a vendor
   has 3+ packs (the case the mock shows), and honest about the long tail everywhere else. */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 12px;
}

.card {
  display: flex; gap: 13px; align-items: flex-start;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 14px 15px;
  transition: border-color .12s, background .12s;
  min-width: 0;
}
.card:hover { border-color: var(--accent); }

.card-art {
  width: 58px; height: 58px; flex: none;
  background: var(--bg-fill); border-radius: var(--radius-input);
}
.card-body { min-width: 0; flex: 1; }
.card-name {
  font-size: 13.5px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card-meta {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--text-muted);
  margin-top: 3px;
}

/* honesty flags — requirements, not decoration (R6/R9) */
.flag { font-size: 11.5px; margin-top: 5px; display: block; }
.flag-noda { color: var(--amber); }
.flag-inv { color: var(--teal); }

/* R9: the zero-DA card is a designed state (28% of packs), not a card that failed to load. */
.card.is-empty .card-art { background: var(--bg-fill); opacity: .7; }

/* ---------------------------------------------------------------- chips */
.chips { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 7px; }
.chip {
  font-size: 10px; font-weight: 600; padding: 2px 8px;
  border-radius: var(--radius-pill); text-transform: capitalize;
  background: var(--chip-other-bg); color: var(--chip-other-fg);
}
.chip.performance { background: var(--chip-performance-bg); color: var(--chip-performance-fg); }
/* Two spellings, one concept: powerpack's kind chips say "config", while analytics' DataType
   enum value is "configuration". The shared layer answers to both rather than making either
   app rename a stored value for a stylesheet's convenience. */
.chip.config,
.chip.configuration { background: var(--chip-config-bg);      color: var(--chip-config-fg); }
.chip.state       { background: var(--chip-state-bg);       color: var(--chip-state-fg); }
.chip.other       { background: var(--chip-other-bg);       color: var(--chip-other-fg); }

/* inventory-only provenance badge (digest packs) */
.badge-inv {
  font-size: 11px; padding: 2px 8px; border-radius: var(--radius-pill); margin-left: 10px;
  border: 1px solid var(--teal-border); color: var(--teal); font-weight: 400;
  text-transform: none; letter-spacing: normal; vertical-align: middle; white-space: nowrap;
}
.inventory-note {
  border: 1px solid var(--border); border-left: 3px solid var(--teal);
  background: var(--bg-surface); border-radius: var(--radius-input); padding: 10px 14px;
  font-size: 13px; color: var(--text-secondary); margin: 12px 0;
}
.cls {
  font-size: 11px; color: var(--teal); white-space: nowrap;
  border: 1px solid var(--teal-border); border-radius: var(--radius-pill); padding: 1px 9px;
}

/* ---------------------------------------------------------------- in-page filter */
.filterbar { margin: 0 0 16px; }
/* One text-input treatment for every in-page filter box — the grid's (.filterbar input) and
   the pack panels' (.panel-filter). They differ only in placement and width, below. */
.filterbar input, .panel-filter {
  width: 100%; max-width: 420px; font: inherit; font-size: 13px;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-input); padding: 7px 12px; color: var(--text); outline: none;
  transition: border-color .12s;
}
.filterbar input:focus, .panel-filter:focus { border-color: var(--accent-border); }
.filterbar input::placeholder, .panel-filter::placeholder { color: var(--text-muted); }
.filter-empty { color: var(--text-muted); padding: 18px 2px; }

/* ---------------------------------------------------------------- pagination (Unit 6) */
.pagination {
  display: flex; align-items: center; gap: 12px;
  margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--border);
  font-size: 12.5px;
}
.page-showing { color: var(--text-muted); font-family: var(--font-mono); font-size: 11.5px; }
.page-controls { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.page-link {
  min-width: 28px; text-align: center;
  padding: 4px 8px; border-radius: var(--radius-page);
  border: 1px solid var(--border); background: var(--bg-surface);
  font-family: var(--font-mono); font-size: 12px; color: var(--text-secondary);
  transition: border-color .12s, background .12s;
}
.page-link:hover { border-color: var(--accent); color: var(--accent); }
.page-link.current {
  background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600;
}
.page-link.disabled {
  color: var(--text-disabled); border-color: var(--border-subtle);
  pointer-events: none;
}
.page-gap { color: var(--text-disabled); padding: 0 2px; font-family: var(--font-mono); }

/* ---------------------------------------------------------------- empty states */
.empty-state {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 28px 24px; text-align: center;
  color: var(--text-muted); font-size: 13px;
}
.empty-state strong { color: var(--text); font-weight: 600; }


/* ---------------------------------------------------------------- auth card (slice 4)
 * The centered sign-in card, shared by the hub's auth screens and (in 4c) the Explorer's
 * standalone ones. It is the one screen shape that has no banner and no rail: you reach it
 * WITHOUT a session, so there is no identity to show and nothing to navigate to yet.
 *
 * This is styling only. The hub owns every piece of auth behavior — the forms, the `next`
 * handling, the session. Nothing here reads or writes any of it (see the guard test in
 * packages/tenxse-core/tests/test_web_assets.py).
 */
.auth-page {
  margin: 0; min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-app); color: var(--text);
}
.auth {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 32px 30px; width: 360px; max-width: 92vw;
  box-shadow: 0 1px 3px rgba(26, 29, 33, .06);
}
.auth h1 {
  font-size: 19px; font-weight: 700; margin: 0 0 6px; letter-spacing: -.2px;
}
.auth h1 .dot { color: var(--accent); }
.auth p.lead { color: var(--text-muted); font-size: 13px; margin: 0 0 20px; }
.auth label {
  display: block; font-size: 12px; color: var(--text-muted); margin: 0 0 6px;
}
.auth input[type=email], .auth input[type=text], .auth textarea {
  width: 100%; background: var(--bg-app); border: 1px solid var(--border);
  border-radius: var(--radius-input); color: var(--text); padding: 11px 13px;
  font: inherit; font-size: 15px; outline: none; transition: border-color .12s;
}
.auth textarea { min-height: 90px; resize: vertical; font-size: 14px; }
.auth input:focus, .auth textarea:focus { border-color: var(--accent-border); }
.auth .field { margin-bottom: 16px; }

/* The six-box code entry. The visible boxes are decoration over a hidden field that carries
   the real value — see the hub's verify.html. Keep .code-box and .filled: its script binds
   to them by name. */
.auth .code-row { display: flex; gap: 8px; margin-bottom: 16px; }
.auth .code-box {
  width: 100%; text-align: center; font-size: 22px; font-family: var(--font-mono);
  padding: 10px 0; background: var(--bg-app); border: 1px solid var(--border);
  border-radius: var(--radius-input); color: var(--text); outline: none;
  transition: border-color .12s;
}
.auth .code-box:focus { border-color: var(--accent); }
.auth .code-box.filled { border-color: var(--accent-border); }

.auth button {
  width: 100%; background: var(--accent); color: #fff; border: none;
  border-radius: var(--radius-input); padding: 12px 14px; font: inherit;
  font-size: 15px; font-weight: 600; cursor: pointer; transition: background .12s;
}
.auth button:hover { background: #9d211a; }
.auth button:active { background: #8a1d17; }

.auth .msg {
  font-size: 13px; padding: 10px 12px; border-radius: var(--radius-input);
  margin: 0 0 16px;
}
.auth .msg.info {
  background: var(--accent-tint); color: var(--text-secondary);
  border: 1px solid var(--accent-border);
}
.auth .msg.err {
  background: var(--accent-tint); color: var(--accent);
  border: 1px solid var(--accent-border);
}
/* The small print under a sign-in form — "Use a different email", "Access is limited to
   approved organizations". Six templates across both apps render class="foot". */
.auth .foot {
  margin: 16px 0 0; font-size: 12.5px; color: var(--text-muted); text-align: center;
}
.auth .foot a { color: var(--link); }
.auth .foot a:hover { text-decoration: underline; }
