/* ImpromptBrü — /graph.
   Every value a token; no hex literal. Rules are prefixed .page where they style a
   link, for the same specificity reason site.css documents at the top.

   THE DRAWING CARRIES KIND AND NOTHING ELSE. Note = jade circle, topic = jade
   rounded square, post = violet diamond, unwritten = a dashed outline with no
   fill: the same four vessels the stream row uses, at graph scale. The brew stage
   is deliberately NOT drawn here — a 26% fill level in a 5px circle is a lie about
   how much a reader can see, and the stage is reachable through the filter and
   printed in the panel. Recency is not drawn either. Structure is the information;
   anything else painted onto 179 nodes turns the picture into noise. */

.gwrap{position:relative;display:grid;grid-template-columns:minmax(0,1fr);background:var(--color-bg-page);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;isolation:isolate}
.gstage{position:relative;overflow:hidden;touch-action:none;cursor:grab;min-height:0}
.gstage.dragging{cursor:grabbing}
.gsvg{display:block;width:100%;height:100%}

/* ---------- edges ---------- */
.ged{stroke:var(--color-stage-rim);stroke-width:calc(.9px / var(--gk,1));fill:none;opacity:.3}
/* A dead link is drawn, but as a dotted tether rather than a bond: it is a
   direction of reach, not a connection that resolves. */
.ged.dead{stroke-dasharray:calc(2px / var(--gk,1)) calc(3px / var(--gk,1));opacity:.2}
.ged.lit{stroke:var(--color-accent);opacity:.95;stroke-width:calc(1.5px / var(--gk,1))}
.ged.dim{opacity:.04}

/* ---------- nodes ---------- */
.gn{cursor:pointer}
/* 130ms ease as a literal, matching site.css:465. There are no motion tokens in
   this system's tokens/ closure — --dur-* and --ease-* do not exist — and a
   transition written against a missing custom property is dropped whole at parse
   time, so the fade silently did nothing. Adding motion tokens is a system-wide
   decision, not a graph one. */
.gn .gsh{stroke-width:calc(1.4px / var(--gk,1));transition:opacity 130ms ease}
.gn[data-k="note"] .gsh{fill:var(--color-stage-wash);stroke:var(--color-type-note)}
.gn[data-k="topic"] .gsh{fill:var(--color-type-note);stroke:var(--color-type-note)}
.gn[data-k="post"] .gsh{fill:var(--color-type-post);stroke:var(--color-type-post)}
/* Unwritten: outline only, dashed, no fill. It reads as an outline of a page that
   is not there, which is exactly what it is. No badge, no question mark, no icon. */
.gn[data-k="ghost"] .gsh{fill:none;stroke:var(--color-text-faint);stroke-dasharray:calc(2.2px / var(--gk,1)) calc(2.4px / var(--gk,1));stroke-width:calc(1px / var(--gk,1))}
/* Not published: the same grey, but a SOLID outline. The page exists — it just does
   not leave the vault — and a dashed outline would say the opposite. */
.gn[data-k="private"] .gsh{fill:none;stroke:var(--color-text-faint);stroke-width:calc(1px / var(--gk,1))}
.gn .ghit{fill:transparent;stroke:none}
.gn.lit .gsh{stroke:var(--color-accent);stroke-width:calc(2px / var(--gk,1))}
.gn.dim{opacity:.12}
.gn.hidden{display:none}
.gn.sel .gsh{stroke:var(--color-heading);stroke-width:calc(2.4px / var(--gk,1))}

/* ---------- labels ----------
   Three tiers, shown by zoom level so the page is never a field of overlapping
   type. Tier 1 is the crossroads that gather 6 or more, and they are legible at
   rest — a graph whose landmarks are unlabelled until you zoom is a maze.

   Every metric is divided by --gk, the live zoom scale published by graph.js, so a
   label renders at the same size whatever the zoom. Without this the names are 4px
   at fit and 30px zoomed in, and the tier system is pointless. */
.glab{font-family:var(--font-sans);font-size:calc(10px / var(--gk,1));font-weight:var(--weight-semibold);fill:var(--color-text-muted);paint-order:stroke;stroke:var(--color-bg-page);stroke-width:calc(3px / var(--gk,1));stroke-linejoin:round;pointer-events:none;display:none;transform:translate(calc(6px / var(--gk,1)),calc(3.4px / var(--gk,1)))}
.gn[data-tier="1"] .glab{display:block;fill:var(--color-heading);font-size:calc(11.5px / var(--gk,1))}
.gwrap[data-z="1"] .gn[data-tier="2"] .glab,.gwrap[data-z="2"] .gn[data-tier="2"] .glab{display:block}
.gwrap[data-z="2"] .gn[data-tier="3"] .glab{display:block}
.gn.lit .glab,.gn.sel .glab{display:block;fill:var(--color-heading)}
.gn.dim .glab{display:none}

/* ---------- the control channel ----------
   The bench's own parts, in the bench's own order: what is shown, then what stage.
   Same class names as site.css where the control is the same control. */
.gctl{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-4);padding:var(--space-3) var(--space-4);border-bottom:var(--border-hair) solid var(--color-border);background:var(--color-bg-surface)}
.gctl .knob{display:flex;align-items:center;gap:var(--space-2)}
.gspace{margin-left:auto}
.gzoomctl{display:inline-flex;align-items:center;gap:2px}
.gzb{appearance:none;display:grid;place-items:center;width:26px;height:26px;padding:0;border:var(--border-hair) solid var(--color-border);background:var(--color-bg-page);color:var(--color-text-muted);border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:var(--text-xs);cursor:pointer}
.gzb:hover{border-color:var(--color-accent);color:var(--color-text-accent)}
.gzb.wide{width:auto;padding:0 9px;letter-spacing:var(--tracking-micro);text-transform:uppercase;font-size:var(--text-2xs)}
.greadout{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}

/* ---------- the panel ----------
   Overlaid on the canvas, not a third column: a graph that loses a quarter of its
   width the moment you touch it is a graph you stop touching. It is the same width
   as the note page's rail so the property has one measure for "a column of type
   beside something". */
.gpanel{position:absolute;top:var(--space-3);right:var(--space-3);width:290px;max-width:calc(100% - var(--space-5));background:var(--color-bg-surface-raised);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-md);box-shadow:var(--shadow-card);padding:var(--space-4);display:none;max-height:calc(100% - var(--space-6));overflow:auto}
.gpanel.open{display:block}
.gpx{position:absolute;top:6px;right:8px;appearance:none;border:0;background:none;color:var(--color-text-faint);font-family:var(--font-mono);font-size:var(--text-sm);cursor:pointer;line-height:1}
.gpx:hover{color:var(--color-heading)}
.gpeyebrow{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;margin-bottom:var(--space-2)}
.gpt{margin:0 0 var(--space-2);font-family:var(--font-display);font-weight:var(--weight-extrabold);font-size:var(--text-lg);line-height:1.2;color:var(--color-heading);letter-spacing:-.015em;text-wrap:pretty;padding-right:var(--space-4)}
.gpcount{margin:0 0 var(--space-3);font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
/* The category, as a word on the arc's own hue. Not a chip: the badge beside it is
   already a chip, and two chips in one eyebrow row read as two controls. */
.gpcat{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:oklch(var(--cl,74%) var(--cc,.058) var(--h,158))}
.gpcat.neutral{color:var(--color-text-faint)}
.gpmore{margin:var(--space-2) 0 0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.gplist{list-style:none;margin:0;padding:0;border-top:var(--border-hair) solid var(--color-border)}
.gplist li{border-bottom:var(--border-hair) solid var(--color-border)}
.page .gpr{display:grid;grid-template-columns:12px minmax(0,1fr);gap:var(--space-2);align-items:baseline;padding:6px 2px;text-decoration:none;color:var(--color-text)}
.page .gpr:hover{color:var(--color-text-accent);text-decoration:none}
.gprt{font-size:var(--text-sm);font-weight:var(--weight-semibold);line-height:1.35;color:inherit;letter-spacing:-.01em;text-wrap:pretty}
.gprd{display:block;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);font-weight:var(--weight-regular)}
.gpopen{display:inline-flex;align-items:center;gap:6px;margin-top:var(--space-3);padding:7px 14px;border-radius:var(--radius-pill);background:var(--color-accent);color:var(--color-on-accent);font-size:var(--text-sm);font-weight:var(--weight-semibold);text-decoration:none}
.page .gpopen:hover{background:var(--color-accent-hover);color:var(--color-on-accent);text-decoration:none}
/* An unwritten page has no route, so it gets a sentence instead of a button. No
   apology and no invitation to come back later — just what is true. */
.gpnone{margin:var(--space-3) 0 0;font-size:var(--text-sm);color:var(--color-text-muted);text-wrap:pretty}

/* ---------- legend ---------- */
.glegend{display:flex;flex-wrap:wrap;gap:var(--space-4);padding:var(--space-3) var(--space-4);border-top:var(--border-hair) solid var(--color-border);background:var(--color-bg-surface);font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.glg{display:inline-flex;align-items:center;gap:7px}
.glgm{width:11px;height:11px;flex:none;box-sizing:border-box}
.glgm[data-k="note"]{border-radius:50%;border:1.5px solid var(--color-type-note);background:var(--color-stage-wash)}
.glgm[data-k="topic"]{border-radius:2px;background:var(--color-type-note)}
.glgm[data-k="post"]{width:9px;height:9px;border-radius:1.5px;background:var(--color-type-post);transform:rotate(45deg)}
.glgm[data-k="ghost"]{border-radius:50%;border:1px dashed var(--color-text-faint)}

/* ---------- the no-JS picture ----------
   The settled graph as a plain SVG, shipped in the page. It loses every control
   and keeps the shape, which is the half of this page that is worth having
   without a script. */
.gnojs{display:block;padding:var(--space-4)}
.gjs .gnojs{display:none}
.gjs .gctl,.gjs .gstage,.gjs .glegend{display:flex}
.gjs .gstage{display:block}
.gwrap:not(.gjs) .gctl,.gwrap:not(.gjs) .gstage,.gwrap:not(.gjs) .glegend{display:none}

@media (max-width:760px){
  .gctl{gap:var(--space-3)}
  .gspace{margin-left:0}
  .gpanel{top:auto;bottom:var(--space-3);left:var(--space-3);right:var(--space-3);width:auto;max-height:56%}
}
@media (prefers-reduced-motion:reduce){.gn .gsh{transition:none}}
