/* ==========================================================================
   Grid-first: the map is the room, and white panels float on it.
   At rest: the world as the dot matrix the Map and Overview use, your areas pinned
   on it (the one being searched larger, breathing). Once you are in an area the
   land fills in (data-tier 1) and plots appear, substations and lines after them
   (2 and 3); links and the 20 km ring when a plot is selected.
   Type: the contract's styles only. On the map: meta for names at rest, strong
   for what you are looking at; lines are knocked out under labels, never haloed.
   Colour: ink and the neutrals. The scout at work is motion, never a colour: dots
   that breathe, and each substation it assesses turning from a faint ring to ink.
   What waits for a person (a plot to decide on, an area that stopped) wears the
   one attention ring: .attn in the panels, the same drifting ring on the map.
   ========================================================================== */
.view-grid { overflow: hidden; }
.gf {
  position: absolute; inset: 0; overflow: hidden; background: var(--bg);
  --lw: 340px; --rw: 368px;
  --ss: .42; --ps: .3; /* substation and plot size by zoom tier (SS and PS in grid.js) */
}
.gf[data-tier="2"] { --ss: .62; --ps: .8; }
.gf[data-tier="3"] { --ss: 1; --ps: 1; }

/* ---------- The map ---------- */
.gf-svg { position: absolute; inset: 0; width: 100%; height: 100%; cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
.gf.is-panning .gf-svg { cursor: grabbing; }
.gf.is-drawing .gf-svg { cursor: crosshair; }
.gf-svg text { font-family: var(--font); font-variant-numeric: tabular-nums; }
/* The land: the dot matrix over the world (data-tier 0), filled land once you are in an area (1 and up), handing over in a cross-fade.
   The hidden one stops painting once it has faded. */
.gf-land, .gf-dots { transition: opacity var(--dur-3) var(--ease), visibility 0s; }
.gf[data-tier="0"] .gf-land, .gf:not([data-tier="0"]) .gf-dots { opacity: 0; visibility: hidden; transition: opacity var(--dur-3) var(--ease), visibility 0s var(--dur-3); }
.gf-land path { stroke: var(--bg); stroke-width: 1; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.gf-rest path { fill: var(--fill); }
.gf-dots { pointer-events: none; }
.gf-dots path { fill: none; stroke-linecap: round; }
.gf-dots-rest { stroke: var(--stroke-faint); }
/* The grid the zoom calls for (is-on) hands over to the next; the other stops painting once it has faded */
.gf-dgrid { opacity: 0; visibility: hidden; transition: opacity 240ms var(--ease), visibility 0s 240ms; }
.gf-dgrid.is-on { opacity: 1; visibility: inherit; transition: opacity 240ms var(--ease), visibility 0s; }

/* Marks keep their pixel size at any zoom: --inv = 1 / zoom */
.gf-m { transform: scale(var(--inv, 1)); }
.gf-plot > .gf-m, .gf-apin > .gf-m { transform: scale(var(--inv, 1)) translate(var(--dx, 0px), var(--dy, 0px)); }
.gf-sz { transform: scale(var(--ss)); transition: transform var(--dur-3) var(--ease); }
.gf-pz { transform: scale(var(--ps)); transition: transform var(--dur-3) var(--ease); }
.gf-hit { fill: transparent; }

/* Detail appears as you zoom in: plots from tier 1, the grid and pipeline sites from tier 2 */
.gf-lines, .gf-subs, .gf-sites, .gf-slabels, .gf-reachg, .gf-reach-edge, .gf-ghosts { transition: opacity var(--dur-3) var(--ease); }
.gf:is([data-tier="0"], [data-tier="1"]) :is(.gf-lines, .gf-subs, .gf-sites, .gf-slabels, .gf-reachg, .gf-reach-edge) { opacity: 0; pointer-events: none; }
.gf[data-tier="0"] .gf-ghosts { opacity: 0; }
.gf[data-tier="2"] .gf-lines { opacity: .75; }
.gf[data-tier="2"] .gf-sites { opacity: .8; }

/* ---------- Areas of interest: a soft fill under a fine ink outline, or a pin while too small to see ---------- */
.gf-area, .gf-aedge, .gf-apin { transition: opacity var(--dur-2) var(--ease); }
.gf-area { cursor: pointer; }
.gf-area.is-selected { cursor: inherit; }
.gf-area-fill { fill: var(--hover); transition: fill var(--dur-2) var(--ease); }
.gf-area.is-queued .gf-area-fill { fill: transparent; }
.gf-area.is-hot .gf-area-fill { fill: var(--fill); }
.gf-area.is-selected .gf-area-fill { fill: color-mix(in srgb, var(--surface) 45%, transparent); }
.gf-aedge {
  fill: none; stroke: var(--ink); stroke-width: 1.25; stroke-linejoin: round; vector-effect: non-scaling-stroke; pointer-events: none;
  transition: opacity var(--dur-2) var(--ease), stroke var(--dur-2) var(--ease), stroke-width var(--dur-2) var(--ease);
}
.gf-aedge.is-queued { stroke: var(--stroke); stroke-dasharray: 5 4; }
.gf-aedge.is-hot { stroke-width: 1.75; }
.gf-aedge.is-selected { stroke-width: 2; }
.gf-aedge.is-queued:is(.is-hot, .is-selected) { stroke: var(--ink); }
.gf-area.is-pin, .gf-aedge.is-pin { opacity: 0; pointer-events: none; }

/* The pin: a ring at the area's middle.  Done: an ink dot in it.  Searching: a larger ring, the dot breathes.  Queued: an empty dashed ring.
   Waiting for a person (it stopped, or holds plots to decide on): the attention mark, ink in the drifting ring, its dot white.
   Pins too close to each other are nudged apart (--dx, --dy, from grid.js). */
.gf-apin { cursor: pointer; opacity: 0; pointer-events: none; }
.gf-apin.is-pin { opacity: 1; pointer-events: auto; }
.gf-apin.is-selected { cursor: inherit; }
.gf-apin-ring { fill: var(--surface); stroke: var(--ink); stroke-width: 1.5; transition: stroke-width var(--dur-1) var(--ease); }
.gf-apin-dot { fill: var(--ink); }
.gf-apin.is-searching .gf-apin-ring { stroke-width: 2; }
.gf-apin.is-searching .gf-apin-dot { animation: breathe var(--breath) var(--ease-io) infinite; }
.gf-apin.is-queued .gf-apin-ring { stroke: var(--stroke); stroke-dasharray: 2.6 2.2; }
.gf-apin.is-queued .gf-apin-dot { display: none; }
.gf-apin:is(:hover, .is-hot, .is-selected) .gf-apin-ring { stroke-width: 2.25; }
.gf-apin.is-searching:is(:hover, .is-hot, .is-selected) .gf-apin-ring { stroke-width: 2.75; }
.gf-apin.is-queued:is(:hover, .is-hot, .is-selected) .gf-apin-ring { stroke: var(--ink); }
.gf-apin.gf-attn .gf-apin-ring { fill: var(--attn-fill); stroke: url(#gf-attn-ring); stroke-width: 2.5; }
.gf-apin.gf-attn .gf-apin-dot { fill: var(--on-ink); }
.gf-apin.gf-attn:is(:hover, .is-hot, .is-selected) .gf-apin-ring { stroke-width: 3.25; }

/* The scout off, or on but not at work right now: what it was doing stays where it is, still; nothing claims to be working */
.gf:is(.is-off, .is-idle) .gf-apin-dot { animation: none; }
.gf:is(.is-off, .is-idle) :is(.gf-plot-live, .gf-rk-live) { visibility: hidden; }

/* What waits for a person on the map wears the attention ring (grid.js ATTN_RING: the stops of .attn's border). It drifts on the
   page's one clock, which runs while anything waits, as it does for .attn and the attention mark; still under reduced motion. */
:root:has(.gf-attn) { animation: attn-drift var(--attn-period) steps(300) infinite; }
@media (prefers-reduced-motion: reduce) { :root:has(.gf-attn) { animation: none; } }

/* ---------- Labels: plain type, placed clear of the marks and of each other ---------- */
.gf-lab { pointer-events: none; }
.gf-lab.is-hidden { visibility: hidden; }
.gf-lab text { fill: var(--ink); font-size: var(--fs-meta); font-weight: 400; }
.gf-lab-area { pointer-events: auto; cursor: pointer; }
.gf-lab-area.is-selected { cursor: inherit; }
.gf-lab-area:is(.is-selected, .is-searching) .gf-lab-name, .gf-lab-sub .gf-lab-name { font-size: var(--fs-body); font-weight: 500; }
.gf-lab-sub { animation: gf-fade var(--dur-3) var(--ease) both; }

/* ---------- The grid as OpenStreetMap has it: fine lines, and the substations the scout assesses.  Assessed with a figure: an ink
   core in a halo whose area grows with its spare MW.  Assessed without one: a hollow ink ring.  Not assessed yet: a faint ring. ---------- */
.gf-line { fill: none; stroke: var(--stroke); stroke-width: .8; vector-effect: non-scaling-stroke; stroke-linecap: round; }
.gf-line.kv-hi { stroke-width: 1.2; }
.gf-line.is-new { animation: gf-fade 1200ms var(--ease) both; }
.gf-sub { cursor: pointer; }
.gf-sub-halo {
  fill: var(--fill); stroke: var(--stroke); stroke-width: 1;
  transition: fill var(--dur-2) var(--ease), stroke var(--dur-2) var(--ease);
}
.gf-sub-core { fill: var(--ink); }
.gf-sub-ring { fill: var(--surface); stroke: var(--ink); stroke-width: 1.5; transition: stroke var(--dur-2) var(--ease), stroke-width var(--dur-2) var(--ease); }
.gf-sub.is-todo .gf-sub-ring { stroke: var(--stroke); }
.gf-sub:is(:hover, .is-hot, .is-open) .gf-sub-halo { fill: var(--fill-2); stroke: var(--ink); }
.gf-sub.is-linked .gf-sub-halo { stroke: var(--ink); }
.gf-sub:is(:hover, .is-hot, .is-open, .is-linked) .gf-sub-ring { stroke: var(--ink); stroke-width: 2.25; }
/* Just read from OpenStreetMap: it fades in.  Just assessed by the scout: its mark grows in on an ink ring, then settles. */
.gf-sub.is-new { animation: gf-fade 1200ms var(--ease) both; }
.gf-sub.is-fresh :is(.gf-sub-halo, .gf-sub-ring) { animation: gf-sub-in 1600ms var(--ease) backwards; }
@keyframes gf-sub-in {
  0% { transform: scale(0); stroke: var(--ink); stroke-width: 2; }
  30% { transform: scale(1.12); }
  60% { stroke: var(--ink); stroke-width: 2; }
}

/* ---------- Pipeline sites: faint rings only ---------- */
.gf-site { cursor: pointer; }
.gf-site-ring { fill: color-mix(in srgb, var(--surface) 55%, transparent); stroke: var(--stroke); stroke-width: 1.5; transition: stroke var(--dur-1) var(--ease); }
.gf-site:hover .gf-site-ring { stroke: var(--ink); }

/* ---------- Plots: ink squares numbered by rank ---------- */
.gf-plot { cursor: pointer; }
.gf-plot-sq { fill: var(--ink); stroke: var(--surface); stroke-width: 2; }
.gf-plot-n { fill: var(--on-ink); font-size: var(--fs-meta); font-weight: 400; text-anchor: middle; dominant-baseline: central; pointer-events: none; transition: opacity var(--dur-2) var(--ease); }
.gf:is([data-tier="0"], [data-tier="1"]) :is(.gf-plot-n, .gf-plot-live) { opacity: 0; }
.gf-plot-ring { fill: color-mix(in srgb, var(--surface) 70%, transparent); stroke: var(--ink); stroke-width: 1.5; opacity: 0; transition: opacity var(--dur-2) var(--ease); }
.gf-plot:hover .gf-plot-ring, .gf-plot.is-hot .gf-plot-ring { opacity: .5; }
.gf-plot.is-selected .gf-plot-ring { opacity: 1; }
.gf-plot-live { fill: var(--ink); stroke: var(--surface); stroke-width: 2; animation: breathe var(--breath) var(--ease-io) infinite; }
/* Waits for a person's decision: the square wears the attention ring, 2 px outside it at any size (the square covers the inner half) */
.gf-plot-attn { fill: none; stroke: url(#gf-attn-ring); stroke-width: 4; vector-effect: non-scaling-stroke; }
.gf-plot.gf-attn .gf-plot-sq { stroke: none; }
/* Just found by the scout: the square grows in and an ink ring rings out once */
.gf-plot.is-new .gf-pz { animation: gf-plot-in 520ms var(--ease) backwards; }
.gf-plot.is-new .gf-plot-ring { animation: gf-ping 1600ms var(--ease) backwards; }
@keyframes gf-plot-in { from { opacity: 0; transform: scale(calc(var(--ps) * .55)); } }
@keyframes gf-ping { 0% { opacity: 1; transform: scale(.7); fill: transparent; } 100% { opacity: 0; transform: scale(1.7); fill: transparent; } }

/* A selected plot brings its own power forward; everything else steps back */
.gf-plots > *, .gf-subs > *, .gf-sites, .gf-lines { transition: opacity var(--dur-3) var(--ease); }
.gf.has-plot .gf-plot:not(.is-selected) { opacity: .2; }
.gf.has-plot .gf-sub:not(.is-linked) { opacity: .4; }
.gf.has-plot .gf-lines { opacity: .45; }
.gf.has-plot .gf-sites { opacity: .45; }
.gf-link { stroke: var(--ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; stroke-linecap: round; pointer-events: none; animation: gf-fade var(--dur-3) var(--ease) both; }
.gf-link.is-far { stroke-width: 1.25; stroke-dasharray: 2 4; }
.gf-reach { fill: color-mix(in srgb, var(--surface) 28%, transparent); pointer-events: none; animation: gf-fade var(--dur-3) var(--ease) both; }
.gf-reach-edge { fill: none; stroke: var(--stroke); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; pointer-events: none; animation: gf-fade var(--dur-3) var(--ease) both; }
.gf-ghost { pointer-events: none; }
.gf-ghost :is(circle, rect) { fill: color-mix(in srgb, var(--surface) 85%, transparent); stroke: var(--ink); stroke-width: 1.5; }
.gf-ghost.is-dismissed rect { stroke-dasharray: 3 2.5; }
/* At world scale the areas speak for their plots (the selected one stays) */
.gf[data-tier="0"] .gf-plot:not(.is-selected) { opacity: 0; pointer-events: none; }
@keyframes gf-fade { from { opacity: 0; } }

/* ---------- Drawing an area: ink, because a person is drawing ---------- */
.gf-draft-shape { fill: var(--fill); stroke: var(--ink); stroke-width: 1.5; stroke-dasharray: 6 4; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.gf.is-naming .gf-draft-shape { stroke-dasharray: none; fill: color-mix(in srgb, var(--surface) 40%, transparent); }
.gf-draft-line { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-dasharray: 6 4; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.gf-draft-pt { fill: var(--surface); stroke: var(--ink); stroke-width: 1.5; }
.gf-draft-pt.is-first { fill: var(--ink); }
.gf-draft-size { fill: var(--ink); font-size: var(--fs-meta); font-weight: 400; }

/* ==========================================================================
   Floating pieces: white, on the map, 16 px from the edges
   ========================================================================== */
.gf-panel { position: absolute; z-index: 5; display: flex; flex-direction: column; min-height: 0; top: 72px; max-height: calc(100% - 88px); background: var(--surface); border-radius: var(--r-4); box-shadow: var(--shadow-2); }

/* ---------- Left: areas.  Area names and plot names share one left edge (68 px in); everything on the right
   (Start now, the settings glyph, Mark an area, the progress bar) ends 24 px in ---------- */
.gf-left { left: 16px; width: var(--lw); }
.gf-lhead { flex: none; padding: 16px 24px 12px; transition: box-shadow var(--dur-2) var(--ease); }
.gf-lrow { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 36px; }
.gf-lrow > .icon-btn { margin-right: -10px; } /* its glyph ends on that edge; its hover disc reaches past it */
.gf-scout { align-items: flex-start; margin-top: 4px; }
.gf-scout .dot { margin: 6px 2px 0; }
.gf-scout .live-text { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.45; }
.gf-list { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 4px 8px 8px; }
.gf-lfoot { flex: none; padding: 8px 24px 16px; transition: box-shadow var(--dur-2) var(--ease); }
/* Only while rows scroll beneath them do the head and foot get a hairline */
.gf-left.is-scrolled .gf-lhead { box-shadow: 0 1px 0 var(--line); }
.gf-left.has-more .gf-lfoot { box-shadow: 0 -1px 0 var(--line); }

/* An area: its outline, name, and what it holds (each figure saying what it counts); while the scout searches it, the bar of its
   substations assessed under them, to the right edge.  Open, or being searched while none is open, its plots below it; stopped, the
   reason and "Try again".  Areas are parted by a hairline. */
.gf-arow { position: relative; }
.gf-arow + .gf-arow { margin-top: 9px; }
.gf-arow + .gf-arow::before { content: ""; position: absolute; left: 16px; right: 16px; top: -5px; height: 1px; background: var(--line); }
.gf-ahead { position: relative; display: flex; border-radius: var(--r-3); transition: background var(--dur-1) var(--ease); }
.gf-arow.is-active > .gf-ahead { background: var(--fill); }
.gf-abtn {
  flex: 1 1 auto; min-width: 0; display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; grid-template-areas: "g m f" ". p p";
  column-gap: 12px; align-items: start; padding: 10px 16px; border: 0; border-radius: var(--r-3);
  background: none; text-align: left; color: inherit; transition: background var(--dur-1) var(--ease);
}
.gf-ahead:hover > .gf-abtn, .gf-arow.is-hot > .gf-ahead .gf-abtn { background: var(--hover); }
.gf-aglyph { grid-area: g; width: 32px; height: 32px; margin-top: 2px; padding: 4px; border-radius: var(--r-2); background: var(--fill); overflow: visible; }
.gf-aglyph path { fill: var(--fill); stroke: var(--ink); stroke-width: 1.3; stroke-linejoin: round; }
.gf-arow.is-queued .gf-aglyph path { fill: none; stroke: var(--stroke); stroke-dasharray: 2.5 2; }
.gf-arow.is-active .gf-aglyph { background: var(--surface); }
.gf-amain { grid-area: m; display: flex; flex-direction: column; min-width: 0; }
.gf-aname { font-weight: 500; line-height: 1.45; overflow-wrap: anywhere; }
.gf-asub { font-size: var(--fs-meta); line-height: 1.4; }
.gf-ameta { grid-area: f; padding-top: 2px; font-size: var(--fs-meta); line-height: 1.4; white-space: nowrap; }
.gf-aprog { grid-area: p; display: block; margin: 8px 0 2px; }
/* A queued area waits its turn: "Next" on the right.  Pointing at the row, or opening it, offers to start it now in that place. */
.gf-arow.is-queued .gf-ameta { align-self: center; padding-top: 0; min-width: 112px; text-align: right; transition: opacity var(--dur-1) var(--ease); }
.gf-astart { position: absolute; right: 16px; top: 50%; translate: 0 -50%; opacity: 0; transition: opacity var(--dur-1) var(--ease), background var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease); }
.gf-ahead:hover > .gf-astart, .gf-astart:focus-visible, .gf-arow.is-open .gf-astart { opacity: 1; }
.gf-arow.is-queued > .gf-ahead:hover .gf-ameta, .gf-arow.is-queued.is-open .gf-ameta { opacity: 0; }
.gf-arow.is-queued > .gf-ahead:has(.gf-astart:focus-visible) .gf-ameta { opacity: 0; }
@media (hover: none) {
  .gf-astart { opacity: 1; }
  .gf-arow.is-queued .gf-ameta { opacity: 0; }
}

/* An area whose search stopped waits for a person: why, and "Try again", under its name and in the attention treatment */
.gf-astop { margin: 0 8px 8px 54px; padding: 12px 14px 14px; border-radius: var(--r-2); }
.gf-astop .callout-text { overflow-wrap: anywhere; }

/* Plots of the open area: the rank square sits under the area's outline.  One that waits for a person's decision takes the
   attention treatment (.attn): the open one a lighter ink and a wider ring, as in the review queue. */
.gf-plist { display: flex; flex-direction: column; gap: 6px; padding: 4px 0; }
.gf-prow {
  display: flex; align-items: center; gap: 16px; width: 100%; min-width: 0; padding: 8px 16px 8px 20px; border: 0; border-radius: var(--r-2);
  background: none; text-align: left; color: inherit; transition: background var(--dur-1) var(--ease);
}
.gf-prow:hover, .gf-prow.is-hot { background: var(--hover); }
.gf-prow.is-selected { background: var(--fill); }
.gf-prow.is-new { animation: rise var(--dur-3) var(--ease) both; }
.gf-prow.attn { padding: calc(8px - var(--attn-w)) calc(16px - var(--attn-w)) calc(8px - var(--attn-w)) calc(20px - var(--attn-w)); transition: --attn-bg var(--dur-1) var(--ease); }
.gf-prow.attn.is-hot { --attn-bg: var(--attn-fill-hover); }
.gf-prow.attn.is-selected { --attn-bg: var(--attn-fill-hover); --attn-w: 2.5px; }
.gf-pmain, .gf-pfig { display: flex; flex-direction: column; min-width: 0; }
.gf-pmain { flex: 1 1 auto; }
.gf-pfig { flex: none; align-items: flex-end; white-space: nowrap; }
.gf-pname, .gf-pmw { font-weight: 500; line-height: 1.45; }
.gf-pname { overflow-wrap: anywhere; }
.gf-psub, .gf-peur { font-size: var(--fs-meta); line-height: 1.4; }
.gf-psub { overflow-wrap: anywhere; }
.gf-prow:is(.is-moved, .is-dismissed) .gf-pname { font-weight: 400; }

/* The rank square: the same mark as on the map.  In the pipeline: a site's ring; dismissed: a dashed square. */
.gf-rk {
  position: relative; flex: none; display: grid; place-items: center; width: 24px; height: 24px; border-radius: var(--r-1);
  background: var(--ink); color: var(--on-ink); font-size: var(--fs-meta); font-weight: 400; line-height: 1; font-variant-numeric: tabular-nums;
}
.gf-rk:is(.is-moved, .is-dismissed) { background: none; }
.gf-rk:is(.is-moved, .is-dismissed)::after { content: ""; width: 12px; height: 12px; border: 1.5px solid var(--stroke); border-radius: 50%; }
.gf-rk.is-dismissed::after { border-style: dashed; border-radius: 3px; }
.gf-rk-live { position: absolute; right: -3px; top: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--ink); box-shadow: 0 0 0 2px var(--surface); animation: breathe-dot var(--breath) var(--ease-io) infinite; }
.gf-rk-lg { width: 32px; height: 32px; border-radius: var(--r-2); font-size: var(--fs-body); font-weight: 500; }

/* ---------- Right: one plot ---------- */
.gf-right { right: 16px; width: var(--rw); }
.gf-right.is-entering { animation: gf-panel-in var(--dur-3) var(--ease); }
@keyframes gf-panel-in { from { opacity: 0; transform: translateX(14px); } }
.gf-rhead { position: relative; flex: none; display: flex; align-items: flex-start; gap: 16px; padding: 20px 24px 0; }
.gf-rhead > .icon-btn { position: absolute; top: 14px; right: 14px; } /* in the corner, so the place line runs the full width */
.gf-rheading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.gf-rheading > .t-title { padding-right: 28px; }
.gf-rbody { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 24px 24px 20px; }
/* The two figures that decide it, set big and light; under each, its name with how sure we are */
.gf-figs { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.gf-fig { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; min-width: 0; }
.gf-sec { margin-top: 28px; }
.gf-sec > .section-title { margin-bottom: 2px; }
.gf-sec-by { margin-left: auto; font-size: var(--fs-meta); font-weight: 400; line-height: 1.4; }
.gf-right .attr.is-far .attr-val { font-weight: 400; }
/* A nearest substation: its name opens its assessment beside the panel; a figure not known reads like "Not known yet" */
.gf-srow-name { display: inline; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.gf-srow-name:hover, .gf-srow-name[aria-expanded="true"] { text-decoration: underline; text-decoration-color: var(--stroke); }
.gf-srow-name:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 2px; }
.gf-right .gf-srow:is(.is-todo, .is-unknown) .attr-val { font-weight: 400; }
.gf-rfoot { flex: none; display: flex; align-items: center; gap: 8px; padding: 16px 24px 20px; border-top: 1px solid var(--line); }
.gf-rstate { white-space: nowrap; font-weight: 500; }

/* ---------- A substation's popover (on the page, beside its mark or its row): where it comes from, then the assessment ---------- */
.popover.gf-subpop { padding: 16px 18px 8px; }
.gf-sp-head { display: flex; flex-direction: column; gap: 2px; padding-bottom: 12px; }
.gf-sp-head .popover-title { margin: 0; }
.gf-sp-meta { font-size: var(--fs-meta); line-height: 1.4; }
.gf-sp-meta .btn-link { gap: 3px; font-size: inherit; font-weight: 400; }
.gf-sp-meta .btn-link .ic, .gf-sp-prose .btn-link .ic { width: 12px; height: 12px; }
.gf-sp-attrs { --attr-label: 104px; }
.gf-sp-prose .attr-val, .gf-sp-spare:is(.is-todo, .is-unknown) .attr-val { font-weight: 400; }
.gf-sp-prose .btn-link { display: inline; white-space: normal; font-size: inherit; font-weight: 400; text-decoration: underline; text-decoration-color: var(--stroke); }
.gf-sp-prose .btn-link .ic { display: inline; margin-left: 3px; vertical-align: -1px; }

/* ---------- Zoom: the shared UI.zoomctl, at the bottom right of the free map.  Beside an open plot it stands with
   its foot level with the panel's (--gf-zb, from grid.js), so the plot keeps the whole height. ---------- */
.gf > .zoomctl { transition: right var(--dur-3) var(--ease), bottom var(--dur-3) var(--ease); }
.gf.has-plot > .zoomctl { right: calc(var(--rw) + 32px); bottom: var(--gf-zb, 16px); }

/* ---------- Drawing: the bar at the top and the name card ---------- */
.gf-hint {
  position: absolute; z-index: 6; top: 12px; left: 50%; translate: -50% 0;
  display: flex; align-items: center; gap: 16px; height: 44px; padding: 0 6px 0 5px; white-space: nowrap;
  border-radius: var(--r-pill); background: var(--surface); box-shadow: var(--shadow-2);
  animation: pop-in var(--dur-2) var(--ease);
}
.gf-hint-text { font-weight: 500; }
.gf-hint .btn .kbd { margin-left: 2px; }

.gf-card {
  position: absolute; z-index: 7; left: 0; top: 0; width: 312px;
  display: flex; flex-direction: column; gap: 12px; padding: 20px;
  border-radius: var(--r-4); background: var(--surface); box-shadow: var(--shadow-3);
  animation: gf-fade var(--dur-2) var(--ease);
}
.gf-card-fact { display: flex; align-items: center; gap: 10px; padding-top: 2px; line-height: 1.4; }
.gf-card-fact .ic { width: 16px; height: 16px; }
/* Larger than one import reads: its size and the limit, in an outlined note; the card offers to draw again */
.gf-card-over { padding: 12px 14px; gap: 10px; }
.gf-card-over > .ic { width: 16px; height: 16px; margin-top: 2px; }
.gf-card-actions { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.gf-card-actions .icon-btn { margin-right: -6px; }

/* ---------- Smaller screens (1280 × 800) ---------- */
@media (max-width: 1360px) {
  .gf { --lw: 316px; --rw: 344px; }
}
@media (max-height: 860px) {
  .gf-lhead { padding-top: 12px; }
  .gf-abtn { padding-top: 8px; padding-bottom: 8px; }
  .gf-rhead { padding-top: 16px; }
  .gf-rbody { padding-top: 20px; }
  .gf-sec { margin-top: 24px; }
  .gf-rfoot { padding-top: 12px; padding-bottom: 16px; }
}
