/* Shared display palette.
   Eight hues stepped between high-chroma and earthy, picked to stay legible
   against a white background. Used for fact bullets, graph nodes,
   inline fact marks, scenario labels, and accent UI bits. */
:root {
  --ncs-1: #E8A04A;  /* warm orange  */
  --ncs-2: #C13B5E;  /* red-magenta  */
  --ncs-3: #277F8E;  /* teal         */
  --ncs-4: #D49926;  /* gold         */
  --ncs-5: #218B5F;  /* leaf green   */
  --ncs-6: #6F4A7E;  /* purple       */
  --ncs-7: #2B6CB0;  /* blue         */
  --ncs-8: #B26450;  /* brick        */

  /* Neutral surface scale: white page, grey-tinted surfaces. Only the console
     (--term-*) keeps the old warm-parchment look. */
  --ncs-paper: #FFFFFF;     /* white background          */
  /* Search-results surface (the panel AND the pinned cluster strips that
     must blend with it) — one knob. */
  --results-surface: var(--ncs-paper);
  --ncs-ink: #1B1B1F;       /* ink                       */
  --ncs-stone: #6B6B72;     /* muted body text           */
  --ncs-cream: #F3F3F5;     /* card surface tint         */
  --ncs-pale: #F8F8FA;      /* card highlight surface    */
  --ncs-rule: #E4E4E9;      /* hairline borders          */

  /* Terminal / console surface — the query prompt + Stanislaw's answer.
     The one DARK slab on a white page: warm graphite, not black, with paper-
     warm text — a lawyer's desk lamp on dark leather rather than a neon
     terminal. The accent is BRASS (gilt spines, lamp fittings), deliberately
     not terminal-green: it belongs to the archive this tool reads from and
     never collides with the app's blue. */
  --term-bg: #1B1A18;                            /* console slab (warm graphite) */
  --term-bg-2: #232120;                          /* answer area, one step up     */
  --term-border: #3A3733;                        /* console border / divider     */
  --term-text: #EDE7DA;                          /* console body text (paper)    */
  --term-muted: #9A9284;                         /* placeholder / dim text       */
  --term-accent: #D8A657;                        /* brass — prompt, cursor, keys */
  --term-accent-edge: #8A6B34;                   /* accent border                */
  --term-accent-soft: rgba(216, 166, 87, 0.16);  /* accent hover wash            */
  --term-accent-ink: #7A5A22;                    /* brass dark enough for WHITE  */
}

body {
  margin: 0;
  /* Plain white page — the warm radial washes went with the paper palette. */
  background: var(--ncs-paper);
  color: var(--ncs-ink);
  min-height: 100vh;
}

/* Ghost scrollbars — 6px, invisible until the pointer hovers the scrolling
   pane (the quietest chrome for the white page). Chromium/Safari get the
   custom -webkit bars; the standard properties must NOT be set there, or
   Chromium ignores the -webkit rules entirely and paints its ~11px native
   thin bar — so they are scoped to Firefox via @supports (where :hover on
   the pane recreates the same ghost behavior). */
*::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: transparent;
  border-radius: 999px;
}
*:hover::-webkit-scrollbar-thumb { background: #C9C9CF; }
*::-webkit-scrollbar-thumb:hover { background: #ADADB3; }
@supports (-moz-appearance: none) {
  * {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
  }
  *:hover {
    scrollbar-color: #C9C9CF transparent;
  }
}

.container {
  display: flex;
  align-items: stretch;
  gap: 0;
  max-width: none;
  /* Fill the viewport so the sidebar hugs the window edges full-height and the
     console can dock at the bottom of the main column. No page padding — the
     sidebar runs flush to the left edge (Perplexity-style). */
  height: 100vh;
  overflow: hidden;
  margin: 0;
  padding: 0;
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, "Hoefler Text", Georgia, system-ui, -apple-system, sans-serif;
  color: var(--ncs-ink);
}

/* Left sidebar — a flat, flush-left rail (Perplexity-style): full height, a
   subtle neutral tint distinct from the white page, separated by a single
   hairline on its right edge. No card chrome (no radius / shadow / accent).
   Holds the nav-history buttons and the recent-sessions picker. */
.app-sidebar {
  flex: 0 0 17rem;
  max-width: 17rem;
  height: 100%;
  box-sizing: border-box;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem 0.85rem;
  background: var(--ncs-cream);
  border-right: 1px solid var(--ncs-rule);
  transition: flex-basis 160ms ease, max-width 160ms ease, padding 160ms ease;
}

/* Header row holding the collapse toggle (top-right when open). */
.sidebar-header {
  display: flex;
  justify-content: flex-end;
  flex: 0 0 auto;
}

/* Collapse toggle — flat ghost icon button. */
.sidebar-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--ncs-stone);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.sidebar-toggle:hover {
  background: color-mix(in srgb, var(--ncs-ink) 7%, transparent);
  color: var(--ncs-ink);
}

/* Body holds the real controls, stacked. */
.sidebar-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Each logical block is bounded by a hairline divider so the sections read as
   separate. Hidden blocks (no sessions / no scenarios) draw nothing. */
.sidebar-block:not([hidden]) {
  padding-bottom: 0.85rem;
  margin-bottom: 0.85rem;
  border-bottom: 1px solid var(--ncs-rule);
}
/* The clusters block now hosts the cluster TOC (plus the dropdown-icon +
   label strip). Hide the whole block until the TOC has content (before results),
   so no stray divider / empty "Scenariusze" section is left behind. */
.sidebar-block--clusters:not(:has(.toc-body > *)) {
  display: none;
}

/* Leading clusters icon inside the scenario filter. */
.scenario-filter-icon {
  flex: none;
  color: var(--ncs-stone);
  fill: currentColor;
}

/* ---- Collapsed: icons only ---- */
.app-sidebar.is-collapsed {
  flex-basis: 3.5rem;
  max-width: 3.5rem;
  padding: 1rem 0.4rem;
  align-items: center;
  overflow-x: hidden;
}
.app-sidebar.is-collapsed .sidebar-header { justify-content: center; }
/* Collapsed → drop the block dividers and show only each block's icon. */
.app-sidebar.is-collapsed .sidebar-block {
  border-bottom: 0;
  padding: 0.3rem 0;
  margin-bottom: 0;
}
.app-sidebar.is-collapsed .view-nav,
.app-sidebar.is-collapsed .session-sidebar-label,
.app-sidebar.is-collapsed .recent-sessions-caret,
.app-sidebar.is-collapsed .recent-sessions-list,
.app-sidebar.is-collapsed .scenario-filter-select,
.app-sidebar.is-collapsed .scenario-strip-sidebar {
  display: none !important;
}
.app-sidebar.is-collapsed .session-sidebar-toggle,
.app-sidebar.is-collapsed #scenario-filter-wrap {
  justify-content: center;
  width: 100%;
  min-width: 0;
  padding: 0.45rem 0;
  gap: 0;
}
/* The cluster TOC is only meaningful expanded — on the icon rail keep just the
   clusters icon (the dropdown SVG), hide the TOC head/body. */
.app-sidebar.is-collapsed .toc-head,
.app-sidebar.is-collapsed .toc-body { display: none !important; }

/* ---- Cluster TOC (in the clusters sidebar block) -------------------------
   Nested cluster -> subcluster labels, modeled on a slide-deck's table of
   contents. Bold roots (.toc-h1), indented muted children (.toc-h2); the caret
   unfolds a branch's children, the label button is a single-active filter that
   scopes the center snippets to that cluster's subtree. Superseded the scenario
   dropdown (hidden below). Beige/dev palette. */
.toc-head {
  margin: 0 0 0.4rem;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ncs-stone);
}
.toc-list { list-style: none; margin: 0; padding: 0; }
.toc-item { margin: 0; }
.toc-row { display: flex; align-items: flex-start; gap: 0.1rem; }
.toc-caret {
  appearance: none;
  flex: 0 0 auto;
  margin-top: 0.28rem;
  width: 1.15rem;
  height: 1.4rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ncs-stone);
  font-size: 0.62rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 6px;
  transition: background-color 120ms ease, color 120ms ease;
}
.toc-caret:hover { background: color-mix(in srgb, var(--ncs-ink) 8%, transparent); color: var(--ncs-ink); }
.toc-caret--leaf { visibility: hidden; cursor: default; }
.toc-link {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  padding: 0.28rem 0.45rem;
  padding-left: calc(0.45rem + var(--depth, 0) * 0.85rem);
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--ncs-ink);
  font-family: inherit;
  cursor: pointer;
  transition: background-color 120ms ease;
}
.toc-link:hover { background: color-mix(in srgb, var(--ncs-ink) 6%, transparent); }
.toc-link.is-active { background: color-mix(in srgb, var(--ncs-5) 16%, transparent); }
/* Permanent "Wszystkie" head row — plain text label instead of chips; the
   shared .is-active wash marks "no filter, seeing everything". */
.toc-link--all .toc-all-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.78rem;
  font-weight: 600;
}
.toc-item--all { margin-bottom: 0.15rem; }
.toc-link.is-active .toc-chip { border-color: color-mix(in srgb, var(--chip-color, var(--ncs-stone)) 70%, transparent); }

/* ALP-133: cluster ENTRIES separate with a hairline so the stacked chip
   columns don't blur into one another. */
.toc-item + .toc-item {
  border-top: 1px solid var(--ncs-rule);
  margin-top: 0.35rem;
  padding-top: 0.35rem;
}

/* ALP-133: within a cluster's (single, flat) sidebar entry the label chips
   stack ONE AFTER ANOTHER — a column, one chip per line — instead of flowing
   side by side. The entry's count stays pinned right of the column. */
.toc-chips {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2rem;
}
.toc-chip {
  appearance: none;
  /* Block (NOT inline-flex): `text-overflow: ellipsis` does nothing inside a
     flex container — labels hard-clipped at the chip edge instead of getting
     the "…" placeholder. Chips hold plain text only, so block is safe. */
  display: block;
  box-sizing: border-box;
  max-width: 100%;
  /* Air around the text — cramped 0.08rem padding had labels touching the
     chip boundary. */
  padding: 0.28rem 0.65rem;
  border-radius: 0.8rem;
  font-family: inherit;
  font-size: 0.72rem;
  line-height: 1.3;
  color: var(--ncs-ink);
  background: color-mix(in srgb, var(--chip-color, var(--ncs-stone)) 20%, var(--ncs-paper));
  border: 1px solid color-mix(in srgb, var(--chip-color, var(--ncs-stone)) 42%, transparent);
  /* ALP-133 stacked chips: one per line, full sidebar width before the
     ellipsis kicks in; tooltips carry full text. */
  flex: 0 0 auto;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
}
.toc-chip:hover { background: color-mix(in srgb, var(--chip-color, var(--ncs-stone)) 34%, var(--ncs-paper)); }
.toc-chip:focus-visible { outline: 2px solid var(--ncs-ink); outline-offset: 1px; }
/* Active label filter: this anchor is highlighted in EVERY chip that carries it. */
.toc-chip.is-active {
  background: color-mix(in srgb, var(--chip-color, var(--ncs-stone)) 55%, var(--ncs-paper));
  border-color: color-mix(in srgb, var(--chip-color, var(--ncs-ink)) 75%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chip-color, var(--ncs-ink)) 55%, transparent);
  font-weight: 600;
}
/* Top-level clusters read a touch stronger than their subclusters. */
.toc-h1 .toc-chip { font-weight: 600; }
.toc-h2 .toc-chip { font-size: 0.68rem; }
.toc-link:focus-visible { outline: 2px solid var(--ncs-ink); outline-offset: 1px; }
.toc-count {
  flex: 0 0 auto;
  margin-top: 0.12rem;
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
  color: var(--ncs-stone);
}

/* Scenario dropdown replaced by the TOC. Hide the control while the rail is
   open (the target stays in the DOM so the filter/persistence guards no-op
   cleanly); on the collapsed rail its icon remains as the clusters affordance. */
.app-sidebar:not(.is-collapsed) #scenario-filter-wrap { display: none !important; }

/* Main column — takes the rest. A flex column: `.app-views` (the scrolling view
   stack) grows to fill, and the docked `.query-overview` console sits below it.
   Carries the page breathing room the container no longer applies. min-width:0
   lets flex/grid children shrink instead of overflowing. */
.app-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  /* border-box so the padding (esp. the bottom gap) counts INSIDE the 100vh
     height — otherwise it overflows and the container's overflow:hidden clips
     the bottom gap off-screen, gluing the console to the edge. */
  box-sizing: border-box;
  /* No bottom gap: the console lives at the TOP now and the results pane
     should run to the viewport's bottom edge (the old 3rem gap was breathing
     room for the bottom-docked console era). */
  padding: 1.25rem 1.5rem 0.5rem;
}

/* Scroll region holding every per-view section. Grows to fill the space above
   the docked console; overflow scrolls here so the console stays pinned. The
   bottom border is a visible section divider between the main view and the
   docked console below it. */
.app-views {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* `overflow-y: auto` otherwise makes the x-axis compute to `auto` too, so a
     few px of incidental width showed a horizontal scrollbar. Clip it. */
  overflow-x: hidden;
}
/* ALP-131: once a search has started the view stack stops being one big
   scroller — the console keeps its natural height at the top and the ACTIVE
   pane below becomes its own scroller (that's what lets the outline's
   cluster/sub-cluster headers pin under the console while rulings scroll).
   Landing mode (:not([data-started])) keeps its own centered layout. */
.container[data-started] .app-views {
  display: flex;
  flex-direction: column;
  overflow-y: hidden;
}
.container[data-started] .app-views > .view--search { flex: 0 0 auto; }
.container[data-started] .app-views > .view--workspace,
.container[data-started] .app-views > .view--draft {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}
/* With results on screen the console yields vertical space: Stanislaw's
   answer caps at a few lines and scrolls inside itself, so the results
   scroller (and its pinned headers) starts high on the page. */
.container[data-has-results] .query-overview-body {
  max-height: 7rem;
}

/* ---- Google-style landing ----------------------------------------------
   Before the first search (`data-started` unset, set on submit / restore) the
   main column shows ONLY a centered search bar + title — every other view, the
   layer-switcher and the console are hidden. The sidebar stays visible. */
.container:not([data-started]) .app-views {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  /* No console on the landing → no section divider. */
  border-bottom: 0;
}
/* Hide every non-search view until real results exist — so the landing AND the
   in-flight search show only the search bar (+ the console once it rises),
   never an empty "Scenariusze / Brak scenariuszy" placeholder. Higher
   specificity than the per-zoom/per-view display rules below. */
.container:not([data-has-results]) .app-views > .view:not(.view--search) {
  display: none;
}
.container:not([data-started]) .view--search {
  width: min(620px, 100%);
}
.container:not([data-started]) .view--search > header { margin-bottom: 1.75rem; }
.container:not([data-started]) .view--search > header h1 {
  font-size: 3.25rem;
  letter-spacing: -0.02em;
}
.container:not([data-started]) .search-bar {
  height: 56px;
}

/* Single-column fallback on narrow viewports. */
@media (max-width: 900px) {
  /* Drop the viewport-height dock layout; let the page scroll normally. */
  .container {
    flex-direction: column;
    height: auto;
    overflow: visible;
  }
  .app-sidebar {
    flex-basis: auto;
    max-width: none;
    width: 100%;
    height: auto;
    overflow-y: visible;
    /* Stacked on top: divider moves to the bottom edge. */
    border-right: none;
    border-bottom: 1px solid var(--ncs-rule);
  }
  .app-main { height: auto; }
  .app-views { overflow-y: visible; }
}

/* NCS hue strip — eight elementary bands as a header flourish. */
.ncs-strip {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  height: 6px;
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 1rem;
  box-shadow: 0 1px 3px rgba(27, 27, 31, 0.12);
}
.ncs-strip span { display: block; }
.ncs-strip span:nth-child(1) { background: var(--ncs-1); }
.ncs-strip span:nth-child(2) { background: var(--ncs-2); }
.ncs-strip span:nth-child(3) { background: var(--ncs-3); }
.ncs-strip span:nth-child(4) { background: var(--ncs-4); }
.ncs-strip span:nth-child(5) { background: var(--ncs-5); }
.ncs-strip span:nth-child(6) { background: var(--ncs-6); }
.ncs-strip span:nth-child(7) { background: var(--ncs-7); }
.ncs-strip span:nth-child(8) { background: var(--ncs-8); }

/* The search pane's masthead — air between the title and the search form. */
.view--search > header { margin-bottom: 1.25rem; }

header h1 {
  margin: 0 0 0.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  font-size: 1.85rem;
}

header .subtitle {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--ncs-stone);
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  margin-bottom: 0.5rem;
  background: var(--ncs-pale);
}

.hint {
  margin: 0 0 1.5rem;
  color: #666;
  font-size: 0.9rem;
}

.hint code,
.meta code {
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
  padding: 0.05rem 0.3rem;
  border-radius: 4px;
  font-size: 0.85em;
}

form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

textarea {
  width: 100%;
  font: inherit;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  resize: vertical;
  box-sizing: border-box;
  background: var(--ncs-pale);
  box-shadow: inset 0 1px 2px rgba(27, 27, 31, 0.04);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

textarea:focus {
  outline: none;
  border-color: var(--ncs-7);
  box-shadow: inset 0 1px 2px rgba(27, 27, 31, 0.04),
              0 0 0 3px rgba(43, 108, 176, 0.18);
}

/* Google-style search bar — icon + single-line input + submit, in a pill. */
/* Keep the search bar pinned to the top of the scrolling view stack so it's
   always reachable while scrolling results / scenarios. The paper background
   masks content scrolling underneath it. */
.search-bar {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.25rem 0.4rem 0.25rem 1.25rem;
  height: 52px;
  background: #FFFFFF;
  border: 1px solid #DFE1E5;
  border-radius: 26px;
  box-shadow: none;
  transition: box-shadow 140ms ease, border-color 140ms ease;
}

.search-bar:hover {
  border-color: rgba(223, 225, 229, 0);
  box-shadow: 0 1px 6px rgba(32, 33, 36, 0.18);
}

.search-bar:focus-within {
  border-color: rgba(223, 225, 229, 0);
  box-shadow: 0 1px 6px rgba(32, 33, 36, 0.28);
}

.search-icon {
  width: 22px;
  height: 22px;
  color: #9AA0A6;
  flex-shrink: 0;
}

.search-bar input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-size: 1rem;
  line-height: 1.4;
  padding: 0.6rem 0;
  border: 0;
  background: transparent;
  outline: none;
  color: #202124;
}

.search-bar input[type="text"]::placeholder {
  color: #9AA0A6;
}

/* Tighter, rounded submit pill that fits inside the bar. */
.search-bar button {
  align-self: stretch;
  margin: 0;
  padding: 0 1.1rem;
  border-radius: 22px;
  font-size: 0.92rem;
  letter-spacing: 0.01em;
  box-shadow: none;
}

.search-bar button:hover:not(:disabled) {
  transform: none;
}

button {
  align-self: flex-start;
  padding: 0.6rem 1.4rem;
  border: 0;
  border-radius: 8px;
  background: linear-gradient(180deg, var(--ncs-2) 0%, #A02D4E 100%);
  color: white;
  font: inherit;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(193, 59, 94, 0.4),
              0 4px 14px -4px rgba(193, 59, 94, 0.5);
  transition: transform 80ms ease, box-shadow 120ms ease, filter 120ms ease;
}

button:hover:not(:disabled) {
  filter: brightness(1.05);
  transform: translateY(-1px);
}

button:active:not(:disabled) {
  transform: translateY(0);
  filter: brightness(0.97);
}

button:disabled {
  background: linear-gradient(180deg, #C4C4C9 0%, #ADADB3 100%);
  box-shadow: none;
  cursor: not-allowed;
}

.error {
  margin-top: 1.5rem;
  padding: 0.75rem 1rem;
  background: #fee2e2;
  border: 1px solid #fecaca;
  border-radius: 6px;
  color: #991b1b;
}

[hidden] {
  display: none !important;
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin: 1.75rem 0 1.25rem;
  font-size: 0.85rem;
  color: #555;
}

/* Terminal/console panel — Stanislaw's transcript streams into the body on top
   and the query input is the prompt line at the bottom, like a real shell.
   Dark + monospace. The prompt line is pinned at the bottom (flex: none); only
   the transcript above it scrolls. */
.query-overview {
  width: 100%;
  display: flex;
  flex-direction: column;
  max-height: 32vh;
  margin: 0 0 0.5rem;
  padding: 0;
  border: 1px solid var(--term-border);
  border-radius: 10px;
  /* The slab: a hairline of lamp-light along the top edge, and a shadow that
     lifts it off the white page. */
  background:
    linear-gradient(var(--term-bg-2), var(--term-bg-2)) padding-box,
    var(--term-bg);
  box-shadow:
    inset 0 1px 0 rgba(237, 231, 218, 0.08),
    0 10px 28px -18px rgba(27, 26, 24, 0.75);
  color: var(--term-text);
  font-family: ui-monospace, SFMono-Regular, Menlo, "Cascadia Code", Consolas, monospace;
  box-shadow: 0 10px 28px -16px rgba(0, 0, 0, 0.55);
  animation: qo-fade-in 320ms ease both;
}

/* Shell prompt line: `>` caret + the query input + a compact submit. */
.qo-input-line {
  flex: none;
  display: flex;
  flex-direction: row; /* override the global `form { flex-direction: column }` */
  align-items: center;
  gap: 0.55rem;
  padding: 0.65rem 0.85rem;
}
.qo-caret {
  color: var(--term-accent);
  font-weight: 700;
  line-height: 1;
}
.qo-input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: none;
  color: var(--term-text);
  font: inherit;
  font-size: 0.92rem;
  padding: 0.15rem 0;
}
.qo-input::placeholder { color: var(--term-muted); }
.qo-input:disabled { opacity: 0.6; }
/* Compact ghost submit — override the global red pill. */
.qo-submit {
  flex: none;
  align-self: center;
  margin: 0;
  padding: 0.22rem 0.6rem;
  background: none;
  border: 1px solid var(--term-accent-edge);
  border-radius: 6px;
  box-shadow: none;
  color: var(--term-accent);
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0;
  cursor: pointer;
}
.qo-submit:hover:not(:disabled) { background: var(--term-accent-soft); transform: none; }
.qo-submit:disabled { opacity: 0.45; cursor: default; }

/* Watch tick — quiet trailer on the prompt line; the action it governs is an
   invisible background POST, so the title text carries the explanation and an
   unticked box just dims. */
.qo-watch {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex: none;
  margin-left: 0.15rem;
  color: var(--term-muted);
  font-size: 0.72rem;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
.qo-watch input { margin: 0; accent-color: var(--term-accent); }
.qo-watch:has(input:not(:checked)) { opacity: 0.55; }

/* PROTOTYPE_FRONT (ALP-127): canned-query chips under the search line. */
.qo-prototype-queries {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.5rem;
}
.qo-proto-label {
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.55;
}
.qo-proto-chip {
  padding: 0.16rem 0.5rem;
  background: none;
  border: 1px dashed var(--term-accent-edge);
  border-radius: 999px;
  color: var(--term-accent);
  font-size: 0.72rem;
  cursor: pointer;
}
.qo-proto-chip:hover { background: var(--term-accent-soft); }

/* Stage tabs — one strip under the console navigates the three panes
   (wyniki · tablica · prezentacja); replaces the per-pane arrow pills.
   Monospace + the prompt's olive accent keep the family tie to the console,
   but the strip sits on the white page (a hairline underlines the row; the
   active tab's accent underline sits on top of it). Active state is painted
   from the container's data-view attribute, so the workspace controller
   needs no new JS; ←/→ keys keep working. Hidden until a search starts. */
.qo-tabs { display: none; }
.container[data-started] .qo-tabs {
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  margin: 0.55rem 0 0.9rem;
  border-bottom: 1px solid var(--ncs-rule);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8rem;
}
.qo-tab {
  display: inline-flex;
  align-items: baseline;
  margin-bottom: -1px; /* the active underline covers the strip hairline */
  padding: 0.05rem 0.15rem 0.25rem;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  box-shadow: none;
  color: var(--ncs-stone);
  font: inherit;
  cursor: pointer;
}
.qo-tab:hover:not(:disabled),
.qo-tab:active:not(:disabled) {
  background: var(--term-accent-soft);
  color: var(--term-accent-ink);
  transform: none;
  filter: none;
}
.container[data-view="search"] .qo-tab--search,
.container[data-view="workspace"] .qo-tab--workspace,
.container[data-view="draft"] .qo-tab--draft {
  color: var(--term-accent-ink);
  font-weight: 700;
  border-bottom-color: var(--term-accent-edge);
}
/* Pinned-count baked into the tablica tab, terminal style: `tablica·3`. */
.qo-tab-count::before { content: "·"; }

@keyframes qo-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Transcript area above the prompt. Scrolls inside the console; the bottom
   border is the divider between the transcript and the prompt line below it
   (gone when the body is `hidden`, i.e. before Stanislaw has answered). */
.query-overview-body {
  margin: 0;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--term-border);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--term-text);
}
/* Earlier turns scrolled above the fold fade out at the top edge instead of
   being sliced — the transcript reads as continuing upward. */
.query-overview-body.is-scrolled {
  mask-image: linear-gradient(to bottom, transparent 0, #000 1.6rem);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 1.6rem);
}

/* `$ stanislaw` prompt line. */
.query-overview-prompt {
  color: var(--term-accent);
  font-weight: 700;
  white-space: pre;
}

/* Output of the "command": the real summary sentence. */
.query-overview-summary {
  color: var(--term-text);
  font-weight: 400;
}

/* ALP-137: Stanislaw's clickable next steps — a numbered console list; each
   option is a quiet olive link-button that navigates into the app. */
/* One option per line, full label — the answer body scrolls, so the list is
   not squeezed to fit a cap (that cap exists to protect the results area's
   height, not to hide options). */
.qo-options {
  margin: 0.4rem 0 0;
  padding-left: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.16rem;
}
.qo-options li::marker {
  color: var(--term-accent);
  font-weight: 700;
}
.qo-option {
  margin: 0;
  padding: 0.05rem 0.3rem;
  background: none;
  border: 0;
  border-radius: 6px;
  box-shadow: none;
  font: inherit;
  color: var(--term-accent);
  text-align: left;
  cursor: pointer;
  /* One line per option so all three fit inside the capped answer body;
     the button's title carries the full text. */
  display: block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.qo-option:hover:not(:disabled) {
  background: var(--term-accent-soft);
  transform: none;
  filter: none;
}
/* Keyboard cursor: the focused option reads as a selected TUI line (inverted
   olive), so ↑/↓ navigation is visible without touching the mouse. */
.qo-option:focus,
.qo-option:focus-visible {
  outline: none;
  background: var(--term-accent);
  color: var(--term-bg);
}
/* Earlier turns of the conversation stay as a dimmed transcript above the
   current reply. */
.query-overview-past {
  display: block;
  opacity: 0.5;
}
.query-overview-past + .query-overview-summary { margin-top: 0.15rem; }
/* The user's own line in the transcript — brass, like the prompt caret. */
.query-overview-reply {
  display: block;
  color: var(--term-accent);
  opacity: 0.75;
}
/* A command's reply (help / unknown command) under the current turn. */
.query-overview-note {
  display: block;
  margin-top: 0.3rem;
  color: var(--term-muted);
}
/* The TUI key hint under the options. */
.qo-hint {
  margin: 0.35rem 0 0;
  font-size: 0.7rem;
  color: var(--term-muted);
}

/* Animated waiting indicator while the query runs: a growing row of dots. */
.qo-dots {
  color: var(--term-accent);
  font-weight: 700;
  white-space: pre;
}
.qo-dots::after {
  content: "";
  animation: qo-typing 1.4s steps(1, end) infinite;
}
@keyframes qo-typing {
  0%   { content: ""; }
  11%  { content: "."; }
  22%  { content: ".."; }
  33%  { content: "..."; }
  44%  { content: "...."; }
  55%  { content: "....."; }
  66%  { content: "......"; }
  77%  { content: "......."; }
  88%, 100% { content: "........"; }
}

/* Stanislaw "thinks" — an ASCII Atmospheric-Diving-Suit diver surfaces in the
   console, bubbles rising from the dome. Four stacked frames (same suit,
   bubbles shift per frame) take turns via staggered step keyframes — pure
   CSS round-robin, no JS timers to clean up when the answer arrives. */
.qo-diver {
  display: grid;
  justify-content: start;
  margin: 0.5rem 0 0.15rem;
}
.qo-diver-frame {
  grid-area: 1 / 1;
  white-space: pre;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  line-height: 1.15;
  color: var(--term-accent);
  opacity: 0;
  animation: qo-diver-frame 1.8s step-end infinite;
}
.qo-diver-frame:nth-child(2) { animation-delay: 0.45s; }
.qo-diver-frame:nth-child(3) { animation-delay: 0.9s; }
.qo-diver-frame:nth-child(4) { animation-delay: 1.35s; }
@keyframes qo-diver-frame {
  0%, 24.99% { opacity: 1; }
  25%, 100%  { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .qo-diver-frame { animation: none; }
  .qo-diver-frame:first-child { opacity: 1; }
}

.results {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.card {
  border: 1px solid var(--ncs-rule);
  border-radius: 12px;
  padding: 1.1rem 1.25rem;
  background: var(--ncs-paper);
  box-shadow: 0 1px 2px rgba(27, 27, 31, 0.04),
              0 6px 24px -8px rgba(27, 27, 31, 0.08);
  position: relative;
  overflow: hidden;
}

/* ALP-131: the detail card body renders the ENTIRE raw interpretacja instead
   of the SF summary (prototype; lazily fetched from the demo backend's
   committed ruling dump). A scrollable reader — the ghost scrollbars and the
   shared query <mark>s do the rest. */
.card-fulltext {
  /* 55vh keeps the whole open card under one viewport, so the PREVIOUS and
     NEXT snippet rows stay within reach around it. */
  max-height: 55vh;
  overflow-y: auto;
  /* Anchor for the jump-to-passage flash overlays (.reader-flash). */
  position: relative;
  padding-right: 0.4rem;
  /* The document renders as ONE flat run through buildCardBodyHtml (so
     fact-marks + query marks work); pre-wrap keeps its paragraph structure. */
  white-space: pre-wrap;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--ncs-ink);
}

/* Two-pane reader (open card with the rail): the card body takes a fixed
   height and BOTH panes scroll internally — the document on the left, and on
   the right the rail (breadcrumb + questions, capped) with the comparison
   filling the rest. No sticky inside the card; each pane owns its scroll. */
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) {
  /* Adaptive reader height: fill what's left of the viewport under the
     console block + the chrome above/below the card body in each context,
     so the record's bottom lands near the pane bottom instead of clipping.
     The console is height-stable now (answer capped), so a per-context
     constant works. */
  height: clamp(18rem, calc(100dvh - 36.5rem), 48rem);
  /* One row, three side-by-side tiles (areas come from the rule above); each
     tile owns the full height and scrolls internally. minmax(0,1fr) — not
     `auto` — is what stops a tall tile from collapsing its neighbours. */
  grid-template-rows: minmax(0, 1fr);
}
/* Tablica / prezentacja records carry the note instead of a snippet row above
   — slightly more room available. */
.view--workspace .card.has-fulltext .card-body:has(.card-rail:not([hidden])),
.view--draft .card.has-fulltext .card-body:has(.card-rail:not([hidden])) {
  height: clamp(18rem, calc(100dvh - 31.5rem), 48rem);
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .text {
  /* card-body aligns items `start` (they size to content) — the reader panes
     must stretch to their grid rows instead, or they overflow the card. And
     the legacy SF-era cap (max-height: 22rem on .card .text) must lift, or
     the document pane stops at half the record's height. */
  align-self: stretch;
  min-height: 0;
  max-height: none;
  display: flex;
  flex-direction: column;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .card-fulltext {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .card-rail {
  position: static;
  align-self: stretch;
  /* No vh cap here — the grid row owns the height (see above). */
  max-height: none;
  min-height: 0;
  overflow-y: auto;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points-col {
  align-self: stretch;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  overflow-y: auto;
}
/* ---- Reader tiles: one box, not three -------------------------------------
   The card used to nest card > panel > cell, each with its own border,
   background, radius and padding — ~26px of a 272px column gone to chrome
   before a word is drawn. In the reader the three tiles are ZONES separated by
   hairlines instead: no panel skin, no cell skin, all the width goes to text. */
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) {
  column-gap: 0;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .card-rail,
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points-col {
  border-left: 1px solid var(--ncs-rule);
  padding-left: 0.9rem;
  margin-left: 0.9rem;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .card-rail-section {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .card-rail-section + .card-rail-section {
  margin-top: 0.7rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--ncs-rule);
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points,
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points.irrelevant,
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points.placeholder {
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .points-col .card-rail-title {
  flex: 0 0 auto;
  margin: 0 0 0.35rem;
}

/* ---- The compliance strip (signature) --------------------------------------
   A schemat step and its verdict STACK instead of splitting the column 50/50
   (160px cells wrapped five-word verdicts over four lines). The verdict hangs
   behind a 2px status edge; stacked down the column those edges read as one
   green/amber strip — the ruling's fit with the schema, before a word. */
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .schemat-compare { gap: 0.5rem; }
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-row {
  grid-template-columns: minmax(0, 1fr);
  gap: 0.15rem;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-step {
  padding: 0;
  background: none;
  border: 0;
  gap: 0.4rem;
  font-weight: 600;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-num {
  flex: 0 0 auto;
  width: auto;
  height: auto;
  margin: 0;
  background: none;
  box-shadow: none;
  border-radius: 0;
  color: var(--ncs-stone);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-num::after { content: "."; }
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-verdict {
  margin-left: 0.15rem;
  padding: 0.05rem 0 0.05rem 0.55rem;
  background: none;
  border-radius: 0;
  border-left: 2px solid var(--ncs-rule);
  color: var(--ncs-stone);
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-row--match .cmp-verdict {
  border-left-color: #2f9e44;
}
.card.has-fulltext .card-body:has(.card-rail:not([hidden])) .cmp-row--nomatch .cmp-verdict {
  border-left-color: #e8924a;
}
.card-fulltext mark {
  background: hsl(var(--scenario-hue, 210), 72%, 88%);
  color: inherit;
  border-radius: 3px;
  padding: 0 0.1em;
}
.card-fulltext-loading {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--ncs-stone);
}
/* A matched step's quote in the comparison panel is a jump-link into the
   full-text reader — pointer + subtle underline signal it. */
.cmp-text--linked {
  cursor: pointer;
  text-decoration: underline dotted transparent;
  text-underline-offset: 2px;
  transition: text-decoration-color 120ms ease;
}
.cmp-text--linked:hover { text-decoration-color: var(--ncs-7); }
/* Landing flash on the jumped-to fact-mark (reuses the focus-flash pulse). */
.fact-mark--flash {
  animation: focus-flash 1.2s ease both;
  border-radius: 4px;
}

.card::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: linear-gradient(180deg,
    var(--ncs-1), var(--ncs-2), var(--ncs-3), var(--ncs-4),
    var(--ncs-5), var(--ncs-6), var(--ncs-7), var(--ncs-8));
  opacity: 0.55;
}

.card.relevant::before { opacity: 1; }
.card.irrelevant::before {
  background: linear-gradient(180deg, var(--ncs-4), var(--ncs-1));
  opacity: 0.55;
}

/* The result card's header row (`.card-head`) was removed — its compare /
   feedback / liskus / expand controls and the (hidden) signature/chips no
   longer render on the full card. Remaining `.card-head …` / `.compare-btn` /
   `.expand-btn` rules elsewhere match nothing and are inert. */

.scenario-chips {
  --scenario-hue: 210;
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  align-self: center;
}

.scenario-chip {
  /* ALP-135: tags are UNIFORM — no per-label colour. Every chip renders with
     this default; nothing sets --chip-color inline anymore. */
  --chip-color: var(--ncs-7);
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.4;
  /* Unified toward the beige palette: fill + border mix the label hue into the
     cream surface (not white), and the text is muted with warm ink instead of
     full-chroma — so labels stay distinguishable by hue but read as one beige
     family rather than a rainbow. */
  background: color-mix(in srgb, var(--chip-color) 13%, var(--ncs-cream));
  color: color-mix(in srgb, var(--chip-color) 55%, var(--ncs-ink));
  border: 1px solid color-mix(in srgb, var(--chip-color) 28%, var(--ncs-cream));
  white-space: nowrap;
}

/* Honesty badge: when no result covers the query's full concept conjunction
   (results_loaded's facet_coverage.full_intersection === 0), each card is
   labelled with the facet subset it does cover ("Dotyczy tylko: IP Box").
   Rendered by `buildCardFromTemplate` next to the signature. */
.card .facet-only-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
  background: color-mix(in srgb, #e67e22 14%, white);
  color: #b35c0e;
  border: 1px solid color-mix(in srgb, #e67e22 38%, white);
}

/* "Scenarios only" zoom level — a board of scenario cards, one per scenario,
   showing the scenario's labels and its general schemat. Capped at 3
   columns so a wide viewport doesn't fan out into tiny cells; drops to 2
   then 1 as the viewport narrows. */
/* Vertical tree: top-level scenarios stack, and an unfolded branch reveals its
   sub-clusters indented beneath it in place (see `.scenario-children`). */
.scenario-board {
  display: block;
  padding: 0.5rem 0 1rem;
  position: relative;
}
.scenario-board::after {
  content: "";
  position: absolute;
  top: 0.5rem;
  bottom: 1rem;
  right: 0.82rem;
  width: 1px;
  background: color-mix(in srgb, var(--ncs-ink) 16%, transparent);
  pointer-events: none;
}

/* ---- Drill-right cascade (Clusters → Snippets → Details) ---- */
/* The three layer views become horizontally-scrollable columns. Only active in
   the search view once results exist; otherwise hidden so the landing / other
   data-view modes are untouched. */
.layer-cascade { display: none; }
.container[data-view="search"][data-has-results] .layer-cascade {
  position: relative;
  display: flex;
  align-items: stretch; /* all region columns share the tallest one's height */
  gap: 0; /* zero gap → the 25%/25%/50%/50% column widths add up exactly */
  overflow-x: auto;
  /* ALP-131: the cascade is the RESULTS' own vertical scroller — the console
     block above stays put and the sticky cluster/sub-cluster rows pin to
     THIS scroller's top. */
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  padding-bottom: 0.5rem;
}
/* Inside a shown cascade, all three columns render side-by-side (override the
   single-view data-zoom display rules). */
.container[data-view="search"][data-has-results] .layer-cascade > .view:not([hidden]) {
  display: block !important;
  /* Snippets + Details each take exactly HALF the main view; Scenarios +
     Specific scenarios are overridden below to a quarter each (together a half).
     border-box + gap:0 so the padding is included and the widths add up exactly;
     the padding itself gives the visual separation between columns. */
  box-sizing: border-box;
  flex: 0 0 50%;
  min-width: 0;
  scroll-snap-align: start;
  /* Neutralise the single-view "air below search" rule (margin-top/padding-top/
     border-top on whichever column matches the active data-zoom). In the cascade
     the columns sit side-by-side, so that offset would misalign one column's
     header against the others. */
  margin-top: 0;
  padding-top: 0.65rem;
  border-top: 0;
}
/* Scenarios + Sub-clusters tiles are small blocks → narrower columns. The
   `:not([hidden])` matches the base rule's specificity so this flex-basis wins. */
.container[data-view="search"][data-has-results] .layer-cascade > .view--scenarios:not([hidden]),
.container[data-view="search"][data-has-results] .layer-cascade > .view--subclusters:not([hidden]) {
  /* A quarter each → Scenarios + Specific scenarios add up to half the view. */
  flex: 0 0 25%;
  /* Kill the old single-view `.container[data-zoom="scenarios"] .view--scenarios`
     margin-top (2.25rem) that pushed this column down + shrank its stretch. */
  margin-top: 0;
}
/* Bottom-pinned "coming next" footers must sit flush with the panel's bottom
   edge: sticky `bottom` insets resolve against the scroller's CONTENT box, so
   any bottom padding on the board (or the panel wrappers) becomes a strip
   where results visibly scroll UNDER the pinned footer. Flatten them all in
   the outline mode. */
.container[data-view="search"][data-has-results] .layer-cascade { padding-bottom: 0; }
.container[data-view="search"][data-has-results] .layer-cascade > .view--scenarios { padding-bottom: 0; }
.container[data-view="search"][data-has-results] .layer-cascade .scenario-board { padding-bottom: 0; }
/* Outline mode: the board (the results scroller) reaches the viewport bottom
   on ANY monitor — the legacy 62vh cap left a dead band that grew with screen
   height. 19rem ≈ the height-stable chrome above the board (banner + console
   with its capped answer + tabs + panel paddings). */
.container[data-view="search"][data-has-results] .layer-cascade .scenario-board {
  max-height: calc(100dvh - 19.5rem);
}

/* In the narrow cluster columns the tiles stack one after another (single
   column) rather than the wider auto-fill card grid. */
.layer-cascade > .view--subclusters .scenario-board {
  grid-template-columns: 1fr;
}
/* The focal column (the one drilled to) gets a subtle lift. */
.layer-col.is-focal { box-shadow: 0 0 0 2px color-mix(in srgb, var(--ncs-5) 35%, transparent); border-radius: 12px; }

/* Dashed SVG connectors overlaying the cascade, linking the drilled cluster →
   its snippet group → the detail cards. */
.layer-cascade-wrap { position: relative; }

/* ---- Result cascade band ----
   The depth regions (Scenarios → Specific → Snippets → Details). Only shown
   once results exist. */
.cascade-stack { display: none; }
.container[data-view="search"][data-has-results] .cascade-stack {
  display: flex;
  flex-direction: column;
}

/* The linked tiles across the columns (cluster → specific → snippet → details)
   light up together when you drill, so it's obvious WHAT the clicked parent
   connects to. Strong ring + glow + tint, plus a one-shot pulse so the eye
   catches what just lit up. */
.cascade-linked {
  position: relative;
  z-index: 2;
  outline: 3px solid var(--ncs-5);
  outline-offset: 2px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--ncs-5) 20%, var(--ncs-paper)) !important;
  box-shadow:
    0 0 0 5px color-mix(in srgb, var(--ncs-5) 28%, transparent),
    0 10px 26px -8px color-mix(in srgb, var(--ncs-5) 55%, transparent);
  transition: outline-color 120ms ease, background-color 120ms ease, box-shadow 120ms ease;
  animation: cascade-link-pulse 700ms ease-out;
}
/* Dim everything that is NOT part of the highlighted chain, so the lit tiles
   stand out unmistakably. */
.layer-cascade:has(.cascade-linked) .scenario-card:not(.cascade-linked):not(:has(.cascade-linked)),
.layer-cascade:has(.cascade-linked) .snippet-group:not(.cascade-linked),
.layer-cascade:has(.cascade-linked) .view--details .card:not(.cascade-linked) {
  opacity: 0.45;
  transition: opacity 160ms ease;
}
@keyframes cascade-link-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ncs-5) 65%, transparent); }
  60%  { box-shadow: 0 0 0 10px color-mix(in srgb, var(--ncs-5) 18%, transparent); }
  100% {
    box-shadow:
      0 0 0 5px color-mix(in srgb, var(--ncs-5) 28%, transparent),
      0 10px 26px -8px color-mix(in srgb, var(--ncs-5) 55%, transparent);
  }
}
/* Each cascade column is a shaded REGION (not a card) — a different tint per
   depth so the layers read distinctly. */
.layer-cascade > .view {
  border-radius: 14px;
  padding: 0.65rem 0.75rem;
}
.layer-cascade > .view--scenarios   { background: var(--results-surface); }
.layer-cascade > .view--subclusters { background: color-mix(in srgb, var(--ncs-3) 8%, var(--ncs-paper)); }
.layer-cascade > .view--snippets    { background: color-mix(in srgb, var(--ncs-5) 7%, var(--ncs-paper)); }
.layer-cascade > .view--details     { background: color-mix(in srgb, var(--ncs-4) 8%, var(--ncs-paper)); }

/* Every cascade column scrolls INTERNALLY (like the Snippets list) instead of
   growing the column / page, and is capped so the band fits the viewport.
   Scenarios / Specific scenarios / Snippets render into their boards; Details
   uses the .zone-grid. */
.layer-cascade .scenario-board,
.layer-cascade .snippet-board,
.layer-cascade .view--details .zone-grid {
  max-height: 62vh;
  overflow-y: auto;
  overflow-x: hidden; /* chips wrap (below) — never scroll a column horizontally */
}

/* In the narrow Scenarios / Specific-scenarios / Snippets tiles, let long chip
   labels WRAP instead of overflowing. Scoped to those boards ONLY — NOT the
   detail-card header chips, which must stay single-line pills that flow as a row
   (otherwise they stack into tall skinny columns). */
.layer-cascade .scenario-board .scenario-chip,
.layer-cascade .snippet-group .scenario-chip {
  white-space: normal;
  /* `break-word` (not `anywhere`) — `anywhere` collapses the chip's
     min-content width to a single glyph, so in the narrow 25% cascade
     columns long one-word Polish labels stacked one letter per line. */
  overflow-wrap: break-word;
}
/* Keep the doc-count beside/under the actions, never overlapping the title. */
.layer-cascade .scenario-card-head,
.layer-cascade .snippet-group-head {
  flex-wrap: wrap;
}
.layer-cascade .scenario-card-headline { flex: 1 1 0; min-width: 0; }

/* Unify the item cards across every cascade region (Scenarios, Specific
   scenarios, Snippets, Details) so they read as one family: same surface,
   border, radius and padding; a single hue-coded left rail; and one quiet
   "active/drilled" treatment instead of the snippet group's heavy ring. */
.layer-cascade .scenario-card,
.layer-cascade .snippet-group,
.layer-cascade .results > .card {
  background: var(--ncs-paper);
  border: 1px solid var(--ncs-rule);
  border-left: 4px solid hsl(var(--scenario-hue, 210), 45%, 60%);
  border-radius: 10px;
  padding: 0.85rem 1rem;
  box-shadow: none;
}
/* Cluster + sub-cluster boxes only: slimmer vertical profile (the schema is
   hidden behind the toggle, so a collapsed card is just the header row). */
.layer-cascade .scenario-card {
  padding-top: 0.4rem;
  padding-bottom: 0.4rem;
}
.layer-cascade .snippet-group--selected {
  border-color: var(--ncs-rule);
  border-left-color: hsl(var(--scenario-hue, 210), 60%, 48%);
  border-left-width: 5px; /* the active item just gets a slightly bolder rail */
  background: var(--ncs-paper); /* no tint — keep one shared surface */
  box-shadow: none;
}

/* The details result pool scrolls inside its panel rather than growing the
   column. */
.zone-grid {
  max-height: 42vh;
  overflow-y: auto;
  padding-right: 0.2rem;
}
.snippet-group-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.snippet-group-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 1.65rem;
  column-gap: 0.45rem;
  align-items: start;
}
.snippet-group-row > .snippet-group { grid-column: 1; }
.snippet-group-row > .snippet-select-tick { grid-column: 2; }

.scenario-tree-node {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 1.65rem;
  column-gap: 0.45rem;
  align-items: start;
  position: relative;
}
.scenario-tree-node > .scenario-card { grid-column: 1; }
.scenario-tree-node > .scenario-select-tick { grid-column: 2; }
.scenario-tree-node > .scenario-children { grid-column: 1 / -1; }
.scenario-card {
  --scenario-hue: 210;
  background: var(--ncs-paper);
  border: 1px solid var(--ncs-rule);
  border-left: 4px solid hsl(var(--scenario-hue), 55%, 58%);
  border-radius: 8px;
  padding: 0.4rem 0.65rem 0.4rem 0.9rem;
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease, transform 120ms ease;
}
.scenario-select-tick,
.snippet-select-tick {
  grid-column: 2;
  grid-row: 1;
  justify-self: center;
  align-self: start;
  margin-top: 0.08rem;
  width: 20px;
  height: 20px;
  padding: 0;
  position: relative;
  z-index: 1;
  border: 2px solid var(--ncs-rule);
  border-radius: 6px;
  background: #fff;
  color: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.82rem;
  font-weight: 800;
  line-height: 1;
}
.scenario-select-tick::before,
.snippet-select-tick::before { content: '\2713'; }
.scenario-select-tick:hover,
.snippet-select-tick:hover { border-color: var(--ncs-5); }
.scenario-select-tick.is-partial,
.snippet-select-tick.is-partial {
  background: #F2C94C;
  border-color: #D6A800;
  color: #3b2f00;
}
.scenario-select-tick.is-partial::before,
.snippet-select-tick.is-partial::before { content: '–'; }
.scenario-select-tick.is-selected,
.snippet-select-tick.is-selected {
  background: #2EAA5E;
  border-color: #2EAA5E;
  color: #fff;
}

.scenario-card:hover {
  border-color: hsl(var(--scenario-hue), 55%, 48%);
  background: white;
  transform: translateY(-1px);
}
.scenario-card:focus-visible {
  outline: 2px solid hsl(var(--scenario-hue), 55%, 48%);
  outline-offset: 2px;
}
.scenario-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0;
}
/* Right-side controls: pin, doc-count and the schema toggle sit inline in a
   single row so the card stays one line tall (the compare button is hidden). */
.scenario-card-actions {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
}
.scenario-card-count {
  font-size: 0.78rem;
  color: var(--ncs-stone);
  white-space: nowrap;
}
/* Toggle that reveals a top-level scenario's general schema (hidden by
   default at the first cluster level to keep the tiles compact). */
.scenario-schema-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  margin: 0;
  white-space: nowrap;
  cursor: pointer;
  font: inherit;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ncs-7, #475569);
  background: color-mix(in srgb, var(--ncs-ink) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--ncs-ink) 18%, transparent);
  border-radius: 999px;
  padding: 0.1rem 0.45rem;
  transition: background 120ms ease, border-color 120ms ease;
}
.scenario-schema-toggle-caret { font-size: 0.6rem; line-height: 1; }
.scenario-schema-toggle-label {
  max-width: 8ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.scenario-schema-toggle:hover {
  background: color-mix(in srgb, var(--ncs-ink) 10%, transparent);
  border-color: color-mix(in srgb, var(--ncs-ink) 30%, transparent);
}
.scenario-schema-toggle[aria-expanded="true"] {
  background: color-mix(in srgb, var(--ncs-5) 14%, transparent);
  border-color: color-mix(in srgb, var(--ncs-5) 40%, transparent);
}
.scenario-schema-body[hidden] { display: none; }

/* Hover clouds — floating speech-bubble previews, no click and no reflow.
   Two anchors share the pattern: the scenario card's schema toggle (clouds
   the general schemat) and the snippet row's "Brak" marker (clouds the
   schemat steps the ruling does NOT cover). Each anchor wrapper is
   position:relative; the cloud is an absolutely-positioned sibling of the
   trigger revealed on its :hover / :focus-visible. Clouds are aria-hidden —
   the same content is reachable via the click toggle / the details card. */
.scenario-schema-hover,
.snippet-schema-hover {
  position: relative;
  display: inline-flex;
}
.scenario-schema-cloud,
.snippet-schema-cloud {
  display: none;
  position: absolute;
  top: calc(100% + 0.45rem);
  right: 0;
  z-index: 40;
  width: min(24rem, 65vw);
  padding: 0.65rem 0.85rem;
  background: var(--ncs-paper);
  border: 1px solid var(--ncs-rule);
  border-radius: 12px;
  box-shadow: 0 12px 32px -14px rgba(27, 27, 31, 0.4);
  white-space: normal;
  text-align: left;
  font-size: 0.8rem;
  font-weight: 400;
  letter-spacing: normal;
  color: var(--ncs-ink);
  cursor: default;
}
/* Transparent bridge over the anchor gap so the pointer can travel from the
   trigger into the cloud without it closing. */
.scenario-schema-cloud::before,
.snippet-schema-cloud::before {
  content: "";
  position: absolute;
  top: -0.5rem;
  left: 0;
  right: 0;
  height: 0.5rem;
}
/* The bubble tail, pointing up at the trigger. */
.scenario-schema-cloud::after,
.snippet-schema-cloud::after {
  content: "";
  position: absolute;
  top: -0.3rem;
  right: 1rem;
  width: 0.55rem;
  height: 0.55rem;
  background: var(--ncs-paper);
  border-left: 1px solid var(--ncs-rule);
  border-top: 1px solid var(--ncs-rule);
  transform: rotate(45deg);
}
.scenario-schema-toggle:hover + .scenario-schema-cloud,
.scenario-schema-toggle:focus-visible + .scenario-schema-cloud,
.scenario-schema-cloud:hover,
.snippet-schema-missing:hover + .snippet-schema-cloud,
.snippet-schema-missing:focus-visible + .snippet-schema-cloud,
.snippet-schema-cloud:hover {
  display: block;
}
/* Schema already pinned open inline (click toggle)? No cloud on top of it.
   Comes after the reveal rule so the specificity tie resolves this way. */
.scenario-schema-toggle[aria-expanded="true"] + .scenario-schema-cloud { display: none; }
.scenario-schema-cloud .scenario-card-schema { margin: 0; padding-left: 1.1rem; }
.snippet-schema-cloud ul {
  margin: 0;
  padding-left: 1.1rem;
}
.snippet-schema-cloud li { margin: 0.15rem 0; }
.snippet-schema-cloud-title {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: #B35C0E;
}

/* Stacking fix: the hovered card's transform (translateX/Y) creates a
   stacking context that traps the cloud's z-index beneath the NEXT rows'
   positioned tickers (.select-tick z-index:1). Lift whatever row currently
   hosts the pointer above its siblings — hovering the cloud itself keeps the
   ancestor row :hover, so the cloud never flips underneath mid-travel. */
.outline-row { position: relative; }
.outline-row:hover,
.snippet-card:hover,
.scenario-tree-node:hover {
  z-index: 45;
}

/* The "Brak" marker itself — the amber inverse of .snippet-schema-ok. */
.snippet-schema-missing {
  flex: 0 0 auto;
  color: #B35C0E;
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: help;
}

.scenario-card-title {
  margin: 0.4rem 0 0.3rem;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #475569;
}
.scenario-card-schema {
  margin: 0;
  padding-left: 1.3rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.9rem;
  line-height: 1.4;
}
.scenario-card-noschema {
  margin: 0;
  font-size: 0.85rem;
  color: #94a3b8;
}

/* Unfolded sub-clusters: indented beneath their parent with a guide rail in
   a neutral rail, so the nesting reads at a glance to any depth. */
.scenario-children {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0.4rem 0 0.2rem 0.85rem;
  padding-left: 1rem;
  border-left: 2px solid var(--ncs-rule);
}

/* Headline row: the unfold caret sits left of the label chips on branch cards. */
.scenario-card-headline {
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  min-width: 0;
}
.scenario-caret {
  flex: 0 0 auto;
  color: var(--ncs-stone);
  font-size: 0.8rem;
  line-height: 1.4;
  user-select: none;
}

/* Branch cards (scenarios that contain sub-clusters) read as a small stack of
   cards via layered shadows, and carry an unfold hint until opened. An open
   branch flattens (no stack) and tints to show it owns the rail beneath it. */
.scenario-card--branch {
  box-shadow: 4px 4px 0 -1px var(--ncs-pale),
              4px 4px 0 0 var(--ncs-rule);
}
.scenario-card--branch:hover {
  box-shadow: 5px 5px 0 -1px var(--ncs-pale),
              5px 5px 0 0 hsl(var(--scenario-hue), 55%, 58%);
}
.scenario-card--branch.is-expanded {
  box-shadow: none;
  background: hsl(var(--scenario-hue), 60%, 97%);
  border-color: hsl(var(--scenario-hue), 55%, 58%);
}
.scenario-card-drill {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ncs-stone);
}

/* Scenario label-filter strip — a tidy filter bar above the scenario tree.
   Standard filter-chip patterns: uniform pill height, a leading check on the
   active chip, soft fade (not grayscale) for non-matching chips. Grouping
   follows Gestalt proximity + common region — a theme's near-duplicate
   wordings sit inline right after its head chip behind a thin colour divider,
   with more space *between* themes than *within* one, so morphological
   variants read as a single unit instead of a ragged column stack. */
.scenario-label-strip {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.7rem;            /* row-gap · column-gap — themes spaced apart */
  margin-bottom: 0.7rem;
  padding: 0.6rem 0.75rem;
  background: linear-gradient(var(--ncs-pale), color-mix(in srgb, var(--ncs-pale) 55%, white));
  border: 1px solid var(--ncs-rule);
  border-radius: 14px;
}

/* Sidebar placement: chips sit directly on the cream rail — no inner card
   surface (the gradient panel + border would be a card-on-card). */
.scenario-strip-sidebar .scenario-label-strip {
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  max-height: calc(100vh - 13rem);
  overflow-y: auto;
}

/* Scenario dropdown filter in the sidebar — full-width rounded rectangle, NOT
   the giant oval the row styles produce. The row's `.search-filter { flex: 0 1
   22rem }` would otherwise set a 22rem flex-basis on the sidebar column's
   vertical axis (→ a ~352px-tall box) and `.workspace-filter { border-radius:
   999px }` rounds that into a circle. Override both here. Scoped by id (not a
   child combinator) so it keeps working wherever it nests under the sidebar. */
.app-sidebar #scenario-filter-wrap {
  flex: 0 0 auto;
  width: 100%;
  min-width: 0;
  height: auto;
  margin: 0;
  border-radius: 10px;
  /* Flat outlined control on the cream rail — drop the white pill surface. */
  background: transparent;
  border: 1px solid var(--ncs-rule);
}
.app-sidebar #scenario-filter-wrap .scenario-filter-select {
  width: 100%;
  max-width: none;
}
/* One theme: head chip + its variants, kept on a single baseline. The inner
   gap is tighter than the strip gap, so a theme reads as one unit. */
.scenario-label-group {
  --group-color: var(--ncs-7);
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem;
}
.scenario-label-variants {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0;
  padding-left: 0.4rem;
  border-left: 2px solid color-mix(in srgb, var(--group-color) 45%, white);
}

/* Base chip — a uniform pill. */
.scenario-label-filter {
  --chip-color: var(--ncs-7);
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  height: 1.7rem;
  padding: 0 0.62rem;
  border-radius: 999px;
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
  color: color-mix(in srgb, var(--chip-color) 72%, black);
  background: color-mix(in srgb, var(--chip-color) 14%, white);
  border: 1px solid color-mix(in srgb, var(--chip-color) 30%, white);
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease,
              border-color 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
}
.scenario-label-filter:hover {
  border-color: var(--chip-color);
  box-shadow: 0 2px 9px -4px color-mix(in srgb, var(--chip-color) 65%, transparent);
}
.scenario-label-filter:focus-visible {
  outline: 2px solid var(--chip-color);
  outline-offset: 2px;
}
.scenario-label-filter-text {
  max-width: 13rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Bold-diff: within a variant chip only the words differing from the head
   label are <b> — the shared stem stays quiet, the eye lands on what's new. */
.scenario-label-filter-text b {
  font-weight: 700;
  color: var(--ncs-ink);
}

/* Linked brushing (label comparison): hovering ANY label chip fills every
   chip that carries the same canonical label — across cluster cards, snippet
   groups, result cards, the TOC and the strip. On-demand exact matching in
   the state colour; nothing is painted until the user asks by pointing. */
.scenario-chip.chip-linked,
.toc-chip.chip-linked,
.scenario-label-filter.chip-linked {
  background: var(--ncs-7);
  border-color: var(--ncs-7);
  color: #fff;
}
.chip-linked b { color: inherit; }

/* Variant chip — subordinate to its head: outline style, lighter weight,
   same height so the row stays even. */
.scenario-label-filter--variant {
  font-weight: 500;
  background: transparent;
  border-color: color-mix(in srgb, var(--chip-color) 24%, white);
  color: color-mix(in srgb, var(--chip-color) 58%, black);
}

/* Active — part of the current filter: filled, with a leading check (the
   Material filter-chip convention for "selected"). */
.scenario-label-filter.active {
  background: var(--chip-color);
  border-color: var(--chip-color);
  color: white;
}
.scenario-label-filter.active::before {
  content: "✓";
  font-size: 0.72rem;
  font-weight: 800;
  margin-left: -0.05rem;
}
/* The exact chip the user clicked — ringed so it stands out from the labels
   that merely co-occur on the still-visible scenarios. */
.scenario-label-filter.selected {
  outline: 2px solid var(--chip-color);
  outline-offset: 2px;
}

/* Count badge — a quiet secondary pill. */
.scenario-label-filter-count {
  min-width: 1.05rem;
  padding: 0 0.3rem;
  border-radius: 999px;
  text-align: center;
  font-size: 0.68rem;
  font-weight: 700;
  background: color-mix(in srgb, var(--chip-color) 16%, white);
  color: color-mix(in srgb, var(--chip-color) 70%, black);
}
.scenario-label-filter.active .scenario-label-filter-count {
  background: hsla(0, 0%, 100%, 0.25);
  color: white;
}

/* When a filter is on, fade the chips that don't co-occur — opacity only,
   no harsh grayscale. Hover restores any of them. */
.scenario-label-strip.has-active
  .scenario-label-filter:not(.active):not(.scenario-label-filter--reset) {
  opacity: 0.4;
}
.scenario-label-strip.has-active
  .scenario-label-filter:not(.active):not(.scenario-label-filter--reset):hover {
  opacity: 1;
}

/* Reset ("Wszystkie") — a neutral leading chip; filled when no filter is on. */
.scenario-label-filter--reset {
  --chip-color: var(--ncs-stone, #6b7280);
  background: white;
  color: var(--ncs-1);
  border-color: var(--ncs-rule);
  font-weight: 700;
}
.scenario-label-filter--reset.active {
  background: var(--ncs-1);
  color: white;
  border-color: var(--ncs-1);
}
.scenario-label-filter--reset.active::before { content: none; }
.scenario-label-filter--reset:hover {
  color: var(--ncs-6);
  border-color: var(--ncs-6);
  box-shadow: none;
}
.scenario-label-filter--reset.active:hover { color: white; }

/* Scenario compare ("git diff") — selection state + action bar.
   The bar lives inside `.scenario-board` (a grid) so it must span all
   columns; otherwise it would occupy a single cell. */
.scenario-compare-bar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.25rem;
  padding: 0.55rem 0.9rem;
  background: rgba(111, 74, 126, 0.07);
  border: 1px solid rgba(111, 74, 126, 0.25);
  border-radius: 10px;
}
.scenario-compare-bar-label {
  font-size: 0.88rem;
  color: var(--ncs-6);
}
.scenario-compare-bar-label strong { font-weight: 700; }
.scenario-compare-bar-btn {
  margin-left: auto;
  padding: 0.4rem 1rem;
  background: var(--ncs-6);
  color: white;
  border: 1px solid var(--ncs-6);
  border-radius: 999px;
  font: inherit;
  font-weight: 600;
  font-size: 0.86rem;
  cursor: pointer;
  transition: transform 120ms ease, opacity 120ms ease;
}
.scenario-compare-bar-btn:hover:not(:disabled) { transform: translateY(-1px); }
.scenario-compare-bar-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.scenario-compare-bar-clear {
  padding: 0.4rem 0.85rem;
  background: white;
  color: var(--ncs-1);
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  font: inherit;
  font-size: 0.84rem;
  cursor: pointer;
}
.scenario-compare-bar-clear:hover { border-color: var(--ncs-6); }

.scenario-compare-btn {
  flex: 0 0 auto;
  padding: 0.18rem 0.55rem;
  background: white;
  color: var(--ncs-6);
  border: 1px solid rgba(111, 74, 126, 0.4);
  border-radius: 999px;
  font: inherit;
  font-size: 0.72rem;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.scenario-compare-btn:hover {
  background: rgba(111, 74, 126, 0.08);
  border-color: var(--ncs-6);
}
.scenario-card.comparing {
  border-color: var(--ncs-6);
  background: rgba(111, 74, 126, 0.06);
  box-shadow: 0 0 0 2px rgba(111, 74, 126, 0.18);
}
.scenario-card.comparing .scenario-compare-btn {
  background: var(--ncs-6);
  color: white;
  border-color: var(--ncs-6);
}

.scenario-filter-select {
  appearance: none;
  -webkit-appearance: none;
  background: #fff;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  padding: 4px 28px 4px 10px;
  font: inherit;
  font-size: 0.85rem;
  color: #0f172a;
  cursor: pointer;
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background-image:
    linear-gradient(45deg, transparent 50%, #64748b 50%),
    linear-gradient(135deg, #64748b 50%, transparent 50%);
  background-position:
    calc(100% - 14px) 50%,
    calc(100% - 9px) 50%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}

.scenario-filter-select option {
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scenario-filter-select:focus {
  outline: 2px solid #2563eb;
  outline-offset: 1px;
}

.card-head .id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem;
  color: #94a3b8;
}

.card-head .link {
  font-size: 0.8rem;
  color: #2563eb;
  text-decoration: none;
  margin-left: auto;
}

.card-head .link:hover {
  text-decoration: underline;
}

.card-head .link.open {
  color: var(--ncs-5);
  font-weight: 600;
}

.card .liskus-frame {
  display: block;
  width: 100%;
  margin-top: 1rem;
}

.card .liskus-frame .liskus-frame-inner {
  width: 100%;
  height: 70vh;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  background: var(--ncs-pale);
}

/* Card body — facts go on top, SF below, DNA sticks on the right. Order
   change from the original "text then facts" so the schemat comparison
   (the actionable per-step verdicts) sits above the long raw SF a reader
   only consults when a verdict looks off. */
.card-body {
  display: grid;
  /* The schema comparison panel sits ABOVE the ruling text, full width, and is
     always visible when a result is opened; the graph column is reserved only
     when actually shown. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "question"
    "facts"
    "text";
  gap: 1rem 1.25rem;
  align-items: start;
}
/* Reserve the graph column ONLY when the graph is actually shown. */
.card-body:has(.graph-col:not([hidden])) {
  grid-template-columns: minmax(0, 1fr) 240px;
  grid-template-areas:
    "question graph"
    "facts    graph"
    "text     graph";
}

/* ALP-131 reader-first layout, active while the rail is revealed (JS shows
   it with the full text): the ENTIRE interpretacja on the left; questions
   (rail) and the schemat comparison (facts) stacked on the right. The
   ALP-119 question row is display:none here (has-fulltext), so it needs no
   area. Without full text the base stacked layout above applies. */
/* Three tiles SIDE BY SIDE in one row: the ruling (large, flexible) then the
   two smaller tiles — PYTANIA and PORÓWNANIE. The comparison gets the wider
   of the two because its rows are a step|verdict pair. */
.card-body:has(.card-rail:not([hidden])) {
  grid-template-columns: minmax(0, 1fr) 19rem 21rem;
  grid-template-rows: minmax(0, 1fr);
  grid-template-areas: "text rail facts";
}
.card-body:has(.card-rail:not([hidden])):has(.graph-col:not([hidden])) {
  grid-template-columns: minmax(0, 1fr) 19rem 21rem 240px;
  grid-template-areas: "text rail facts graph";
}
.card-body .card-rail        { grid-area: rail; }

.card-body .card-question    { grid-area: question; }
.card-body .text             { grid-area: text; }

/* ALP-119 — the ruling's question above the SF. Collapsed = a single truncated
   line (JS caps it to ~14 words + …); click to expand to the full question. */
.card-question {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  width: 100%;
  margin: 0;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--ncs-rule);
  border-left: 3px solid var(--ncs-7);
  border-radius: 8px;
  background: var(--ncs-pale);
  font: inherit;
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--ncs-9);
  text-align: left;
  cursor: pointer;
}
.card-question[hidden] { display: none; }
/* No doubled question: once the card shows the full interpretacja, the right
   rail lists ALL the ruling's questions — the ALP-119 question row is fully
   redundant there and hides. Cards without full text (workspace/draft clones,
   fetch fallback) keep the row. */
.card.has-fulltext .card-question { display: none; }

/* ALP-131: the rail itself — quiet stacked panels, sticky beside the long
   reader so question + digest stay in view while the document scrolls. */
.card-rail {
  align-self: start;
  position: sticky;
  top: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  max-height: 80vh;
  overflow-y: auto;
}
.card-rail[hidden] { display: none; }
.card-rail-section {
  padding: 0.6rem 0.75rem;
  background: var(--ncs-pale);
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
}
.card-rail-title {
  margin: 0 0 0.35rem;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ncs-stone);
}
.card-rail-body {
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--ncs-ink);
}
.card-rail-body p { margin: 0 0 0.5rem; }
.card-rail-body p:last-child { margin-bottom: 0; }
.card-rail-question + .card-rail-question {
  border-top: 1px solid var(--ncs-rule);
  padding-top: 0.5rem;
}
/* ALP-137: a rail question is a jump-link into the reader — button reset back
   to the plain question paragraph look, plus the dotted jump affordance the
   comparison quotes use. */
.card-rail-question--linked {
  display: block;
  width: 100%;
  margin: 0 0 0.5rem;
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  text-decoration: underline dotted transparent;
  text-underline-offset: 2px;
  transition: text-decoration-color 120ms ease;
}
.card-rail-question--linked:hover:not(:disabled) {
  background: none;
  transform: none;
  filter: none;
  text-decoration-color: var(--ncs-7);
}
.card-rail-question--linked + .card-rail-question--linked {
  border-top: 1px solid var(--ncs-rule);
  padding-top: 0.5rem;
}

/* The landing flash: an overlay box per line of the matched passage — never a
   DOM edit, so the reader's query <mark>s and fact-marks stay intact. */
.reader-flash {
  position: absolute;
  z-index: 1;
  border-radius: 3px;
  background: hsl(var(--scenario-hue, 210), 85%, 62%);
  opacity: 0;
  pointer-events: none;
  animation: reader-flash 1.5s ease both;
}
@keyframes reader-flash {
  0%   { opacity: 0; }
  15%  { opacity: 0.38; }
  70%  { opacity: 0.28; }
  100% { opacity: 0; }
}

/* PYTANIA fold/unfold — a quiet text button under the (first) question. */
.card-rail-qtoggle {
  margin: 0.35rem 0 0;
  padding: 0.1rem 0.3rem;
  background: none;
  border: 0;
  border-radius: 6px;
  box-shadow: none;
  font: inherit;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--ncs-7);
  cursor: pointer;
}
.card-rail-qtoggle:hover:not(:disabled) {
  background: color-mix(in srgb, var(--ncs-7) 12%, transparent);
  transform: none;
  filter: none;
}
/* Cluster › sub-cluster breadcrumb in the rail — quiet reading context. */
.card-rail-path { font-size: 0.78rem; }
.card-rail-crumb { font-weight: 600; }
.card-rail-crumb-sep { color: var(--ncs-stone); }
.card-rail-body mark {
  background: hsl(var(--scenario-hue, 210), 72%, 88%);
  color: inherit;
  border-radius: 3px;
  padding: 0 0.1em;
}

/* Tablica (ALP-131): the pinned card is the user's working copy — their own
   note leads the record, above the document. Flex order only; DOM unchanged
   (the note stays where comment syncing expects it). */
.view--workspace .results .card {
  display: flex;
  flex-direction: column;
}
.view--workspace .results .card .comment-section { order: -1; }
.card-question:hover { border-color: var(--ncs-7); }
.card-question-text { flex: 1; min-width: 0; }
.card-question:not(.expanded) .card-question-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Expanded: every question on its own line. */
.card-question.expanded .card-question-text {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.card-question-item { display: block; }
.card-question-item::before {
  content: "▸ ";
  color: var(--ncs-7);
}
/* [+N] badge for the other questions when collapsed. */
.card-question-more {
  flex: none;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ncs-7);
}
.card-question-more:empty { display: none; }
.card-question-caret {
  flex: none;
  color: var(--ncs-7);
  transition: transform 120ms ease;
}
.card-question.expanded .card-question-caret { transform: rotate(180deg); }

/* Snippet [+N] badge (question line under the SF). */
.snippet-question-more {
  font-weight: 600;
  color: var(--ncs-7);
  font-style: normal;
}
.card-body .points-col       { grid-area: facts; }
.card-body .graph-col        { grid-area: graph; }

@media (max-width: 980px) {
  .card-body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "facts"
      "text";
  }
  .card-body .graph-col {
    display: none;
  }
}

.graph-col {
  position: sticky;
  top: 1rem;
}

/* The SF section is now the FULL reassembled text (raw Liskus JSON via
   `_resolve_raw_fs`), often many KB — keep the cell short and let it
   scroll in place so a single long ruling doesn't dwarf the rest of the
   card. The graph column is sticky, so the facts/DNA stay aligned while
   the reader scrolls the text. */
.card .text {
  margin: 0;
  line-height: 1.5;
  white-space: pre-wrap;
  max-height: 22rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.card .text mark,
.card .points mark {
  background: #FFF1A8;
  color: inherit;
  padding: 0 2px;
  border-radius: 3px;
  box-shadow: inset 0 -2px 0 rgba(212, 153, 38, 0.35);
}

/* Inline fact markers — colored ribbon under each fact-related span,
   matching the fact's bullet/graph color. Adds emphasis on hover so the
   matching paragraph stands out as the user scans facts. */
.fact-mark {
  background-color: var(--fact-color, transparent);
  background-image: linear-gradient(180deg, transparent 60%, var(--fact-color, transparent) 60%);
  background-size: 100% 100%;
  background-position: 0 100%;
  background-repeat: no-repeat;
  border-radius: 2px;
  padding: 0 1px;
  transition: background-color 140ms ease, color 140ms ease, padding 140ms ease;
  cursor: default;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.fact-mark.hovered {
  background-image: none;
  background-color: var(--fact-color, #FFF1A8);
  color: white;
  padding: 0 4px;
  border-radius: 4px;
}

.points-col {
  margin-top: 0.25rem;
}

/* ---------- Selection (+ → ✓) ---------- */

/* Selection "ticker": a leading checkbox column on each result card. Wired to
   the same `toggleSelection` mechanism as the old "+" button; reuses the
   `.card.selected` state to render the tick. */
.select-tick {
  align-self: center;
  margin: 0;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  position: relative;
  z-index: 1;
  border: 2px solid var(--ncs-rule);
  border-radius: 6px;
  background: white;
  color: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.select-tick::before { content: '\2713'; }
.select-tick:hover {
  border-color: #2EAA5E;
  background: rgba(46, 170, 94, 0.08);
}
.select-tick.is-selected {
  background: #2EAA5E;
  border-color: #2EAA5E;
  color: white;
}
.select-tick.is-selected:hover { background: #28934F; }

.add-btn {
  margin-left: auto;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: rgba(43, 108, 176, 0.08);
  color: var(--ncs-7);
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  transition: background-color 120ms ease, color 120ms ease, transform 120ms ease;
}

.add-btn::before { content: '+'; }
.add-btn:hover {
  background: rgba(43, 108, 176, 0.18);
  transform: scale(1.06);
}

.card.selected .add-btn {
  background: #2EAA5E;
  color: white;
  box-shadow: 0 1px 3px rgba(46, 170, 94, 0.45);
}
.card.selected .add-btn::before { content: '\2713'; } /* ✓ */
.card.selected .add-btn:hover {
  background: #28934F;
}

/* "Dodaj do porównania" — a second, independent pick on each card,
   distinct from the green workspace pin. Picked cards get a purple ring
   and a filled pill. */
/* "Dodaj do porównania" buttons hidden by request — the comparison feature
   stays fully wired (markup + handlers intact), it's just not surfaced. */
.compare-btn,
.scenario-compare-btn { display: none !important; }

.compare-btn {
  height: 28px;
  margin-left: 0.25rem;
  padding: 0 0.7rem;
  border: 0;
  border-radius: 999px;
  background: rgba(111, 74, 126, 0.1);
  color: var(--ncs-6);
  cursor: pointer;
  font-size: 0.78rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  white-space: nowrap;
  transition: background-color 120ms ease, color 120ms ease, transform 120ms ease;
}
.compare-btn:hover {
  background: rgba(111, 74, 126, 0.22);
  transform: scale(1.04);
}
.card.comparing {
  outline: 2px solid var(--ncs-6);
  outline-offset: -2px;
}
.card.comparing .compare-btn {
  background: var(--ncs-6);
  color: white;
  box-shadow: 0 1px 3px rgba(111, 74, 126, 0.45);
}
.card.comparing .compare-btn:hover { background: #5b3d68; }

/* Three-pane deck. The .container shows one .view at a time; the workspace
   controller flips `data-view` between search / workspace / draft.
   Every pane is hidden by default and the active one un-hidden.
   Each pane animates in from the side matching the arrow that opened it. */
/* The search bar (`view--search`) is now always visible regardless of
   data-view mode — workspace / draft keep the search bar reachable at the
   top. The mode-specific panes below toggle on their data-view attribute. */
.view--workspace,
.view--draft,
.view--details,
.view--snippets,
.view--scenarios,
.view--dna { display: none; }

.container[data-view="workspace"]   .view--workspace,
.container[data-view="draft"]       .view--draft { display: block; }

/* Layer-driven panes stack BELOW the always-visible search section: the
   Overview kanban (data-zoom="compact") or the Scenarios board
   (data-zoom="scenarios") sit beneath view--search. The layer switcher
   floats in the corner rather than separating them inline. view--search
   itself is no longer hidden at these layers — the deck stays on screen so
   the user always has the filter row + results within reach. Workspace /
   draft remain hidden since those are separate data-view modes. */
/* Only the result deck (`.view--details`) is gated by the zoom layer — it
   shows solely at data-view="search" + data-zoom="medium" (rule below). The
   Workspace / Draft panes are pure data-view modes and must NOT be hidden by
   the current zoom, otherwise opening the Workspace from the scenario outline
   (data-zoom="scenarios") lands on an empty screen. So they are controlled
   only by the data-view rule above. */
.container[data-zoom="scenarios"] .view--details,
.container[data-zoom="snippets"]  .view--details { display: none; }
/* view--details is the result deck (the cards). It hides outside
   data-view="search" (workspace / draft modes don't show
   the deck) and outside zoom="medium" (scenarios / overview replace it). */
.container[data-view="search"][data-zoom="medium"] .view--details { display: block; }
.container[data-view="search"][data-zoom="snippets"] .view--snippets { display: block; }
.container[data-zoom="compact"]  .view--dna     { display: block; }
.container[data-zoom="scenarios"] .view--scenarios { display: block; }

/* Breathing room between the always-on search section and whichever layer
   pane is shown beneath it. The old .zoom-bar used to provide this break;
   since the layer switcher moved to the sidebar, the panes need their own
   top rule + air so the two blocks don't butt together. */
.container[data-view="search"][data-zoom="medium"] .view--details,
.container[data-view="search"][data-zoom="snippets"] .view--snippets,
.container[data-zoom="compact"]  .view--dna,
.container[data-zoom="scenarios"] .view--scenarios {
  margin-top: 2.25rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--ncs-rule);
}

/* The deck-only widgets ("Przestrzeń robocza" arrow + comparisons
   sidebar) now live inside .view--details, so they hide automatically
   with the deck at non-Details zoom — no extra rule needed. */

.container[data-view="workspace"]   .view--workspace   { animation: view-slide-in-right 220ms ease both; }
.container[data-view="draft"]       .view--draft       { animation: view-slide-in-right 220ms ease both; }
.container[data-view="search"][data-zoom="medium"] .view--details { animation: view-slide-in-left 220ms ease both; }
.container[data-view="search"][data-zoom="snippets"] .view--snippets { animation: view-slide-in-left 220ms ease both; }
.container[data-zoom="compact"]     .view--dna         { animation: view-slide-in-right 220ms ease both; }
.container[data-zoom="scenarios"]    .view--scenarios    { animation: view-slide-in-right 220ms ease both; }

@keyframes view-slide-in-right {
  from { opacity: 0; transform: translateX(28px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes view-slide-in-left {
  from { opacity: 0; transform: translateX(-28px); }
  to   { opacity: 1; transform: translateX(0); }
}

/* --- Scenario → Details "zoom in" --------------------------------------- *
   Clicking a scenario card (factual-state#openScenarioFacts) briefly swells
   that card and recedes its neighbours, then the Details deck fades in. The
   container carries `.scenario-zooming` for the duration of the transition. */
.scenario-card {
  transition: transform 110ms ease, opacity 110ms ease, box-shadow 110ms ease;
}
.view--scenarios.is-zooming .scenario-card {
  opacity: 0.25;
  transform: scale(0.96);
}
.view--scenarios.is-zooming .scenario-card.is-opening {
  opacity: 1;
  transform: scale(1.12);
  box-shadow: 0 18px 48px rgba(27, 27, 31, 0.22);
  z-index: 2;
}

/* While zooming, the incoming Snippets layer just fades in — NOT a
   transform: scale. The "zoom" cue is carried by the scenario card swell
   above. Extra `.scenario-zooming` class outranks the slide rule above. */
.container.scenario-zooming[data-view="search"][data-zoom="snippets"] .view--snippets {
  animation: scenario-fade-in 120ms ease both;
}
@keyframes scenario-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .scenario-card,
  .view--scenarios.is-zooming .scenario-card,
  .container.scenario-zooming[data-view="search"][data-zoom="snippets"] .view--snippets {
    transition: none;
    animation: none;
  }
}

/* --- Snippets layer ---------------------------------------------------- *
   Between Scenarios and Details: each ruling as a one-line card (signature +
   its most query-relevant sentence), grouped by scenario. Clicking a card
   drops into Details focused on that ruling. */
.snippet-board {
  display: flex;
  flex-direction: column;
  gap: 1.4rem;
  padding: 0.5rem 0 1rem;
}

/* Each scenario group is a card in the same family as .scenario-card: white
   surface, hairline border, a 4px left edge in the scenario's hue, rounded. */
.snippet-group {
  --scenario-hue: 210;
  scroll-margin-top: 1rem;
  background: var(--ncs-paper);
  border: 1px solid var(--ncs-rule);
  border-left: 4px solid hsl(var(--scenario-hue), 55%, 58%);
  border-radius: 8px;
  padding: 1rem 1.1rem;
  transition: box-shadow 140ms ease, border-color 140ms ease, background-color 140ms ease;
}

/* The scenario the user drilled in from (Scenarios → Snippets) stays
   persistently highlighted in its own hue so it's easy to spot in the list. */
.snippet-group--selected {
  border-color: hsl(var(--scenario-hue), 55%, 58%);
  background: hsl(var(--scenario-hue), 60%, 97%);
  box-shadow: 0 0 0 2px hsl(var(--scenario-hue), 60%, 58%),
              0 10px 26px -10px hsla(var(--scenario-hue), 50%, 40%, 0.45);
}
.snippet-group-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.6rem;
}
.scenario-chip--none {
  background: var(--ncs-cream);
  color: var(--ncs-stone);
  border-color: var(--ncs-rule);
}

/* Ruling rows inside a group — quiet by default, lift to a white scenario-hued
   row on hover/focus (mirrors .scenario-card:hover). */
.snippet-list { display: flex; flex-direction: column; gap: 0.45rem; }

.snippet-card {
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
}
.snippet-card:hover,
.snippet-card:focus-visible {
  background: white;
  border-color: hsl(var(--scenario-hue), 55%, 62%);
  outline: none;
  transform: translateX(2px);
}
.snippet-right-controls {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  flex: 0 0 auto;
}
.snippet-schema-ok {
  flex: 0 0 auto;
  color: #2f9e44;
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
}
.snippet-result-tick {
  flex: 0 0 auto;
}

/* ALP-132: the publication date trails the signature — same monospace line,
   quieter, so the signature stays the primary identifier. */
.snippet-date {
  margin-left: 0.45rem;
  color: var(--ncs-stone);
  font-weight: 500;
}
.snippet-sig {
  flex: none;
  width: auto;
  max-width: none;
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ncs-stone);
  /* Always show the full signature — no truncation. */
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
.snippet-text {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 0.88rem;
  color: var(--ncs-ink);
  /* Show more of the snippet: wrap across several lines instead of a single
     ellipsised line. */
  white-space: normal;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
}
/* ALP-129: the ruling's question LEADS (above the SF), formatted like the
   factual state — same ink/size/wrap — so they read as one consistent pair,
   stacked in a column above the signature/controls row. */
.snippet-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.snippet-main > .snippet-text,
.snippet-main > .snippet-card-line { flex: 0 0 auto; }
/* ALP-129 follow-up: a hairline rule divides the question from the SF beneath
   it. Matches only when a question line precedes the SF — a question-less card
   (SF as the lead line) keeps no divider. In the outline variant the question
   sits inside .snippet-card-line, hence the second selector. */
.snippet-main > .snippet-question + .snippet-text,
.snippet-main > .snippet-card-line + .snippet-text {
  border-top: 1px solid var(--ncs-rule);
  padding-top: 0.25rem;
}
.snippet-question {
  margin: 0;
  font-size: 0.88rem;
  color: var(--ncs-ink);
  /* Match the SF: wrap across a few lines instead of a single ellipsised one. */
  white-space: normal;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
}
.snippet-text mark,
.snippet-question mark {
  background: hsl(var(--scenario-hue), 72%, 88%);
  color: inherit;
  border-radius: 3px;
  padding: 0 0.1em;
}
.snippet-empty { color: var(--ncs-stone); }

/* ALP-120: the authority's verdict on the applicant's position, carried on the
   card / snippet root as `data-ruling-outcome` and read off it by CSS alone —
   the verdict belongs to the RULING, so every question inside inherits it and
   no re-render of the question list can drop the colour.

   A left rule + the question's own ink, NOT a filled background: these sit
   inside dense result lists where a wash of green and red would fight the
   scenario chips and the query highlight for attention. Colours come from the
   established palette (leaf green / red-magenta / gold), so the page keeps one
   set of hues; `czesciowo` is amber because it is a genuine third state — the
   ruling answered some questions each way — not a weaker red. */
:root {
  --outcome-ok: var(--ncs-5);       /* prawidlowe     — in favour            */
  --outcome-bad: var(--ncs-2);      /* nieprawidlowe  — against              */
  --outcome-partial: var(--ncs-4);  /* czesciowo      — partly each way      */
}

.snippet-question,
.card-question,
.card-rail-question {
  /* Reserved even when no verdict is known, so a coloured row and an
     uncoloured one keep identical text alignment. */
  border-left: 3px solid transparent;
  padding-left: 0.5rem;
}
/* The card question is a button with its own box; only take over the rule. */
.card-question { border-left-width: 3px; }

[data-ruling-outcome="prawidlowe"] .snippet-question,
[data-ruling-outcome="prawidlowe"] .card-question,
[data-ruling-outcome="prawidlowe"] .card-rail-question {
  border-left-color: var(--outcome-ok);
}
[data-ruling-outcome="nieprawidlowe"] .snippet-question,
[data-ruling-outcome="nieprawidlowe"] .card-question,
[data-ruling-outcome="nieprawidlowe"] .card-rail-question {
  border-left-color: var(--outcome-bad);
}
[data-ruling-outcome="czesciowo"] .snippet-question,
[data-ruling-outcome="czesciowo"] .card-question,
[data-ruling-outcome="czesciowo"] .card-rail-question {
  border-left-color: var(--outcome-partial);
}

/* Rulings the miner judged irrelevant — muted row, grey flag, sunk to the
   bottom of their scenario group (same verdict that hides their Details card
   in the "irrelevant" pile). */
.snippet-card--irrelevant {
  opacity: 0.55;
}
.snippet-card--irrelevant .snippet-sig { color: var(--ncs-stone); }
.snippet-card--irrelevant .snippet-text mark,
.snippet-card--irrelevant .snippet-question mark {
  background: var(--ncs-cream);
}
.snippet-card--irrelevant:hover,
.snippet-card--irrelevant:focus-visible {
  opacity: 1;
  border-color: var(--ncs-rule);
}
.snippet-flag {
  flex: none;
  align-self: center;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ncs-stone);
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  white-space: nowrap;
}

/* Relevance feedback — 👍/👎 per snippet row. Quiet until the row is
   hovered; a saved verdict keeps its button lit (green = relevant,
   red-magenta = irrelevant, per the NCS palette). */
.snippet-feedback {
  flex: none;
  align-self: center;
  display: inline-flex;
  gap: 0.25rem;
}
.fb-btn {
  width: 1.7rem;
  height: 1.7rem;
  padding: 0;
  margin: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  background: white;
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: none;
  opacity: 0.35;
  transition: opacity 120ms ease, border-color 120ms ease, background 120ms ease;
}
.snippet-card:hover .fb-btn,
.snippet-card:focus-within .fb-btn,
.card:hover .fb-btn,
.card:focus-within .fb-btn,
.fb-btn.is-on { opacity: 1; }
.fb-btn:hover { border-color: var(--ncs-stone); }

/* In card heads the buttons sit at the end of the action row; keep them
   compact so they don't crowd the existing chrome. */
.card-head .fb-btn { flex: none; align-self: center; }

/* Visible verdict badge — shown in every card head (Details, Workspace,
   Draft clones) once the user has judged the ruling. */
.fb-badge {
  flex: none;
  align-self: center;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 0.12rem 0.55rem;
  white-space: nowrap;
}
.fb-badge--relevant {
  color: var(--ncs-5);
  background: color-mix(in srgb, var(--ncs-5) 14%, white);
  border: 1px solid color-mix(in srgb, var(--ncs-5) 45%, transparent);
}
.fb-badge--irrelevant {
  color: var(--ncs-2);
  background: color-mix(in srgb, var(--ncs-2) 12%, white);
  border: 1px solid color-mix(in srgb, var(--ncs-2) 40%, transparent);
}
.fb-btn--up.is-on {
  border-color: var(--ncs-5);
  background: color-mix(in srgb, var(--ncs-5) 18%, white);
}
.fb-btn--down.is-on {
  border-color: var(--ncs-2);
  background: color-mix(in srgb, var(--ncs-2) 16%, white);
}

/* Inline note form, inserted between rows below the judged snippet. */
.snippet-feedback-form {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin: 0.1rem 0 0.3rem;
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 6px;
  background: white;
}
.snippet-feedback-form-verdict {
  flex: none;
  align-self: center;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
}
.snippet-feedback-form-verdict--relevant   { color: var(--ncs-5); }
.snippet-feedback-form-verdict--irrelevant { color: var(--ncs-2); }
.snippet-feedback-note {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 6px;
  font: inherit;
  font-size: 0.85rem;
  resize: vertical;
  background: var(--ncs-pale);
}
.snippet-feedback-send,
.snippet-feedback-cancel {
  flex: none;
  align-self: center;
  padding: 0.4rem 0.8rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  background: var(--ncs-cream);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: none;
}
.snippet-feedback-send {
  border-color: var(--ncs-7);
  color: var(--ncs-7);
  background: white;
}
.snippet-feedback-send:disabled { opacity: 0.5; cursor: wait; }
.snippet-feedback-status {
  flex: none;
  align-self: center;
  font-size: 0.78rem;
  color: var(--ncs-2);
}

@media (max-width: 720px) {
  .snippet-card { flex-direction: column; gap: 0.2rem; }
  .snippet-sig { width: auto; }
  .snippet-text { white-space: normal; }
}

/* Locale switcher — small fixed PL/EN links in the top-right corner. */
.locale-switcher {
  position: fixed;
  top: 0.75rem;
  right: 1rem;
  z-index: 50;
  display: flex;
  gap: 0.2rem;
  padding: 0.2rem 0.3rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  background: var(--ncs-pale);
}
.locale-switcher-link {
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-decoration: none;
  color: var(--ncs-stone);
}
.locale-switcher-link:hover { color: var(--ncs-1); }
.locale-switcher-link.is-active {
  background: var(--ncs-7);
  color: white;
}

/* Brief highlight when a layer transition focuses a target. */
.snippet-group--flash,
.card--flash {
  animation: focus-flash 1.2s ease both;
}
@keyframes focus-flash {
  0%   { box-shadow: 0 0 0 0 rgba(43, 108, 176, 0.45); }
  30%  { box-shadow: 0 0 0 4px rgba(43, 108, 176, 0.25); }
  100% { box-shadow: 0 0 0 0 rgba(43, 108, 176, 0); }
}

/* Keep the search / console pinned to the top of the scrolling view stack so
   it's always visible while results scroll beneath it. `.view--search` is a
   direct child of the `.app-views` scroll container, so sticky works across the
   whole scroll. The paper background masks content scrolling under it. */
.view--search {
  position: sticky;
  top: 0;
  z-index: 15;
  background: var(--ncs-paper);
}
/* Once a search has started, drop the big "Stanislaw" title from the pinned
   area — only the console needs to stay visible. */
.container[data-started] .view--search > header { display: none; }

/* Search view header — title only now. The workspace arrow + comparisons
   sidebar moved to view--details so they only render at Details zoom. */
.view--search > header {
  text-align: center;
}
.view--search > header h1 {
  margin: 0;
}

/* Layer switcher — a map-style base-layer control docked as a left sidebar.
   All layers are shown stacked; the active one is highlighted. Only
   meaningful while the user is in the "search" data-view; the workspace /
   draft modes hide it (mirrors the old .zoom-bar gating). */
/* A compact box that hovers over the scenario board on the right edge, rather
   than a full-height sidebar that reserves page width. It floats on top of the
   content; the deck stays centred underneath it. Collapsible to an icons-only
   strip (see .is-collapsed) where each tile gets a hover-cloud tooltip. */
/* Floating back / forward — hovers in the top-left corner over every view. */
/* In-flow within the left sidebar (was a floating fixed pill). */
.view-nav {
  align-self: flex-start;
  display: flex;
  gap: 0.3rem;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  transition: opacity 140ms ease;
}
.view-nav:hover,
.view-nav:focus-within {
  opacity: 1;
}
.view-nav-btn {
  appearance: none;
  width: 2rem;
  height: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--ncs-ink, #1b1b1f);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.view-nav-btn:hover:not(:disabled) {
  background: white;
  color: var(--ncs-6, #2b6cb0);
}
.view-nav-btn:focus-visible {
  outline: 2px solid var(--ncs-6, #2b6cb0);
  outline-offset: 2px;
}
.view-nav-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.layer-switcher {
  display: none;
  position: fixed;
  top: 5.5rem;
  right: 1.5rem;
  width: 10.5rem;
  z-index: 40;
  flex-direction: column;
  align-items: stretch;
  gap: 0.35rem;
  padding: 0.85rem 0.8rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 12px;
  background: var(--ncs-pale);
  box-shadow: 0 10px 30px -8px rgba(27, 27, 31, 0.25);
  overflow: visible;
  transition: width 140ms ease, padding 140ms ease;
}

/* Only once a search has started — the Google-style landing shows just the
   sidebar + centered search bar, nothing else. */
.container[data-started][data-view="search"] .layer-switcher { display: flex; }
/* The switcher is a floating box, not a sidebar, so it no longer reserves
   layout width — the deck stays centred (the base .container margin: 2rem
   auto) and the box hovers over its top-left corner. */

/* Workspace head: back-arrow on the left, title centered-ish, kbd hint. */
.workspace-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.75rem;
}
/* Tablica: the head (keyword filters) is parked — hidden but kept in the DOM
   for further prototyping. The stage tabs carry the pane's identity. */
.view--workspace > .workspace-head { display: none; }
.workspace-head h2 {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--ncs-1);
}
.workspace-count {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.55rem;
  background: #2EAA5E;
  color: white;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  vertical-align: 0.1em;
}
/* Keyword filter — pill-shaped input wedged between the workspace title
   and the kbd hint on the right. */
.workspace-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  padding: 0.3rem 0.85rem;
  min-width: 220px;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.workspace-filter:focus-within {
  border-color: var(--ncs-7);
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.18);
}
.workspace-filter-icon {
  color: #9AA0A6;
  flex-shrink: 0;
}
.workspace-filter-input {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-size: 0.9rem;
  padding: 0.25rem 0;
  border: 0;
  background: transparent;
  outline: none;
  color: #1B1B1F;
}
.workspace-filter-input::placeholder { color: #9AA0A6; }

/* Search-view filter row — filter pill + anti-filter pill stacked
   horizontally, both pills inherit the .workspace-filter chrome. */
/* Filter dock — pinned to the bottom of the main column (a flex:none sibling of
   the scrolling `.app-views`), folded by default. */
.filter-dock {
  flex: none;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--ncs-rule);
  /* Parked (hidden but kept in the DOM for prototyping): the bottom filter
     dock is gone from the flow, so .app-views runs to the viewport bottom.
     `!important` beats the JS un-hiding of searchFilterWrap once results
     exist. */
  display: none !important;
}
.filter-dock-toggle {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0.3rem 0.6rem;
  background: none;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  box-shadow: none;
  color: var(--ncs-stone);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0;
  cursor: pointer;
}
.filter-dock-toggle:hover:not(:disabled) {
  background: var(--ncs-cream);
  color: var(--ncs-ink);
  transform: none;
}
.filter-dock-caret { font-size: 0.7rem; transition: transform 120ms ease; }
.filter-dock.is-open .filter-dock-caret { transform: rotate(180deg); }
/* Folded by default: the controls show only once the dock is open. */
.filter-dock:not(.is-open) .filter-row { display: none; }

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0.6rem 0 0.25rem;
}
.search-filter {
  display: inline-flex;
  flex: 0 1 22rem;
  margin: 0;
}

/* Sort-direction toggle — sits next to the "Sortuj" select in the filter
   row, flips ascending / descending. Pill chrome to match the filters. */
.sort-dir-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 2.4rem;
  padding: 0.3rem 0.7rem;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  color: #475569;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.sort-dir-btn:hover {
  border-color: var(--ncs-7);
}
.sort-dir-btn:focus-visible {
  outline: none;
  border-color: var(--ncs-7);
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.18);
}

/* Anti-filter pill — same shape but tinted red to signal "exclude". The
   `--anti` modifier applies in both views (search + workspace header). */
.workspace-filter--anti {
  border-color: #E8B4B4;
  background: #FFF7F7;
}
.workspace-filter--anti:focus-within {
  border-color: #C13B5E;
  box-shadow: 0 0 0 3px rgba(193, 59, 94, 0.18);
}
.workspace-filter--anti .workspace-filter-icon { color: #C13B5E; }
.workspace-filter--anti .workspace-filter-input::placeholder { color: #C98989; }

/* Filter-match highlights painted on the visible workspace cards. Distinct
   from the query-highlight `<mark>` (yellow) and the per-fact `.fact-mark`
   (colored underline) so all three layers can coexist legibly. */
mark.filter-match {
  background: #FFD43B;
  color: #1B1B1F;
  padding: 0 2px;
  border-radius: 3px;
  box-shadow: inset 0 -2px 0 rgba(212, 153, 38, 0.55);
}
mark.filter-match mark,
mark mark.filter-match {
  /* When a token coincides with a query-highlight mark, drop the inner
     background so the outer filter-match stays the dominant cue. */
  background: transparent;
  box-shadow: none;
}
.workspace-filter-input::-webkit-search-cancel-button {
  appearance: none;
  width: 14px;
  height: 14px;
  background: #9AA0A6;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M18 6L6 18M6 6l12 12' stroke='black' stroke-width='3' stroke-linecap='round'/></svg>") center / contain no-repeat;
  cursor: pointer;
}

.workspace-empty {
  margin: 1.5rem 0;
  padding: 1.25rem 1rem;
  background: var(--ncs-pale);
  border: 1px dashed var(--ncs-rule);
  border-radius: 10px;
  color: var(--ncs-1);
  text-align: center;
}
.workspace-empty kbd {
  display: inline-block;
  margin: 0 0.15rem;
  padding: 0.05rem 0.4rem;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85em;
}

/* Draft/presentation pane — no summary card: the deck + its footer toolbar
   ARE the view. Phase / error / empty-state render as quiet lines above. */
.exec-phase {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
  font-size: 0.9rem;
  color: var(--ncs-1);
}
.exec-error { margin-bottom: 0.6rem; }

.exec-empty {
  margin: 0 0 0.75rem;
  color: var(--ncs-stone);
  font-size: 0.92rem;
}

/* Small chrome button (Prezentacja header's "Prezentuj"). */
.md-toolbar-btn {
  appearance: none;
  padding: 0.35rem 0.85rem;
  background: var(--ncs-7);
  color: white;
  border: 1px solid var(--ncs-7);
  border-radius: 999px;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease,
              transform 120ms ease;
}
.md-toolbar-btn:hover { transform: translateY(-1px); background: #1f5894; }
.md-toolbar-btn--ghost {
  background: white;
  color: var(--ncs-1);
  border-color: var(--ncs-rule);
}
.md-toolbar-btn--ghost:hover {
  background: var(--ncs-pale);
  border-color: var(--ncs-7);
  color: var(--ncs-7);
}
.md-toolbar-btn--ok {
  background: #2EAA5E;
  border-color: #2EAA5E;
}
.md-toolbar-btn--err {
  background: #b91c1c;
  border-color: #b91c1c;
}
.md-source {
  display: block;
  width: 100%;
  min-height: 14rem;
  margin-top: 0.6rem;
  padding: 0.75rem 0.9rem;
  background: #0b1020;
  color: #e6edf3;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  font: 0.82rem ui-monospace, SFMono-Regular, Menlo, monospace;
  line-height: 1.55;
  resize: vertical;
  white-space: pre;
  overflow: auto;
}

/* ---------- Markdown document (custom project dialect) ----------
   Rendered by `renderMarkdown` in factual_state_controller.js. Lives in:
   - `.dna-exec` (DNA overview, condensed summary column)
   (The presentation itself is the Richie slide deck — see .prez-deck-frame
   and the richie/slides stylesheet.)
*/
.md-doc {
  --md-text: #1B1B1F;
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--md-text);
}
.md-doc > * + * { margin-top: 0.7rem; }
.md-doc .md-h { margin: 1.2rem 0 0.4rem; font-weight: 700; color: var(--md-text); }
.md-doc .md-h:first-child { margin-top: 0; }
.md-doc .md-h1 { font-size: 1.55rem; letter-spacing: -0.005em; }
.md-doc .md-h2 { font-size: 1.2rem; color: var(--ncs-7); }
.md-doc .md-h3 {
  font-size: 0.86rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #475569;
}
.md-doc .md-p { margin: 0; }
.md-doc .md-list {
  margin: 0;
  padding-left: 1.4rem;
}
.md-doc .md-list li { margin-bottom: 0.3rem; }
.md-doc .md-list li:last-child { margin-bottom: 0; }

.md-doc .md-quote {
  margin: 0;
  padding: 0.6rem 0.9rem;
  background: var(--ncs-pale);
  border-left: 3px solid var(--ncs-7);
  border-radius: 6px;
}
.md-doc .md-quote p { margin: 0; }
.md-doc .md-quote p + p { margin-top: 0.35rem; }
.md-doc .md-quote-cite {
  display: block;
  margin-top: 0.4rem;
  font-style: normal;
  font-size: 0.86rem;
  color: var(--ncs-1);
}

.md-doc .md-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.md-doc .md-table th,
.md-doc .md-table td {
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--ncs-rule);
  text-align: left;
}
.md-doc .md-table th {
  background: var(--ncs-pale);
  font-weight: 700;
}
.md-doc .md-table tr:nth-child(even) td { background: rgba(0, 0, 0, 0.02); }

.md-doc hr {
  margin: 1.1rem 0;
  border: 0;
  border-top: 1px dashed var(--ncs-rule);
}

/* Inline marks. `*x*` → bold, `**x**` → italic (the dialect inverts the
   standard); `__x__` → underline; `==name:text==` → highlight class
   `md-hl-name`. Default + a handful of named variants below. */
.md-doc strong { font-weight: 700; }
.md-doc em { font-style: italic; }
.md-doc u { text-decoration-thickness: 1px; text-underline-offset: 2px; }

.md-doc .md-hl {
  padding: 0 0.25rem;
  border-radius: 3px;
  background: #FFF1A8;
  color: inherit;
  box-shadow: inset 0 -2px 0 rgba(212, 153, 38, 0.35);
}
.md-doc .md-hl-highlight-1 { background: #FFE6A8; box-shadow: inset 0 -2px 0 rgba(212, 130, 38, 0.4); }
.md-doc .md-hl-highlight-2 { background: #C9F0D0; box-shadow: inset 0 -2px 0 rgba(46, 170, 94, 0.45); }
.md-doc .md-hl-highlight-3 { background: #D7E3FB; box-shadow: inset 0 -2px 0 rgba(43, 108, 176, 0.45); }
.md-doc .md-hl-key { background: #FFE6A8; font-weight: 700; }
.md-doc .md-hl-warn { background: #FECACA; color: #7f1d1d; }

/* Strikethrough — agent uses ~~x~~ to mark "the wrong approach" in lists. */
.md-doc s { text-decoration-thickness: 1px; opacity: 0.7; }

/* `[[name:text]]` → coloured run of TEXT (not background). Five canonical
   tokens map to project palette; unknown names fall through to inherit. */
.md-doc .md-color { color: inherit; }
.md-doc .md-color-primary   { color: var(--ncs-7); font-weight: 600; }
.md-doc .md-color-secondary { color: var(--ncs-6); font-weight: 600; }
.md-doc .md-color-accent    { color: #D49926; font-weight: 600; }
.md-doc .md-color-success   { color: #166534; font-weight: 700; }
.md-doc .md-color-danger    { color: #b91c1c; font-weight: 700; }

/* Inline `code` + fenced code blocks. Stage-style dark blocks; the prose
   inline form sits flush against surrounding text in a paler chip. */
.md-doc .md-code {
  padding: 0.05rem 0.35rem;
  background: rgba(27, 27, 31, 0.07);
  border: 1px solid var(--ncs-rule);
  border-radius: 4px;
  font: 0.88em ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #0f172a;
}
.md-doc .md-code-block {
  margin: 0;
  padding: 0.9rem 1rem;
  background: #0b1020;
  color: #e6edf3;
  border-radius: 8px;
  overflow-x: auto;
}
.md-doc .md-code-block code {
  font: 0.85rem ui-monospace, SFMono-Regular, Menlo, monospace;
  line-height: 1.55;
  white-space: pre;
  background: transparent;
  padding: 0;
  border: 0;
  color: inherit;
}

/* Links: subtle until hovered, never garish (NCS-7 blue, dotted underline). */
.md-doc .md-link {
  color: var(--ncs-7);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
}
.md-doc .md-link:hover { text-decoration-style: solid; }

/* Inline image — capped width so a stage-sized chart doesn't blow up the
   reading column. Centered with vertical breathing room. */
.md-doc .md-img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0.5rem auto;
  border-radius: 8px;
  border: 1px solid var(--ncs-rule);
}

/* Standalone "— Author" attribution line. Visually hugs whatever block came
   above (negative top margin pulls it up against the preceding code block /
   table / list), italic + muted so it reads as a footer line. */
.md-doc .md-attribution {
  margin: -0.25rem 0 0 0;
  padding-left: 0.25rem;
  font-style: italic;
  font-size: 0.9rem;
  color: var(--ncs-1);
}

/* The draft view's embedded Richie deck: a standalone stage below the summary
   card (not inside it). The turbo-frame is the sizing box the deck fills
   (.richie-deck.slideshow is height:100% of it). A 4:3 slide, centered —
   width yields when the 62vh height cap binds, so the stage reads as a real
   slide instead of a letterboxed strip; the full-screen "Prezentuj" view is
   the real stage. Hidden until a presentation has been generated (JS toggles
   the [hidden] attribute, which must beat the display:block). */
.prez-deck-frame {
  display: block;
  aspect-ratio: 4 / 3;
  width: min(100%, calc(62vh * 4 / 3));
  margin-inline: auto;
  border: 1px solid var(--ncs-rule);
  /* Square bottom corners — the footer toolbar joins directly beneath. */
  border-radius: 12px 12px 0 0;
  background: #fff;
  overflow: hidden;
}
.prez-deck-frame[hidden] { display: none; }
.prez-deck-frame:focus-within { border-color: var(--ncs-7); }

/* Deck footer toolbar — the presentation's actions live on the stage itself
   (player chrome), not above it. Before a deck exists the bar stands alone
   under the summary card and carries the Generate entry point; once the deck
   renders, frame and bar join into one framed block. */
.prez-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 12px;
  background: var(--ncs-pale);
}
.prez-deck-frame:not([hidden]) + .prez-toolbar {
  border-top: 0;
  border-radius: 0 0 12px 12px;
  /* Match the 4:3 stage's width so frame + bar stay one joined block. */
  width: min(100%, calc(62vh * 4 / 3));
  margin-inline: auto;
}
.prez-toolbar-btn {
  padding: 0.3rem 0.9rem;
  background: none;
  border: 1px solid transparent;
  border-radius: 8px;
  box-shadow: none;
  color: var(--ncs-stone);
  font: inherit;
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
}
.prez-toolbar-btn:hover:not(:disabled) {
  background: white;
  border-color: var(--ncs-rule);
  color: var(--ncs-ink);
  transform: none;
  filter: none;
}
.prez-toolbar-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.prez-toolbar-btn--primary { color: var(--ncs-7); }
.prez-toolbar-btn--primary:hover:not(:disabled) {
  border-color: var(--ncs-7);
  color: var(--ncs-7);
}

/* ---------- Zoom toolbar + density modes ---------- */

/* Child-element styles for the layer switcher (the only place these chrome
   elements live now that the per-view header toolbars are gone). */

/* Panel header — "Warstwy" caption with a stacked-layers glyph, like a GIS
   portal's layers panel. A rule under it separates it from the list. */
.layer-switcher-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0 0.2rem 0.55rem;
  margin-bottom: 0.3rem;
  border-bottom: 1px solid var(--ncs-rule);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ncs-stone);
}
.layer-switcher-icon { flex: none; }
.layer-switcher-icon path {
  fill: none;
  stroke: var(--ncs-7);
  stroke-width: 1.2;
  stroke-linejoin: round;
}

/* A layer row: a legend swatch + label, full width. */
.layer-tile {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: 0.5rem 0.6rem;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  color: var(--ncs-1);
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1;
  text-align: left;
  cursor: pointer;
  box-shadow: none;
  transition: background 120ms ease, border-color 120ms ease;
}

.layer-tile:hover {
  background: var(--ncs-cream);
  filter: none;
  transform: none;
}

/* Active layer = a clean filled row inside the floating box. The highlight
   alone signals selection. */
.layer-tile.is-active {
  border-color: transparent;
  background: var(--ncs-7);
  color: #fff;
}

.layer-thumb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.layer-thumb svg { display: block; }
.layer-thumb svg rect { fill: var(--ncs-stone); }
.layer-tile.is-active .layer-thumb svg rect { fill: #fff; }

.layer-tile-label {
  white-space: nowrap;
}

/* SF (ruling text) is shown BY DEFAULT now that the per-card "Pokaż SF"
   toggle is gone. The graph stays hidden by default. */

/* Per-card "Pokaż SF" toggle in the card head, plus the global "Pokaż
   wszystkie SF" toggle in the search-filter row. Both use the same
   ghost-button look so they read as a paired control. */
.sf-toggle-btn,
.graph-toggle-btn,
.sf-toggle-all-btn {
  appearance: none;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  padding: 0.3rem 0.65rem;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ncs-1);
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.sf-toggle-btn:hover,
.graph-toggle-btn:hover,
.sf-toggle-all-btn:hover {
  border-color: var(--ncs-6);
  background: var(--ncs-pale);
}
/* Per-card SF + graph buttons sit inside `.points-col` below the
   schemat-comparison block, so they read as the section's own actions. */
.points-col .sf-toggle-btn,
.points-col .graph-toggle-btn {
  display: block;
  width: 100%;
  margin-top: 0.55rem;
  padding: 0.35rem 0.6rem;
  font-size: 0.76rem;
}
.card.sf-shown .sf-toggle-btn {
  background: var(--ncs-6);
  color: white;
  border-color: var(--ncs-6);
}

/* Level 2 ("DNA only") no longer shrinks the search cards — it swaps the
   whole deck for the kanban overview (see the data-zoom="compact" rules
   above and the .view--dna / .dna-board styles below). */

.card .points {
  padding: 0.5rem 0.7rem;
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
  border-left: 4px solid var(--ncs-7);
  border-radius: 8px;
  /* ALP-131 follow-up: cap the comparison panel so the full-interpretacja
     reader below stays visible on open — the panel scrolls inside itself
     (ghost scrollbars) instead of pushing the document off-screen. */
  max-height: 30vh;
  overflow-y: auto;
}

.card .points.pending {
  border-left-color: #C0C0C7;
}

.card .points.placeholder {
  border-left-color: var(--ncs-rule);
  background: transparent;
}

.card .points.irrelevant {
  border-left-color: var(--ncs-1);
  background: rgba(232, 160, 74, 0.08);
}

.card .points .note {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  font-style: italic;
  color: #64748b;
}

.card .points .facts {
  margin: 0;
  padding-left: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.card .points .facts li {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  line-height: 1.4;
  font-size: 0.92rem;
  padding: 0.25rem 0.4rem;
  border-radius: 6px;
  transition: background-color 120ms ease, transform 120ms ease;
  cursor: default;
}

/* Numbered NCS-colored disc — color set inline via JS to match the same-
   index graph node and the inline fact-mark in the card body. */
.card .points .facts .fact-disc {
  flex: 0 0 1.45rem;
  height: 1.45rem;
  margin-top: 0.05rem;
  border-radius: 50%;
  color: white;
  font-size: 0.78rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 2px rgba(27, 27, 31, 0.18);
}

.card .points .facts .fact-text {
  flex: 1 1 auto;
}

.card .points .facts li.hovered {
  background: color-mix(in srgb, var(--fact-color, #FFF1A8) 22%, transparent);
  transform: translateX(2px);
}

.fact-graph-host {
  width: 100%;
  min-height: 60px;
}

.card .graph-col [data-controller~="fact-graph"] {
  display: block;
}

.card .points h3 {
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #475569;
}

.card .points p {
  margin: 0;
  line-height: 1.45;
  white-space: pre-wrap;
}

.card .points.pending h3 {
  color: #94a3b8;
}

.card .points .spinner-text {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 1.5em;
  margin: 0;
  color: #94a3b8;
}

/* Default comparison view: only the LACKING steps (what the ruling doesn't
   fulfil), each marked "(–)". The full per-step grid lives behind the
   "Pełne porównanie" toggle. */
.card .points .cmp-summary {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin: 0.2rem 0 0.4rem;
}
.card .points .cmp-summary--ok {
  margin: 0.2rem 0 0.4rem auto;
  width: fit-content;
  max-width: 100%;
  text-align: right;
  font-style: italic;
  color: #2f9e44;
}
/* "Niezgodne ze schematem" header + the reason (miner's note) shown on the
   detail card when a ruling does NOT fully match the schema. */
.card .points .cmp-summary--nomatch {
  margin: 0.2rem 0 0.15rem;
  font-weight: 700;
  color: #c1392c;
}
.card .points .cmp-reason {
  margin: 0 0 0.4rem;
  padding: 0.35rem 0.5rem;
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--ncs-ink);
  background: color-mix(in srgb, #c1392c 8%, transparent);
  border-left: 3px solid #c1392c;
  border-radius: 4px;
}
.card .points .cmp-lack {
  display: flex;
  gap: 0.4rem;
  font-size: 0.9rem;
  line-height: 1.4;
}
.card .points .cmp-lack-mark {
  flex: 0 0 auto;
  font-weight: 700;
  color: #c1392c;
}
.card .points .cmp-advanced-toggle {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0.15rem 0;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ncs-stone);
  cursor: pointer;
}
.card .points .cmp-advanced-toggle:hover { color: var(--ncs-7); }
.card .points .cmp-advanced { margin-top: 0.4rem; }

/* Schemat comparison — one row per general-schemat step. The left cell holds
   the numbered schemat step (the scenario's "DNA" — its common event); the
   right cell holds how this ruling's factual state measures against it: green
   when the step is fulfilled, orange when it is not. Rendered inside the
   `.points` box. */
/* ALP-131 follow-up: compact comparison — the panel is a companion to the
   full-interpretacja reader now, not the card's main content; tighter rows
   and a smaller face keep it in its 30vh cap with room to spare. */
.card .points .schemat-compare {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.card .points .cmp-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.35rem;
  align-items: stretch;
}

.card .points .cmp-step,
.card .points .cmp-verdict {
  padding: 0.25rem 0.45rem;
  border-radius: 6px;
  font-size: 0.78rem;
  line-height: 1.35;
}

/* Left cell — the schemat step itself: neutral cream box, numbered disc. */
.card .points .cmp-step {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
}

/* Numbered teal disc — mirrors the `.fact-disc` of the mined points list. */
.card .points .cmp-num {
  flex: 0 0 1.1rem;
  height: 1.1rem;
  margin-top: 0.05rem;
  border-radius: 50%;
  background: var(--ncs-3);
  color: white;
  font-size: 0.66rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 2px rgba(27, 27, 31, 0.18);
}

.card .points .cmp-text {
  flex: 1 1 auto;
}

/* Right cell — the comparison verdict: green when the factual state fulfils
   the step, orange when it does not, neutral while mining is still pending.
   The left border-stripe sits against the schemat-step cell it answers. */
.card .points .cmp-row--match .cmp-verdict {
  background: rgba(47, 158, 68, 0.13);
  border-left: 3px solid #2f9e44;
}

.card .points .cmp-row--nomatch .cmp-verdict {
  background: rgba(232, 146, 74, 0.15);
  border-left: 3px solid #e8924a;
}

.card .points .cmp-row--pending .cmp-verdict {
  background: rgba(255, 255, 255, 0.5);
  border-left: 3px solid var(--ncs-rule);
}

.card .points .cmp-verdict .cmp-empty {
  color: #94a3b8;
}

@media (max-width: 980px) {
  /* When stacked, DOM order already shows the schemat step first, then the
     verdict — no `order` overrides needed. */
  .card .points .cmp-row {
    grid-template-columns: 1fr;
  }
}

/* Per-ruling notes — only rendered into workspace clones. Hidden in the
   search view (`display: none`); the workspace selector overrides. */
.comment-section {
  display: none;
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--ncs-rule);
}

.view--workspace .comment-section {
  display: block;
}

.comment-label {
  margin: 0 0 0.4rem 0;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #1B1B1F;
}

.comment-input {
  width: 100%;
  font: inherit;
  font-size: 0.92rem;
  line-height: 1.45;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  background: var(--ncs-pale);
  color: var(--ncs-1);
  box-sizing: border-box;
  resize: vertical;
  min-height: 4rem;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.comment-input:focus {
  outline: none;
  border-color: var(--ncs-7);
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.18);
  background: white;
}

/* Pile of cards the agent marked irrelevant (or returned an empty facts[]
   for). Always visible at the bottom of the main results as compact boxes
   that surface signature + the agent's reason (`note`). The per-card ↕
   button promotes a single box back to the full card view. */
.results-hidden-wrap {
  margin-top: 1.75rem;
  padding-top: 1rem;
  border-top: 1px dashed var(--ncs-rule);
}

.results-hidden-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.6rem;
}

.results-hidden-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.6rem;
  background: var(--ncs-pale);
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--ncs-1);
}

.results-hidden-note {
  font-size: 0.8rem;
  color: #64748b;
}
.results-hidden-note kbd {
  display: inline-block;
  padding: 0.02rem 0.35rem;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-bottom-width: 2px;
  border-radius: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85em;
  color: var(--ncs-1);
}

/* An irrelevant card collapses to a compact box wherever it lives: the
   card-head's signature stays, the body shrinks to just the .note (agent's
   "reason") from points, and the rest — paragraph, facts list, DNA graph,
   liskus turbo-frame — is hidden so the list stays scannable. Keyed off
   `.card.irrelevant` directly so it fires regardless of container. */
/* The ruling text reveals on EITHER the ↕ expand OR "Pokaż SF", so Show SF
   works on irrelevant rulings too (not just the expand button). */
.card.irrelevant:not(.expanded):not(.sf-shown) .text {
  display: none !important;
}
.card.irrelevant:not(.expanded) .graph-col,
.card.irrelevant:not(.expanded) .liskus-frame,
.card.irrelevant:not(.expanded) .points h3,
.card.irrelevant:not(.expanded) .points .facts {
  display: none !important;
}
/* Collapsed irrelevant card: single column, no phantom 240px graph slot.
   The selector matches the specificity (0,5,0) of the zoom="medium" rule
   above so a default-zoom irrelevant card doesn't keep the graph column
   reserved — without this the "Porównanie ze schematem" cell ends up
   squeezed alongside an empty right column. */
.container[data-zoom] .card.irrelevant:not(.expanded):not(.sf-shown) .card-body {
  grid-template-columns: 1fr;
  grid-template-areas: "facts";
  gap: 0;
  margin: 0;
}
.card.irrelevant .points,
.card.irrelevant .points.irrelevant {
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
}
.card.irrelevant .points .note,
.card.irrelevant .points .spinner-text {
  margin: 0;
  font-size: 0.88rem;
  font-style: italic;
  color: #64748b;
}
.card.irrelevant .points-col {
  margin-top: 0;
}

/* Per-card "↕" expand button — only shown inside the irrelevant pile.
   Clicking promotes the box back to a normal-size card. */
.expand-btn {
  display: none;
  width: 28px;
  height: 28px;
  margin-left: 0.25rem;
  border: 0;
  border-radius: 50%;
  background: rgba(43, 108, 176, 0.08);
  color: var(--ncs-7);
  cursor: pointer;
  font-size: 0.95rem;
  font-weight: 700;
  align-items: center;
  justify-content: center;
}
.expand-btn:hover { background: rgba(43, 108, 176, 0.18); }

.card.irrelevant .expand-btn {
  display: inline-flex;
}

/* Expanded irrelevant card: restore the full 2-col layout used by relevant
   cards (facts on top, SF below, DNA graph on the right). The selector has
   specificity (0,5,0) — same as the zoom="medium" rule above — and comes
   later in the file, so it wins regardless of zoom mode. */
.container[data-zoom] .card.irrelevant.expanded .card-body {
  grid-template-columns: minmax(0, 1fr) 240px;
  grid-template-areas:
    "facts graph"
    "text  graph";
  gap: 1rem 1.25rem;
  margin: 0;
}
.card.irrelevant.expanded .points {
  margin-top: 0.25rem;
  padding: 0.75rem 0.9rem;
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
  border-left-width: 3px;
}
.card.irrelevant.expanded {
  padding: 1rem 1.25rem;
  background: white;
}
.card.irrelevant.expanded .expand-btn {
  background: var(--ncs-7);
  color: white;
}

/* Three-dot loader for the "Kluczowe fakty" placeholder while the agent
   is mining points. Staggered bounce so the dots read as `.` `..` `...`. */
.bounce-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.32;
  animation: bounce-dot 1.2s ease-in-out infinite;
}
.bounce-dot:nth-child(2) { animation-delay: 0.15s; }
.bounce-dot:nth-child(3) { animation-delay: 0.30s; }

@keyframes bounce-dot {
  0%, 80%, 100% { opacity: 0.32; transform: translateY(0); }
  40%           { opacity: 1;    transform: translateY(-3px); }
}

/* The search phase chip ("szukam…", loading, mining…) is redundant now that
   the Stanislaw console surfaces the same status — hide it. */
#factual-phase { display: none !important; }

.phase {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 1.5rem 0 0;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  background: rgba(43, 108, 176, 0.10);
  border: 1px solid color-mix(in srgb, var(--ncs-7) 40%, transparent);
  color: var(--ncs-7);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.phase[data-phase="searching"]    { color: var(--ncs-7); background: rgba(43, 108, 176, 0.10); border-color: color-mix(in srgb, var(--ncs-7) 40%, transparent); }
.phase[data-phase="loading"]      { color: var(--ncs-3); background: rgba(39, 127, 142, 0.10); border-color: color-mix(in srgb, var(--ncs-3) 40%, transparent); }
.phase[data-phase="mining"]       { color: var(--ncs-4); background: rgba(212, 153, 38, 0.12); border-color: color-mix(in srgb, var(--ncs-4) 45%, transparent); }
.phase[data-phase="consolidating"]{ color: var(--ncs-6); background: rgba(111, 74, 126, 0.12); border-color: color-mix(in srgb, var(--ncs-6) 45%, transparent); }
.phase[data-phase="done"]         { color: var(--ncs-5); background: rgba(33, 139, 95, 0.12); border-color: color-mix(in srgb, var(--ncs-5) 45%, transparent); }
.phase[data-phase="error"]        { color: var(--ncs-2); background: rgba(193, 59, 94, 0.10); border-color: color-mix(in srgb, var(--ncs-2) 45%, transparent); }

.phase .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.phase[data-phase="searching"] .dot,
.phase[data-phase="loading"] .dot,
.phase[data-phase="mining"] .dot,
.phase[data-phase="consolidating"] .dot {
  animation: pulse 1.1s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.7); }
}

.raw {
  margin-top: 1rem;
}

.raw pre {
  background: #0b1020;
  color: #e6edf3;
  padding: 1rem;
  border-radius: 6px;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---------- Comparison bar + diff overlay ---------- */

/* Action bar — appears under the meta row once ≥1 card is picked for the
   diff compare. Uses the purple comparison accent (--ncs-6). */
.compare-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
  padding: 0.6rem 0.9rem;
  background: rgba(111, 74, 126, 0.07);
  border: 1px solid rgba(111, 74, 126, 0.25);
  border-radius: 10px;
}
.compare-bar-label {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ncs-6);
}
.compare-bar-btn {
  margin-left: auto;
  padding: 0.4rem 1rem;
  background: var(--ncs-6);
  color: white;
  border: 1px solid var(--ncs-6);
  border-radius: 999px;
  font: inherit;
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
  transition: transform 120ms ease, opacity 120ms ease;
}
.compare-bar-btn:hover:not(:disabled) { transform: translateY(-1px); }
.compare-bar-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.compare-bar-clear {
  padding: 0.4rem 0.85rem;
  background: white;
  color: var(--ncs-1);
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.compare-bar-clear:hover { border-color: var(--ncs-6); }

/* Full-screen overlay holding the diff modal. */
.compare-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2.5rem 1.5rem;
  background: rgba(27, 27, 31, 0.55);
  animation: compare-fade-in 140ms ease both;
}
@keyframes compare-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.compare-modal {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 52rem;
  max-height: 100%;
  background: white;
  border-radius: 14px;
  box-shadow: 0 24px 64px -16px rgba(27, 27, 31, 0.5);
  overflow: hidden;
}
.compare-modal-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.4rem;
  border-bottom: 1px solid var(--ncs-rule);
}
.compare-modal-head h2 {
  margin: 0;
  font-size: 1.05rem;
}
.compare-modal-close {
  margin-left: auto;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 50%;
  background: var(--ncs-pale);
  color: var(--ncs-1);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}
.compare-modal-close:hover { background: white; border-color: var(--ncs-6); }

/* The search view is a two-column layout: the main column (form + results)
   and a persistent comparison-history sidebar that appears once the user has
   run at least one comparison. */
.search-layout {
  display: flex;
  gap: 1.5rem;
  align-items: flex-start;
}
.search-main {
  flex: 1 1 auto;
  min-width: 0;
}
.search-compare-sidebar {
  flex: 0 0 17rem;
  position: sticky;
  top: 1rem;
  max-height: calc(100vh - 2rem);
  display: flex;
  flex-direction: column;
  background: var(--ncs-pale);
  border: 1px solid var(--ncs-rule);
  border-radius: 12px;
  overflow: hidden;
}
.compare-sidebar-title {
  margin: 0;
  padding: 0.75rem 0.9rem;
  border-bottom: 1px solid var(--ncs-rule);
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ncs-6);
}
.compare-sidebar-list {
  list-style: none;
  margin: 0;
  padding: 0.5rem;
  overflow-y: auto;
}
.compare-sidebar-empty {
  padding: 1rem 0.75rem;
  color: var(--ncs-1);
  font-size: 0.88rem;
}
.compare-entry-wrap {
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
  margin-bottom: 0.3rem;
}
.compare-entry {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.5rem 0.6rem;
  text-align: left;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  cursor: pointer;
  font: inherit;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.compare-entry:hover { border-color: var(--ncs-6); }
.compare-entry.active {
  border-color: var(--ncs-6);
  background: rgba(111, 74, 126, 0.1);
}
.compare-entry-sigs {
  font-size: 0.85rem;
  font-weight: 600;
  color: #1B1B1F;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.compare-entry-meta {
  font-size: 0.74rem;
  color: var(--ncs-1);
}
.compare-entry-remove {
  flex: 0 0 auto;
  width: 1.7rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  background: white;
  color: var(--ncs-1);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.compare-entry-remove:hover {
  background: #fde7e9;
  border-color: #b91c1c;
  color: #7f1d1d;
}

.compare-modal-body {
  flex: 1 1 auto;
  padding: 1.25rem 1.4rem;
  overflow-y: auto;
}
.compare-detail-sigs {
  margin: 0 0 0.9rem 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ncs-6);
}
.compare-empty,
.compare-none { color: var(--ncs-1); }

/* Comparison result — the comparator agent's structured output rendered
   inside the overlay modal: summary paragraph, two bullet sections
   (common points / differences), and a closing conclusion line. */
.compare-loading {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 1rem 0;
  color: var(--ncs-1);
}
.compare-loading-text { margin-left: 0.4rem; }

.compare-error-msg {
  margin: 0;
  padding: 0.7rem 0.9rem;
  background: #fde7e9;
  color: #7f1d1d;
  border-radius: 8px;
}

.compare-summary {
  margin: 0 0 1rem 0;
  font-size: 0.98rem;
  line-height: 1.55;
  color: #1B1B1F;
}
.compare-section-title {
  margin: 1rem 0 0.5rem 0;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ncs-6);
}
.compare-list {
  margin: 0;
  padding-left: 1.2rem;
}
.compare-list li {
  margin-bottom: 0.35rem;
  line-height: 1.5;
}
.compare-none { margin: 0; }
.compare-conclusion {
  margin: 1.1rem 0 0 0;
  padding-top: 0.7rem;
  border-top: 1px solid var(--ncs-rule);
  font-size: 0.96rem;
  font-style: italic;
  color: var(--ncs-1);
}

/* Scenario schemat diff — git-style monospace block rendered into the same
   `.compare-modal-body` as the agent comparisons. One `.git-diff` section
   per consecutive pair of picked scenarios; lines carry +/-/space markers
   and red/green backgrounds. */
.git-diff {
  margin: 0 0 1.1rem 0;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
  overflow: hidden;
  background: #fafafa;
}
.git-diff + .git-diff { margin-top: 1rem; }
.git-diff-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  padding: 0.55rem 0.8rem;
  background: #eef2f7;
  border-bottom: 1px solid var(--ncs-rule);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
}
.git-diff-from { color: #b91c1c; }
.git-diff-to   { color: #166534; }
.git-diff-stat {
  margin-left: auto;
  color: var(--ncs-1);
  font-weight: 600;
}
.git-diff-body {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
  line-height: 1.5;
}
.git-diff-line {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  padding: 0.18rem 0.8rem;
  white-space: pre-wrap;
  word-break: break-word;
}
.git-diff-marker {
  flex: 0 0 1ch;
  user-select: none;
  font-weight: 700;
  color: var(--ncs-1);
}
.git-diff-line--add {
  background: #ecfdf5;
  color: #14532d;
}
.git-diff-line--add .git-diff-marker { color: #166534; }
.git-diff-line--del {
  background: #fef2f2;
  color: #7f1d1d;
}
.git-diff-line--del .git-diff-marker { color: #b91c1c; }
.git-diff-line--ctx { color: #475569; }

/* Inline word-level highlights inside a paired -/+ row. Only the words that
   actually differ get colored; the kept common words sit on the row's base
   red/green tint so the diff reads at a glance. */
.git-diff-word-del {
  background: #fecaca;
  color: #7f1d1d;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  padding: 0 2px;
  border-radius: 2px;
}
.git-diff-word-add {
  background: #bbf7d0;
  color: #14532d;
  font-weight: 600;
  padding: 0 2px;
  border-radius: 2px;
}

.git-diff-empty {
  margin: 0;
  padding: 0.8rem;
  color: var(--ncs-1);
  font-style: italic;
}

/* ---------- DNA overview (kanban) ---------- */

/* Three columns side by side — workspace cards, the executive summary, and
   the presentation slides — so the whole research artifact is visible at
   once, just condensed. Each column scrolls on its own. */
.dna-board {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: flex-start;
}
.dna-col {
  flex: 1 1 18rem;
  min-width: 16rem;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 9rem);
  background: var(--ncs-pale);
  border: 1px solid var(--ncs-rule);
  border-radius: 12px;
  overflow: hidden;
}
.dna-col-title {
  margin: 0;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--ncs-rule);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ncs-7);
}
/* The content area of each column fills the remaining height and scrolls on
   its own — `min-height: 0` lets a flex child shrink below its content so
   `overflow-y` actually engages (otherwise the column just clips it). */
.dna-search,
.dna-workspace,
.dna-exec,
.dna-slides {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0.75rem;
  overflow-y: auto;
}

/* SVG overlay drawing the search ↔ workspace connector curves. Sits above
   the columns but ignores pointer events so cards stay interactive. */
.dna-links {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
  z-index: 2;
}
.dna-link {
  fill: none;
  stroke: var(--ncs-6);
  stroke-width: 2;
  stroke-opacity: 0.55;
  stroke-dasharray: 4 3;
}
.dna-link-dot {
  fill: var(--ncs-6);
  fill-opacity: 0.85;
}
.dna-empty {
  padding: 0.75rem;
  margin: 0;
  color: var(--ncs-1);
  font-size: 0.85rem;
  line-height: 1.5;
}

/* Workspace column — the pinned-card clones, condensed: card body stacks
   to a single column, the heavy graph/iframe layers are dropped. */
.view--dna .card { padding: 0.55rem 0.7rem; }
.view--dna .card-body { display: block; }
/* Workspace cards show the key facts (+ notes), not the ruling text or the
   DNA strand — the strand lives only in the Search column. */
.view--dna .card .text,
.view--dna .graph-col,
.view--dna .liskus-frame,
.view--dna .card .link { display: none; }
.view--dna .card .points h3 { font-size: 0.7rem; }
.view--dna .points-col { margin-top: 0.45rem; }
.view--dna .comment-section { margin-top: 0.45rem; }
.view--dna .comment-input { font-size: 0.78rem; }

/* Executive-summary column — renders the same markdown document the
   Prezentacja view does, just at a smaller scale to fit the DNA board. */
.dna-exec .md-doc { font-size: 0.82rem; line-height: 1.5; }
.dna-exec .md-doc .md-h1 { font-size: 1rem; }
.dna-exec .md-doc .md-h2 { font-size: 0.9rem; }
.dna-exec .md-doc .md-h3 { font-size: 0.74rem; }
.dna-exec .md-doc .md-list { padding-left: 1.1rem; }
.dna-exec .md-doc .md-list li { margin-bottom: 0.22rem; }

/* DNA overview — search column: condensed read-only result rows. */
.dna-search {
  padding: 0.75rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.dna-search-item {
  padding: 0.55rem 0.65rem;
  background: white;
  border: 1px solid var(--ncs-rule);
  border-radius: 8px;
}
.dna-search-sig {
  margin: 0 0 0.35rem 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  font-weight: 600;
  color: #0f172a;
}
/* Each search row carries its DNA strand (echarts) — the controller sizes
   its own height; it just needs the column's width. */
.dna-search-graph { width: 100%; }
.dna-search-nofacts {
  margin: 0;
  font-size: 0.74rem;
  font-style: italic;
  color: var(--ncs-stone);
}

/* DNA overview — drag & drop between the Search and Workspace columns. */
.dna-search-item { cursor: grab; }
.dna-search-item:active { cursor: grabbing; }
.dna-search-item.dragging { opacity: 0.45; }
.dna-search-item.pinned {
  border-color: var(--ncs-5);
  background: rgba(33, 139, 95, 0.08);
}
.dna-search-item.pinned .dna-search-sig::after {
  content: " \2713";
  color: var(--ncs-5);
}
.view--dna .card[draggable="true"] { cursor: grab; }
.view--dna .card.dragging { opacity: 0.45; }
.dna-col.drag-over {
  border-color: var(--ncs-7);
  box-shadow: inset 0 0 0 2px rgba(43, 108, 176, 0.35);
}
.dna-col-hint {
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ncs-stone);
}

/* -------------------- draggable widgets -------------------- */

/* The drag handle (a widget's title bar) shows a move cursor; while dragging
   we suppress text selection and lift the element with a stronger shadow. */
.draggable-handle { cursor: move; touch-action: none; }
.dragging {
  user-select: none;
  box-shadow: 0 18px 44px -12px rgba(0, 0, 0, 0.6);
}

/* -------------------- recent-sessions sidebar -------------------- */

/* In-flow block within the left sidebar; the list opens as a dropdown below
   the toggle. */
.session-sidebar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  font-size: 0.85rem;
}

/* The toggle is a sidebar tab — give it a surface (override the global red
   submit pill). */
.session-sidebar-toggle {
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.7rem;
  border: 0;
  border-radius: 10px;
  background: transparent;
  box-shadow: none;
  color: var(--ncs-ink);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0;
  cursor: pointer;
  transition: background-color 120ms ease;
}

.session-sidebar-toggle:hover:not(:disabled) {
  background: color-mix(in srgb, var(--ncs-ink) 6%, transparent);
  transform: none;
}

.session-sidebar-icon { font-size: 0.95rem; line-height: 1; }
.session-sidebar-label { flex: 1 1 auto; text-align: left; }

.recent-sessions-caret {
  font-size: 0.7rem;
  transition: transform 120ms ease;
}

.session-sidebar-toggle[aria-expanded="true"] .recent-sessions-caret {
  transform: rotate(180deg);
}

/* Dropdown panel under the toggle. */
.recent-sessions-list {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0.3rem;
  border: 1px solid var(--ncs-rule);
  border-radius: 10px;
  background: var(--ncs-pale);
  box-shadow: 0 6px 18px -12px rgba(27, 27, 31, 0.22);
  max-height: min(28rem, calc(100vh - 7rem));
  overflow-y: auto;
}

.recent-sessions-empty {
  padding: 0.5rem 0.6rem;
  color: var(--ncs-stone);
}

.recent-sessions-item + .recent-sessions-item {
  border-top: 1px solid var(--ncs-rule);
}

/* Each entry is a full-width ghost button — override the global pill again. */
.recent-sessions-link {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  background: none;
  border: 0;
  border-radius: 8px;
  box-shadow: none;
  color: var(--ncs-ink);
  font-weight: 500;
  letter-spacing: 0;
  text-align: left;
  cursor: pointer;
}

.recent-sessions-link:hover:not(:disabled) {
  background: var(--ncs-cream);
  transform: none;
}

.recent-sessions-query {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.recent-sessions-meta {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.72rem;
  color: var(--ncs-stone);
}

.recent-sessions-badge {
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
}

/* Development badge (layouts/application.html.erb): identifies which repo
   checkout is serving this tab when several run in parallel tmux sessions. */
.dev-badge {
  position: fixed;
  bottom: 0.4rem;
  left: 0.5rem;
  z-index: 9999;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.68rem;
  line-height: 1.4;
  color: var(--ncs-ink);
  background: var(--ncs-cream);
  border: 1px solid var(--ncs-rule);
  border-radius: 999px;
  padding: 0.1rem 0.6rem;
  opacity: 0.75;
}
.dev-badge:hover { opacity: 1; }

/* PROTOTYPE_FRONT (ALP-127): top-of-page banner marking the mock-backend demo. */
.proto-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.75rem;
  padding: 0.35rem 0.9rem;
  background: repeating-linear-gradient(
    45deg, #7a1f1f, #7a1f1f 10px, #641818 10px, #641818 20px);
  color: #fff;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.72rem;
  line-height: 1.4;
  border-bottom: 1px solid rgba(0, 0, 0, 0.35);
}
.proto-banner-tag {
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 0.02rem 0.4rem;
  background: rgba(0, 0, 0, 0.35);
  border-radius: 3px;
  white-space: nowrap;
}
.proto-banner-msg { opacity: 0.95; }
.proto-banner-path { margin-left: auto; opacity: 0.7; white-space: nowrap; }

/* ============================================================================
   Nested unfolding outline
   One continuous column: cluster ▸ subcluster ▸ snippet ▸ result, each level
   unfolding in place. Replaces the drill-right cascade for snippets + details —
   clicks never reveal the Sub-clusters / Snippets / Details columns (they stay
   `hidden`), so the Scenarios column becomes the single full-width scroller and
   everything nests inside its `.scenario-children` rails. Reuses the existing
   `--scenario-hue`, scenario-chip, snippet-card and `.card` visuals.
   ========================================================================== */

/* The scenarios column is no longer a quarter-width cascade lane — it is the
   whole board. Override the `flex: 0 0 25%` cascade rule (same selector prefix
   so specificity matches, plus this comes later in the file). */
.container[data-view="search"][data-has-results] .layer-cascade > .view--scenarios:not([hidden]) {
  flex: 1 1 100%;
  max-width: none;
}
.container[data-view="search"][data-has-results] .view--scenarios > .scenario-board {
  padding-top: 0;
}

/* Snippet rows unfolded under a leaf scenario: same indent rail as sub-clusters,
   just holding one-line ruling rows instead of nested tiles. */
.scenario-children--snippets {
  gap: 0.3rem;
}

/* One ruling: a caret + its best sentence + signature, and (when open) its full
   detail card in the slot below. Quiet by default, lifts on hover/focus like the
   legacy snippet card. */
.snippet-row {
  --scenario-hue: inherit;
  display: flex;
  flex-direction: column;
  /* Room for the pinned cluster + sub-cluster headers when an opened row is
     auto-scrolled to the top of the board (toggleResult). */
  scroll-margin-top: 6.5rem;
}
.snippet-card--outline {
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.45rem 0.6rem;
  /* Each snippet reads as its own boxed card so adjacent rulings are clearly
     separated; only the left edge carries the ruling's CLUSTER hue (white
     surface — color is reserved for the identity rail, marks and states). */
  border: 1px solid var(--ncs-rule);
  border-left: 3px solid hsl(var(--scenario-hue), 45%, 60%);
  border-radius: 8px;
  background: var(--ncs-paper);
}
/* Breathing room between the snippet boxes. */
.outline-row[data-kind="snippet"] > .row-body { margin: 0.3rem 0; }
/* Cluster rows (ALP-130: the hierarchy is back in the center, unfolded) get a
   touch more air so each cluster card reads as its group's heading. */
/* Breathing room lives as PADDING on the sticky row (inside its opaque
   background), not as row-body margin — transparent margin slits would let
   scrolling text ghost through between the pinned headers. */
.outline-row[data-kind="cluster"] > .row-body { margin: 0; }
/* ALP-131: cluster & sub-cluster rows pin at the results scroller's top while
   their rulings scroll; deeper headers pin below the MEASURED heights of
   their ancestors (`_pinOutlineHeaders` sets inline `top` after each outline
   render — heights vary when chips wrap). Opaque background masks content
   sliding under. Ancestors stack above descendants, so a subcluster being
   pushed out slides under its still-pinned cluster; every level remains above
   the hover-lift (45). */
.outline-row[data-kind="cluster"] {
  position: sticky;
  top: 0;
  z-index: calc(100 - var(--depth, 0));
  /* Same surface as the rulings area behind them (.layer-cascade >
     .view--scenarios) — shared knob, so they always blend. */
  background: var(--results-surface);
  padding: 0.45rem 0 0.25rem;
}
/* Caret + sentence share a line; the signature trails on the right. */
.snippet-card-line {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  flex: 1 1 auto;
  min-width: 0;
}
.snippet-caret {
  flex: 0 0 auto;
  color: var(--ncs-stone);
  font-size: 0.75rem;
  line-height: 1.5;
  user-select: none;
}
.snippet-card--outline.is-open {
  background: hsl(var(--scenario-hue), 60%, 97%);
  border-color: hsl(var(--scenario-hue), 55%, 62%);
}

/* The relocated detail card, framed to read as a child of its snippet row:
   indented under the row on the node's hue rail, slightly inset. */
.result-slot {
  margin: 0.35rem 0 0.5rem 0.6rem;
  padding-left: 0.8rem;
  border-left: 2px solid var(--ncs-rule);
}
.result-slot[hidden] { display: none; }
.result-slot > .card {
  margin: 0;
  box-shadow: 0 1px 10px -6px rgba(27, 27, 31, 0.3);
}

/* ---- Flat outline + aligned ticker gutter (right) --------------------------
   The outline is rendered as a FLAT list of sibling `.outline-row`s (not nested
   containers) so every row shares one right edge. Each row is a 2-col grid:
   an indented body on the left and the selection ticker in a fixed-width gutter
   pinned to the right — so all tickers line up in one column regardless of the
   row's nesting depth. Indentation moves from container margins onto the body's
   padding, keyed by `--depth`. */
.scenario-board { position: relative; }
.outline-row {
  display: grid;
  /* Even-px gutter so the centred connecting line lands on an integer pixel. */
  grid-template-columns: minmax(0, 1fr) var(--tick-col-w, 30px);
  column-gap: 0.5rem;
  align-items: start;
  --scenario-hue: 210;
}
.outline-row > .row-body {
  grid-column: 1;
  min-width: 0;
  /* Recreate the nested indent (was `.scenario-children` margins) per row. A
     faint hue rail marks the depth without a nested container. */
  padding-left: calc(var(--depth, 0) * 1.15rem);
  margin: 0.18rem 0;
}
.outline-row[data-depth]:not([data-depth="0"]) > .row-body {
  border-left: 2px solid var(--ncs-rule);
  margin-left: calc(var(--depth, 0) * 1.15rem - 1.15rem + 0.1rem);
  padding-left: 0.9rem;
}
/* The ticker gutter: one continuous column down the right edge. A vertical
   connecting line runs through the CENTRE of every ticker, linking them down
   the column; each ticker sits on top of the line (its own white background
   makes the line read as connecting INTO the box). */
.outline-row > .tick-col {
  grid-column: 2;
  grid-row: 1;
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  align-self: stretch;
  padding-top: 0.35rem;
  /* No background / divider — only the centred connecting line below. */
}
/* The connecting line: a 2px element centred with `left:0; right:0; margin:auto`
   (NOT `left:50% + translate`, which lands on a fractional pixel and doubles).
   In an even-width gutter the auto margins are integer, so the line is crisp AND
   dead-centred on the checkbox (which the flex also centres). */
.outline-row > .tick-col::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  width: 2px;
  margin-inline: auto;
  background: color-mix(in srgb, var(--ncs-ink) 18%, transparent);
  z-index: 0;
}
/* Ticks sit above the line; their opaque fill breaks it at each box. */
.outline-row > .tick-col > .select-tick,
.outline-row > .tick-col > .scenario-select-tick {
  margin: 0;
  position: relative;
  z-index: 1;
}
