/* ════════════════════════════════════════════════════════════════════════
   ClassicHarness - application chrome.

   Two design rules run through this file, and most of the specific numbers
   fall out of them.

   1. The chrome recedes; the drawing does not. A schematic editor is a
      window onto a sheet, so every pixel the frame takes is a pixel the work
      does not get. Hence 12px text, 2-3px radii, 4-6px padding and flat
      greys - dense enough that a full toolbar, a rail, two docks and a
      status bar together cost less height than the old header alone.

   2. The sheet keeps its own palette. Below the "canvas" heading nothing is
      themed with the chrome tokens: a wiring diagram's colours ARE the wire
      colours, black wire has to read as black, and that cannot happen on a
      dark ground. Those rules are unchanged - only the frame around them.
   ════════════════════════════════════════════════════════════════════════ */

:root {
    /* Window greys, darkest at the back. */
    --bg: #1b1d20;
    --chrome: #26282c;
    --panel: #2b2d31;
    --panel-2: #33363b;
    --line: #3d4046;
    --line-soft: #34373c;

    --text: #dee0e4;
    --muted: #8d939c;
    --dim: #6d727a;

    /* Amber, echoing the selection colour on the sheet so the frame and the
       drawing agree about what "active" looks like. Used sparingly. */
    --accent: #e0900f;
    --accent-dim: rgba(224, 144, 15, 0.16);

    --good: #4ec9a5;
    --bad: #e0645e;
    --warn: #dfae3d;

    --radius: 3px;
    --row: 22px;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    margin: 0;
    overflow: hidden;
    background: var(--bg);
    color: var(--text);
    font: 12px/1.45 "Segoe UI", system-ui, -apple-system, sans-serif;
    -webkit-font-smoothing: antialiased;
}

/* While a splitter is in hand, nothing else may claim the cursor or start
   selecting text under it. */
body.resizing, body.resizing * { cursor: col-resize !important; user-select: none; }

.booting { padding: 3rem; color: var(--muted); }

button, input, select, textarea { font: inherit; }

/* ── Shell ──────────────────────────────────────────────────────────────
   Column: menu, toolbar, workspace, status. The workspace is the only part
   that flexes, and min-height:0 is what lets it actually shrink rather than
   pushing the status bar off the bottom. */

.app {
    height: 100vh;
    display: flex;
    flex-direction: column;
}

.workspace {
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: stretch;
}

.canvas-col {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* ── Menu bar ───────────────────────────────────────────────────────── */

.menubar {
    display: flex;
    align-items: stretch;
    height: 26px;
    background: var(--chrome);
    border-bottom: 1px solid var(--line);
    position: relative;
    z-index: 30;
    flex: none;
}

.menubar-item { position: relative; display: flex; }

.menubar-title {
    background: none;
    border: none;
    color: var(--text);
    padding: 0 10px;
    cursor: default;
    font-size: 12px;
    white-space: nowrap;
}

.menubar-title:hover { background: var(--panel-2); }
.menubar-title.open { background: var(--accent-dim); color: var(--accent); }

.menubar-backdrop { position: fixed; inset: 0; z-index: 29; }

.menu-popup {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 31;
    min-width: 232px;
    padding: 3px 0;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
}

.menu-row {
    display: grid;
    grid-template-columns: 20px 1fr auto;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 0 10px 0 4px;
    height: var(--row);
    background: none;
    border: none;
    color: var(--text);
    text-align: left;
    cursor: default;
    white-space: nowrap;
}

.menu-row.hot:not(.disabled) { background: var(--accent-dim); }
.menu-row.disabled { color: var(--dim); }

.menu-label { overflow: hidden; text-overflow: ellipsis; }
.menu-gutter { display: flex; align-items: center; justify-content: center; color: var(--accent); }
.menu-row.disabled .menu-gutter { color: var(--dim); }
.menu-accel { color: var(--muted); font-size: 11px; }
.menu-row.disabled .menu-accel { color: var(--dim); }

.menu-sep { height: 1px; margin: 3px 8px; background: var(--line); }

/* ── Toolbar ────────────────────────────────────────────────────────── */

.toolbar {
    display: flex;
    align-items: center;
    gap: 1px;
    height: 30px;
    padding: 0 4px;
    background: var(--chrome);
    border-bottom: 1px solid var(--line);
    flex: none;
}

.toolbtn, .icon-btn, .railbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--text);
    cursor: default;
    padding: 0;
}

.toolbtn { width: 24px; height: 24px; }

.toolbtn:hover:not(:disabled),
.icon-btn:hover:not(:disabled) { background: var(--panel-2); border-color: var(--line); }

.toolbtn:disabled, .icon-btn:disabled { color: var(--dim); }

.toolbtn.on { background: var(--accent-dim); border-color: var(--accent); color: var(--accent); }

.toolbar-sep { width: 1px; height: 18px; margin: 0 4px; background: var(--line); }

.icon-btn { width: 20px; height: 20px; color: var(--muted); }
.icon-btn:hover:not(:disabled) { color: var(--text); }

/* ── Tool rail ──────────────────────────────────────────────────────── */

.toolrail {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px 3px;
    width: 30px;
    flex: none;
    background: var(--chrome);
    border-right: 1px solid var(--line);
}

.railbtn { width: 24px; height: 24px; color: var(--muted); }
.railbtn:hover { background: var(--panel-2); color: var(--text); }

.railbtn.on {
    background: var(--accent-dim);
    border-color: var(--accent);
    color: var(--accent);
}

.toolrail-sep { height: 1px; margin: 3px 2px; background: var(--line); }

/* ── Docks ──────────────────────────────────────────────────────────── */

.dock {
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--panel);
    flex: none;
}

/* Remembered widths are absolute, but a width that was reasonable on a wide
   monitor must not squeeze the drawing to nothing on a laptop. The cap yields
   before the canvas does, and the stored figure is untouched - go back to the
   big screen and the dock is the width it was left at. */
.dock-left { width: 210px; max-width: 32vw; border-right: 1px solid var(--line); }
.dock-right { width: 290px; max-width: 34vw; border-left: 1px solid var(--line); }

.dock-bottom {
    height: 168px;
    border-top: 1px solid var(--line);
}

.dock-head {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 24px;
    padding: 0 4px 0 8px;
    flex: none;
    background: var(--chrome);
    border-bottom: 1px solid var(--line);
}

.dock-title {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted);
}

.dock-note {
    font-size: 11px;
    color: var(--dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dock-body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: 6px;
}

/* The divider. Four pixels wide with a wider invisible grab area, so it is
   easy to hit without being a visible bar. Double-click resets it. */
.dock-split {
    width: 4px;
    flex: none;
    cursor: col-resize;
    background: var(--bg);
    position: relative;
}

.dock-split::after {
    content: "";
    position: absolute;
    inset: 0 -3px;
}

.dock-split:hover { background: var(--accent-dim); }

/* ── Palette ────────────────────────────────────────────────────────── */

.pal-group { margin-bottom: 10px; }

.pal-cat {
    font-size: 10px;
    color: var(--dim);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    margin-bottom: 3px;
}

.pal-head { display: flex; align-items: center; gap: 4px; margin: 12px 0 5px; }

.pal-count {
    padding: 0 5px;
    border-radius: 7px;
    background: var(--panel-2);
    color: var(--muted);
    font-size: 10px;
    line-height: 15px;
}

.pal-filter { margin-bottom: 7px; }

.pal-item {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    text-align: left;
    background: var(--panel-2);
    color: var(--text);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    padding: 3px 6px;
    margin-bottom: 2px;
    cursor: default;
    min-height: 22px;
}

.pal-item svg { flex: none; color: var(--muted); }
.pal-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pal-item:hover { border-color: var(--accent); }
.pal-item:hover svg { color: var(--accent); }

/* The part the Place tool is currently loaded with. */
.pal-item.armed { border-color: var(--accent); background: var(--accent-dim); }
.pal-item.armed svg { color: var(--accent); }

.pal-part { display: flex; gap: 2px; align-items: stretch; }
.pal-part .pal-item { flex: 1; min-width: 0; }

.pal-edit {
    width: 20px;
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    background: var(--panel-2);
    color: var(--dim);
    cursor: default;
}

.pal-edit:hover { color: var(--text); border-color: var(--muted); }

/* Wins over the bare `button.danger` colour rule above, which would otherwise
   make this one transparent against the palette's panel. */
.pal-edit.danger { background: var(--panel-2); color: var(--dim); }
.pal-edit.danger:hover { background: var(--bad); color: #fff; border-color: var(--bad); }

.empty { color: var(--dim); font-size: 11px; line-height: 1.5; margin: 4px 0 10px; }

.file-input { display: none; }

/* ── Inspector ──────────────────────────────────────────────────────── */

/* Collapsible groups, built on <details> so the disclosure is native: it is
   keyboard reachable and screen-reader correct without a line of script. */
.section { border-bottom: 1px solid var(--line-soft); }

.section > summary {
    display: flex;
    align-items: center;
    gap: 5px;
    height: var(--row);
    padding: 0 2px;
    cursor: default;
    list-style: none;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted);
}

.section > summary::-webkit-details-marker { display: none; }
.section > summary:hover { color: var(--text); }

.section .twist { color: var(--dim); transition: transform 0.12s ease; }
.section[open] > summary .twist { transform: rotate(90deg); }

.section-body { padding: 2px 0 8px; }

.control { margin-bottom: 8px; }
.control .label { font-size: 11px; color: var(--muted); margin-bottom: 3px; }

.kv {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    padding: 2px 0;
    border-bottom: 1px solid var(--line-soft);
    font-size: 11.5px;
}

.kv .k { color: var(--muted); }
.kv .v { font-variant-numeric: tabular-nums; text-align: right; }
.kv .v.ok { color: var(--good); }
.kv .v.bad { color: var(--bad); }
.kv .v.osc { color: var(--warn); }

/* Notes explaining electrical behaviour that the drawing cannot show - the
   wiper's park run, what an earth bond resistance means. Demoted to a margin
   note: still there when wanted, never competing with the values. */
.hint {
    color: var(--dim);
    font-size: 11px;
    line-height: 1.5;
    margin: 6px 0 0;
    padding-left: 7px;
    border-left: 2px solid var(--line);
}

.hint.active { color: var(--accent); border-left-color: var(--accent); }

.segmented { display: flex; flex-wrap: wrap; gap: 2px; }

.segmented button {
    flex: 1 1 auto;
    padding: 2px 6px;
    background: var(--panel-2);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    cursor: default;
    font-size: 11px;
    min-height: 22px;
}

.segmented button:hover { border-color: var(--muted); }

.segmented button.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #17120a;
    font-weight: 600;
}

input.text, select.text, input[type=search] {
    width: 100%;
    background: var(--panel-2);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 3px 6px;
    min-height: 22px;
}

input.text:focus, select.text:focus, input[type=search]:focus {
    outline: none;
    border-color: var(--accent);
}

input[type=range] { width: 100%; accent-color: var(--accent); }

label.check {
    display: flex;
    gap: 6px;
    align-items: center;
    font-size: 11.5px;
    color: var(--muted);
    margin-top: 7px;
}

.mono { font-family: ui-monospace, Consolas, monospace; font-size: 11px; }

/* ── Diagnostics dock ───────────────────────────────────────────────── */

.dock-body.issues { padding: 3px; }

.issue {
    display: grid;
    grid-template-columns: 16px 62px 1fr;
    align-items: start;
    gap: 7px;
    width: 100%;
    text-align: left;
    padding: 4px 6px;
    border: none;
    border-radius: var(--radius);
    background: none;
    color: var(--text);
    font-size: 11.5px;
    line-height: 1.5;
    cursor: default;
}

.issue:hover:not(.flat) { background: var(--panel-2); }
.issue .issue-icon { margin-top: 2px; }
.issue.bad .issue-icon { color: var(--bad); }
.issue.warn .issue-icon { color: var(--warn); }

/* A finding whose subjects are netlist labels rather than document ids
   cannot be pointed at, so it must not look like it can. */
.issue.flat { cursor: default; }

.issue-code {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 10.5px;
    color: var(--muted);
    margin-top: 1px;
}

/* Diagnostic messages are full sentences and must wrap, not clip - the
   explanation of what to do about a finding is usually the tail of it. */
.issue-msg { white-space: normal; overflow-wrap: anywhere; }

.issue-none { padding: 8px; color: var(--dim); display: flex; align-items: center; gap: 6px; }

/* ── Status bar ─────────────────────────────────────────────────────── */

.statusbar {
    display: flex;
    align-items: center;
    gap: 0;
    height: 22px;
    padding: 0 2px 0 6px;
    flex: none;
    background: var(--chrome);
    border-top: 1px solid var(--line);
    font-size: 11px;
    color: var(--muted);
    white-space: nowrap;
    overflow: hidden;
}

.sb-tool { display: inline-flex; align-items: center; gap: 5px; color: var(--accent); }
.sb-toolname { font-weight: 600; }

.sb-hint {
    margin-left: 10px;
    color: var(--dim);
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.sb-spacer { flex: 1 1 auto; }

.sb-cell {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 0 8px;
    height: 100%;
    border-left: 1px solid var(--line);
    background: none;
    color: var(--muted);
    font-size: 11px;
    flex: none;
}

.sb-k { color: var(--dim); }
.sb-v { color: var(--text); font-variant-numeric: tabular-nums; }

/* Fixed width so the bar does not twitch as the cursor moves - the readout
   is updated sixty times a second and reflow would be visible. */
.sb-coord .sb-v { display: inline-block; min-width: 3.4em; }

.sb-btn {
    background: none;
    border: none;
    color: var(--text);
    cursor: default;
    padding: 0;
    font-variant-numeric: tabular-nums;
}

.sb-btn:hover { color: var(--accent); }

.sb-good { color: var(--muted); }
.sb-bad { color: var(--bad); border: none; border-left: 1px solid var(--line); cursor: default; }
.sb-bad:hover { background: var(--panel-2); }

/* ── Banner ─────────────────────────────────────────────────────────── */

.banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 6px 5px 10px;
    flex: none;
    background: #3a2c18;
    border-bottom: 1px solid var(--warn);
    color: var(--text);
    font-size: 11.5px;
}

.banner svg { color: var(--warn); flex: none; }
.banner-text { flex: 1; }

/* ── Context menu ───────────────────────────────────────────────────── */

.menu-backdrop { position: fixed; inset: 0; z-index: 40; }

.context-menu {
    position: fixed;
    z-index: 41;
    min-width: 186px;
    padding: 3px 0;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
    display: flex;
    flex-direction: column;
}

.context-menu button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0 12px;
    height: var(--row);
    border: none;
    background: transparent;
    color: var(--text);
    font-size: 12px;
    cursor: default;
}

.context-menu button:hover { background: var(--accent-dim); }
.context-menu button.danger { color: var(--bad); }
.context-menu button.danger:hover { background: var(--bad); color: #fff; }

/* Greyed rather than hidden, so the menu keeps the same shape whatever is
   selected - a menu whose items move between openings cannot be learned. */
.context-menu button:disabled { color: var(--dim); }
.context-menu button:disabled:hover { background: none; }

/* ── Modals ─────────────────────────────────────────────────────────── */

.modal-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); z-index: 50; }

.modal {
    position: fixed;
    z-index: 51;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(680px, 92vw);
    max-height: 86vh;
    overflow-y: auto;
    padding: 12px 16px 16px;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 5px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

.modal h2 { margin: 0 0 10px; font-size: 14px; font-weight: 600; }
.modal h3 { margin: 14px 0 4px; font-size: 12px; font-weight: 600; color: var(--muted); }
.modal hr { border: none; border-top: 1px solid var(--line); margin: 12px 0; }

.form-row { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.form-row > label { width: 110px; flex: none; color: var(--muted); font-size: 11.5px; }
.form-row > .text, .form-row > select { flex: 1; }

table, .editor-table { width: 100%; border-collapse: collapse; font-size: 11.5px; }

th, td { text-align: left; padding: 3px 6px; border-bottom: 1px solid var(--line-soft); }

th {
    color: var(--muted);
    font-weight: 600;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

td.num { text-align: right; font-variant-numeric: tabular-nums; }
tr.dim td { color: var(--muted); }

.editor-table th { padding: 0 4px 3px 0; text-transform: none; letter-spacing: 0; font-size: 11px; }
.editor-table td { padding: 0 4px 4px 0; border: none; }
.editor-table td:last-child { width: 24px; padding-right: 0; }

.row-remove {
    width: 22px;
    height: 24px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--panel-2);
    color: var(--muted);
    cursor: default;
}

.row-remove:hover { background: var(--bad); color: #fff; border-color: var(--bad); }

.position-card {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 7px;
    margin-bottom: 6px;
    background: var(--panel-2);
}

.position-head { display: flex; gap: 7px; align-items: center; margin-bottom: 5px; }
.position-head .text { flex: 1; }
.position-head .check { flex: none; white-space: nowrap; margin-top: 0; }

.pair-row { display: flex; gap: 5px; align-items: center; margin-bottom: 4px; }
.pair-row .text { flex: 1; }
.pair-row .control { flex: 1; margin: 0; }
.pair-join { color: var(--muted); font-size: 11px; flex: none; }

.form-error {
    margin-top: 10px;
    padding: 6px 8px;
    border-radius: var(--radius);
    border-left: 2px solid var(--bad);
    background: var(--panel-2);
    font-size: 11.5px;
}

.modal-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 14px; }

button.ghost, a.ghost, label.ghost {
    background: var(--panel-2);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 3px 10px;
    min-height: 24px;
    cursor: default;
    text-decoration: none;
}

button.ghost:hover, a.ghost:hover, label.ghost:hover { border-color: var(--muted); }

button.primary {
    background: var(--accent);
    border: 1px solid var(--accent);
    color: #17120a;
    border-radius: var(--radius);
    padding: 3px 14px;
    min-height: 24px;
    font-weight: 600;
    cursor: default;
}

button.primary:hover { filter: brightness(1.1); }

/* A confirm button that is going to delete something. Still the primary
   action - the user asked for it - but wearing the colour that says so. */
button.primary.destructive {
    background: var(--bad);
    border-color: var(--bad);
    color: #fff;
}

/* ── Confirmations ───────────────────────────────────────────────────── */

.modal.confirm { width: min(460px, 92vw); }

.confirm-message { margin: 0 0 10px; line-height: 1.55; }

/* Named items rather than a bare count: "3 wires will be cut" is a claim,
   and the list is how the user checks it before agreeing. */
.confirm-list {
    margin: 0 0 10px;
    padding: 6px 8px 6px 24px;
    max-height: 180px;
    overflow-y: auto;
    background: var(--panel-2);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    font-size: 11.5px;
    color: var(--muted);
}

.confirm-list li { margin: 1px 0; }

/* A full-width button inside a dock body - "Edit this component…". */
button.ghost.wide {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    margin-top: 8px;
}

/* Destructive actions read red wherever they appear - but the SHAPE of the
   button belongs to the place it sits in: a full-width bar in the inspector, a
   20px square in the palette, an ordinary row in the context menu.

   This rule used to carry `width: 100%` and a top margin, which is layout, not
   colour. Being an element+class selector it out-specified `.pal-edit`, so the
   palette's little remove button silently stretched to fill its row and
   squeezed the part name out of existence. Colour here; geometry below. */
button.danger {
    background: transparent;
    color: var(--bad);
    cursor: default;
}

/* The inspector's block delete button - the one this rule was written for. */
.dock-body > button.danger {
    width: 100%;
    margin-top: 12px;
    padding: 4px;
    border: 1px solid var(--bad);
    border-radius: var(--radius);
}

.dock-body > button.danger:hover { background: var(--bad); color: #fff; }

/* The soak dialog's Cancel, which sits in a modal action row. */
.modal-actions button.danger {
    padding: 3px 10px;
    min-height: 24px;
    border: 1px solid var(--bad);
    border-radius: var(--radius);
}

.modal-actions button.danger:hover { background: var(--bad); color: #fff; }

/* The soak dialog. The bar is honesty: interpreted WASM is slow at this. */
.modal.soak { max-width: 860px; width: min(860px, 94vw); }

.soak-bar {
    height: 8px;
    border-radius: 4px;
    background: var(--line);
    overflow: hidden;
    margin: 9px 0 4px;
}

.soak-fill { height: 100%; background: var(--accent); transition: width 0.2s linear; }

/* The pre-run brief. A whole car is minutes of work, so the size of the walk
   leads and the reasoning follows - the user is agreeing to the wait. */
.soak-brief { margin: 4px 0 2px; }
.soak-headline { font-size: 13px; margin: 0 0 4px; }
.soak-headline strong { font-size: 15px; }
.soak-rate { color: var(--muted); }
.soak-count { color: var(--muted); }
.soak-clean { color: var(--good); font-weight: 600; }
.soak-witness { color: var(--muted); font-size: 11px; margin-top: 2px; }
.soak-dead { padding: 2px 0; }
.soak-mode { color: var(--muted); margin: -4px 0 8px; }

/* The example library. Cards rather than a list, because the blurb is the
   point - what a circuit demonstrates is more use than its filename. Faults
   are marked, never hidden: a drawing that is wrong on purpose is the fastest
   way to learn what a finding looks like. */
.modal.examples { max-width: 860px; width: min(860px, 94vw); }

.examples-error { color: var(--bad); }
.examples-kind { margin-top: 14px; }
.examples-kind > h3 { margin-bottom: 2px; }
.examples-caption { color: var(--muted); font-size: 11px; margin: 0 0 8px; }
.examples-group { margin-bottom: 10px; }

.examples-category {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    margin-bottom: 4px;
}

.example-card {
    display: block;
    width: 100%;
    text-align: left;
    padding: 7px 9px;
    margin-bottom: 5px;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-left: 2px solid var(--accent);
    border-radius: var(--radius);
    cursor: pointer;
    font: inherit;
    color: inherit;
}

.example-card:hover { border-color: var(--accent); background: var(--panel); }
.example-card.fault { border-left-color: var(--bad); }

.example-title { display: block; font-weight: 600; font-size: 12px; margin-bottom: 2px; }
.example-blurb { display: block; color: var(--muted); font-size: 11px; line-height: 1.45; }
.example-symptoms { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }

.example-code {
    font-size: 10px;
    padding: 1px 5px;
    border-radius: 2px;
    background: var(--line);
    color: var(--muted);
    font-family: ui-monospace, Consolas, monospace;
}

.example-code.behaviour { font-family: inherit; }

/* Behavioural rules in the soak report. */
.expect {
    padding: 6px 8px;
    margin-bottom: 6px;
    border-radius: var(--radius);
    background: var(--panel-2);
    border-left: 2px solid var(--line);
    font-size: 11.5px;
}

.expect.ok { border-left-color: var(--good); }
.expect.bad { border-left-color: var(--bad); }
.expect.ok .expect-head svg { color: var(--good); }
.expect.bad .expect-head svg { color: var(--bad); }

.expect-head { display: flex; align-items: baseline; gap: 6px; }
.expect-head svg { flex: none; align-self: center; }
.expect-rule { font-weight: 600; }
.expect-desc { color: var(--muted); overflow-wrap: anywhere; }

/* A rule that has not been checked since the drawing changed. Deliberately
   neutral rather than green: an out-of-date pass is worse than no verdict. */
.expect.stale { border-left-color: var(--line); }
.expect.stale .expect-head svg { color: var(--dim); }

/* Held vacuously: the rule has clauses and none ever matched, so nothing was
   checked. Amber, because a tick built on zero checks would be a lie. */
.expect.warn { border-left-color: var(--warn); }
.expect.warn .expect-head svg { color: var(--warn); }

/* "or" between clauses in the rule editor - the shape of the rule, in words. */
.clause-or {
    margin: 4px 0;
    font-size: 10.5px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

.term-op { flex: 0 0 74px; }

.proposal-accept { margin-right: 8px; }

/* The clause the soak worked out for you. */
.expect-proposal {
    margin-top: 5px;
    padding: 5px 7px;
    border-radius: var(--radius);
    background: var(--accent-dim);
    color: var(--text);
}

.expect-proposal code {
    font-family: ui-monospace, Consolas, monospace;
    color: var(--accent);
}

/* The service is not running. A state with a fix in it, not an error. */
.soak-offline p { line-height: 1.6; margin: 0 0 10px; }

.soak-command {
    margin: 0 0 10px;
    padding: 8px 10px;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-family: ui-monospace, Consolas, monospace;
    font-size: 11.5px;
    color: var(--text);
    overflow-x: auto;
    user-select: all;
}

.diag {
    padding: 6px 8px;
    border-radius: var(--radius);
    background: var(--panel-2);
    border-left: 2px solid var(--warn);
    margin-bottom: 6px;
    font-size: 11.5px;
}

.diag .code { color: var(--muted); font-family: ui-monospace, Consolas, monospace; font-size: 10.5px; }
.diag.finding { border-left-color: var(--bad); }

/* The ? overlay: every key the canvas answers to. */
.keys-overlay {
    position: fixed;
    z-index: 60;
    inset: 0;
    background: rgba(10, 11, 13, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
}

.keys-panel {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 5px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
    padding: 14px 18px 18px;
    max-width: 660px;
    max-height: 82vh;
    overflow-y: auto;
    color: var(--text);
}

.keys-panel h2 { margin: 0 0 10px; font-size: 14px; }

.keys-head {
    margin: 14px 0 5px;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted);
}

.keys-head:first-of-type { margin-top: 4px; }

.keys-grid {
    display: grid;
    grid-template-columns: max-content 1fr max-content 1fr;
    gap: 4px 14px;
    font-size: 11.5px;
    align-items: baseline;
}

.keys-grid .k {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 10.5px;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1px 5px;
    white-space: nowrap;
    justify-self: start;
}

.keys-grid .d { color: var(--muted); }

/* Instant tooltip: no browser hover delay, styled, gone on leave. */
.tipbox {
    position: fixed;
    z-index: 45;
    background: rgba(20, 22, 25, 0.96);
    border: 1px solid var(--line);
    color: #f2f1ec;
    font-size: 11px;
    line-height: 1.5;
    padding: 4px 8px;
    border-radius: var(--radius);
    pointer-events: none;
    max-width: 340px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}

.tipbox .tip-title { font-weight: 600; }
.tipbox .tip-muted { color: #a8adb5; }

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 10px 16px;
    background: var(--bad);
    color: #fff;
    z-index: 99;
}

/* ════════════════════════════════════════════════════════════════════════
   The canvas. Everything below here is the drawing, not the frame, and is
   deliberately untouched by the theme above.

   The canvas is paper, not screen. A wiring diagram's colours ARE the wire
   colours, and black wire has to read as black - which it cannot on a dark
   ground. The app chrome stays dark; the drawing sits on white - and the
   white sheet sits on a grey desk, so its edges are honest when zoomed out.
   ════════════════════════════════════════════════════════════════════════ */

:root { --ink: #16181c; --paper: #fbfaf6; --rule: #b9b6ac; --desk: #9ba0a8; }

/* A viewport, not a scrolling page: the sheet pans and zooms inside it as one
   SVG transform, so the wrap clips and the svg fills it. Flexes to whatever
   the docks leave, which is why there is no height calculation here. */
.canvas-wrap {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    touch-action: none;
    background: var(--desk);
}

/* Focus is what routes Delete and the arrow keys here; the halo would be noise. */
.canvas-wrap:focus { outline: none; }

svg.canvas {
    display: block;
    width: 100%;
    height: 100%;
}

.desk { fill: var(--desk); }

/* The shadow is its own rect rather than a filter: drop-shadow re-evaluates
   on every repaint of this very large element, and the sheet repaints on
   every frame of every drag. A flat offset rect reads the same at a glance
   and costs nothing. */
.sheet {
    fill: var(--paper);
    stroke: #7e838c;
    stroke-width: 1;
}

.sheet-shadow { fill: rgba(15, 18, 22, 0.30); pointer-events: none; }

.node { cursor: grab; }

.box {
    fill: #ffffff;
    stroke: var(--ink);
    stroke-width: 1.4;
}

.box.live { stroke: #b8860b; stroke-width: 2; }
.box.sel { stroke: #d97706; stroke-width: 2.6; }

/* Unframed symbols still need something to grab and to show selection on. */
.hitbox { fill: transparent; stroke: none; }
.hitbox.sel { stroke: #d97706; stroke-width: 1.3; stroke-dasharray: 4 3; }
.hitbox.live { stroke: #b8860b; stroke-width: 1; stroke-dasharray: 2 3; }

/* Component name, printed under the artwork rather than in a title bar. */
.caption {
    fill: var(--ink);
    font-size: 11px;
    font-weight: 600;
    text-anchor: middle;
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--paper);
    stroke-width: 3px;
    stroke-linejoin: round;
}

/* Sheet heading, the way the factory diagram is titled. */
.sheet-title {
    fill: var(--ink);
    font-size: 42px;
    font-weight: 800;
    letter-spacing: 0.05em;
    text-anchor: middle;
    pointer-events: none;
}

.side-label {
    fill: #4a4a4a;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.14em;
    pointer-events: none;
}

.side-label.r { text-anchor: end; }

/* Reference designator - A battery, S fuses, J relays, and so on. */
.designator {
    fill: #b45309;
    font-size: 11.5px;
    font-weight: 800;
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--paper);
    stroke-width: 2.5px;
    stroke-linejoin: round;
}

.titleblock rect { fill: #ffffff; stroke: var(--ink); stroke-width: 1.4; }
.titleblock line { stroke: var(--ink); stroke-width: 1; }
.titleblock .tb-main { fill: var(--ink); font-size: 19px; font-weight: 700; letter-spacing: 0.02em; }
.titleblock .tb-sub { fill: #4a4a4a; font-size: 11px; }

.stub { stroke: var(--ink); stroke-width: 1.4; pointer-events: none; }

.port {
    fill: #ffffff;
    stroke: var(--ink);
    stroke-width: 1.4;
    cursor: crosshair;
}

.port:hover { fill: #d97706; stroke: #d97706; }
.port.pending { fill: #d97706; stroke: #d97706; }

/* DIN terminal numbers, printed against the stub root inside the body. */
.din {
    fill: #333;
    font-size: 9px;
    font-weight: 700;
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--paper);
    stroke-width: 2.5px;
    stroke-linejoin: round;
}

.din.r { text-anchor: end; }

.splice { fill: var(--ink); stroke: var(--ink); stroke-width: 2; cursor: crosshair; }
.splice:hover, .splice.pending { fill: #d97706; stroke: #d97706; }
.splice.sel { stroke: #d97706; stroke-width: 3; }

.grid-dot { fill: rgba(40, 40, 40, 0.10); }

.splice-label {
    fill: #4a4a4a;
    font-size: 9.5px;
    text-anchor: middle;
    pointer-events: none;
}

/* Schematic symbols: line art, never filled unless asked. */
.glyph { pointer-events: none; }
.glyph line, .glyph rect, .glyph circle, .glyph path, .glyph ellipse {
    fill: none;
    stroke: var(--ink);
    stroke-width: 1.3;
}
.glyph .thick { stroke-width: 2.2; }
.glyph .fill { fill: var(--ink); }
.glyph .dashed { stroke-dasharray: 2 2; }

/* Energised: a closed contact and a deflecting needle read at a glance. */
.glyph .live { stroke: #c2410c; }
.glyph .needle { stroke: #c2410c; }

/* Rotary dial: unselected detents stay quiet so the one you are on reads at a
   glance, and the live detent matches the pointer. */
.glyph .detent { stroke: var(--rule); stroke-width: 1.4; }
.glyph .detent.on { stroke: #c2410c; stroke-width: 2.6; }

.position-label {
    font-size: 10px;
    font-weight: 600;
    text-anchor: middle;
    fill: var(--rule);
    letter-spacing: 0.02em;
}

.position-label.on { fill: #c2410c; }

/* Lamps glow in proportion to the power they are actually dissipating.
   A flashing lamp needs no rule here: the flash clock re-solves the instant at
   every contact edge, so the glow goes dark because the circuit is dark - the
   decorative 900ms CSS blink this replaced is exactly what decisions.md A23
   promised to delete. */
.glyph .glow { fill: #ffc21f; stroke: none; }
.glyph .glow.amber { fill: #ff9d2e; }

/* Looms. Drawn as a sleeve under the strands that run inside them. */
.sleeve {
    fill: none;
    stroke: #cfcabb;
    stroke-opacity: 0.75;
    stroke-linejoin: round;
    stroke-linecap: round;
    pointer-events: stroke;
}

.waypoint { fill: #ffffff; stroke: var(--ink); stroke-width: 1.4; cursor: grab; }

.wp-label {
    fill: #6b6b6b;
    font-size: 9px;
    font-style: italic;
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--paper);
    stroke-width: 2.5px;
    stroke-linejoin: round;
}

/* Wires. Rendered in their real insulation colour; a tracer is the same route
   drawn again, dashed, in the stripe colour. */
.wire { cursor: move; }
.wire path { fill: none; stroke-linejoin: round; stroke-linecap: butt; }
.wire .casing { stroke: #6b6b6b; }
.wire .tracer { stroke-dasharray: 7 7; }
.wire .hit { stroke: transparent; stroke-width: 14; }

/* Current flow: square markers marching along the run. Animating the dash
   offset keeps the whole thing on the compositor - no per-frame work in
   Blazor, and a fifty-wire loom costs no more than one wire. The dash pattern
   and duration are set per wire; --flow-period is the marker plus its gap. */
.wire .flow { pointer-events: none; animation: wire-flow linear infinite; }
.wire .flow.reverse { animation-direction: reverse; }

@keyframes wire-flow {
    from { stroke-dashoffset: var(--flow-period); }
    to { stroke-dashoffset: 0; }
}

/* Motion is decoration here: the same currents are on the wire tooltips and in
   the inspector, so honouring the OS setting costs the user nothing. */
@media (prefers-reduced-motion: reduce) {
    .wire .flow { animation: none; }
}

.wire:hover .core { stroke-width: 6; }
.wire.sel .core { stroke-dasharray: 9 4; }

/* Cross-section in mm², haloed so it reads where it crosses a run. */
.size-label {
    fill: var(--ink);
    font-size: 9.5px;
    font-weight: 600;
    text-anchor: middle;
    pointer-events: none;
    paint-order: stroke;
    stroke: var(--paper);
    stroke-width: 3px;
    stroke-linejoin: round;
}

/* ── The CAD hands: ghosts, marquee, handles, snap, tooltips ─────────── */

/* Placement ghost: what a stamp will put down, riding the cursor.
   NOT named .ghost - that class is the button style, and a
   pointer-events:none rule on it once turned every toolbar button off. */
.placement-ghost { opacity: 0.45; pointer-events: none; }

/* Marquee selection: press empty sheet and drag. */
.marquee {
    fill: rgba(217, 119, 6, 0.07);
    stroke: #d97706;
    stroke-width: 1;
    stroke-dasharray: 5 3;
    pointer-events: none;
}

/* The wire being drawn (or re-pinned), previewing its actual route. */
.wire-ghost {
    fill: none;
    stroke: #d97706;
    stroke-width: 2;
    stroke-dasharray: 6 4;
    opacity: 0.85;
    pointer-events: none;
}

/* Tee preview: where a click would split a run and branch from it. */
.tee-dot { fill: #d97706; stroke: #ffffff; stroke-width: 1.5; pointer-events: none; }

/* Fat, invisible port targets - constant screen size, like the wire hit path. */
.port-hit { fill: transparent; stroke: none; cursor: crosshair; }

/* Halo shown on approach, and stronger when the wire ghost has snapped here. */
.port-halo {
    fill: none;
    stroke: #d97706;
    stroke-width: 2;
    opacity: 0;
    pointer-events: none;
}

.port-halo.snap { opacity: 1; }
g.portgroup:hover .port-halo { opacity: 0.55; }

/* Corner and end handles on a selected wire. */
.handle { fill: #ffffff; stroke: var(--ink); stroke-width: 1.4; cursor: move; }
.handle:hover { stroke: #d97706; stroke-width: 2; }
.handle.end { stroke: #d97706; }

/* ── Cursors: the tool, made visible before the click ────────────────── */

/* The active tool sets the canvas cursor. Select keeps the per-element
   cursors below (grab on a node, crosshair on a port); the other tools
   override everything, because in a mode the whole sheet does one thing. */
.canvas-wrap.tool-wire *,
.canvas-wrap.tool-junction * { cursor: crosshair; }

.canvas-wrap.tool-place *,
.canvas-wrap.tool-earth * { cursor: copy; }

.canvas-wrap.tool-pan * { cursor: grab; }

.canvas-wrap.space-pan .desk,
.canvas-wrap.space-pan .sheet,
.canvas-wrap.space-pan .node,
.canvas-wrap.space-pan .wire { cursor: grab; }

.canvas-wrap.panning .desk,
.canvas-wrap.panning .sheet,
.canvas-wrap.panning * { cursor: grabbing; }

.canvas-wrap.placing * { cursor: copy; }

.node rect.box, .node rect.hitbox { cursor: move; }
.desk, .sheet { cursor: default; }
.caption-hit { cursor: text; }

/* Inline rename, sitting exactly where the caption was. */
.rename-input {
    font: 600 11px system-ui, sans-serif;
    text-align: center;
    border: 1px solid #d97706;
    border-radius: 3px;
    padding: 1px 4px;
    background: #ffffff;
    color: var(--ink);
    width: 100%;
    box-sizing: border-box;
}
