/* Per-page polish — P3.1-004.
 *
 * Page-specific styling for the graph, search, report, and login pages,
 * built ONLY on the tokens.css custom properties (no second palette).
 * First-party file: no remote rules, no cross-file includes (INV-4).
 * graph.css keeps the load-bearing #graph dimensions (codex P3-008 P1);
 * this sheet only restyles surfaces around it and loads AFTER it.
 */

/* --- Graph page ------------------------------------------------------- */
/* P3.3-010 #4: the Layout / Stage filter / Legend controls moved into
   console-card sub-panels in the right rail; the old top #graph-controls
   band (and its fieldset/legend chrome) is gone — console.css supplies the
   card surface. */

#graph {
  border-color: var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}

/* #detail-panel is a console-card in the rail (console.css supplies the
   surface); keep the word-break so long edge keys wrap inside it. */
#detail-panel {
  font-size: 0.9rem;
  word-break: break-all;
}

#detail-panel p:first-child {
  margin-top: 0;
}

/* --- Search page ------------------------------------------------------ */

.search-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: var(--space-4);
  margin-bottom: var(--space-5);
}

.search-form label {
  flex-basis: 100%;
  margin-bottom: 0;
}

.search-form input[type="text"] {
  flex: 1 1 22rem;
}

#search-error {
  color: var(--danger);
}

#search-results td {
  font-family: var(--font-mono);
  font-size: 0.85rem;
}

#search-results td:first-child {
  font-family: var(--font-sans);
  font-size: inherit;
}

/* --- Sankey diagram readability --------------------------------------- */

.sankey-intro {
  color: var(--muted);
  margin-bottom: var(--space-4);
}

/* mermaid renders sankey link bands at 0.5 stroke-opacity through its own
   id-scoped <style> injected into the SVG (there is no inline attribute to
   target). On the light theme those pastel gradients are too faint to read,
   so raise the opacity. !important is required to beat mermaid's id-scoped
   rule -- the vendored bundle is never patched. Hover emphasises one
   corridor. */
pre.mermaid svg g.links path {
  stroke-opacity: 0.72 !important;
}

pre.mermaid svg g.links path:hover {
  stroke-opacity: 0.95 !important;
}

/* P3.3 #1: the Sankey diagram fills the card width. mermaid sizes the SVG to
   the diagram's intrinsic width and leaves it offset to one side; force it to
   span the container. Scoped to .sankey-chart so the report-page diagram keeps
   its own centred sizing. !important beats mermaid's inline max-width. */
pre.sankey-chart {
  width: 100%;
  text-align: center;
}

pre.sankey-chart svg {
  width: 100% !important;
  max-width: 100% !important;
  height: auto;
  /* P3.3-012 #1: cap the height so the diagram always sits inside one screen.
     The mermaid sankey height config (mermaid_init.js) sets a flatter viewBox
     aspect; this rule is the belt-and-suspenders ceiling on very tall/wide
     viewports (the SVG's preserveAspectRatio="meet" keeps it undistorted). */
  max-height: 70vh;
}

/* P3.3-018-A: shrink the rendered sankey label text so AMOUNT-mode value
   labels (long native-integer weights mermaid auto-places next to each node
   band) stop colliding. mermaid emits node + value labels as <text> elements
   inside the SVG and styles them with an id-scoped <style> block (no inline
   attribute to target), so the override is scoped to `.sankey-chart svg text`
   and uses !important to beat mermaid's id-scoped rule (the vendored bundle is
   never patched). Paired with the taller mermaid_init.js sankey `height`
   (more vertical room between bands). Verified selector at runtime: see
   _workspace/P3.3-018-A_builder_notes.md. */
pre.sankey-chart svg text {
  font-size: 11px !important;
}

/* --- Sankey controls form (P3.3-011 #3) ------------------------------- */
/* Plain GET form (no JS): grouping dimension + chain / stage-range / weight
   filters. Built only on tokens.css custom properties. */

/* P3.3 #2: lay the controls (Group by .. Apply) out in one roomy row —
   generous row/column gaps + padding, each control a comfortable min width so
   they sit side by side, and the Apply button pushed to the row's end. */
.sankey-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-4) var(--space-5);
  margin: 0 0 var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.sankey-controls .control {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 9rem;
}

.sankey-controls .control > label,
.sankey-controls legend {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 600;
}

/* P3.3-012 #2: the chains fieldset also carries the .control class, which sets
   flex-direction:column — that stacked the legend + every checkbox VERTICALLY.
   Override to a horizontal wrapping row (higher specificity: two classes + the
   fieldset element beats `.sankey-controls .control`) with the legend pushed to
   its own full-width line above the checkbox row. */
.sankey-controls fieldset.control-chains {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  margin: 0;
  padding: 0;
  border: none;
}

.sankey-controls .control-chains legend {
  /* a fieldset legend is laid out specially; flex-basis:100% forces it onto
     its own row so the checkboxes flow side by side beneath it. */
  flex: 0 0 100%;
  float: none;
  padding: 0;
}

.sankey-controls .control-chains .checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 400;
}

.sankey-controls .range-inputs {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.sankey-controls input[type="number"] {
  width: 4rem;
}

.sankey-controls button[type="submit"] {
  /* push Apply to the end of the row so it reads as the action, with the
     roomy column gap separating it from the last filter. */
  margin: 0 0 0 auto;
}

/* --- Canvas Sankey view (P3.3-019 Slice B) ----------------------------- */
/* The Canvas|Mermaid toggle + the Canvas-2D host + the data table the
   renderer (sankey_canvas.js) builds beneath it. Tokens.css custom properties
   only — no inline style (CSP). */

.view-switcher {
  display: inline-flex;
  gap: 0;
  margin: 0 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.view-switcher button {
  appearance: none;
  border: none;
  background: var(--surface);
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
}

.view-switcher button[aria-pressed="true"] {
  background: var(--accent, #2bb5b8);
  color: #fff;
}

#sankey-canvas-view canvas {
  display: block;
  width: 100%;
  height: 480px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}

/* P3.3-021 A / P3.3-026: on the dark theme give the Canvas-Sankey a DEEP NAVY
   backdrop (the reference widget's signature look) so the vivid node bars and
   the moving particles pop the way they do in the reference. The light theme
   keeps the clean surface and the bars use a DARKENED variant palette (see
   _sankey_color.py) so they stay readable on white. The canvas itself is
   theme-blind; this is purely the framing. */
:root[data-theme="dark"] #sankey-canvas-view canvas {
  background: #0b1120;
}

/* P3.3-026: the Mermaid Sankey view gets the SAME deep-navy panel on the dark
   theme so its widget-palette bars pop and clear WCAG 3:1 (the page surface
   #1a212b alone is too light for the slate bar). Scoped to #sankey-alt-view (the
   sankey page's Mermaid <pre>) so the report-page sankey diagram is untouched.
   The light theme keeps the bright card (the bars use the darkened palette). */
:root[data-theme="dark"] #sankey-alt-view {
  background: #0b1120;
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

/* The data table beneath the chart: sticky header, right-aligned value
   column, capped height with its own scroll (reference table semantics). */
#sankey-table {
  margin-top: var(--space-4);
  max-height: 220px;
  overflow-y: auto;
}

.sankey-data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.sankey-data-table th,
.sankey-data-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  text-align: left;
}

.sankey-data-table th {
  position: sticky;
  top: 0;
  background: var(--surface);
  color: var(--muted);
  font-weight: 600;
}

.sankey-data-table th.num,
.sankey-data-table td.num {
  text-align: right;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* --- Recipients page (P3.3-009 #7) ------------------------------------ */

/* The intro now lives INSIDE the console-card header, so it carries no
   bottom margin against the card edge (the card pads it). */
.recipients-intro {
  color: var(--muted);
  margin: 0 0 var(--space-4);
}

/* Termination-reason legend: the genuine + frontier groups sit SIDE BY SIDE
   (P3.3 #3 — the stacked column was too tall) in a 2-column grid; the intro
   note spans both. Each group is a list of badge + Korean gloss rows reusing
   the .status-badge tokens (same colour coding as the table's reason column).
   Below the mobile breakpoint the two groups stack. */
.termination-legend {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3) var(--space-5);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.termination-legend .legend-note {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
}

@media (max-width: 800px) {
  .termination-legend {
    grid-template-columns: 1fr;
  }
}

.legend-heading {
  margin: 0 0 var(--space-2);
  font-size: 0.85rem;
  color: var(--heading);
}

.legend-hint {
  display: block;
  font-weight: 400;
  font-size: 0.74rem;
  color: var(--muted);
}

.legend-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Layout modifiers — declared AFTER the base .legend-list so they WIN the
   cascade (same specificity, later rule) and actually override its
   display / flex-direction / gap (codex P2). Genuine = a 2-up wrapping row;
   frontier's 5 reasons = a 3+2 grid (3 in the first row, then 2) — P3.3-011 #4. */
.legend-list--row {
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
}

/* Frontier's 5 reasons as a 2-up grid (the group now occupies only HALF the
   card width beside the genuine group — P3.3 #3), collapsing to one column on
   narrow screens where the two groups stack. */
.legend-list--grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-4);
}

@media (max-width: 800px) {
  .legend-list--grid {
    grid-template-columns: 1fr;
  }
}

.legend-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.legend-row .status-badge {
  font-family: var(--font-mono);
  flex: 0 0 auto;
}

.legend-gloss {
  font-size: 0.82rem;
  color: var(--text);
}

/* WEB-SCALE (depth-B): the 9-column table's min-content width (full 42-char
   addresses + white-space:nowrap tabular amounts like
   "1468.09630931002757 ETH") can exceed the card width; the flush card's
   generic `overflow: hidden` then CLIPPED the trailing 금액/Amount column with
   no way to reach it. Override the x-axis to scroll INSIDE the card (mirrors
   the `#report-body { overflow-x: auto }` wide-table guard) so every column
   stays reachable; the compound selector out-specifies the generic flush rule
   regardless of stylesheet order, and the y-axis keeps the rounded-corner
   clipping. */
.console-card--flush.recipients-table-card {
  overflow-x: auto;
}

/* The table sits in a flush console-card, so it owns no border/shadow of
   its own (the card provides them) — only its column behaviour stays here. */
#recipients-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0;
  border: none;
  box-shadow: none;
  border-radius: 0;
  font-size: 0.88rem;
}

#recipients-table th,
#recipients-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

#recipients-table thead th {
  color: var(--heading);
  background: var(--surface-raised);
  white-space: nowrap;
}

#recipients-table tbody tr:hover {
  background: var(--surface-raised);
}

#recipients-table .addr {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  word-break: break-all;
}

#recipients-table .num {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

#recipients-table .muted {
  color: var(--muted);
}

/* P3.3-018-B: the Service column names the DeFi/bridge protocol the inflow
   edges passed through (its OWN column beside Amount, never inside the
   white-space:nowrap .num cell). Wraps freely so a long service display
   ("THORChain Vault") never forces the row wide. */
#recipients-table .recipient-service {
  font-size: 0.82rem;
  color: var(--text);
}

/* P3.3-011 #4: client-side filter controls — a flex row of labelled
   inputs/selects above the table. .recipients-control overrides the base
   block-label layout so each control stacks its caption over the field. */
.recipients-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3) var(--space-4);
}

.recipients-control {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: 0;
  flex: 1 1 12rem;
  min-width: 10rem;
  color: var(--muted);
  font-size: 0.8rem;
}

.recipients-control input,
.recipients-control select {
  width: 100%;
  max-width: none;
}

.recipients-filter-summary {
  flex: 0 0 auto;
  align-self: flex-end;
  margin: 0;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.8rem;
}

/* Sortable headers (P3.3-011 #4): click / Enter / Space toggles the sort;
   aria-sort renders the active direction caret. */
#recipients-table th.is-sortable {
  cursor: pointer;
  user-select: none;
}

#recipients-table th.is-sortable:hover {
  color: var(--accent-strong);
}

#recipients-table th.is-sortable:focus-visible {
  outline: 2px solid var(--btn-focus);
  outline-offset: -2px;
}

#recipients-table th[aria-sort="ascending"]::after {
  content: " \2191"; /* up arrow */
}

#recipients-table th[aria-sort="descending"]::after {
  content: " \2193"; /* down arrow */
}

/* Rows hidden by the client filter: display:none via a class (CSP-clean, and
   the <tr> node survives so a cleared filter re-shows it). */
.recipients-row--hidden {
  display: none;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- Report executive summary (P3.3 #5) ------------------------------- */
/* An at-a-glance band above the forensic report: a prominent attribution line
   over the dashboard stat-grid (console.css supplies the card + stat cards). */
.report-exec {
  margin-bottom: var(--space-5);
}

.report-exec-attribution {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-4);
}

.report-exec-actor {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--heading);
}

.report-exec-aliases {
  color: var(--muted);
  font-size: 0.85rem;
}

/* P3.3-012 #4: at-a-glance chain-coverage badges in the executive band. */
.report-exec-chains {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
}

.report-exec-chains-label {
  color: var(--muted);
  font-size: 0.85rem;
}

.report-exec-chain {
  display: inline-flex;
  padding: 0.1rem var(--space-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-raised);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--heading);
}

/* Exec-band card polish: a subtle accent rail on the left edge marks the
   executive summary as the headline band above the full report, and the
   stat cards inside it pick up a hover lift to read as at-a-glance tiles. */
.report-exec {
  border-left: 3px solid var(--accent);
}

.report-exec .stat-card {
  transition:
    border-color 0.15s ease,
    transform 0.15s ease;
}

.report-exec .stat-card:hover {
  border-color: var(--accent);
  transform: translateY(-1px);
}

/* --- Report TOC sticky right sidebar + collapsible sections (P3.3-012 #4
   + sidebar follow-up) -------------------------------------------------- */
/* PAGE-LEVEL 2-column layout (P3.3 #4 sidebar relocation): the TOC is pulled
   OUT of the report card to follow the scroll across the WHOLE page. The main
   column (.report-main) holds the exec band + report card + back links; the
   #report-toc <aside> is a STICKY right rail (pure CSS position:sticky — the
   .app-header is not fixed and no ancestor clips overflow, so it sticks against
   the viewport). The sidebar + jump/expand controls are built client-side into
   #report-toc (report_sections.js); the report body inside the card stays the
   byte-unchanged golden surface, and the sidebar is `hidden` until JS fills it
   (progressive enhancement). On narrow screens the grid collapses and the
   sidebar stacks ABOVE the main column. */
/* Default = ONE column (the TOC is `hidden` until JS builds it; reserving a
   second column here would leave an empty rail narrowing the body with JS off
   or when the report has no <h2> sections — codex P2). report_sections.js adds
   `.report-page--active` once the sidebar is populated, which opens the
   second column. */
.report-page {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  align-items: start;
}

.report-page--active {
  grid-template-columns: minmax(0, 1fr) minmax(11rem, 15rem);
}

/* The main column carries min-width:0 so a wide table inside #report-body
   scrolls (via its own `#report-body { overflow-x: auto }`) instead of blowing
   the column past its grid track. min-width:0 lives HERE (not on #report-body)
   so the overflow-guard test's first-match regex still finds the real
   `#report-body { overflow-x: auto }` rule below, not a grid placement. */
.report-main {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
}

.report-toc {
  grid-column: 2;
  grid-row: 1;
  position: sticky;
  top: var(--space-4);
  max-height: calc(100vh - 2 * var(--space-4));
  overflow-y: auto;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}

.report-toc-heading {
  margin: 0 0 var(--space-2);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}

.report-toc-jump,
.report-toc-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.report-toc-jump button,
.report-toc-controls button {
  flex: 1 1 auto;
}

.report-toc-btn {
  margin: 0;
  font-size: 0.74rem;
  padding: var(--space-1) var(--space-2);
}

.report-toc-list {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-5);
}

.report-toc-list li {
  margin-bottom: var(--space-1);
}

.report-toc-list a {
  display: block;
  color: var(--accent-strong);
  text-decoration: none;
}

.report-toc-list a:hover,
.report-toc-list a:focus-visible {
  text-decoration: underline;
}

/* Scroll-spy: the link of the section currently in view is marked
   `.is-active` by report_sections.js (an IntersectionObserver toggles the
   class on the JS-built TOC <a>, never on the golden body). */
.report-toc-list a.is-active {
  font-weight: 700;
  color: var(--heading);
  border-left: 2px solid var(--accent);
  margin-left: calc(-1 * var(--space-2));
  padding-left: calc(var(--space-2) - 2px);
}

@media (max-width: 900px) {
  .report-page,
  .report-page--active {
    grid-template-columns: 1fr;
  }

  /* Reset BOTH the main column and the TOC to natural auto-flow so the source
     order (the <aside> TOC comes FIRST) puts the sidebar ABOVE the main column
     on narrow screens — keeping the desktop `grid-row: 1` on .report-main would
     pin row 1 and push the TOC below the whole report (codex P2). */
  .report-main {
    grid-column: auto;
    grid-row: auto;
  }

  .report-toc {
    grid-column: auto;
    grid-row: auto;
    position: static;
    max-height: none;
    margin-bottom: var(--space-4);
  }
}

/* The <h2> sections are wrapped in <details> at runtime; the <h2> becomes the
   <summary>. Spacing moves onto the details so the caret + heading align.
   scroll-margin-top keeps a TOC anchor jump from landing flush against the
   top of the viewport (clears the page header). */
#report-body .report-section {
  margin: var(--space-6) 0 0;
  scroll-margin-top: var(--space-5);
}

#report-body .report-section-summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  list-style: none;
}

#report-body .report-section-summary::-webkit-details-marker {
  display: none;
}

#report-body .report-section-summary::before {
  content: "\25B8"; /* right-pointing triangle, rotates open */
  color: var(--muted);
  font-size: 0.9rem;
  transition: transform 0.15s ease;
}

#report-body details[open] > .report-section-summary::before {
  transform: rotate(90deg);
}

#report-body .report-section-summary h2 {
  flex: 1 1 auto;
  margin: 0;
}

#report-body .report-section-summary:hover h2 {
  color: var(--accent-strong);
}

/* --- Report page (P3.3-009 #9 — presentation polish) ------------------ */

/* The body now sits INSIDE a .report-card console-card (which owns the
   surface/border/shadow), so #report-body is just the scroll container +
   typography. The card pads the edges; the body keeps overflow-x for wide
   forensic tables. */
.report-card {
  padding: var(--space-5) var(--space-6);
}

#report-body {
  overflow-x: auto;
}

#report-body > :first-child {
  margin-top: 0;
}

/* Visual hierarchy: the top heading is the report title; deeper headings
   step down in weight. A hairline under H1/H2 sections the document. */
#report-body h1 {
  font-size: 1.5rem;
  color: var(--heading);
  border-bottom: 2px solid var(--border);
  padding-bottom: var(--space-2);
  margin-top: var(--space-6);
}

#report-body h2 {
  font-size: 1.2rem;
  color: var(--heading);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2);
  margin-top: var(--space-6);
}

#report-body h3 {
  font-size: 1.02rem;
  color: var(--heading);
  margin-top: var(--space-5);
}

/* Stage-detail emits `####` -> <h4>; style it as a clear step down from h3
   (smaller, muted-uppercase eyebrow) so the deepest forensic sub-headings
   stay visually distinct without competing with the section headings. */
#report-body h4 {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: var(--space-4);
  margin-bottom: var(--space-2);
}

/* Body readability: a roomier line-height + consistent paragraph rhythm so
   the dense forensic prose is comfortable to read. */
#report-body p {
  line-height: 1.65;
  margin: 0 0 var(--space-3);
}

#report-body table {
  /* Fill the content column. The global base.css table width-100% rule
     was being neutralised by an unconditional display block; with that
     gone the table fills #report-body. Wide-table horizontal scroll is
     owned by the #report-body container (overflow-x) on desktop and by
     the base.css mobile breakpoint on narrow screens.
     NOTE: never re-introduce a block display here — it neutralises
     width:100% and trips the table-fill pin (test_web_report_table_fill). */
  width: 100%;
  font-size: 0.85rem;
}

/* Amount columns read straight down the page with tabular (monospaced)
   figures, so digits in adjacent rows line up. */
#report-body td {
  font-variant-numeric: tabular-nums;
}

/* Zebra striping + hover highlight make wide forensic tables scannable. */
#report-body tbody tr:nth-child(even) {
  background: var(--surface-raised);
}

#report-body tbody tr:hover {
  background: var(--surface);
}

/* The header row sticks to the top of the #report-body scroll container
   (overflow-x:auto) so column labels stay visible down a long table. */
#report-body thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface);
}

#report-body code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-1);
  /* Long tx hashes / addresses wrap inside narrow cells instead of forcing
     the whole table wider. */
  word-break: break-all;
}

#report-body pre {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  overflow-x: auto;
}

#report-body pre code {
  border: none;
  padding: 0;
  background: transparent;
  /* Long hashes in a code block wrap rather than overflow the <pre>. */
  word-break: break-all;
}

#report-body pre.mermaid {
  /* Mermaid replaces the text with an inline SVG diagram at init;
     give the rendered chart a light track to sit on. */
  text-align: center;
  position: relative;
  cursor: zoom-in;
}

/* --- Report sections — per-kind header cards (P3.3-020 #5) ------------- */
/* report_sections.js tags the four well-known sections with data-section-kind.
   Give each a header-CARD look (raised band + per-kind accent rail + glyph) so an
   analyst can tell 사건개요 / 귀속분석 / 귀속증거 / 추적방법론 apart at a glance. The
   plain disclosure triangle (::before on the summary) is preserved; the kind
   glyph rides the <h2>::before. Only DESCENDANT/attribute selectors here — never
   a new bare `#report-body {` (keeps the first-match overflow-guard pin green). */
#report-body details[data-section-kind] > .report-section-summary {
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
  background: var(--surface-raised);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
}

#report-body details[data-section-kind] .report-section-summary h2 {
  border-bottom: none;
  padding-bottom: 0;
  margin-top: 0;
}

#report-body details[data-section-kind] .report-section-summary h2::before {
  margin-right: var(--space-2);
  font-weight: 700;
}

#report-body details[data-section-kind="overview"] > .report-section-summary {
  border-left-color: var(--accent);
}
#report-body details[data-section-kind="overview"] .report-section-summary h2::before {
  content: "\25A6"; /* ▦ */
  color: var(--accent);
}

#report-body details[data-section-kind="attribution"] > .report-section-summary {
  border-left-color: var(--accent-strong);
}
#report-body details[data-section-kind="attribution"] .report-section-summary h2::before {
  content: "\25CE"; /* ◎ */
  color: var(--accent-strong);
}

#report-body details[data-section-kind="evidence"] > .report-section-summary {
  border-left-color: var(--danger);
}
#report-body details[data-section-kind="evidence"] .report-section-summary h2::before {
  content: "\2726"; /* ✦ */
  color: var(--danger);
}

#report-body details[data-section-kind="flow"] > .report-section-summary {
  border-left-color: var(--status-traced);
}
#report-body details[data-section-kind="flow"] .report-section-summary h2::before {
  content: "\2933"; /* ⤳ */
  color: var(--status-traced);
}

/* 사건개요 / 귀속분석 read as a clean key/value list (markdown emits a `<strong>`
   key per bullet); mute the key and drop the disc so the rows scan as a table. */
#report-body details[data-section-kind="overview"] > ul,
#report-body details[data-section-kind="attribution"] > ul {
  list-style: none;
  padding-left: var(--space-2);
}
#report-body details[data-section-kind="overview"] > ul > li,
#report-body details[data-section-kind="attribution"] > ul > li {
  padding: var(--space-1) 0;
}
#report-body details[data-section-kind="overview"] > ul > li > strong:first-child,
#report-body details[data-section-kind="attribution"] > ul > li > strong:first-child {
  color: var(--muted);
}

/* 추적 방법론: the tagged <ol> renders as numbered circular badges (P3.3-020 #5). */
#report-body ol.report-methodology-steps {
  list-style: none;
  padding-left: 0;
  counter-reset: vca-method-step;
}
#report-body ol.report-methodology-steps > li {
  counter-increment: vca-method-step;
  position: relative;
  padding-left: 2.4rem;
  margin-bottom: var(--space-3);
}
#report-body ol.report-methodology-steps > li::before {
  content: counter(vca-method-step);
  position: absolute;
  left: 0;
  top: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  background: var(--accent);
  color: #fff;
  border-radius: 50%;
  font-size: 0.78rem;
  font-weight: 700;
}

/* --- Report timeline table — reveal/copy cue (P3.3-020 #4) ------------- */
/* report_table_tooltip.js sets a `title` (full value) + a click-to-copy listener
   on the truncated 출발 / 도착 / TX cells; these style the affordance + the flash. */
#report-body td.report-cell-revealable {
  cursor: copy;
}
#report-body td.report-cell-revealable:hover {
  outline: 1px solid var(--accent);
  outline-offset: -1px;
}
#report-body td.report-cell-copied {
  background: var(--status-traced-bg);
}

/* --- Report overview flow strip (P3.3-020 #6) ------------------------- */
/* report_overview_flow.js builds a numbered colour-chip strip from the "전체 흐름
   개요" table and inserts it ABOVE the table (the table/data stay). The per-chip
   --rof-color is a positional hue sweep (a sequence cue, set inline by the JS). */
.report-overview-flow {
  margin: var(--space-3) 0 var(--space-4);
  overflow-x: auto;
}
.report-overview-flow .rof-list {
  display: flex;
  flex-wrap: wrap;
  /* P3.3-021 #1: a wider horizontal gap so the bigger flow arrows below have
     room to sit between steps without crowding the cards. */
  gap: var(--space-3) var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}
.report-overview-flow .rof-step {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  flex: 1 1 9rem;
  min-width: 8rem;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-left: 4px solid var(--rof-color, var(--accent));
  border-radius: var(--radius-sm);
}
.report-overview-flow .rof-step:not(:last-child)::after {
  /* P3.3-021 #1: a larger, accent-coloured flow arrow (was a small muted › at
     the inherited step font size). → at 1.6rem reads clearly as the money-flow
     direction between stages; centred in the wider rof-list gap. */
  content: "\2192"; /* → connector */
  position: absolute;
  right: calc(-1 * var(--space-4) - 1px);
  top: 50%;
  transform: translateY(-50%);
  line-height: 1;
  font-size: 1.6rem;
  font-weight: 400;
  color: var(--accent);
  pointer-events: none;
}
.report-overview-flow .rof-num {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  background: var(--rof-color, var(--accent));
  color: #fff;
  border-radius: 50%;
  font-size: 0.76rem;
  font-weight: 700;
}
.report-overview-flow .rof-info {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}
.report-overview-flow .rof-stage {
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--heading);
}
.report-overview-flow .rof-behavior {
  font-size: 0.78rem;
  color: var(--muted);
}
.report-overview-flow .rof-asset {
  font-size: 0.76rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 700px) {
  .report-overview-flow .rof-step {
    flex-basis: 100%;
  }
  .report-overview-flow .rof-step:not(:last-child)::after {
    content: none;
  }
}

/* --- Diagram full-screen modal (P3.1-009b) --------------------------- */

/* JS-built wrapper around each mermaid pre: the position:relative
   anchor that pins .diagram-expand to ITS diagram instead of the
   viewport (codex round-2 P2 -- sibling buttons otherwise stack at one
   spot when a report has several diagrams). */
.diagram-holder {
  position: relative;
}

.diagram-expand {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  font-size: 0.8rem;
}

.diagram-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  background: rgba(0, 0, 0, 0.85);
}

.diagram-modal-toolbar {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.diagram-modal-toolbar button {
  min-width: 2.4rem;
  font-size: 1rem;
}

.diagram-modal-canvas {
  flex: 1 1 auto;
  overflow: hidden;
  cursor: grab;
  touch-action: none;
}

.diagram-modal-canvas.is-grabbing {
  cursor: grabbing;
}

.diagram-modal-wrapper {
  transform-origin: 0 0;
  width: max-content;
}

.diagram-modal-detail {
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border-top: 1px solid var(--border);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  word-break: break-all;
}

/* Blockquotes read as callout cards: a raised surface + radius + padding,
   keeping the accent left border as the callout cue. */
#report-body blockquote {
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  margin-left: 0;
  margin-right: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  color: var(--muted);
}

/* --- Login page ------------------------------------------------------- */

.login-card {
  max-width: 26rem;
  margin: 10vh auto 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  padding: var(--space-6);
}

.login-card h1 {
  margin-top: 0;
  font-size: 1.3rem;
}

.login-card button[type="submit"] {
  width: 100%;
  margin-top: var(--space-4);
}

.login-card .error {
  color: var(--danger);
}
