/* ============================================================
   studio.INTERVAL — v2 FLOW engine
   One persistent tile per project; every mode is just a target
   geometry. CSS transitions morph between galerie / raster /
   index / zeit / graph. Raum 3D crossfades to WebGL.
   ============================================================ */

.flowwrap{ position:relative; }
.flow{ position:relative; width:100%; }

/* the persistent project tile */
.fc{
  position:absolute; top:0; left:0; display:block;
  transition:left .82s cubic-bezier(.5,.05,.15,1),
             top .82s cubic-bezier(.5,.05,.15,1),
             width .82s cubic-bezier(.5,.05,.15,1),
             height .82s cubic-bezier(.5,.05,.15,1),
             opacity .5s ease;
  will-change:left,top,width,height;
}
.fc__img{ position:relative; display:block; width:100%; height:100%; overflow:hidden; background:#fafafa;
  border-radius:0%;   /* %-based so it can morph to 50% (a circle) without length↔% snap */
  transition:opacity .5s ease, border-radius .82s ease; }
.fc__img img{ width:100%; height:100%; object-fit:cover; display:block;
  -webkit-user-drag:none; user-drag:none; pointer-events:none; }  /* no native image drag → node-drag grabs the node */
.fc__massing{ position:absolute; inset:0; opacity:0; transition:opacity .5s ease; pointer-events:none; }
.fc.models .fc__img img{ opacity:0; }
.fc.models .fc__massing{ opacity:1; }
/* 3D models are freestanding objects on the page — never a boxed container */
.fc.models .fc__img{ overflow:visible; border-radius:0; background:transparent; box-shadow:none; }
.fc.models .fc__massing{ overflow:visible; inset:-25% -20%; }
.fc.models .massing{ overflow:visible; background:transparent; }
.fc.models .massing__shadow{ opacity:.45; }

/* caption: number + title (hover only in image modes) */
.fc__cap{ position:absolute; left:0; right:0; top:100%; margin-top:9px;
  display:flex; justify-content:space-between; gap:12px; align-items:baseline;
  opacity:0; transition:opacity .3s ease; pointer-events:none; }
.fc:hover .fc__cap{ opacity:1; }
.fc__t{ font-size:14px; font-weight:500; letter-spacing:-0.01em; color:var(--ink); white-space:nowrap; }
.fc__no{ font-family:var(--mono); font-size:12px; color:var(--grey); margin-right:8px; }
.fc__m{ font-family:var(--mono); font-size:12px; color:var(--grey); white-space:nowrap; }
.fc:hover .fc__t{ color:var(--accent); }

/* ---- INDEX mode: tiles become rows, image hidden, text shown ---- */
.flow.is-index .fc .fc__img{ opacity:0; }
.flow.is-index .fc .fc__cap{ opacity:1; top:0; margin-top:0; height:100%; align-items:center;
  border-bottom:1px solid var(--line); }
.flow.is-index .fc{ }
.flow.is-index .fc__t{ font-size:clamp(16px,1.5vw,19px); }
.flow.is-index .fc:hover{ padding-left:0; }
.flow.is-index .fc .ix-extra{ opacity:1; }
.ix-extra{ font-family:var(--mono); font-size:12px; color:var(--grey); opacity:0; transition:opacity .4s; }

/* in graph + zeit, captions are smaller and hover-only */
.flow.is-graph .fc__cap, .flow.is-zeit .fc__cap{ justify-content:flex-start; }
/* title stays on one line; Ort · Jahr drops to the second line if it doesn't fit */
.flow.is-graph .fc__cap, .flow.is-raum .fc__cap{ flex-wrap:wrap; }
.flow.is-graph .fc__m, .flow.is-raum .fc__m{ flex-basis:100%; }

/* ---- overlay: graph connector lines ---- */
.flow-lines{ position:absolute; inset:0; width:100%; height:100%; overflow:visible; pointer-events:none;
  opacity:0; transition:opacity .6s ease; z-index:0; }
.flow.is-graph .flow-lines, .flow.is-raum .flow-lines, .flow.is-zeit .flow-lines{ opacity:1; }
.flow-lines line{ stroke:var(--ink); stroke-width:1; opacity:.16; }
.flow-lines line.hot{ stroke:var(--accent); opacity:.85; stroke-width:1.4; }
.flow-lines line.tlc{ stroke:var(--line); stroke-width:1; opacity:.9; }
.flow-lines line.dim{ opacity:.05; }

/* ---- graph + raum: nodes become circles (radius driven inline by JS) ---- */
.flow.is-graph .fc, .flow.is-raum .fc{ z-index:2; }
.flow.is-graph .fc{ cursor:grab; }
.flow.is-graph.graph-dragging .fc{ cursor:grabbing; }
/* hover grows the node back to full size; image stays centred on the node */
.flow.is-graph .fc.hot{ transform:scale(1.34); z-index:60; }
.flow.is-graph.has-hot .fc:not(.hot):not(.rel){ opacity:.2; }
.flow.is-graph .fc.q-out{ opacity:.12 !important; }
.flow.is-graph .fc__img, .flow.is-raum .fc__img{ overflow:hidden; box-shadow:none; background:transparent; }
.flow.is-graph .fc__cap, .flow.is-raum .fc__cap{ top:auto; bottom:-26px; justify-content:center; }
.flow.is-raum{ cursor:grab; touch-action:none; }
.flow.is-raum:active{ cursor:grabbing; }
/* 3D graph: lift connector lines + hashtag labels above the fixed header
   (nodes are inline-styled ~z2000, so these sit just under them) */
.flow.is-raum .flow-lines{ z-index:1500; }
.flow.is-raum .flow-kw{ z-index:1520; }

/* ---- 3D models in graph/raum: real floating objects, NOT clipped ---- */
.flow.is-graph .fc.models .fc__img, .flow.is-raum .fc.models .fc__img{
  overflow:visible; border-radius:0; background:transparent; box-shadow:none; }
.flow.is-graph .fc.models .fc__massing, .flow.is-raum .fc.models .fc__massing{
  border-radius:0; overflow:visible; inset:-40% -30%; }
.flow.is-graph .fc.models .massing, .flow.is-raum .fc.models .massing{
  overflow:visible; background:transparent; }
.flow.is-graph .fc.models .massing__shadow, .flow.is-raum .fc.models .massing__shadow{ opacity:.5; }

/* ---- raster: drawn plan grid with A/B/C + 1/2/3 axes ---- */
.flow.is-raster .fc{ z-index:1; }
.rgrid{ position:absolute; left:0; top:0; pointer-events:none; z-index:0; overflow:visible; }
.rgrid line{ stroke:var(--line); stroke-width:1;
  stroke-dasharray:var(--len); stroke-dashoffset:var(--len);
  animation:rdraw .55s cubic-bezier(.45,.55,.15,1) forwards; animation-delay:var(--d,0s); }
@keyframes rdraw{ to{ stroke-dashoffset:0; } }
.rgrid text{ font-family:var(--mono); font-size:11px; letter-spacing:0.04em; fill:var(--grey);
  opacity:0; animation:rfade .45s ease forwards; animation-delay:var(--d,0s); }
@keyframes rfade{ to{ opacity:1; } }
@media (prefers-reduced-motion: reduce){
  .rgrid line{ stroke-dashoffset:0; animation:none; }
  .rgrid text{ opacity:1; animation:none; }
}

/* ---- overlay: timeline axis ---- */
.flow-axis{ position:absolute; top:0; bottom:0; left:50%; width:1px; background:var(--ink);
  opacity:0; transition:opacity .6s ease; }
.flow.is-zeit .flow-axis{ opacity:1; }
/* fine „month“ ruler ticks straddling the axis (the measured cadence) */
.flow-ticks{ position:absolute; display:none; opacity:0; z-index:1; pointer-events:none;
  transition:opacity .5s ease; }
.flow.is-zeit .flow-ticks{ opacity:.6; }
/* on entrance, ticks fade in only AFTER the whole axis line has been drawn (~.8s) */
.flow.zeit-in .flow-ticks{ opacity:0; animation:tickIn .5s ease both; animation-delay:.95s; }
@keyframes tickIn{ from{ opacity:0; } to{ opacity:.6; } }
.flow-year{ position:absolute; transform:translate(-50%,-50%); background:#fff; padding:3px 10px;
  font-family:var(--mono); font-size:12px; letter-spacing:0.04em; color:var(--accent-deep);
  opacity:0; transition:opacity .5s ease; z-index:3; }
.flow.is-zeit .flow-year{ opacity:1; }
.flow-dot{ position:absolute; transform:translate(-50%,-50%); width:7px; height:7px; border-radius:50%;
  background:var(--ink); opacity:0; transition:opacity .5s ease, background .25s; z-index:3; }
.flow.is-zeit .flow-dot{ opacity:1; }
/* horizontal timeline becomes a sideways-scrolling strip (scrollbar hidden) */
.flow.is-zeit-h{ overflow-x:auto; overflow-y:hidden; scrollbar-width:none; -ms-overflow-style:none; }
.flow.is-zeit-h::-webkit-scrollbar{ display:none; width:0; height:0; }
/* during the rotate, clip the long accent bar to the crop container */
.flow.zeit-fading{ overflow:hidden; }
.flow.is-zeit-h .fc__cap{ position:absolute; }

/* ---- zeit axis tools (flip · rotate) — icon-only, like the graph solver.
   Live in the flow WRAPPER (not the scroller) so they stay put in horizontal mode. ---- */
.zeit-tools{ position:absolute; top:12px; left:12px; z-index:30; display:none;
  align-items:center; gap:4px; opacity:.45; transition:opacity .2s ease; }
.flowwrap:has(.flow.is-zeit) .zeit-tools{ display:inline-flex; }
.flowwrap:has(.flow.is-zeit) .zeit-tools:hover{ opacity:1; }
.tl-flip{ display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; padding:0; background:none; border:none; cursor:pointer;
  color:var(--grey-2); border-radius:5px; transition:color .15s ease, background .15s ease; }
.tl-flip:hover{ color:var(--ink); }
.tl-flip[aria-pressed="true"]{ color:var(--accent); background:color-mix(in srgb,var(--accent) 14%, transparent); }
.tl-flip svg{ width:15px; height:15px; }

/* ---- KARTE / map mode ---- */
.flow-map{ display:none; position:absolute; inset:0; width:100%; }
.mtoggle[hidden]{ display:none !important; }
.flow.is-karte .flow-map{ display:block; }
.flow.is-karte .flow-lines,
.flow.is-karte .flow-axis,
.flow.is-karte .flow-ticks,
.flow.is-karte .flow-kw,
.flow.is-karte .zeit-tools{ display:none !important; }
/* in Karte the project tiles become round photo-markers — mirror the graph look
   (transparent container, no white box) so the rect→circle morph matches */
.flow.is-karte .fc{ z-index:6; }
.flow.is-karte .fc__img{ overflow:hidden; box-shadow:none; background:transparent; }
.flow.is-karte .fc__cap, .flow.is-karte .fc__info{ display:none !important; }
.fc.fc--map{ overflow:visible; aspect-ratio:auto !important; }
.fc.fc--map .fc__img{ border-radius:50%; width:100%; height:100%; }
.fc.fc--map .fc__img img{ width:100%; height:100%; object-fit:cover; }
.fc.fc--map.is-active .fc__img{ box-shadow:0 0 0 3px var(--accent), 0 6px 18px rgba(0,0,0,.34); }
.flow-map__svg{ display:block; width:100%; height:auto; }
.map-gl{ width:100%; height:100%; opacity:0; transition:opacity 1.5s ease; }
.map-gl.shown{ opacity:1; }
/* connector overlay (dots + lines) above the basemap, below the tiles */
.map-conn{ position:absolute; inset:0; width:100%; height:100%; z-index:5; pointer-events:none; overflow:visible; }
.map-conn .map-node__line{ stroke:var(--ink); stroke-width:1; opacity:.5; stroke-dasharray:2 3; }
.map-conn .map-node__cdot{ fill:var(--accent); }
/* edit mode: the dot is the drag handle */
.map-editing .map-conn{ pointer-events:none; }
.map-editing .map-conn .map-node__cdot{ pointer-events:all; cursor:grab; stroke:transparent; stroke-width:20; }
.map-editing .map-conn .map-node__cdot:active{ cursor:grabbing; }
/* hidden admin entry on the map — tiny dot bottom-right on desktop, hidden on mobile */
.map-edit{ position:absolute; bottom:0; left:50%; transform:translateX(-50%); width:30px; height:30px; z-index:7; border:none; background:none; cursor:pointer; padding:0; }
.map-edit::after{ content:""; position:absolute; bottom:11px; left:50%; transform:translateX(-50%); width:6px; height:6px; border-radius:50%;
  background:var(--ink); opacity:0; transition:opacity .25s ease, background .2s ease; }
.map-edit:hover::after{ opacity:.4; }
.map-edit.on::after{ opacity:1; background:var(--accent); box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 20%, transparent); }
@media(max-width:640px){ .map-edit{ display:none; } }
.map-editing .map-mk{ cursor:grab; }
.map-editing .map-mk.is-drag{ box-shadow:0 0 0 2px #fff, 0 0 0 4px var(--accent); }
.map-flash{ position:absolute; left:50%; bottom:16px; transform:translate(-50%,8px); z-index:8;
  background:var(--ink); color:#fff; font-family:var(--mono); font-size:11px; letter-spacing:0.03em;
  padding:8px 14px; border-radius:8px; opacity:0; pointer-events:none; transition:opacity .2s, transform .2s; }
.map-flash.on{ opacity:1; transform:translate(-50%,0); }
.map-gl .maplibregl-ctrl-attrib,
.map-gl .maplibregl-ctrl-logo{ display:none !important; }

/* ---- project nodes: dot → connector → round photo ---- */
.map-nodes{ position:absolute; inset:0; z-index:5; pointer-events:none; }
.map-nodes__svg{ position:absolute; inset:0; width:100%; height:100%; overflow:visible; }
.map-node__line{ stroke:var(--ink); stroke-width:1; }
.map-node__dot{ position:absolute; top:0; left:0; width:7px; height:7px; margin:-3.5px 0 0 -3.5px;
  border-radius:50%; background:var(--accent); box-shadow:0 0 0 2px #fff; }
.map-node__dot.is-office{ width:13px; height:13px; margin:-6.5px 0 0 -6.5px; background:#fff;
  border:none; box-shadow:0 0 0 2px var(--accent), 0 0 0 5px color-mix(in srgb,var(--accent) 20%, transparent); }
.map-node__olabel{ font-family:var(--mono); font-size:12px; letter-spacing:0.04em; font-weight:500;
  color:var(--accent-deep); white-space:nowrap;
  position:absolute; left:50%; top:13px; transform:translateX(-50%);
  background:color-mix(in srgb,#FBFAF8 78%, transparent); -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
  padding:1px 6px; border-radius:4px; }
/* mini-map office marker (Kontakt) — same dot + label as the projects map */
.map-mini-office{ position:relative; width:0; height:0; }
.map-nodes.is-editing .map-node__photo{ cursor:grab; }
.map-nodes.is-editing .map-node__photo:active{ cursor:grabbing; }
.map-nodes.is-editing .map-node__photo.is-office{ pointer-events:auto; cursor:grab; }
.map-intl{ position:absolute; left:14px; bottom:14px; z-index:6; display:flex; flex-direction:column; gap:6px; }
.map-intl__chip{ display:inline-flex; align-items:center; gap:8px; font-family:var(--mono); font-size:11px;
  letter-spacing:0.04em; color:var(--ink-2); background:color-mix(in srgb,var(--bg) 88%, transparent);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px); border:1px solid var(--line-2);
  border-radius:999px; padding:5px 12px; cursor:pointer; transition:color .15s, border-color .15s; }
.map-intl__chip:hover{ color:var(--accent); border-color:var(--accent); }
.map-intl__chip .d{ width:7px; height:7px; border-radius:50%; background:var(--grey); }
.map-intl__chip:hover .d{ background:var(--accent); }
.map-grat line{ stroke:var(--line); stroke-width:1; opacity:.7; }
.map-nb__t{ fill:var(--grey-2); font-family:var(--mono); font-size:13px; letter-spacing:0.12em; text-anchor:middle; }
.map-lakes path{ fill:color-mix(in srgb,var(--accent) 12%, transparent); stroke:color-mix(in srgb,var(--accent) 35%, transparent); stroke-width:1; }
.map-border{ fill:color-mix(in srgb,var(--ink) 3%, transparent); stroke:var(--ink); stroke-width:1.6; stroke-linejoin:round; }
.map-spoke{ stroke:var(--grey-2); stroke-width:1; opacity:.6; }
.map-city{ fill:var(--ink); }
.map-city__t{ fill:var(--ink-2); font-family:var(--mono); font-size:13px; letter-spacing:0.04em; }
.map-pin{ cursor:pointer; outline:none; }
.map-pin__hit{ fill:transparent; }
.map-pin__dot{ fill:var(--ink); transition:fill .15s ease, r .15s ease; }
.map-pin:hover .map-pin__dot,
.map-pin:focus .map-pin__dot{ fill:var(--accent); r:4.4; }
.map-office__ring{ fill:none; stroke:var(--accent); stroke-width:1.4; }
.map-office__t{ fill:var(--accent-deep); font-family:var(--mono); font-size:13px; letter-spacing:0.05em; font-weight:500; }
.map-intl__t{ fill:var(--grey); font-family:var(--mono); font-size:12.5px; letter-spacing:0.05em; }
.map-pin--intl .map-pin__dot{ fill:var(--grey); }

.map-tip{ position:absolute; z-index:6; width:220px; pointer-events:none;
  background:color-mix(in srgb,var(--bg) 92%, transparent); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border:1px solid var(--line-2); box-shadow:0 16px 40px rgba(0,0,0,.13); border-radius:10px; overflow:hidden;
  display:flex; gap:0; }
.map-tip[hidden]{ display:none; }
.map-tip__thumb{ flex:none; width:74px; align-self:stretch; background:#ddd center/cover no-repeat; }
.map-tip__meta{ display:flex; flex-direction:column; gap:2px; padding:9px 11px; min-width:0; }
.map-tip__meta b{ font-size:13px; letter-spacing:-0.01em; }
.map-tip__meta span{ font-family:var(--mono); font-size:10.5px; color:var(--grey); }
.map-tip__typ{ color:var(--accent-deep) !important; }

/* ---- raum layer (legacy WebGL crossfade — no longer used) ---- */
.flow-raum{ display:none; }

/* ---- zeit hover info: reveals details about each piece ---- */
.fc__info{ position:absolute; left:0; right:0; bottom:0; z-index:4; padding:12px 13px;
  display:none; flex-direction:column; gap:5px;
  background:color-mix(in srgb, #ffffff 92%, transparent);
  -webkit-backdrop-filter:blur(5px); backdrop-filter:blur(5px);
  opacity:0; transform:translateY(8px); transition:opacity .26s ease, transform .26s ease; pointer-events:none; }
.flow.is-zeit .fc__info{ display:flex; }
.flow.is-zeit .fc:hover .fc__info{ opacity:1; transform:none; }
.fc__info-t{ font-family:var(--mono); font-size:10.5px; letter-spacing:0.04em; color:var(--accent); }
.fc__info-d{ font-size:12px; line-height:1.42; color:var(--ink); text-wrap:pretty;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
.fc__info-m{ font-family:var(--mono); font-size:10px; letter-spacing:0.03em; color:var(--grey); }

/* ---- the axis line physically rotating ---- */
.flow.zeit-spinning .flow-dot,
.flow.zeit-spinning .flow-year{ opacity:0 !important; }
.flow.zeit-spinning .flow-lines{ opacity:0 !important; }
.flow.zeit-spinning .flow-axis{ z-index:5; background:var(--accent); }

/* 90° rotate: fade tiles fully out, snap to the new layout, fade back in
   (opacity-only transition makes the position change instant = no nervous move) */
.flow.zeit-fading .fc{ opacity:0 !important; transition:opacity .3s ease !important; }
/* during the rotate, the axis line + dots + years + connectors fade too,
   then the axis is redrawn growing from the centre of the screen */
.flow.zeit-chrome-out .flow-ticks{ opacity:0 !important; transition:opacity .28s ease !important; }
/* reverse draw-out: YEARS leave first, then dots + connector lines */
.flow.zeit-chrome-out .flow-year{ opacity:0 !important; transition:opacity .22s ease !important; }
.flow.zeit-chrome-out .flow-dot,
.flow.zeit-chrome-out .flow-lines{ opacity:0 !important; transition:opacity .28s ease .2s !important; }

/* ---- zeit entrance: line draws from the centre out, then dots + lines, then years ----
   OVERLAP v1 (slower + overlapping). Previous tight values:
   axis .6s · dots .52s/.4s · lines .58s/.5s · years .96s/.45s */
.flow.zeit-in .flow-axis{ animation:axisGrowV .8s cubic-bezier(.5,.05,.15,1) both; }
.flow.zeit-in.is-zeit-h .flow-axis{ animation:axisGrowH .8s cubic-bezier(.5,.05,.15,1) both; }
@keyframes axisGrowV{ from{ transform:scaleY(0); } to{ transform:scaleY(1); } }
@keyframes axisGrowH{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }
.flow.zeit-in .flow-dot{ animation:zeitPop .55s ease both; animation-delay:calc(.42s + var(--di,0) * .045s); }
.flow.zeit-in .flow-lines{ opacity:0; animation:zeitFadeIn .75s ease both; animation-delay:.55s; }
.flow.zeit-in .flow-year{ animation:zeitPop .6s ease both; animation-delay:calc(.92s + var(--di,0) * .04s); }
@keyframes zeitPop{ from{ opacity:0; transform:translate(-50%,-50%) scale(.4); } to{ opacity:1; transform:translate(-50%,-50%) scale(1); } }
@keyframes zeitFadeIn{ to{ opacity:1; } }

/* rotate commit: dots + connector lines together first, years last
   OVERLAP v1 (slower). Previous: dots .06s · lines .1s · years .46s */
.flow.zeit-in-dots .flow-dot{ animation:zeitPop .55s ease both; animation-delay:calc(.04s + var(--di,0) * .04s); }
.flow.zeit-in-dots .flow-lines{ opacity:0; animation:zeitFadeIn .7s ease both; animation-delay:.14s; }
.flow.zeit-in-dots .flow-year{ animation:zeitPop .6s ease both; animation-delay:calc(.5s + var(--di,0) * .04s); }

/* while pinch-zooming, snap (no per-tile transition lag) */
.flow.zeit-zooming .fc,
.flow.zeit-zooming .flow-dot,
.flow.zeit-zooming .flow-year{ transition:none !important; }

@media(prefers-reduced-motion:reduce){
  .flow.zeit-in .flow-axis, .flow.zeit-in .flow-dot, .flow.zeit-in .flow-year, .flow.zeit-in .flow-lines,
  .flow.zeit-in-dots .flow-dot, .flow.zeit-in-dots .flow-year, .flow.zeit-in-dots .flow-lines{ animation:none; }
  .flow-ticks{ transition:none; animation:none; }
}

/* sublabel under the switch */
.flow-note{ font-family:var(--mono); font-size:12.5px; color:var(--grey); }

/* ---- hashtag labels floating in graph / raum (capsule fields) ---- */
.flow-kw{ position:absolute; inset:0; pointer-events:none; z-index:1; }
.flow-kw .kw{ position:absolute; transform:translate(-50%,-50%); white-space:nowrap;
  font-family:var(--mono); letter-spacing:0.02em; color:var(--ink-2);
  background:color-mix(in srgb,#ffffff 88%, transparent); border:1px solid var(--line);
  border-radius:999px; padding:3px 10px; box-shadow:0 1px 2px rgba(0,0,0,.05);
  opacity:0; transition:opacity .5s ease, color .2s ease, border-color .2s ease; }
/* v2: hashtags are real nodes the hover can light up (text + border only) */
.flow-kw .kw.kw-hot{ color:var(--accent); border-color:var(--accent); }

/* ---- fine solver toggle, top-left of the graph ---- */
.graph-solver{ position:absolute; top:12px; left:12px; z-index:30; display:none;
  align-items:center; gap:4px; font-family:var(--mono); opacity:.45; transition:opacity .2s ease; }
.flow.is-graph .graph-solver{ display:inline-flex; }
.flow.is-graph .graph-solver:hover{ opacity:1; }
.graph-solver__l{ font-size:9px; letter-spacing:0.09em; text-transform:uppercase; color:var(--grey-2); margin-right:1px; }
.graph-solver button{ width:17px; height:17px; border:none; background:none; border-radius:4px;
  font-family:var(--mono); font-size:10px; color:var(--grey-2); cursor:pointer;
  transition:color .15s ease, background .15s ease; }
.graph-solver button:hover{ color:var(--ink); }
.graph-solver button.on{ background:color-mix(in srgb,var(--accent) 16%, transparent); color:var(--accent); }
.flow.is-graph .flow-kw, .flow.is-raum .flow-kw{ }
.flow:not(.is-graph):not(.is-raum) .flow-kw .kw{ opacity:0 !important; }

/* staged graph entrance: project nodes fly first, then lines fade, then hashtags (JS-timed) */
.flow.is-graph .flow-lines, .flow.is-raum .flow-lines{ transition:opacity .55s ease .35s; }
.flow.is-graph .flow-kw .kw, .flow.is-raum .flow-kw .kw{ transition:opacity .5s ease, color .2s ease, border-color .2s ease; }
/* hashtag hover → light up the label + let JS mark matching project tiles */
.flow.is-graph .flow-kw .kw, .flow.is-raum .flow-kw .kw{ pointer-events:auto; cursor:pointer; }
.flow-kw .kw.kw-hot{ color:var(--accent); border-color:var(--accent); }
