/* Truss dashboard — "Blueprint" look. Served same-origin via the asset route
   (not inlined), so a strict CSP needs only style-src 'self' for this sheet.
   Theme-reactive via prefers-color-scheme; a data-theme="light|dark" on <html>
   can force a theme. The Mermaid diagram is themed here too (see bottom), so it
   adapts to light/dark with no re-render — Mermaid's own theme is left neutral.

   Fonts are system stacks (no CDN, no bundled font files): a monospace for the
   technical/blueprint feel, a system sans for longer copy. */

/* IBM Plex Mono, self-hosted (woff2 served by the asset route; the relative
   URLs resolve against this stylesheet's own {prefix}/assets/ path). No CDN. */
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("ibm-plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("ibm-plex-mono-500.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("ibm-plex-mono-600.woff2") format("woff2");
}

:root {
  color-scheme: light dark;

  --bp-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --bp-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Light (default) */
  --bp-bg: #eef1f6;
  --bp-grid: rgba(18, 53, 107, .07);
  --bp-grid-strong: rgba(18, 53, 107, .12);
  --bp-panel: #fbfcfe;
  --bp-panel-line: #c9d4e6;
  --bp-ink: #12356b;
  --bp-cyan: #0e7c9b;
  --bp-fg: #132741;
  --bp-muted: #5f7396;
  --bp-field: #ffffff;
  --bp-field-line: #c9d4e6;

  --bp-entity-bg: #ffffff;
  --bp-row-odd: #ffffff;
  --bp-row-even: #e9f0f9;
  --bp-entity-border: #12356b;
  --bp-hair: #c4d5ec;
  --bp-entity-text: #12263c;
  --bp-rel: #6f8fbf;
  --bp-edge: #5f7396;
  --bp-edge-bg: #eef1f6;
  --bp-focus-border: #0e7c9b; --bp-focus-bg: #e6f7fc;

  --bp-info-bg: #e5eefb; --bp-info-fg: #12356b;
  --bp-warn-bg: #fbeeda; --bp-warn-fg: #8a5a00;
  --bp-error-bg: #f7dede; --bp-error-fg: #a11;
}

/* Dark — applied on OS-dark (unless forced light) and on forced dark. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bp-bg: #0b1a2b;
    --bp-grid: rgba(120, 200, 230, .055);
    --bp-grid-strong: rgba(120, 200, 230, .11);
    --bp-panel: #0f2338;
    --bp-panel-line: #22496b;
    --bp-ink: #5fd0e6;
    --bp-cyan: #7fe0f2;
    --bp-fg: #d7e7f4;
    --bp-muted: #7fa2c0;
    --bp-field: #0e2135;
    --bp-field-line: #22496b;

    --bp-entity-bg: #10273f;
    --bp-row-odd: #10273f;
    --bp-row-even: #16324e;
    --bp-entity-border: #3f7fa0;
    --bp-hair: #274a6b;
    --bp-entity-text: #d7e7f4;
    --bp-rel: #4a7d99;
    --bp-edge: #9fb8d0;
    --bp-edge-bg: #0b1a2b;
    --bp-focus-border: #7fe0f2; --bp-focus-bg: #103a4a;

    --bp-info-bg: #10314a; --bp-info-fg: #8fd4e6;
    --bp-warn-bg: #3a2f16; --bp-warn-fg: #e6b877;
    --bp-error-bg: #40201f; --bp-error-fg: #f0a0a0;
  }
}
:root[data-theme="dark"] {
  --bp-bg: #0b1a2b;
  --bp-grid: rgba(120, 200, 230, .055);
  --bp-grid-strong: rgba(120, 200, 230, .11);
  --bp-panel: #0f2338;
  --bp-panel-line: #22496b;
  --bp-ink: #5fd0e6;
  --bp-cyan: #7fe0f2;
  --bp-fg: #d7e7f4;
  --bp-muted: #7fa2c0;
  --bp-field: #0e2135;
  --bp-field-line: #22496b;

  --bp-entity-bg: #10273f;
  --bp-row-odd: #10273f;
  --bp-row-even: #16324e;
  --bp-entity-border: #3f7fa0;
  --bp-hair: #274a6b;
  --bp-entity-text: #d7e7f4;
  --bp-rel: #4a7d99;
  --bp-edge: #9fb8d0;
  --bp-edge-bg: #0b1a2b;
  --bp-focus-border: #7fe0f2; --bp-focus-bg: #103a4a;

  --bp-info-bg: #10314a; --bp-info-fg: #8fd4e6;
  --bp-warn-bg: #3a2f16; --bp-warn-fg: #e6b877;
  --bp-error-bg: #40201f; --bp-error-fg: #f0a0a0;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  color: var(--bp-fg);
  font-family: var(--bp-sans);
  background:
    linear-gradient(var(--bp-grid-strong) 1px, transparent 1px) 0 0 / 96px 96px,
    linear-gradient(90deg, var(--bp-grid-strong) 1px, transparent 1px) 0 0 / 96px 96px,
    linear-gradient(var(--bp-grid) 1px, transparent 1px) 0 0 / 24px 24px,
    linear-gradient(90deg, var(--bp-grid) 1px, transparent 1px) 0 0 / 24px 24px,
    var(--bp-bg);
}

/* ---- toolbar ---------------------------------------------------------- */

.truss-toolbar {
  display: flex; align-items: center; gap: 18px; height: 54px; padding: 0 18px;
  background: var(--bp-panel); border-bottom: 2px solid var(--bp-ink);
  box-shadow: 0 1px 0 var(--bp-panel-line); position: sticky; top: 0; z-index: 5;
}

.truss-brand {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--bp-mono); font-weight: 600; font-size: 15px;
  letter-spacing: .22em; text-transform: uppercase; color: var(--bp-ink);
}
.truss-mark { flex: none; display: block; color: var(--bp-ink); }

.truss-secondary { display: inline-flex; align-items: center; gap: 18px; }

.truss-field {
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
}
.truss-field[hidden] { display: none; }
.truss-field--search input { width: 150px; }
.truss-field-label {
  font-family: var(--bp-mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--bp-muted);
}

.truss-toolbar input[type="search"],
.truss-toolbar select,
.truss-toolbar input[type="number"] {
  font-family: var(--bp-mono); font-size: 12.5px; letter-spacing: 0; color: var(--bp-fg);
  background: var(--bp-field); border: 1px solid var(--bp-field-line); border-radius: 2px; padding: 6px 9px;
}
.truss-toolbar input[type="number"] { width: 52px; }
.truss-toolbar input:focus, .truss-toolbar select:focus {
  outline: none; border-color: var(--bp-ink);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--bp-ink) 22%, transparent);
}

/* right-hand utility cluster */
.truss-utils { margin-left: auto; display: inline-flex; align-items: center; gap: 10px; }
.truss-util {
  width: 32px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px; line-height: 1; color: var(--bp-ink);
  background: var(--bp-field); border: 1px solid var(--bp-field-line); border-radius: 2px; cursor: pointer;
}
.truss-util:hover { border-color: var(--bp-ink); }
.truss-util[aria-expanded="true"] { background: var(--bp-ink); color: var(--bp-panel); border-color: var(--bp-ink); }
.truss-util--more { display: none; } /* only on phone */

/* zoom control, floated over the canvas (bottom-right) */
.truss-zoom {
  position: absolute; right: 16px; bottom: 16px; z-index: 15;
  display: inline-flex; align-items: center; gap: 11px; padding: 7px 11px;
  background: var(--bp-panel); border: 1px solid var(--bp-panel-line); border-radius: 4px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .14);
}
.truss-zoom button {
  font-family: var(--bp-mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--bp-ink); background: var(--bp-field); border: 1.5px solid var(--bp-ink);
  border-radius: 2px; padding: 5px 11px; cursor: pointer;
}
.truss-zoom button:hover { background: var(--bp-ink); color: var(--bp-panel); }
.truss-zoom input[type="range"] { width: 120px; accent-color: var(--bp-cyan); }
#truss-zoom-pct {
  font-family: var(--bp-mono); font-size: 12px; color: var(--bp-muted);
  min-width: 40px; text-align: right; font-variant-numeric: tabular-nums;
}

/* legend popover */
.truss-legend {
  position: absolute; top: 62px; right: 14px; z-index: 20; width: 210px;
  background: var(--bp-panel); border: 1px solid var(--bp-panel-line); border-radius: 4px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .18); padding: 12px 14px; color: var(--bp-fg);
}
.truss-legend[hidden] { display: none; }
.truss-legend-head {
  font-family: var(--bp-mono); font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--bp-muted); margin-bottom: 10px;
}
.truss-legend-list { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 8px 12px; align-items: center; }
.truss-legend-list dt { font-family: var(--bp-mono); font-weight: 600; font-size: 12px; color: var(--bp-ink); }
.truss-legend-list dd { margin: 0; font-size: 12.5px; color: var(--bp-fg); }
.truss-legend-list svg { color: var(--bp-rel); display: block; }

/* enum/set label turned into a clickable accent (opens the value popover).
   The high-specificity selectors beat Mermaid's inherited label colour. */
#truss-canvas svg .nodeLabel.truss-enum-type,
#truss-canvas svg .nodeLabel.truss-enum-type p {
  color: var(--bp-cyan) !important;
  text-decoration: underline dotted; text-underline-offset: 2px;
  cursor: pointer; pointer-events: auto;
}
#truss-canvas svg .nodeLabel.truss-enum-type:hover,
#truss-canvas svg .nodeLabel.truss-enum-type:hover p { color: var(--bp-ink) !important; }

.truss-popover {
  position: fixed; z-index: 40; min-width: 120px; max-width: 260px; max-height: 280px; overflow: auto;
  background: var(--bp-panel); border: 1px solid var(--bp-panel-line); border-radius: 4px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .2); padding: 10px 12px;
  font-family: var(--bp-mono); font-size: 12px; color: var(--bp-fg);
}
.truss-popover[hidden] { display: none; }
.truss-popover-head { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--bp-muted); margin-bottom: 8px; }
.truss-popover ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
.truss-popover li { white-space: nowrap; }
.truss-popover li::before { content: "•"; color: var(--bp-cyan); margin-right: 7px; }

/* table name as the per-table export/focus trigger. Kept subtle so headers stay
   clean: it only reveals itself as clickable on hover. */
#truss-canvas svg .nodeLabel.truss-table-name { cursor: pointer; pointer-events: auto; }
#truss-canvas svg .nodeLabel.truss-table-name:hover,
#truss-canvas svg .nodeLabel.truss-table-name:hover p {
  color: var(--bp-cyan) !important;
  text-decoration: underline dotted; text-underline-offset: 3px;
}

/* action menu inside the shared popover */
.truss-menu { display: flex; flex-direction: column; gap: 2px; min-width: 150px; }
.truss-menu button {
  all: unset; box-sizing: border-box; display: block; width: 100%;
  font-family: var(--bp-mono); font-size: 12px; color: var(--bp-fg);
  padding: 7px 9px; border-radius: 3px; cursor: pointer;
}
.truss-menu button:hover, .truss-menu button:focus-visible { background: var(--bp-info-bg); color: var(--bp-info-fg); }

/* status footer */
.truss-footer {
  height: 28px; display: flex; align-items: center; gap: 14px; padding: 0 16px;
  background: var(--bp-panel); border-top: 1px solid var(--bp-panel-line);
  font-family: var(--bp-mono); font-size: 11px; letter-spacing: .04em; color: var(--bp-muted);
}
.truss-footer-spacer { margin-left: auto; }
.truss-footer .truss-flag { color: var(--bp-warn-fg); }
#truss-stat-conn:not(:empty)::before { content: "· "; }

/* ---- banners ---------------------------------------------------------- */

#truss-banners { display: flex; flex-direction: column; }
.truss-banner {
  font-family: var(--bp-mono); font-size: 11.5px; letter-spacing: .03em;
  padding: 8px 18px; border-bottom: 1px solid var(--bp-panel-line);
  display: flex; align-items: center; gap: 9px;
}
.truss-banner::before { content: "◧"; opacity: .8; }
.truss-banner--info { background: var(--bp-info-bg); color: var(--bp-info-fg); }
.truss-banner--warn { background: var(--bp-warn-bg); color: var(--bp-warn-fg); }
.truss-banner--error { background: var(--bp-error-bg); color: var(--bp-error-fg); }

/* ---- diagram viewport (pan/zoom) -------------------------------------- */

#truss-viewport {
  position: relative; overflow: hidden; height: calc(100vh - 54px - 28px);
  cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none;
}
#truss-viewport.is-panning { cursor: grabbing; }
#truss-canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; will-change: transform; }

/* ---- Mermaid ER theming ----------------------------------------------- */
/* Mermaid draws entity/row fills as SVG presentation attributes, which any CSS
   rule overrides. Repainting them here (rather than via Mermaid's theme) makes
   the diagram follow light/dark with no re-render, and fixes the pale odd-rows
   Mermaid's v11 renderer produces in dark mode. */

#truss-canvas svg { color: var(--bp-entity-text); }

#truss-canvas svg .outer-path path[fill]:not([fill="none"]) { fill: var(--bp-entity-bg) !important; }
#truss-canvas svg .outer-path path[stroke]:not([stroke="none"]) { stroke: var(--bp-entity-border) !important; stroke-width: 1.3px !important; }

#truss-canvas svg .row-rect-odd path[fill]:not([fill="none"]) { fill: var(--bp-row-odd) !important; }
#truss-canvas svg .row-rect-even path[fill]:not([fill="none"]) { fill: var(--bp-row-even) !important; }
#truss-canvas svg .row-rect-odd path[stroke]:not([stroke="none"]),
#truss-canvas svg .row-rect-even path[stroke]:not([stroke="none"]) { stroke: var(--bp-hair) !important; }

#truss-canvas svg .divider path { fill: var(--bp-hair) !important; stroke: var(--bp-hair) !important; }

/* Backgrounds/decorations take no pointer events, so the injected enum eye
   (an HTML button in a label cell, painted on top) reliably receives clicks
   instead of the row-rect behind it. Panning still works — events over these
   fall through to the label cells / viewport, which bubble to the pan handler. */
#truss-canvas svg .row-rect-odd path,
#truss-canvas svg .row-rect-even path,
#truss-canvas svg .outer-path path,
#truss-canvas svg .divider path { pointer-events: none; }

/* the focused table: accent border, tinted header, bold accent name */
#truss-canvas svg g.node.truss-focused .outer-path path[fill]:not([fill="none"]) { fill: var(--bp-focus-bg) !important; }
#truss-canvas svg g.node.truss-focused .outer-path path[stroke]:not([stroke="none"]) { stroke: var(--bp-focus-border) !important; stroke-width: 2.4px !important; }
#truss-canvas svg g.node.truss-focused g.label.name .nodeLabel { color: var(--bp-focus-border) !important; font-weight: 600; }

#truss-canvas svg .nodeLabel,
#truss-canvas svg .nodeLabel p { color: var(--bp-entity-text) !important; font-family: var(--bp-mono) !important; }

#truss-canvas svg .relationshipLine { stroke: var(--bp-rel) !important; }
#truss-canvas svg marker path { stroke: var(--bp-rel) !important; }

#truss-canvas svg .edgeLabel,
#truss-canvas svg .edgeLabel p { color: var(--bp-edge) !important; font-family: var(--bp-mono) !important; font-size: 11px; }
#truss-canvas svg .edgeLabel .labelBkg,
#truss-canvas svg .labelBkg { background: var(--bp-edge-bg) !important; opacity: .9; }

/* ---- responsive ------------------------------------------------------- */

/* Tablet: drop the field labels to reclaim horizontal room; inputs stay. */
@media (max-width: 900px) {
  .truss-toolbar { gap: 12px; }
  .truss-secondary { gap: 12px; }
  .truss-field-label { display: none; }
  .truss-field--search input { width: 130px; }
  .truss-legend { top: 58px; }
}

/* Phone: collapse the secondary controls behind the ⋯ button as a panel;
   the top bar keeps the mark, filter, and the utility cluster. */
@media (max-width: 560px) {
  .truss-toolbar { gap: 10px; padding: 0 12px; }
  .truss-brand { font-size: 0; gap: 0; } /* mark only, hide the wordmark */
  .truss-field--search { flex: 1; min-width: 0; }
  .truss-field--search input { width: 100%; }
  .truss-util--more { display: inline-flex; }

  .truss-secondary {
    display: none; position: absolute; top: 100%; right: 8px; margin-top: 6px;
    flex-direction: column; align-items: flex-start; gap: 14px; padding: 14px 16px;
    background: var(--bp-panel); border: 1px solid var(--bp-panel-line); border-radius: 4px;
    box-shadow: 0 8px 28px rgba(0, 0, 0, .18); z-index: 25;
  }
  .truss-secondary.is-open { display: flex; }
  .truss-secondary .truss-field-label { display: inline; } /* labels back inside the panel */

  /* legend as a bottom sheet */
  .truss-legend {
    top: auto; bottom: 0; left: 0; right: 0; width: auto;
    border-radius: 10px 10px 0 0; border-bottom: none;
  }

  .truss-footer { gap: 10px; font-size: 10px; }
  #truss-stat-updated { display: none; }
  .truss-zoom { right: 10px; bottom: 10px; padding: 6px 8px; gap: 8px; }
  .truss-zoom input[type="range"] { width: 84px; }
}
