/* ── canvas ───────────────────────────────────────────────────────────────────
 * One node's neighborhood, drawn. Every position in the markup was computed
 * on the server, so this file only sizes and colors what is already placed.
 * The tokens are the SERP's own: the accent ring is where you are, the danger
 * red is a controlled node, and the grey ramp between them is the year.
 * ------------------------------------------------------------------------- */
.canvas-grid {
  display:grid; grid-template-columns:var(--ox-rail-width) minmax(0,1fr) 380px;
  height:calc(100vh - var(--ox-topbar-height));
}
.canvas-list { position:relative; overflow-y:auto; border-right:1px solid var(--ox-border-default); }
/* The rows ARE result rows -- same markup, same class -- so a reader moving
   from the search to the canvas is reading the same list. These add only the
   state the SERP has no use for: which row the cursor is on. */
.cres { border-left:3px solid transparent; }
/* On a report page .res is not loaded, so a row carries the little it needs. */
.canvas-boxed .cres { display:block; padding:10px 14px; border-bottom:1px solid var(--ox-border-subtle); font-size:13px; line-height:1.4; }
.canvas-boxed .cres .rt { color:var(--ox-link); font-size:14px; }
.canvas-boxed .cres .meta { color:var(--ox-text-tertiary); }
.cres.origin { background:var(--ox-bg-subtle); border-left-color:var(--ox-accent); }
.cres.on { background:var(--ox-bg-muted); border-left-color:var(--ox-text-tertiary); }

.canvas-stage { position:relative; overflow:hidden; background:var(--ox-bg-base); }
/* user-select, because a drag across a labelled drawing otherwise selects the
   labels instead of moving the view. */
.canvas-svg { width:100%; height:100%; cursor:grab; touch-action:none; user-select:none; }
.canvas-grid.panning .canvas-svg { cursor:grabbing; }
/* Lines are drawn at 1px and scale with the view, so the stroke thins as the
   reader zooms in. The wide companion is what the pointer actually hits: a
   1px target is not one a cursor can find. */
.cedge-line { stroke:var(--ox-border-default); stroke-width:calc(1px / var(--k, 1)); pointer-events:none; }
.cedge-hit { stroke:transparent; stroke-width:calc(11px / var(--k, 1)); }
.cedge.hot .cedge-line { stroke:var(--ox-text-tertiary); stroke-width:calc(1.8px / var(--k, 1)); }
.cedge.lit .cedge-line { stroke:var(--ox-accent); stroke-width:calc(2.4px / var(--k, 1)); }
.cedge { cursor:pointer; }
.cnode { cursor:pointer; }
/* Truncated on the server; the tip and the panel carry the whole name. The
   halo is what keeps two overlapping labels readable, and the label holds its
   size as the drawing grows so zooming in reads as moving closer. */
.clabel {
  font-size:calc(14px / var(--k, 1)); fill:var(--ox-text-tertiary); text-anchor:middle;
  pointer-events:none; paint-order:stroke; stroke:var(--ox-bg-base);
  stroke-width:calc(4px / var(--k, 1)); stroke-linejoin:round;
}
.clabel.origin, .clabel.on { fill:var(--ox-text-primary); font-weight:600; }
.clabel.dim { opacity:.28; }
.cfront { pointer-events:none; }
/* A crowded drawing names only some nodes at rest; the rest are named when
   pointed at, focused or picked. */
.clabel.quiet:not(.on):not(.origin) { display:none; }
/* Every stroke on a circle is divided by the zoom for the same reason the
   lines are: a 2px ring drawn inside a group scaled to 3x is a 6px ring, and
   the rings swallowed their own circles as soon as a reader zoomed in. */
.cnode .dot { stroke:var(--ox-border-strong); stroke-width:calc(.8px / var(--k, 1)); }
.cnode.ctrl .dot { stroke:var(--ox-danger-fg); stroke-width:calc(2px / var(--k, 1)); }
.cnode .ring { fill:none; stroke:var(--ox-accent); stroke-width:calc(2px / var(--k, 1)); opacity:0; }
.cnode.origin .ring, .cnode.on .ring { opacity:1; }
.cnode.dim { opacity:.28; }
.cnode:focus-visible { outline:none; }
.cnode:focus-visible .ring { opacity:1; stroke-dasharray:calc(3px / var(--k, 1)) calc(3px / var(--k, 1)); }

.canvas-tip {
  position:absolute; z-index:2; transform:translateX(-50%); pointer-events:none;
  max-width:260px; padding:6px 10px; border-radius:var(--ox-radius-sm);
  background:var(--ink); color:var(--ox-text-inverse); font-size:13px; line-height:1.35;
  box-shadow:var(--ox-elev-overlay);
}
/* The cap and the counts ride here, in the line the canvas already carries.
   There is no box on the drawing explaining itself. */
.canvas-status { position:absolute; left:20px; bottom:16px; margin:0; font-size:13px; color:var(--ox-text-muted); }
.canvas-back { position:absolute; left:14px; top:12px; z-index:2; }
.canvas-tools { position:absolute; right:14px; top:12px; z-index:2; display:flex; gap:6px; }
/* Its own button, not .btn: a report page loads this file without search.css. */
.canvas-btn {
  height:30px; padding:0 12px; border:1px solid var(--ox-border-default); border-radius:var(--ox-radius-md);
  background:var(--ox-bg-base); color:var(--ox-text-primary); font:500 13px var(--ox-font-sans); cursor:pointer;
}
.canvas-btn:hover { background:var(--ox-bg-muted); }
.canvas-legend { position:absolute; right:20px; bottom:16px; width:190px; }
.canvas-legend .ramp { height:8px; border-radius:2px; background:linear-gradient(90deg,#e8e8e8,#4a4a4a); }
.canvas-legend .ends {
  display:flex; justify-content:space-between; align-items:center; gap:8px;
  font-size:12px; color:var(--ox-text-muted); padding-top:3px;
}
.canvas-legend .ends svg { display:inline-block; vertical-align:-2px; }

.canvas-detail { overflow-y:auto; border-left:1px solid var(--ox-border-default); padding:22px 24px 60px; }
.cpanel h2 { font-size:26px; font-weight:700; line-height:1.2; letter-spacing:-.02em; margin:12px 0 18px; text-wrap:balance; }
.cpanel .here { color:var(--ox-text-muted); }
.cpanel .mono { font-family:var(--ox-font-mono); font-size:13px; }

/* The same canvas inside a report: a box of fixed height in the page, and the
   whole screen once Expand hands it to the browser's full-screen mode. */
.canvas-grid.canvas-boxed {
  grid-template-columns:240px minmax(0,1fr) 320px; height:680px;
  border:1px solid var(--ox-border-default); background:var(--ox-bg-base); color:var(--ox-text-primary);
}
/* The status line is longer than a boxed stage leaves beside the legend, so the legend moves to the top. */
.canvas-boxed .canvas-legend { top:16px; bottom:auto; left:20px; right:auto; }
.canvas-grid:fullscreen .canvas-legend { top:auto; bottom:16px; left:auto; right:20px; }
/* The background is not decoration here: a full-screen element sits on a
   backdrop the browser paints black, and the list and the panel carry no
   background of their own, so both went black around a white drawing. */
.canvas-grid:fullscreen {
  height:100vh; overflow-y:auto; grid-template-columns:var(--ox-rail-width) minmax(0,1fr) 380px;
  background:var(--ox-bg-base); color:var(--ox-text-primary);
}

@media (max-width:1100px) { .canvas-grid { grid-template-columns:250px minmax(0,1fr) 310px; } }
/* Narrow: the same three parts, stacked, with the drawing between the names
   and the panel so a tap on a row still has somewhere to land. */
@media (max-width:900px) {
  .canvas-grid, .canvas-grid.canvas-boxed { grid-template-columns:1fr; height:auto; }
  .canvas-list { max-height:40vh; border-right:0; border-bottom:1px solid var(--ox-border-default); }
  .canvas-stage { height:70vh; }
  .canvas-detail { border-left:0; border-top:1px solid var(--ox-border-default); }
  /* Status and legend anchor to opposite bottom corners of the same row; at
     900px the legend's fixed 190px plus the status line's text run into each
     other regardless of wrap width, since the status text length varies with
     the query. Give the legend its own row above instead of sharing one. */
  .canvas-status { max-width:calc(100% - 40px); }
  .canvas-legend { bottom:60px; }
}
