/* ZOA Explore — shared rendering core (viewer + editor)
   This file is the single source of visual truth for the stage, scene
   images, hotspot markers, and the floating panel shell. Both the public
   viewer and the internal editor include this file unmodified — if the
   editor should ever look exactly like the viewer, this is why it does. */

:root{
  --bg:#131310;
  --surface:#1c1c17;
  --surface-2:#242420;
  --border:#3a3a33;
  --text:#eee9dd;
  --text-muted:#9c988b;
  --accent:#c9a227;
  --accent-dim:#8a7020;
  --scale:1;
  --marker-bg-rgb:255,255,255;
  --marker-bg-opacity:0.88;
  --marker-icon-color:#20201c;
  --marker-name-color:#17170f;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0; height:100%; overflow:hidden;}
body{
  background:var(--bg);
  color:var(--text);
  font-family:'Inter',system-ui,sans-serif;
}

.stage{
  position:relative;
  width:100%;
  height:100vh;
  height:100dvh;
  background:#0d0d0a;
  overflow:hidden;
}
/* ---- a strip you turn by hand ----
   `touch-action:none` is load-bearing, not decoration: without it a finger drag
   is claimed by the browser as a page scroll before the first pointermove
   arrives, and the building never moves on a phone. The cursor is the only
   thing telling a mouse user the picture is draggable at all, so it is on the
   stage from the moment such a sequence is entered, not only while dragging. */
.stage.freeSpin{ cursor:grab; touch-action:none; }
.stage.freeSpin.spinning{ cursor:grabbing; }
/* a drag must not paint the whole page blue on its way past */
.stage.freeSpin.spinning, .stage.freeSpin.spinning *{ user-select:none; }

/* ---- a picture you pull sideways to fly to the next scene ----
   The same affordance for the projects that have scenes instead of a strip to
   turn. `pan-y pinch-zoom` rather than `none`: a sideways pull is ours, but a
   finger going up or down is the visitor scrolling and a pinch is them looking
   closer, and taking either of those hostage would be a poor price for a
   gesture that only ever reads sideways. */
.stage.sceneDrag{ cursor:grab; touch-action:pan-y pinch-zoom; }
.stage.sceneDrag.spinning{ cursor:grabbing; }
.stage.sceneDrag.spinning, .stage.sceneDrag.spinning *{ user-select:none; }
.stage.sceneDrag img{ -webkit-user-drag:none; user-drag:none; }
.loadError{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  max-width:420px; text-align:center; padding:0 24px;
  color:#eee9dd; font-family:'IBM Plex Mono',monospace; font-size:13px; line-height:1.6;
}
/* This is the actual image's coordinate space: always exactly matches the
   render's native aspect ratio, and is only ever cropped by .stage's
   overflow:hidden — never stretched or reflowed independently of it.
   Everything positioned by percentage (scenes, hotspots, transition video)
   lives inside here, so a hotspot's x/y always lands on the same real point
   in the image regardless of the browser window's aspect ratio. */
.stageInner{
  position:absolute; top:50%; left:50%;
  transform:translate(-50%,-50%);
  aspect-ratio:1920/1072;
  min-width:100%; min-height:100%;
}
.stageInner .scene{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  opacity:0;
  transition:opacity 550ms ease;
}
.stageInner .scene.active{opacity:1;}
/* Unit shapes sit between the render and the markers: over the building, under
   anything clickable.

   These z-indexes are load-bearing. Everything in .stageInner is absolutely
   positioned, so without them the paint order is DOM order — and the scene
   images are inserted with insertBefore(el, hotspotLayer), which puts them
   AFTER the unit layer. The shapes were rendering perfectly and then being
   covered by the very picture they belong to. */
#unitLayer, #previewUnitLayer{ position:absolute; inset:0; z-index:1; pointer-events:none; }
.zUnitSvg{ position:absolute; inset:0; width:100%; height:100%; display:block; pointer-events:none; }

/* Drawn areas sit between the unit shapes and the markers: an area outlines a
   whole building and its own pin sits on top of it, so the pin wins the click.
   Everything below mirrors the unit rules and reuses the same custom-property
   names — the two are one visual language, and a single set of names is easier
   to keep honest than two that drift apart. */
#areaLayer, #previewAreaLayer{ position:absolute; inset:0; z-index:1; pointer-events:none; }
.zAreaSvg{ position:absolute; inset:0; width:100%; height:100%; display:block; pointer-events:none; }
.zAreaSvg polygon.zAreaShape{
  fill:var(--u-fill);
  fill-opacity:var(--u-fill-opacity);
  stroke:var(--u-stroke);
  stroke-width:var(--u-stroke-width);
  transition:fill-opacity 160ms ease, stroke-width 160ms ease;
}
/* only an area that does something takes the pointer; a preview is scenery */
.zAreaSvg.interactive polygon.zAreaShape{ pointer-events:auto; cursor:pointer; }
.zAreaSvg.interactive polygon.zAreaShape:hover{
  fill-opacity:var(--u-fill-opacity-hover);
  stroke-width:var(--u-stroke-width-hover);
}
.zAreaSvg polygon.zAreaErode{
  fill:#fff;
  stroke:#000;
  stroke-width:var(--u-inset, 0);
  stroke-linejoin:round;
  transition:stroke-width 160ms ease;
}
.zAreaSvg g.lift polygon.zAreaErode{ stroke-width:var(--u-inset-hover, 0); }
@media (prefers-reduced-motion: reduce){
  .zAreaSvg polygon{ transition:none; }
}

/* #hotspotLayer is a full-bleed box sitting above the unit shapes, so until it
   was made transparent it was the hit target for the ENTIRE stage — the shapes
   could never see a pointer and :hover never fired. Marker clicks had always
   worked only because the event bubbled up to the stage from here; now the
   markers themselves take the hits, which is what was meant all along. */
#hotspotLayer{ position:absolute; inset:0; z-index:2; pointer-events:none; }
.hotspotPos{ pointer-events:auto; }

/* The layer stays transparent to the pointer and only the shapes themselves
   take hits, so a marker on top always wins and a click on bare stage still
   reaches the stage. Nothing here calls stopPropagation.

   Scoped to .zUnitShape, never to `polygon`: a negative outline thickness puts
   a second polygon inside a <mask>, and that one has to stay flat white with a
   black stroke. Painting it with the unit's colours would silently produce a
   mask of the wrong luminance and the shape would vanish. */
.zUnitSvg polygon.zUnitShape{
  pointer-events:auto;
  /* A clickable shape says so, the same way .zAreaShape does. It only became
     visible on a free-spin sequence, where the stage asks for `grab` and every
     apartment inherited it — the building looked draggable and nothing looked
     clickable. While `.moving` the shape is inert and `grab` correctly shows
     through, because then the drag really is all that is on offer. */
  cursor:pointer;
  fill:var(--u-fill);
  fill-opacity:var(--u-fill-opacity);
  stroke:var(--u-stroke);
  stroke-width:var(--u-stroke-width);
  transition:fill-opacity 160ms ease, stroke-width 160ms ease;
}
/* hover adds to both, rather than replacing them, so one pair of numbers works
   for every unit whatever its own base values are */
.zUnitSvg polygon.zUnitShape:hover{
  fill-opacity:var(--u-fill-opacity-hover);
  stroke-width:var(--u-stroke-width-hover);
}

/* ---- negative outline thickness: a transparent gap instead of a line ----
   White keeps, black removes. The stroke straddles the polygon's edge, so a
   stroke of 2n eats n pixels of the inside — which is the gap. Non-scaling, so
   the gap is n screen pixels whatever the viewBox is doing.

   The hover boost cannot reach this from CSS alone. `g:has(> .zUnitShape:hover)`
   MATCHES — the selector engine agrees, element.matches() returns true — but
   Chrome does not restyle elements inside a <mask> subtree in response to it,
   so the computed stroke-width never moves. One delegated pointerover on the
   svg adds .lift to the group instead; that is a single listener per layer,
   not one per shape, and it drives the list-row highlight too. */
.zUnitSvg polygon.zUnitErode{
  fill:#fff;
  stroke:#000;
  stroke-width:var(--u-inset, 0);
  stroke-linejoin:round;
  transition:stroke-width 160ms ease;
}
.zUnitSvg g.lift polygon.zUnitErode{
  stroke-width:var(--u-inset-hover, 0);
}
/* While the strip is playing the shapes travel with it, but nothing on them
   should respond: a hover highlight chasing the pointer across a moving
   building reads as a glitch, and the transition on fill-opacity would fight
   the per-frame updates. */
#unitLayer.moving .zUnitSvg polygon,
#previewUnitLayer.moving .zUnitSvg polygon{
  pointer-events:none;
  transition:none;
}
/* the visitor's own switch, independent of theme.units.enabled */
#unitLayer.hidden, #previewUnitLayer.hidden{ display:none; }

@media (prefers-reduced-motion: reduce){
  .zUnitSvg polygon{ transition:none; }
}

/* ---- the hover card ----
   Everything a visitor would otherwise open a panel to learn, shown where they
   are already looking. Positioned by createUnitTip in host coordinates, so the
   only thing the stylesheet decides is what it looks like — every value here
   that a project might want to change comes in as a custom property from the
   Units menu.

   pointer-events:none is load-bearing rather than tidy: the card sits over the
   render, and a card that took the pointer would put itself between the
   pointer and the flat it describes — the flat would lose its hover, the card
   would be told to hide, and the two would flicker against each other for as
   long as the pointer stayed still. */
.zUnitTip{
  position:absolute; left:0; top:0;
  z-index:8;
  width:var(--ut-w, 300px);
  /* A floor, never a ceiling: content taller than this pushes the card taller
     rather than being clipped, so setting a height cannot lose a long note. */
  min-height:var(--ut-h, 0px);
  max-width:calc(100% - 16px);
  box-sizing:border-box;
  padding:var(--ut-pad, 16px);
  background:var(--ut-bg, #fff);
  color:var(--ut-ink, #141410);
  border:var(--ut-border, 0) solid var(--ut-border-color, transparent);
  border-radius:var(--ut-radius, 14px);
  box-shadow:var(--ut-shadow, 0 14px 38px rgba(0,0,0,0.22));
  font-family:'IBM Plex Mono', monospace;
  pointer-events:none;
  opacity:0;
  transition:opacity var(--ut-fade, 120ms) ease;
  display:flex; flex-direction:column; gap:10px;
}
.zUnitTip.on{ opacity:1; }

.zUnitTipHead{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.zUnitTipCode{
  font-size:var(--ut-code, 20px); font-weight:600; line-height:1.1;
  letter-spacing:-0.01em; word-break:break-word;
}
.zUnitTipStatus{
  flex:0 0 auto;
  font-size:var(--ut-label, 10.5px); font-weight:600;
  letter-spacing:0.06em; text-transform:uppercase;
  padding:5px 10px; border-radius:999px; white-space:nowrap;
}
.zUnitTipName{ font-size:var(--ut-label, 10.5px); color:var(--ut-muted, #6f6b60); margin-top:-6px; }

/* The picture's box is reserved from the configured height whether or not the
   file has arrived, so the card is its final size from the first frame — a card
   that grows as its plan lands drags the numbers out from under the eye that
   is already reading them. */
.zUnitTipPlan{
  /* Its own height is the basis, and it takes any slack a taller card leaves.
     With no card height set the column is exactly as tall as its contents, so
     there is no slack and this grows by zero — which is the old behaviour,
     unchanged. */
  flex:1 1 var(--ut-plan-h, 150px);
  min-height:var(--ut-plan-h, 150px);
  background:var(--ut-plan-bg, transparent);
  border-radius:calc(var(--ut-radius, 14px) * 0.5);
  /* inside the box, so the paper keeps its colour right out to the corners
     and only the drawing is inset */
  padding:var(--ut-plan-py, 0) var(--ut-plan-px, 0);
  box-sizing:border-box;
  display:flex; align-items:center; justify-content:center;
  overflow:hidden;
}
.zUnitTipPlan img{
  mix-blend-mode:var(--ut-plan-blend, normal);
  max-width:100%; max-height:100%;
  width:auto; height:auto;
  object-fit:contain;
  display:block;
}

/* The same trick on the panel's big view, where only the plan wears it. */
.zFcDetailPlan img.zPlanMultiply{ mix-blend-mode:multiply; }

/* Anything the top line asks for beyond the code and the status pill: the
   caption colour, so the code stays the loudest thing on the row. */
.zUnitTipHeadBit{
  color:var(--ut-muted, #6f6b60);
  font-size:var(--ut-value, 15px);
  align-self:baseline;
}

/* One row, spread evenly — the stats read as a band rather than as a list, so
   two of them look deliberate and four still fit. */
.zUnitTipStats{ display:flex; gap:12px; justify-content:space-between; }
.zUnitTipStat{ display:flex; flex-direction:column; align-items:center; gap:2px; flex:1 1 0; min-width:0; }
.zUnitTipStatKey{
  font-size:var(--ut-label, 10.5px); color:var(--ut-muted, #6f6b60);
  letter-spacing:0.04em; white-space:nowrap;
}
.zUnitTipStatVal{
  font-size:var(--ut-value, 15px); font-weight:600;
  font-variant-numeric:tabular-nums; white-space:nowrap;
}
.zUnitTipNotes{ font-size:var(--ut-label, 10.5px); color:var(--ut-muted, #6f6b60); line-height:1.5; }

@media (prefers-reduced-motion: reduce){
  .zUnitTip{ transition:none; }
}
/* A finger has no hover: on touch the card would appear under the tap and be
   gone before it could be read, over the flat the tap was meant to open. The
   tap opens the panel, which is the whole card and more.

   UNLESS THE CARD IS THE POINT. phoneTapTip is the setting that makes the
   first tap raise the card and the second one open the flat, so on that
   project the sentence above is answered: the card is not appearing under a
   finger that is on its way somewhere else, it is what the tap was for, and
   it stays up until it or the render is tapped again.

   Without this the setting could not work at all. The card was built, took
   its .on class and its position, and was display:none the whole time — so
   the first tap did nothing visible and the second tap, which has to land ON
   the card, had nothing to land on. The rule right below that hands it
   pointer-events was handing them to a box that was not being laid out. */
@media (hover: none){
  .zUnitTip{ display:none; }
  /* Restated, not "revert to initial": the base rule lays the card out as a
     column and display is what carries that. */
  .phoneTapTip .zUnitTip{ display:flex; }
}

/* ---- flat chooser toggle (top-right) ---- */
.zUnitToggleHost{
  position:absolute; top:16px; right:18px;
  z-index:6;
  display:flex; justify-content:flex-end;
}
.zUnitToggle{
  display:flex; flex-direction:column; align-items:flex-end; gap:7px;
  font-family:'IBM Plex Mono', monospace;
}
.zUnitBtn{
  display:flex; align-items:center; gap:8px;
  padding:7px 11px;
  background:rgba(20,20,16,0.78);
  backdrop-filter:blur(3px);
  border:0.5px solid rgba(255,255,255,0.22);
  border-radius:999px;
  color:#eee9dd; font-family:inherit; font-size:11.5px; letter-spacing:0.04em;
  text-transform:uppercase;
  cursor:pointer;
  transition:background 180ms ease, border-color 180ms ease, color 180ms ease;
}
.zUnitBtn:hover{ border-color:rgba(255,255,255,0.45); }
.zUnitBtn svg{ width:13px; height:13px; flex:0 0 13px; fill:currentColor; opacity:0.75; }
/* the state light: lit and accented when on, dim and hollow when off */
.zUnitDot{
  width:7px; height:7px; flex:0 0 7px; border-radius:50%;
  background:transparent;
  border:1px solid rgba(238,233,224,0.5);
  transition:background 180ms ease, border-color 180ms ease, box-shadow 180ms ease;
}
.zUnitToggle.on .zUnitBtn{ color:#141410; background:rgba(201,162,39,0.92); border-color:rgba(201,162,39,0.92); }
.zUnitToggle.on .zUnitBtn svg{ opacity:0.9; }
.zUnitToggle.on .zUnitDot{
  background:#141410; border-color:#141410;
  box-shadow:0 0 0 2px rgba(20,20,16,0.18);
}

/* the legend doubles as the numbers a visitor actually wants; it only exists
   while the overlay is on, since off there is nothing for it to label */
.zUnitLegend{
  display:flex; flex-wrap:wrap; justify-content:flex-end; gap:5px 10px;
  padding:7px 11px;
  background:rgba(20,20,16,0.7);
  backdrop-filter:blur(3px);
  border:0.5px solid rgba(255,255,255,0.16);
  border-radius:10px;
  font-size:10.5px; color:#eee9dd;
  opacity:0; transform:translateY(-4px);
  pointer-events:none;
  transition:opacity 200ms ease, transform 200ms ease;
}
.zUnitToggle.on .zUnitLegend{ opacity:1; transform:none; }
.zUnitKey{ display:flex; align-items:center; gap:5px; }
.zUnitKey i{
  width:9px; height:9px; flex:0 0 9px; border-radius:2px;
  border:1px solid; display:inline-block;
}
.zUnitKey b{ font-weight:600; }
.zUnitKey span{ opacity:0.7; }

/* NARROW means the STAGE is narrow, not the window — see syncStageWidth in
   zoa-core.js. In the viewer the two are the same thing; in the editor's
   preview they are not, and a phone that cannot be looked at anywhere but on
   a phone cannot be worked on. The widths are the ones these rules already
   used as media queries. */
.stageW560 .zUnitToggleHost{ top:12px; right:12px; }
.stageW560 .zUnitBtn .zUnitBtnLabel{ display:none; }
.stageW560 .zUnitLegend{ font-size:10px; }
@media (prefers-reduced-motion: reduce){
  .zUnitBtn, .zUnitDot, .zUnitLegend{ transition:none; }
}

/* ---- flat chooser ------------------------------------------------------
   The stage and the unit list are flex siblings inside .stageWrap, so opening
   the list genuinely narrows the render instead of covering it — which is the
   point: closing it gives the picture the width back. The render re-crops
   itself (object-fit:cover) and the shapes follow, because they reproduce that
   same crop, so nothing has to be recomputed in script.

   .zFlatChooser is display:contents, so its children take part in that flex
   layout directly: the list is the flex item, everything else is absolutely
   positioned against .stageWrap. */
.stageWrap{ position:relative; display:flex; width:100%; height:100vh; height:100dvh; overflow:hidden; }
.stageWrap > .stage{ flex:1 1 auto; width:auto; min-width:0; height:100%; }
.zFlatChooser{ display:contents; }

/* the button rail, top-left */
.zFcRail{
  position:absolute; top:16px; left:18px; z-index:7;
  display:flex; flex-direction:column; gap:8px;
}
/* Both button colours are the project's to set (--zfc-rail-bg / -on), and the
   icon colour is derived from each rather than being a third setting, so a dark
   icon can never land on a dark button. Unset, these are what the rail always
   looked like: near-black translucent, lit in the accent. */
.zFcRailBtn{
  width:38px; height:38px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--zfc-rail-bg, #141410) 78%, transparent);
  backdrop-filter:blur(3px);
  border:0.5px solid rgba(255,255,255,0.22);
  border-radius:10px;
  color:var(--zfc-rail-fg, #eee9dd); cursor:pointer;
  transition:background 180ms ease, border-color 180ms ease, color 180ms ease, transform 180ms ease;
}
.zFcRailBtn svg{ width:17px; height:17px; fill:currentColor; opacity:0.8; }
.zFcRailBtn:hover{ border-color:rgba(255,255,255,0.5); transform:translateX(1px); }
.zFcRailBtn.on{
  background:var(--zfc-rail-bg-on, var(--accent));
  border-color:var(--zfc-rail-bg-on, var(--accent));
  color:var(--zfc-rail-fg-on, #141410);
}
.zFcRailBtn.on svg{ opacity:1; }
/* a dot on the funnel whenever the filter is actually holding something back,
   so a narrowed view is never a mystery with the panel closed */
.zFcRailBtn[data-act="filter"]{ position:relative; }
.zFcRailBtn[data-act="filter"].narrowed::after{
  content:''; position:absolute; top:5px; right:5px;
  width:6px; height:6px; border-radius:50%;
  background:var(--zfc-rail-bg-on, var(--accent));
  box-shadow:0 0 0 2px color-mix(in srgb, var(--zfc-rail-bg, #141410) 80%, transparent);
}
.zFcRailBtn.on[data-act="filter"].narrowed::after{
  background:var(--zfc-rail-fg-on, #141410); box-shadow:none;
}
/* with the overlay off there is nothing to filter or list */
.zFlatChooser.unitsOff .zFcRailBtn[data-act="filter"],
.zFlatChooser.unitsOff .zFcRailBtn[data-act="list"]{ opacity:0.35; pointer-events:none; }
.zFlatChooser.empty .zFcRail{ display:none; }

/* ---- two winks, every few seconds, until the pointer comes near ----
   Three small dark squares in a corner read as decoration, and the filter and
   the unit list — the two things that make this a chooser rather than a
   picture — were being missed entirely. A single pair of winks was easy to
   miss too: a visitor looking at the building at that moment never saw it.
   So the pair repeats on a long cycle, mostly silence, and the JS stops it the
   moment the pointer is anywhere near. Staggered down the rail so it reads as
   one gesture arriving, not as three buttons flashing at each other.

   The two winks take 1.24s of the 5s cycle; the rest is deliberately dead
   time, which is what keeps this a nudge rather than an alarm. */
.zFcRail.wink .zFcRailBtn{ animation:zFcRailWink 5s ease-in-out infinite; }
.zFcRail.wink .zFcRailBtn:nth-child(2){ animation-delay:70ms; }
.zFcRail.wink .zFcRailBtn:nth-child(3){ animation-delay:140ms; }
@keyframes zFcRailWink{
  0%{ transform:translateX(0); border-color:rgba(255,255,255,0.22); box-shadow:0 0 0 0 rgba(255,255,255,0); }
  5.6%{ transform:translateX(4px); border-color:rgba(255,255,255,0.85); box-shadow:0 0 0 4px rgba(255,255,255,0.14); }
  12.4%{ transform:translateX(0); border-color:rgba(255,255,255,0.22); box-shadow:0 0 0 0 rgba(255,255,255,0); }
  18%{ transform:translateX(4px); border-color:rgba(255,255,255,0.85); box-shadow:0 0 0 4px rgba(255,255,255,0.14); }
  24.8%,100%{ transform:translateX(0); border-color:rgba(255,255,255,0.22); box-shadow:0 0 0 0 rgba(255,255,255,0); }
}
@media (prefers-reduced-motion: reduce){
  .zFcRail.wink .zFcRailBtn{ animation:none; }
}

/* the title shares this corner — step it past the rail rather than under it.
   Only when the rail is really there: a project without units keeps the
   title where it has always been. */
.zFcActive .onImageTitle{ --title-left:70px; }

/* the filter, opening beside the rail */
.zFcFilter{
  position:absolute; top:16px; left:64px; z-index:8;
  width:280px; max-width:calc(100% - 84px);
  padding:12px 14px 14px;
  /* Every one of these was a fixed value. The fallback in each var() is that
     same value, so a project that has never opened the menu is unchanged. */
  background:rgba(var(--zfc-filter-bg, 20,20,16), var(--zfc-filter-opacity, 0.9));
  backdrop-filter:blur(6px);
  border:var(--zfc-filter-border-w, 0.5px) solid
         rgba(var(--zfc-filter-border, 255,255,255), var(--zfc-filter-border-opacity, 0.2));
  border-radius:var(--zfc-filter-radius, 12px);
  color:var(--zfc-filter-ink, #eee9dd);
  font-family:var(--zfc-filter-font, 'IBM Plex Mono'), monospace;
  font-size:var(--zfc-filter-size, 11.5px);
  /* Set on the panel, so they describe its voice and anything inside with a
     weight of its own — a heading, a chosen chip — still keeps it. The
     fallbacks are what the panel looked like before it could be asked. */
  font-weight:var(--zfc-filter-font-weight, 400);
  font-style:var(--zfc-filter-font-style, normal);
  text-decoration:var(--zfc-filter-text-decoration, none);
  opacity:0; transform:translateX(-6px) scale(0.98);
  transform-origin:top left;
  pointer-events:none;
  transition:opacity 180ms ease, transform 180ms cubic-bezier(.2,.9,.3,1.1);
}
.zFcFilter.open{ opacity:1; transform:none; pointer-events:auto; }
.zFcFilterHead{
  display:flex; align-items:baseline; justify-content:space-between;
  text-transform:uppercase; letter-spacing:0.06em;
  font-size:var(--zfc-filter-cap-size, 10.5px);
  color:rgba(var(--zfc-filter-heading, 238,233,224), var(--zfc-filter-heading-opacity, 0.65));
  margin-bottom:10px;
}
/* RESET sits in the head row beside the word FILTER and reads as the same
   caption in a different colour, so it takes the caption's face and size
   too — its font-family:inherit came off, or a caption face would change
   one half of that row and not the other. */
.zFcReset{
  background:none; border:none; padding:0;
  color:var(--zfc-filter-accent, var(--accent));
  font-size:var(--zfc-filter-cap-size, 10.5px);
  letter-spacing:0.04em; cursor:pointer; text-transform:uppercase;
}
.zFcReset:hover{ text-decoration:underline; }
.zFcStatusRow{ display:flex; gap:6px; }
.zFcCheck{
  flex:1 1 0; min-width:0;
  display:flex; flex-direction:column; align-items:center; gap:3px;
  padding:7px 4px;
  border:var(--zfc-chip-border-w, 0.5px) solid
         rgba(var(--zfc-chip-border, 255,255,255), var(--zfc-chip-border-opacity, 0.16));
  border-radius:var(--zfc-chip-radius, 8px);
  cursor:pointer; text-align:center;
  transition:border-color 160ms ease, background 160ms ease, opacity 160ms ease;
  opacity:0.45;
}
/* A CHOSEN chip has its own, brighter border — and it was a second fixed
   colour that quietly overrode the one above, so setting the chip outline did
   nothing to the chips a visitor actually sees (they all start chosen). Same
   colour, its own opacity. */
.zFcCheck:has(input:checked){
  opacity:1;
  border-color:rgba(var(--zfc-chip-border, 255,255,255), var(--zfc-chip-on-opacity, 0.36));
  background:rgba(var(--zfc-chip-border, 255,255,255), var(--zfc-chip-on-fill, 0.05));
}
.zFcCheck input{ position:absolute; opacity:0; width:0; height:0; }
.zFcCheck i{ width:10px; height:10px; border-radius:3px; border:1px solid; }
.zFcCheck b{ font-size:12px; font-weight:600; }
/* no text-transform anywhere a status name is shown: these are the project's
   own words now, and forcing case would mangle a translation */
.zFcCheck > span{ font-size:9.5px; opacity:0.75; }
.zFcSliders{ margin-top:12px; display:flex; flex-direction:column; gap:14px; }
/* A field the project invented — "Orientation", "Rooms" — filtered by the
   values the data actually holds. Wraps, because nothing here knows how many
   compass points or how many room counts a development turns out to have. */
.zFcCheckGroup{ display:flex; flex-direction:column; gap:6px; }
.zFcCheckGroupLabel{
  text-transform:uppercase; letter-spacing:0.06em;
  font-size:var(--zfc-filter-cap-size, 10px);
  color:rgba(var(--zfc-filter-heading, 238,233,224), var(--zfc-filter-heading-opacity, 0.65));
}
.zFcCheckWrap{ display:flex; flex-wrap:wrap; gap:6px; }
.zFcCheckWrap .zFcCheck{ flex:1 1 auto; min-width:64px; padding:6px 8px; }
.zFcFilter.noSliders .zFcSliders{ margin-top:0; }
.zFcFilterFoot{
  margin:12px 0 0; padding-top:9px;
  border-top:0.5px solid rgba(255,255,255,0.12);
  font-size:var(--zfc-filter-cap-size, 10px); letter-spacing:0.04em;
  color:rgba(var(--zfc-filter-heading, 238,233,224), var(--zfc-filter-heading-opacity, 0.65));
}

/* two-handled range */
.zFcRange{ display:block; }
.zFcRangeHead{
  display:flex; justify-content:space-between; align-items:baseline; gap:8px;
  margin-bottom:7px;
}
.zFcRangeLabel{
  text-transform:uppercase; letter-spacing:0.06em;
  font-size:var(--zfc-filter-cap-size, 10px);
  color:rgba(var(--zfc-filter-heading, 238,233,224), var(--zfc-filter-heading-opacity, 0.65));
}
/* THE CAPTIONS ARE ONE VOICE, and now a settable one. Five selectors rather
   than a class on each, because these five are already what the heading
   colour applies to — a sixth caption added later belongs on both lists or
   on neither.

   Every fallback is the panel's own answer, not a fixed value: unset, a
   caption inherits the panel exactly as it did before it could be asked. */
.zFcFilterHead, .zFcReset, .zFcCheckGroupLabel, .zFcFilterFoot, .zFcRangeLabel{
  font-family:var(--zfc-filter-cap-font, var(--zfc-filter-font, 'IBM Plex Mono')), monospace;
  font-weight:var(--zfc-filter-cap-font-weight, var(--zfc-filter-font-weight, 400));
  font-style:var(--zfc-filter-cap-font-style, var(--zfc-filter-font-style, normal));
  text-decoration:var(--zfc-filter-cap-text-decoration, var(--zfc-filter-text-decoration, none));
}
/* The numbers a slider is currently at: a VALUE, and it was painted in a
   literal — so setting the panel's text colour left the two figures over
   every slider on somebody else's near-white. */
.zFcRangeVal{ font-size:10.5px; color:var(--zfc-filter-ink, rgba(238,233,224,0.9)); text-align:right; }
.zFcRange.narrowed .zFcRangeVal{ color:var(--zfc-filter-accent, var(--accent)); }
.zFcRangeBody{ position:relative; height:20px; }
.zFcRangeTrack, .zFcRangeFill{
  position:absolute; top:50%; height:3px; border-radius:2px;
  transform:translateY(-50%); pointer-events:none;
}
.zFcRangeTrack{ left:0; right:0; background:rgba(var(--zfc-filter-track, 255,255,255), var(--zfc-filter-track-opacity, 0.18)); }
.zFcRangeFill{ background:var(--zfc-filter-accent, var(--accent)); }
/* the inputs are stacked and transparent to the pointer; only the thumbs take
   hits, so the handle you press is the one that moves even where they overlap */
.zFcRangeInput{
  position:absolute; inset:0; width:100%; height:20px;
  margin:0; padding:0; background:none; border:none;
  -webkit-appearance:none; appearance:none;
  pointer-events:none;
}
.zFcRangeInput:focus{ outline:none; }
.zFcRangeInput::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none;
  pointer-events:auto;
  width:14px; height:14px; border-radius:50%;
  background:#eee9dd; border:2px solid #141410;
  box-shadow:0 1px 4px rgba(0,0,0,0.5);
  cursor:grab;
}
.zFcRangeInput::-moz-range-thumb{
  pointer-events:auto;
  width:14px; height:14px; border-radius:50%;
  background:#eee9dd; border:2px solid #141410;
  box-shadow:0 1px 4px rgba(0,0,0,0.5);
  cursor:grab;
}
.zFcRangeInput:focus-visible::-webkit-slider-thumb{ border-color:var(--accent); }
.zFcRangeInput:focus-visible::-moz-range-thumb{ border-color:var(--accent); }

/* the unit list — a flex sibling of the stage, so it takes width rather than
   covering it */
.zFcList{
  flex:0 0 auto;
  position:relative;
  width:var(--zfc-list-w, 0px);
  height:100%;
  /* Empty in the theme means "keep the project's own surface", which is what
     the list has always used — so the fallback is the variable it had. */
  background:var(--zfc-list-bg, var(--surface));
  color:var(--zfc-list-ink, inherit);
  font-size:var(--zfc-list-size, 12px);
  font-weight:var(--zfc-list-font-weight, 400);
  font-style:var(--zfc-list-font-style, normal);
  text-decoration:var(--zfc-list-text-decoration, none);
  opacity:var(--zfc-list-opacity, 1);
  border-left:1px solid var(--zfc-list-border, var(--border));
  display:flex; flex-direction:column;
  overflow:hidden;
  transition:width 320ms cubic-bezier(.4,0,.2,1);
}
.zFlatChooser.resizing .zFcList{ transition:none; }
.zFcGrip{
  position:absolute; top:0; bottom:0; left:0; width:7px;
  cursor:col-resize; z-index:3;
}
.zFcGrip::after{
  content:''; position:absolute; top:0; bottom:0; left:3px; width:1px;
  background:transparent; transition:background 160ms ease;
}
.zFcGrip:hover::after, .zFlatChooser.resizing .zFcGrip::after{ background:var(--accent); }
.zFcListHead{
  flex:0 0 auto;
  display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:12px 12px 10px 14px;
  border-bottom:1px solid var(--border);
}
.zFcListCounts{
  display:flex; flex-wrap:wrap; gap:4px 10px;
  font-family:var(--zfc-list-font, 'IBM Plex Mono'), monospace; font-size:10.5px;
  /* the tally and the unit code are one voice reading the same thing back */
  color:var(--zfc-list-code, var(--text));
}
.zFcCount{ display:flex; align-items:center; gap:4px; }
.zFcCount i{ width:8px; height:8px; border-radius:2px; flex:0 0 8px; }
.zFcCount b{ font-weight:600; }
.zFcCount span{ opacity:0.6; }
.zFcListClose{
  flex:0 0 auto;
  width:22px; height:22px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface-2); border:1px solid var(--border); border-radius:3px;
  color:var(--text-muted); font-family:'IBM Plex Mono', monospace; font-size:12px;
  cursor:pointer;
}
.zFcListClose:hover{ color:var(--accent); border-color:var(--accent); }

/* ---- the tools row: thumbnail size and sort ---- */
.zFcListTools{
  flex:0 0 auto;
  display:flex; align-items:center; gap:10px;
  padding:7px 12px 7px 14px;
  border-bottom:1px solid var(--border);
}
.zFcSizer{ flex:1 1 auto; display:flex; align-items:center; gap:4px; min-width:0; }
/* The mountains are buttons, not decoration. Given a real hit area — the
   glyphs themselves are 9 and 15px, which is nothing to aim at — and they must
   not grow or shrink with the slider between them. */
.zFcSizeStep{
  flex:0 0 auto;
  width:20px; height:20px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:none; border:1px solid transparent; border-radius:4px;
  cursor:pointer;
}
.zFcSizeStep:hover:not(:disabled){ background:var(--surface-2); border-color:var(--border); }
.zFcSizeStep:active:not(:disabled) .zFcSizeGlyph{ fill:var(--accent); }
/* at the end of its travel it says so, rather than going dead quietly */
.zFcSizeStep:disabled{ cursor:default; opacity:0.3; }
.zFcSizeGlyph{ flex:0 0 auto; fill:var(--text-muted); transition:fill 120ms ease; }
.zFcSizeStep:hover:not(:disabled) .zFcSizeGlyph{ fill:var(--text); }
.zFcSizeRange{
  flex:1 1 auto; min-width:0; height:14px; margin:0;
  -webkit-appearance:none; appearance:none; background:none; cursor:pointer;
}
.zFcSizeRange::-webkit-slider-runnable-track{
  height:3px; border-radius:2px; background:var(--border);
}
.zFcSizeRange::-moz-range-track{ height:3px; border-radius:2px; background:var(--border); }
.zFcSizeRange::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none;
  width:11px; height:11px; margin-top:-4px; border-radius:50%;
  background:var(--accent); border:none; cursor:pointer;
}
.zFcSizeRange::-moz-range-thumb{
  width:11px; height:11px; border-radius:50%;
  background:var(--accent); border:none; cursor:pointer;
}
/* THE FILTER'S SECOND BUTTON, beside the sort. Hidden everywhere the rail's
   own filter button is on screen — two buttons for one panel, a hand's
   width apart, is a page asking which one you meant. It appears exactly
   where the rail's disappears: stacked, where the rail is on the picture
   and the list is a screen below it. */
.zFcFilterBtn{ display:none; }
.stagePanel .zFcListTools .zFcFilterBtn{
  flex:0 0 auto;
  width:24px; height:24px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:none; border:1px solid transparent; border-radius:4px;
  cursor:pointer; color:var(--text-muted);
}
.zFcFilterBtn svg{ width:15px; height:15px; fill:currentColor; }
.zFcFilterBtn:hover{ color:var(--text); border-color:var(--border); background:var(--surface-2); }
/* open, and narrowed — the same two things the rail's button says */
.zFcFilterBtn.on{ color:var(--accent); border-color:var(--border); }
.zFcSortWrap{ position:relative; flex:0 0 auto; }
.zFcSortBtn{
  width:24px; height:24px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:none; border:1px solid transparent; border-radius:4px;
  cursor:pointer; color:var(--text-muted);
}
.zFcSortBtn svg{ width:15px; height:15px; }
.zFcSortBtn svg path{
  fill:none; stroke:currentColor; stroke-width:1.4;
  stroke-linecap:round; stroke-linejoin:round;
}
.zFcSortBtn:hover{ color:var(--text); border-color:var(--border); background:var(--surface-2); }
/* the icon says the list is not in its default order, so the reason the rows
   are where they are is visible without opening the menu */
.zFcSortBtn.sorted{ color:var(--accent); }
/* anchored to the button's left edge, because the button is now the leftmost
   thing in the row — right-anchored it would hang off the panel */
.zFcSortMenu{
  position:absolute; top:calc(100% + 5px); left:0; z-index:4;
  display:none; min-width:150px; padding:4px;
  background:var(--surface); border:1px solid var(--border); border-radius:6px;
  box-shadow:0 8px 22px rgba(0,0,0,0.38);
}
.zFcSortMenu.open{ display:block; }
.zFcSortItem{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  width:100%; padding:6px 9px;
  background:none; border:none; border-radius:4px;
  color:var(--text); font-family:inherit; font-size:12px; text-align:left;
  cursor:pointer;
}
.zFcSortItem:hover{ background:var(--surface-2); }
.zFcSortItem.on{ color:var(--accent); }
.zFcSortArrow{
  font-style:normal; font-family:'IBM Plex Mono', monospace; font-size:12px;
  width:10px; text-align:center;
}

/* Rows are a grid, not a stack: `auto-fill` fits as many row-min tracks as the
   panel allows and stretches them to fill it, so a wide panel shows two
   columns and a narrow one shows a single column, with no breakpoint to pick
   and nothing to keep in step with the thumbnail slider. */
.zFcRows{
  flex:1 1 auto; overflow-y:auto; overflow-x:hidden; padding:6px;
  display:grid; gap:2px; align-content:start;
  grid-template-columns:repeat(auto-fill, minmax(var(--zfc-row-min, 206px), 1fr));
}
.zFcRow{
  display:flex; align-items:center; gap:10px;
  width:100%; min-width:0; padding:7px 8px; margin:0;
  background:none; border:1px solid transparent; border-radius:6px;
  color:var(--text); font-family:inherit; text-align:left;
  cursor:pointer;
  transition:background 140ms ease, border-color 140ms ease;
}
/* A status the project has marked as not clickable. Still a row, still reads —
   it simply stops pretending to be a door. */
.zFcRow.noOpen{ cursor:default; }
/* Past a certain thumbnail the picture is the point and the text is a caption,
   so the row turns into a card rather than letting a 148px plan sit beside two
   lines of type in a 300px column. */
.zFcList.tallThumbs .zFcRow{ flex-direction:column; align-items:stretch; gap:6px; position:relative; }
.zFcList.tallThumbs .zFcThumb{ width:100%; }
.zFcList.tallThumbs .zFcRowDot{ position:absolute; top:12px; right:12px; }
.zFcRow:hover{ background:var(--zfc-list-highlight, var(--surface-2)); border-color:var(--border); }
/* fired from the render rather than from the pointer: same look as :hover, so
   pointing at a building and pointing at its row read as the one gesture */
.zFcRow.hl{ background:var(--zfc-list-highlight, var(--surface-2)); border-color:var(--accent); }
.zFcRow.off{ display:none; }
.zFcThumb{
  flex:0 0 var(--zfc-thumb, 56px);
  width:var(--zfc-thumb, 56px); height:var(--zfc-thumb, 56px);
  border-radius:var(--zfc-thumb-radius, 4px); overflow:hidden;
  background:var(--zfc-list-thumb-bg, var(--surface-2)) center/60% no-repeat;
  /* Every fallback is the value this rule had, so a project that has not
     opened the menu is untouched; 0 removes the line outright. */
  border:var(--zfc-thumb-border-w, 1px) solid var(--zfc-thumb-border, var(--border));
  display:flex; align-items:center; justify-content:center;
}
.zFcThumb img{ width:100%; height:100%; object-fit:contain; display:block; }
/* The same trick the card and the big view use: black lines on white paper
   lose the paper and keep the lines. */
.zFcThumb img.zPlanMultiply{ mix-blend-mode:multiply; }
.zFcRowText{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px; }
.zFcRowTop{ display:flex; align-items:baseline; gap:7px; min-width:0; }
.zFcRowTop b{
  font-family:var(--zfc-list-font, 'IBM Plex Mono'), monospace;
  font-size:11.5px; letter-spacing:0.03em;
  color:var(--zfc-list-code, inherit);
}
.zFcList{ font-family:var(--zfc-list-font, inherit); }
/* The name of a flat is the list's TEXT — it was painted from the project's
   own --text, so a list given a colour of its own coloured everything but
   the thing a visitor is reading. */
.zFcRowName{
  font-size:12.5px; color:var(--zfc-list-ink, var(--text));
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* And what is under it is the list's CAPTION voice. --zfc-list-muted was
   published by applyFcDesign from the first day and read by no rule at all:
   the Captions colour in the menu did nothing whatsoever. */
.zFcRowMeta, .zFcEmpty{
  color:var(--zfc-list-muted, var(--text-muted));
  font-family:var(--zfc-list-cap-font, var(--zfc-list-font, inherit));
  font-weight:var(--zfc-list-cap-font-weight, var(--zfc-list-font-weight, 400));
  font-style:var(--zfc-list-cap-font-style, var(--zfc-list-font-style, normal));
  text-decoration:var(--zfc-list-cap-text-decoration, var(--zfc-list-text-decoration, none));
}
.zFcRowMeta{ font-size:var(--zfc-list-cap-size, 11px); }
.zFcRowDot{ flex:0 0 8px; width:8px; height:8px; border-radius:50%; }

/* ---- THE SMALLEST STOP: A SHEET ----------------------------------------

   Not a smaller card. Every other step of the size control trades picture
   for rows; at the bottom the trade is over — the plan goes and each field
   takes a column, code first, which is the shape the data arrived in and
   the only one that can be read DOWN. The card's own row runs its fields
   together on one line with dots between them: fine to glance at, useless
   for comparing the fourth flat's area with the ninth's. */
/* One column of rows, and the rows scroll sideways if the panel is narrower
   than the columns need. A sheet squeezed to fit is a sheet nobody can
   read; a sheet that scrolls is the spreadsheet it is imitating. */
.zFcSheet .zFcRows{ overflow-x:auto; padding-top:0; gap:1px; }
.zFcSheetHead,
.zFcSheet .zFcRow{
  display:grid;
  grid-template-columns:var(--zfc-sheet-cols, 1fr);
  align-items:center; gap:8px;
  /* every row the same floor, so the columns cannot drift apart from one
     row to the next while the whole thing is scrolled sideways */
  min-width:var(--zfc-sheet-min, 100%);
  padding:5px 8px; border-radius:3px;
}
/* The heads stay while the rows go by — a column of numbers with no name at
   the top of it is a column nobody can use. */
.zFcSheetHead{
  position:sticky; top:0; z-index:2;
  padding-top:7px; padding-bottom:6px;
  background:var(--zfc-list-bg, var(--surface));
  border-bottom:1px solid var(--border); border-radius:0;
  text-transform:uppercase; letter-spacing:0.06em;
  font-size:9.5px;
  color:var(--zfc-list-muted, var(--text-muted));
}
.zFcCell{
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:var(--zfc-list-cap-size, 11px);
  color:var(--zfc-list-ink, var(--text));
  /* figures in a column line up on the decimal, which is most of what makes
     a sheet worth having */
  font-variant-numeric:tabular-nums;
}
/* the code keeps the mono voice it has in the card, so the same flat reads
   the same in both */
.zFcCell.code{
  font-family:var(--zfc-list-font, 'IBM Plex Mono'), monospace;
  font-size:11.5px; letter-spacing:0.03em;
  color:var(--zfc-list-code, inherit);
}
.zFcSheetHead .zFcCell{
  font:inherit; letter-spacing:inherit; color:inherit;
}
.zFcSheet .zFcRowDot{ justify-self:end; }
/* the card's stacking rule has nothing to act on here, and a 148px setting
   left behind from before the slider was pulled down must not turn a sheet
   row into a column */
.zFcList.zFcSheet.tallThumbs .zFcRow{ flex-direction:unset; }
.zFcEmpty{
  display:none; margin:0; padding:18px 16px;
  font-size:var(--zfc-list-cap-size, 12px); text-align:center;
}
/* the list is a flex item, so at width 0 it is already gone — but its content
   must not reflow while it collapses */
.zFcList > *:not(.zFcGrip){ min-width:var(--zfc-list-min, 220px); }

/* highlight fired from a list row, using the same numbers as :hover */
.zUnitSvg polygon.zUnitShape.hl{
  fill-opacity:var(--u-fill-opacity-hover);
  stroke-width:var(--u-stroke-width-hover);
}

/* the unit detail view — same manners as the image panel: dim backdrop,
   centred box, close on the backdrop or Escape */
.zFcDetail{
  position:fixed; inset:0; z-index:40;
  display:flex; align-items:center; justify-content:center;
  padding:28px;
  background:rgba(8,8,6,0.72);
  backdrop-filter:blur(2px);
  opacity:0; pointer-events:none;
  transition:opacity 200ms ease;
}
.zFcDetail.open{ opacity:1; pointer-events:auto; }
.zFcDetailBox{
  /* The panel's layout is a question about the panel, not about the window: a
     980px box on a laptop and a 380px one on a phone are different problems,
     and so is a box a project deliberately made narrow. Named so a project's
     own stylesheet can write @container zPanel (…) of its own. */
  container: zPanel / inline-size;
  position:relative;
  width:min(980px, 100%); max-height:100%;
  display:flex; flex-direction:column;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:8px;
  box-shadow:0 18px 48px rgba(0,0,0,0.55);
  overflow:auto;
  transform:scale(0.94); opacity:0;
  transition:transform 240ms cubic-bezier(.2,.9,.3,1.2), opacity 180ms ease;
}
/* THE OPEN STATE IS WHERE THE PROJECT'S OPACITY LANDS, not the box's own
   rule: the closed state is 0 and has to stay 0, or the panel would never
   fade in — it would sit there at its chosen opacity waiting to be clicked
   through. 1 is what it has always been. */
.zFcDetail.open .zFcDetailBox{ transform:none; opacity:var(--zfc-panel-opacity, 1); }
.zFcDetailClose{
  position:absolute; top:10px; right:10px; z-index:2;
  width:26px; height:26px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface-2); border:1px solid var(--border); border-radius:3px;
  color:var(--text-muted); font-family:'IBM Plex Mono', monospace; font-size:13px;
  cursor:pointer;
}
.zFcDetailClose:hover{ color:var(--accent); border-color:var(--accent); }
/* The panel is a grid whose areas are named after the vocabulary's own slugs,
   so a project can lay it out by writing grid-template-areas and nothing else.
   Three of the names are the panel's own — plan, info, thumbs — and the fields
   sit inside info. A project that wants to place a figure against the picture
   itself sets .zFcDetailInfo{display:contents} and then names every slug in a
   single template on .zFcDetailBody.

   The names themselves are generated per project into #zoaUnitAreas, because a
   grid-area name cannot come from an attribute and the slugs are not known
   until the project's vocabulary is read. */
.zFcDetailBody{
  display:grid;
  grid-template-columns:minmax(0,58%) minmax(0,42%);
  grid-template-areas:
    "plan   info"
    "thumbs thumbs";
  align-items:stretch;
}
/* The box is one height for every picture in the strip, and that is the point
   of setting it at all. A floor plan is portrait and a render is landscape, so
   sizing the box to its picture made the whole panel jump each time a thumbnail
   was clicked — the buttons moved out from under the pointer that was about to
   press them.

   min-height and the picture's max-height are the same figure, so a tall
   picture cannot push past it and a short one cannot fall below: the box is
   that height whatever is in it. --zfc-plan-h is where to change it, and the
   40px is this box's own padding, which the picture does not get to use. */
.zFcDetailPlan{
  --zfc-plan-h:min(62vh, 620px);
  min-width:0; min-height:var(--zfc-plan-h);
  display:flex; align-items:center; justify-content:center;
  padding:20px;
  background:#ffffff;
}
.zFcDetailPlan img{
  max-width:100%; max-height:calc(var(--zfc-plan-h) - 40px);
  display:block; object-fit:contain;
}
.zFcNoPlan{ color:#8a8578; font-family:'IBM Plex Mono', monospace; font-size:12px; }
.zFcDetailInfo{
  min-width:0; padding:22px 22px 20px;
  display:grid; align-content:start;
  /* Space between fields is a margin, not a row-gap. The rows are named areas
     now, so an unfilled field still has a row — one that collapses to nothing,
     while a gap either side of it would not. A margin goes away with the
     element it is on. */
  row-gap:0; column-gap:14px;
}

/* ---- one field ----
   Every one of these is addressable as [data-slug="…"] for which field it is
   and [data-type="…"] for what kind of thing it holds, and the two are worth
   keeping apart: a stylesheet that says how money looks stays right when the
   project adds a second price. */
.zField{ min-width:0; display:flex; flex-direction:column; gap:2px; margin-bottom:11px; }
.zFcDetailInfo > *:last-child{ margin-bottom:0; }
/* An unfilled field is hidden rather than dropped, so a stylesheet that wants
   it to hold its place in the grid can simply say so. */
.zField[data-empty]{ display:none; }
.zFieldLabel{
  font-family:'IBM Plex Mono', monospace;
  font-size:10.5px; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--text-muted);
}
.zFieldValue{ font-size:15px; font-weight:600; }
.zFieldImg{ max-width:100%; height:auto; border-radius:4px; }
.zField[data-type="note"] .zFieldValue{
  font-size:12.5px; font-weight:400; line-height:1.55;
  color:var(--text-muted); white-space:pre-wrap;
}
/* the chip's colours are the unit's own and are set in script, from the status
   colour's luminance */
.zFieldChip{
  align-self:start;
  padding:3px 9px; border-radius:999px;
  font-family:'IBM Plex Mono', monospace;
  font-size:10px; letter-spacing:0.08em; font-weight:400;
}
.zFieldLink{
  align-self:start;
  padding:8px 14px; border-radius:5px;
  background:var(--accent); color:#141410;
  font-size:12px; font-weight:600; text-decoration:none;
}
.zFieldLink:hover{ filter:brightness(1.08); }
.zField[data-type="url"] .zFieldLink{
  background:var(--surface-2); color:var(--text); border:1px solid var(--border);
}
/* Three fields say what they are by how they look, so their captions are for
   a screen reader rather than the eye. Hidden this way rather than with
   display:none, because "APT_061" read out with no word in front of it is not
   much of an answer either. */
.zField[data-slug="unit_code"] .zFieldLabel,
.zField[data-slug="fantasy_name"] .zFieldLabel,
.zField[data-slug="availability"] .zFieldLabel{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
.zField[data-slug="fantasy_name"] .zFieldValue{ font-size:20px; }
.zField[data-slug="unit_code"] .zFieldValue{
  font-family:'IBM Plex Mono', monospace;
  font-size:11.5px; letter-spacing:0.06em; font-weight:400; color:var(--text-muted);
}
/* A flat with no fantasy name has only its code to be called, and a panel
   whose entire heading is 11px of grey monospace does not look like a heading.
   brabus is thirteen flats in exactly that state, and the panel this replaced
   showed the code large whenever there was no name — so the code takes the
   heading back when there is nothing else to put there. */
.zFcDetailInfo:has(> [data-slug="fantasy_name"][data-empty]) [data-slug="unit_code"] .zFieldValue,
.zFcDetailInfo:not(:has(> [data-slug="fantasy_name"])) [data-slug="unit_code"] .zFieldValue{
  font-size:19px; letter-spacing:0.02em; font-weight:600; color:var(--text);
}
/* ---- who to call ----
   A project setting, not a unit field: the same for every flat, so it sits
   below the record rather than in it, in the panel's bottom right corner. The
   flat's own details come first; the number to ring about it signs off. */
.zFcDetailContact{
  display:flex; flex-direction:column; align-items:flex-end; gap:1px;
  justify-self:end; align-self:end; text-align:right;
  margin-bottom:0; margin-top:16px; padding-top:12px;
  border-top:1px solid var(--border);
  min-width:0; max-width:100%;
}
.zFcContactLabel{
  font-family:'IBM Plex Mono', monospace;
  font-size:9.5px; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--zfc-contact-label, var(--text-muted));
  margin-bottom:2px;
}
.zFcContactName{ font-size:13px; font-weight:600; color:var(--zfc-contact-name, inherit); }
.zFcContactRole{ font-size:11px; color:var(--zfc-contact-role, var(--text-muted)); }
.zFcContactLink{
  font-size:12px; color:var(--accent); text-decoration:none;
  overflow-wrap:anywhere;
}
/* AFTER the shared rule and no more specific than it — one class each, so
   source order is what decides, which is what we want: these two ARE the
   link, wearing their own colour. A project that sets neither gets the accent
   from the fallback, exactly as before. */
.zFcContactPhone{ color:var(--zfc-contact-phone, var(--accent)); }
.zFcContactEmail{ color:var(--zfc-contact-email, var(--accent)); }
.zFcContactLink:hover{ text-decoration:underline; }
.zFcContactNote{ font-size:11px; color:var(--zfc-contact-note, var(--text-muted)); margin-top:3px; }

.zFcThumbs{
  display:flex; gap:8px; flex-wrap:wrap;
  padding:14px 22px 18px;
  border-top:1px solid var(--border);
}
.zFcThumbs button{
  width:74px; height:56px; padding:0;
  background:var(--surface-2); border:1px solid var(--border); border-radius:4px;
  overflow:hidden; cursor:pointer;
}
.zFcThumbs button.on{ border-color:var(--accent); }
.zFcThumbs img{ width:100%; height:100%; object-fit:cover; display:block; }

/* Both, deliberately. The container query is the one that is right; the media
   query is what a browser too old for it still understands, and on a phone the
   two say the same thing. Neither is redundant: only the first catches a panel
   made narrow on a wide screen. */
@container zPanel (max-width: 700px){
  .zFcDetailBody{
    grid-template-columns:minmax(0,1fr);
    grid-template-areas:"plan" "info" "thumbs";
  }
  .zFcDetailPlan{ --zfc-plan-h:min(46vh, 420px); padding:14px; }
}
.stageW820 .zFcDetailBody{
  grid-template-columns:minmax(0,1fr);
  grid-template-areas:"plan" "info" "thumbs";
}
.stageW820 .zFcDetailPlan{ --zfc-plan-h:min(46vh, 420px); padding:14px; }
/* Below a certain width the list would leave the render no room worth looking
   at, so it covers the stage instead of narrowing it. Taking it out of flow is
   what does that — the stage reclaims the width on its own, and the inline
   --zfc-list-w (which a stylesheet could not override) goes back to meaning
   just the panel's own width.

   THE CLASS IS SET FROM THE WRAP, NOT FROM THE STAGE. This rule gives the
   stage its width back, so keyed on the stage's own width — it was
   .stageW720 — it undoes the very condition that switched it on, and the
   layout flickers between the two answers once a frame. syncListRoom asks the
   window instead, which nothing in here can move. */
.zFcListOver .zFcList{ position:absolute; top:0; right:0; z-index:20; max-width:88%; box-shadow:-8px 0 24px rgba(0,0,0,0.4); }
.stageW560 .zFcRail{ top:12px; left:12px; }
.stageW560 .zFcRailBtn{ width:34px; height:34px; }
.stageW560 .zFcFilter{ left:54px; top:12px; }
.stageW560.zFcActive .onImageTitle{ --title-left:58px; }
@media (prefers-reduced-motion: reduce){
  .zFcList, .zFcFilter, .zFcDetail, .zFcDetailBox, .zFcRailBtn{ transition:none; }
}

/* on-image title, top-left */
/* ---- THE TOP ROW, WHICH HAS ONE OWNER ----

   The title and the compass both used to claim this band and neither knew
   about the other: the title grew to whatever the project is called, the
   compass took its width from the middle, and on any narrow window they were
   printed through each other. It happened on desktop too — a long enough
   name collided there as well, it was just rarer.

   THE RULE: the compass takes its width first and the title gets what is
   left. It is settled in the stylesheet rather than by measuring, so there is
   never a frame in which one of them is drawn in the wrong place, and it
   holds while the window is being dragged.

   BELOW stageW700 there is no "what is left" worth having — at 375px the
   compass alone is 78% of the picture — so they stop sharing the row and
   stack instead. The compass goes first because it is a fixed 20px tall and
   the title is one line or two: putting the known height on top means the
   second row's position is arithmetic rather than a measurement. */
/* On .stageWrap, not on .stage: both apps hand createCompass the WRAP as its
   host, so the compass is a sibling of the stage rather than inside it. A
   variable declared on the stage does not reach it, and the compass came out
   zero pixels wide — which is what a staged deploy to one site is for. The
   wrap is an ancestor of both readers, so both inherit it. */
.stageWrap{
  --zc-max:62%;
  --zc-used:min(var(--zc-width, 340px), var(--zc-max));
}
.stageWrap.stageW700{ --zc-max:78%; }

.onImageTitle{
  position:absolute;
  top:var(--title-top, 16px);
  left:var(--title-left, 18px);
  z-index:5;
  margin:0;
  padding:var(--title-pad-y, 0px) var(--title-pad-x, 0px);
  /* THE NUDGE IS A TRANSFORM, not an offset added to top/left, and that is the
     only reason any of this composes. The anchor is contested — the flat
     chooser's rail moves it right, the compass moves it down, a phone moves it
     again — and every one of those rules would have to learn about the
     project's own x and y if they shared a property. A translate sits on top
     of whatever the anchor turned out to be. --title-shift is the alignment's
     own half-width for a centred title; the project's nudge is added to it. */
  transform:translate(calc(var(--title-shift, 0px) + var(--title-x, 0px)), var(--title-y, 0px));
  text-shadow:0 1px 4px rgba(0,0,0,0.6);
  pointer-events:none;
  /* A TYPED NEWLINE IS A NEWLINE. The title used to be one run of text that
     broke wherever it ran out of room, so a name that reads as two lines —
     which is most of them, once a development has a district in its name —
     broke in the middle of whichever word happened to reach the edge. */
  white-space:pre-line;
  line-height:var(--title-lh, normal);
  /* Two lines was the rule and is still the default: a name long enough to
     need a third has already said what it is. It is a number now rather than a
     law, because a title WRITTEN as three lines is a different thing from one
     that ran to three by accident. 99 is this project's "as many as it takes". */
  display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:var(--title-lines, 2);
  overflow:hidden;
  max-width:calc(100% - var(--title-left, 18px) - 18px);
}
/* ---- which edge it sits against ----
   Left is what it has always been and needs no rule. The other two are one
   class plus one attribute, which beats `.zFcActive .onImageTitle` on order
   and loses to the three-class phone and compass rules on specificity — both
   of which are what we want, since those move the title for reasons that have
   nothing to do with the project's taste. */
.onImageTitle[data-align="center"]{
  left:50%; right:auto;
  --title-shift:-50%;
  text-align:center;
  /* not var(--title-left): that is the rail's clearance, and the stage has
     already narrowed by the width of the list. A centred title is centred in
     what is left. */
  max-width:calc(100% - 36px);
}
.onImageTitle[data-align="right"]{
  left:auto; right:18px;
  text-align:right;
  max-width:calc(100% - 36px);
}
/* room for the compass, taken out of the middle */
.hasCompass .onImageTitle{
  max-width:calc(50% - var(--zc-used) / 2 - var(--title-left, 18px) - 14px);
}
/* …and below this they stack instead, the compass on the first row */
.stageW700.hasCompass .onImageTitle{
  top:calc(var(--zc-top, 16px) + 28px);
  max-width:calc(100% - var(--title-left, 18px) - 18px);
}
/* A CENTRED TITLE AND A COMPASS WANT THE SAME PLACE — the middle of the top
   row — at every width, not only the narrow ones. So they stack there too,
   rather than the title taking half a row it cannot have. */
.hasCompass .onImageTitle[data-align="center"]{
  top:calc(var(--zc-top, 16px) + 28px);
  max-width:calc(100% - 36px);
}

/* on-image footer, bottom-left — a real link, not just decorative text */
.onImageFooter{
  position:absolute;
  bottom:12px; left:18px;
  z-index:5;
  margin:0;
  text-shadow:0 1px 3px rgba(0,0,0,0.6);
  text-decoration:none;
}
.onImageFooter:hover{ text-decoration:underline; }

/* ---- breadcrumb, directly above the nav ----
   "Project Name > House Name". Its `bottom` is set from script
   (ZOACore.anchorAbove) because the three nav styles are very different
   heights; everything else is themed through the vars renderCrumbs sets.
   Hidden until it has something to say — a project with no sub-sequences
   never sees it. */
.crumbBar{
  position:absolute;
  bottom:0; left:50%;
  transform:translateX(-50%);
  z-index:6;
  display:none;
  align-items:center;
  gap:calc(6px * var(--scale));
  padding:calc(4px * var(--scale)) calc(12px * var(--scale));
  border-radius:20px;
  max-width:min(92%, 760px);
  backdrop-filter:blur(3px);
  background:rgba(var(--crumb-bg-rgb, 20,20,16), var(--crumb-bg-opacity, 0.55));
  font-family:var(--crumb-font-family, inherit);
  font-size:calc(var(--crumb-font-size, 12px) * var(--scale));
  font-weight:var(--crumb-font-weight, 400);
  font-style:var(--crumb-font-style, normal);
  white-space:nowrap;
}
.crumbBar.open{ display:flex; }

.crumbSeg{
  font:inherit;
  color:var(--crumb-color, rgba(238,233,224,0.72));
  text-decoration:var(--crumb-text-decoration, none);
  background:none; border:0; padding:0; margin:0;
  /* every segment can be squeezed, but the one you are on is squeezed last —
     a long project name must not push "House 3" out of the bar */
  min-width:0; overflow:hidden; text-overflow:ellipsis;
}
button.crumbSeg{ cursor:pointer; transition:color 140ms ease; }
button.crumbSeg:hover{ color:var(--crumb-active-color, #eee9dd); }
.crumbSeg.current{ color:var(--crumb-active-color, #eee9dd); flex:0 0 auto; }
/* falls back to the segment colour, which is what it was before it had a var
   of its own — only the bar look sets it */
.crumbSep{ color:var(--crumb-sep-color, var(--crumb-color, rgba(238,233,224,0.72))); opacity:0.6; flex:0 0 auto; }

/* ---- the crumb bar wearing the project's bar navigation ----
   Only on a free-spin sequence, where it IS the navigation: the pill with the
   scene list is gone, so this is the one piece of furniture at the bottom of
   the picture and it should look like the piece the project chose. Every value
   comes from theme.navBar, so restyling the nav restyles this with it.
   The back arrow is the only arrow: forward from here is nowhere.

   NOT scaled by --scale, unlike everything else in the crumb bar. .navBarShell
   is a flat `height:var(--bar-height)` and barStyleSlot writes plain pixel font
   sizes, so multiplying here made the same capsule 20% bigger on the flat
   chooser than on the overview — the one place the difference is visible,
   since the two are a click apart. Matching the nav means matching how the nav
   measures, not just what it measures. */
.crumbBar.asBar{
  border-radius:var(--crumb-radius, 999px);
  min-height:var(--crumb-height, 44px);
  padding:0 18px;
  gap:9px;
  font-size:var(--crumb-font-size, 13px);
  backdrop-filter:blur(4px);   /* the same blur .navBarShell has */
  border:var(--crumb-stroke-width, 0px) solid var(--crumb-stroke-color, transparent);
}
.crumbBar.asBar .crumbSeg.current{
  font-family:var(--crumb-active-family, inherit);
  font-size:var(--crumb-active-size, 15px);
  font-weight:600;   /* the same weight barStyleSlot gives the current scene */
}
.crumbBar.asBar .crumbSep{ opacity:1; }
.crumbBar.asBar .crumbBack{
  color:var(--crumb-arrow-color, var(--crumb-color));
  width:20px; height:20px;
  margin-right:4px;
}

/* the way out, sitting inside the bar rather than being a second piece of
   chrome that says the same thing */
.crumbBack{
  display:flex; align-items:center; justify-content:center;
  flex:0 0 auto;
  width:calc(18px * var(--scale)); height:calc(18px * var(--scale));
  margin-right:calc(2px * var(--scale));
  padding:0; border:0; background:none;
  color:var(--crumb-color, rgba(238,233,224,0.72));
  cursor:pointer;
  transition:color 140ms ease, transform 140ms ease;
}
.crumbBack svg{ width:100%; height:100%; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
.crumbBack:hover{ color:var(--crumb-active-color, #eee9dd); transform:translateX(-2px); }

/* nav pill, bottom-center, on the image */
.navBox{
  position:absolute;
  bottom:14px; left:50%;
  transform:translateX(-50%);
  z-index:6;
  display:flex;
  align-items:center;
  gap:calc(14px * var(--scale));
  padding:calc(7px * var(--scale)) calc(16px * var(--scale));
  border-radius:20px;
  backdrop-filter:blur(3px);
  background:rgba(var(--nav-bg-rgb, 20,20,16), var(--nav-bg-opacity, 0.55));
}
/* A nav box with nothing in it is a blurred smear on the render. The viewer
   hides it outright on a free-spin sequence — but zoa-core.js is one shared
   file and a site's HTML is not versioned, so a visitor holding a cached page
   from before the spin arrows were removed will empty the box and leave it.
   This is for them; the current viewer never relies on it. */
.navBox:empty{ display:none; }

/* ---- "drag to rotate", at both edges of a free-spin picture ----
   Where the nav pill's two arrows used to be. They sit at the sides because
   that is where the gesture goes and because the bottom of the picture already
   has the breadcrumb; they are quiet because they are a hint, not a control,
   and they leave (.retired) once the visitor has turned the building twice.
   One drawing: the left one is the right one mirrored. */
.zSpinHint{
  position:absolute;
  /* Placed off the centre line rather than off the edges: one distance out
     from the middle and one up from the floor, each a setting, the pair
     symmetric by construction because they share a single number with
     opposite signs. Anchored by their centres, hence the -50% that every
     keyframe has to repeat. */
  left:calc(50% + var(--zsh-dx, 230px));
  bottom:var(--zsh-y, 76px);
  transform:translateX(-50%);
  z-index:6;
  pointer-events:none;
  display:flex; flex-direction:column; align-items:center;
  gap:var(--zsh-gap, 6px);
  /* White by default rather than the scroll hint's off-white: these sit over
     renders that are mostly sky and pale ground, where 0.85 off-white came out
     as a grey smudge. The drop shadow carries them over a light frame. */
  color:var(--zsh-color, #ffffff);
  opacity:var(--zsh-opacity, 0.96);
  filter:var(--zsh-shadow, none);
  transition:opacity 420ms ease;
}
.zSpinHint.retired{ opacity:0; }
.zSpinHint svg{
  display:block;
  /* `size` is the arrow's width in pixels — what it looks like on screen,
     rather than a multiplier of some other glyph's size. */
  width:var(--zsh-size, 55px); height:auto;
  fill:currentColor; stroke:none;
  /* Tipped anticlockwise (and mirrored, on the left), which is what turns a
     sweep that goes RIGHT into one that goes ROUND: it leaves horizontally and
     finishes pointing upward. Built in the JS, because the angle is a setting
     and because the mirror has to be applied after the rotation. */
  transform:var(--zsh-rotate, rotate(-30deg));
  overflow:visible;   /* the rotated corners are outside the box, not clipped */
}
.zSpinHintText{
  font-family:var(--nav-font-family, inherit);
  font-size:var(--zsh-text-size, 11px);
  font-weight:500;
  letter-spacing:0.14em;
  text-transform:uppercase;
  white-space:nowrap;
}
/* The nudge goes the way that side turns, so the pair reads as one gesture
   with two directions rather than as two blinking labels. Each keyframe
   restates the -50%, since transform is one property and the base rule's
   centring would otherwise be dropped for the duration. */
.zSpinHint.right{ animation:zSpinNudgeR 2.4s ease-in-out infinite; }
.zSpinHint.left{ animation:zSpinNudgeL 2.4s ease-in-out infinite; }
@keyframes zSpinNudgeR{
  0%,100%{ transform:translateX(-50%); }
  50%{ transform:translateX(calc(-50% + 6px)); }
}
@keyframes zSpinNudgeL{
  0%,100%{ transform:translateX(-50%); }
  50%{ transform:translateX(calc(-50% - 6px)); }
}
@media (prefers-reduced-motion: reduce){
  .zSpinHint{ animation:none; }
}
/* on a phone the two labels meet in the middle; the arrows alone still say it */
.stageW640 .zSpinHintText{ display:none; }

/* The render is scenery, not a file to drag to the desktop. Without this a
   press-and-drag on a free-spin strip starts the browser's native image drag
   and the building never moves — pointermove simply stops arriving. */
.stageInner img.scene{ -webkit-user-drag:none; user-drag:none; }
.stage.freeSpin img{ -webkit-user-drag:none; user-drag:none; }

.navStop{
  display:flex; align-items:center; gap:calc(6px * var(--scale));
  cursor:pointer;
  background:none; border:none;
  padding:0;
  white-space:nowrap;
}
.navDot{
  position:relative;
  width:calc(9px * var(--scale)); height:calc(9px * var(--scale));
  border-radius:50%;
  background:rgba(255,255,255,0.4);
  flex-shrink:0;
  transition:background 150ms ease, transform 150ms ease;
}
.navStop.active .navDot{ background:var(--accent); transform:scale(1.3); }
.navStop:not(.active) .navDot::after{
  content:'';
  position:absolute; inset:calc(-4px * var(--scale));
  border:1px solid rgba(255,255,255,0.55);
  border-radius:50%;
  opacity:0.55;
  animation:navpulse 2.4s ease-out infinite;
  pointer-events:none;
}
.navStop:not(.active):hover .navDot::after{ animation:none; opacity:0; }
@keyframes navpulse{
  0%{ transform:scale(1); opacity:0.55; }
  100%{ transform:scale(2.3); opacity:0; }
}
.navLabel{ transition:color 150ms ease; font-family:var(--nav-font-family, inherit); font-size:var(--nav-font-size-base, 11px); color:var(--nav-text-color, #eee9dd); font-weight:var(--nav-font-weight, 400); font-style:var(--nav-font-style, normal); text-decoration:var(--nav-text-decoration, none); }
.navStop.active .navLabel{ color:var(--nav-selected-color, var(--accent)); }

/* alternate "carousel" nav style: current scene centered, one neighbour
   labelled on each side, connected by a line — clicking a neighbour makes
   it the new centre */
.navBox.carouselMode{
  background:none; backdrop-filter:none; padding:0;
  width:min(380px, 82vw);
}
.navCarouselLine{ position:relative; width:100%; height:1px; background:rgba(255,255,255,0.4); }
.navCarouselDotWrap{
  position:absolute; top:50%; transform:translate(-50%,-50%);
  display:flex; flex-direction:column; align-items:center; gap:calc(8px * var(--scale));
  cursor:pointer; background:none; border:none; padding:0;
}
.navCarouselDot{
  width:calc(9px * var(--scale)); height:calc(9px * var(--scale));
  border-radius:50%; background:rgba(255,255,255,0.5);
  transition:background 150ms ease, transform 150ms ease;
}
.navCarouselDotWrap:hover .navCarouselDot{ transform:scale(1.2); }
.navCarouselDotWrap.active .navCarouselDot{ background:var(--nav-selected-color, var(--accent)); transform:scale(1.3); }
.navCarouselLabel{
  font-family:var(--nav-font-family, inherit);
  font-size:var(--nav-font-size-base, 11px);
  color:var(--nav-text-color, #eee9dd);
  font-weight:var(--nav-font-weight, 400);
  font-style:var(--nav-font-style, normal);
  text-decoration:var(--nav-text-decoration, none);
  white-space:nowrap;
  transition:color 150ms ease, background 150ms ease, padding 150ms ease;
}
.navCarouselDotWrap.active .navCarouselLabel{
  background:rgba(var(--nav-bg-rgb, 20,20,16), var(--nav-bg-opacity, 0.55));
  padding:calc(5px * var(--scale)) calc(12px * var(--scale));
  border-radius:14px;
  color:var(--nav-selected-color, var(--accent));
  backdrop-filter:blur(3px);
}

/* "wheel" nav style — a dipping elliptical line with the current scene
   centred on it, fading out at both edges, one labelled neighbour each side */
.navBox.wheelMode{
  background:none; backdrop-filter:none; padding:0;
  width:min(460px, 88vw);
  height:80px;
}
.navWheelSvg{
  position:absolute; inset:0; width:100%; height:100%;
  pointer-events:none; overflow:visible;
}
.navWheelPath{ stroke: url(#navWheelFade); }
.navWheelDotWrap{
  position:absolute;
  left:0; top:0;
  display:flex; flex-direction:column; align-items:center; gap:6px;
  background:none; border:none; cursor:pointer; padding:0;
}
.navWheelDot{
  position:relative;
  width:12px; height:12px; border-radius:50%;
  background:var(--wheel-dot-color, #ffffff);
  color:var(--wheel-dot-color, #ffffff);
  transition:background 200ms ease, color 200ms ease;
}
.navWheelDotWrap.active .navWheelDot{
  background:var(--nav-selected-color, var(--accent));
  color:var(--nav-selected-color, var(--accent));
}
.navWheelDot::after{
  content:'';
  position:absolute; inset:-6px;
  border-radius:50%;
  border:1px solid currentColor;
  color:inherit;
  opacity:0.5;
  animation:navWheelPulse 2.4s ease-out infinite;
  pointer-events:none;
}
.navWheelDotWrap:hover .navWheelDot::after{ animation:none; opacity:0; }
@keyframes navWheelPulse{
  0%{ transform:scale(1); opacity:0.5; }
  100%{ transform:scale(2.3); opacity:0; }
}
.navWheelLabel{ white-space:nowrap; display:inline-block; transition:transform 500ms ease, color 500ms ease, text-shadow 500ms ease; }

/* hotspot markers — a position wrapper (anchors to x/y, never animates)
   plus an inner element (scales in/out on arrival/departure) */
.hotspotPos{ position:absolute; }
.hotspotPos.plain{ transform:translate(-50%,-50%); }
.hotspotPos.named{ transform:translate(-50%,-100%) translateY(calc(-8px * var(--scale))); }

/* --marker-pad adds to the marker's own geometry, growing the box without
   changing the icon/label inside it. --marker-radius-* is set per shape:
   rounded (the original), pill (fully round ends, a circle when there's no
   label) and rect (square corners). */
.hotspot{
  position:relative;
  cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  background:rgba(var(--marker-bg-rgb), var(--marker-bg-opacity));
  backdrop-filter:blur(2px);
  transform:scale(1);
  transition:transform 150ms ease;
  box-shadow:var(--marker-shadow, none);
}
.hotspot.plain{
  width:calc((28px + 2 * var(--marker-pad-x, var(--marker-pad, 0px))) * var(--scale));
  height:calc((28px + 2 * var(--marker-pad-y, var(--marker-pad, 0px))) * var(--scale));
  border-radius:var(--marker-radius-plain, 4px);
}
.hotspot.plain:hover{ transform:scale(1.12); }
.hotspot.named{
  height:calc((28px + 2 * var(--marker-pad-y, var(--marker-pad, 0px))) * var(--scale));
  padding:0 calc((10px + var(--marker-pad-x, var(--marker-pad, 0px))) * var(--scale)) 0 calc((8px + var(--marker-pad-x, var(--marker-pad, 0px))) * var(--scale));
  border-radius:var(--marker-radius-named, 5px);
  gap:calc(6px * var(--scale));
  white-space:nowrap;
}
.hotspot.named:hover{ transform:scale(1.06); }
/* A LABEL OVER MORE THAN ONE LINE, and only when the name really has one — a
   single-line label keeps the fixed height and the nowrap above it, so nothing
   already set moves by a pixel.

   pre-line rather than pre: runs of spaces still collapse as they always did,
   so only the newline is significant and a name with stray double spaces does
   not suddenly render them. */
.hotspot.named.multiline{
  height:auto;
  min-height:calc((28px + 2 * var(--marker-pad-y, var(--marker-pad, 0px))) * var(--scale));
  /* The extra goes on the TOP alone — that is the whole point of it. Inside
     the same calc so it scales with everything else; 0 by default, which is
     the symmetrical padding this had before. */
  padding-top:calc((var(--marker-pad-y, var(--marker-pad, 0px)) + var(--marker-ml-top, 0px)) * var(--scale));
  padding-bottom:calc(var(--marker-pad-y, var(--marker-pad, 0px)) * var(--scale));
  white-space:pre-line;
}
.hotspot.named.multiline .hsName{ text-align:var(--marker-ml-align, center); line-height:1.25; }
.hotspot.named::after{
  content:'';
  position:absolute;
  left:50%; bottom:calc(-7px * var(--scale));
  transform:translateX(-50%);
  width:0; height:0;
  border-left:calc(6px * var(--scale)) solid transparent;
  border-right:calc(6px * var(--scale)) solid transparent;
  border-top:calc(7px * var(--scale)) solid var(--pin-color, var(--accent));
}
/* ---- PICTOGRAMS ONLY, ON A PHONE THAT ASKED ----

   .hasIcon is the marker's half of it: a marker whose words are the whole
   of it keeps them, because a coloured box saying nothing is worse than a
   label that is too long.

   The box goes back to a plain marker's geometry rather than staying a pill
   with nothing in it — the padding and the width of a label are what make
   the shape, and an empty one reads as a mistake. What does NOT change is
   where it sits: a named marker stands above its point with a tail pointing
   down at it, and that tail is now the only thing saying which spot this
   is. */
.stageWrap.stagePhone.phoneIconsOnly .hotspot.named.hasIcon .hsName{ display:none; }
.stageWrap.stagePhone.phoneIconsOnly .hotspot.named.hasIcon{
  width:calc((28px + 2 * var(--marker-pad-x, var(--marker-pad, 0px))) * var(--scale));
  padding:0; gap:0;
  border-radius:var(--marker-radius-plain, 4px);
  white-space:nowrap;
}
.hotspot .hsIcon{ font-size:calc(13px * var(--scale)); color:var(--marker-icon-color); }
.hotspot .hsName{ font-size:calc(var(--marker-font-size-base, 11.5px) * var(--scale)); color:var(--marker-name-color); font-family:var(--marker-font-family, inherit); font-weight:var(--marker-font-weight, 400); font-style:var(--marker-font-style, normal); text-decoration:var(--marker-text-decoration, none); }
/* the pulsating ring. Colour/thickness/starting opacity/speed are all
   configurable; the defaults reproduce the original look (contour colour,
   1px, 0.6 starting opacity, 2.4s). The animation's starting opacity comes
   from --marker-pulse-opacity so the keyframes stay generic. */
.hotspot::before{
  content:'';
  position:absolute; inset:calc(-6px * var(--scale));
  border-radius:inherit;
  border:calc(var(--marker-pulse-width, 1px) * var(--scale)) solid var(--marker-pulse-color, var(--pin-color, var(--accent)));
  opacity:var(--marker-pulse-opacity, 0.6);
  animation:pulse var(--marker-pulse-duration, 2.4s) ease-out infinite;
  pointer-events:none;
}
/* thickness 0 would still paint a hairline border on some engines */
.hotspot.noPulse::before{ display:none; }
.hotspot:hover::before{ animation:none; opacity:0; }
/* --marker-pulse-scale is how far the ring travels before fading out, as a
   multiple of the marker's size (1 = no growth). var() resolves against the
   element the animation runs on, so this stays per-marker overridable. */
@keyframes pulse{
  0%{ transform:scale(1); opacity:var(--marker-pulse-opacity, 0.6); }
  100%{ transform:scale(var(--marker-pulse-scale, 1.6)); opacity:0; }
}

/* ---- leader marker: a label at the edge, a line back to the thing ----
   ALL THE GEOMETRY IS HERE, and it has to be. The row spans from the target to
   the docking edge, and that is a distance which changes with every resize —
   so it is expressed as two edges of one absolutely-positioned box and the
   line is simply the flex item that takes whatever is left between them.
   Nothing measures the line, nothing recalculates on resize, and a hundred
   leaders cost the same as one.

   The wrapper is full-bleed over .stageInner so BOTH ends can be measured
   against the same box: the dot from the target's own percentage, the label
   from the edge.

   --zc-overhang-x/y is the one thing JS supplies (ZOACore.watchStageOverhang).
   .stageInner is centred and cover-cropped, so it hangs off the stage by half
   the difference on each side; docking to the VISIBLE edge rather than to the
   image's own box means adding that back. Without it the labels dock relative
   to the picture and slide off screen on a wide window. */
.hotspotPos.leader{
  inset:0;
  /* The wrapper is the size of the whole stage now, so it would swallow every
     click on the render if it took the pointer — the exact bug #hotspotLayer's
     note above describes. Only the label takes hits. */
  pointer-events:none;
}
.zcLeadRow{
  position:absolute;
  display:flex;
  align-items:center;
  /* Which way it overflows when there is NOT room. A target closer to the
     edge than the label is wide cannot have both — something has to give, and
     the label leaving the screen is the one outcome that is never acceptable.
     Packing to the end pins the label at its inset and pushes the dot inward
     instead; the line simply runs out of length. flex-end is the DOCK end in
     all four directions, because the reversed rows reverse the axis too. */
  justify-content:flex-end;
  /* --lead-used, not --lead-inset, so a phone can multiply it.
     The multiplication has to be declared ON THE ROW: --lead-inset is set inline
     there, and a var() inside a custom property resolves where that property
     is DECLARED, not where it is finally used — declared on an ancestor it
     would look for an inline value the ancestor has not got. */
  --lead-used-x:var(--lead-inset, 40px);
  --lead-used-y:var(--lead-inset, 40px);
  --zc-dock-x:calc(var(--zc-overhang-x, 0px) + var(--lead-used-x));
  --zc-dock-y:calc(var(--zc-overhang-y, 0px) + var(--lead-used-y));
  /* The target end of the row is where the dot's OUTER EDGE lands, so the row
     starts half a dot early and the dot comes out centred on the target. */
  --zc-lead-half:calc(var(--lead-dot, 5px) * var(--scale) / 2);
}
/* THE HORIZONTAL PAIR DOCK BY THE LABEL'S INNER EDGE, the vertical pair by its
   outer one, and that asymmetry is deliberate rather than an oversight.

   Labels vary enormously in width and barely at all in height. So on the
   horizontal sides the tidy thing is for every LINE to end on the same
   vertical — which means fixing the edge of the label the line arrives at, and
   letting the far end run out to wherever the text needs. On the vertical
   sides the tidy thing is for the label TOPS to line up, which is the outer
   edge, and the lines then end wherever each shape happens to be.

   Both give "a neat column"; they just disagree about which edge of the box is
   the one worth aligning, because in one direction the boxes are ragged and in
   the other they are not.

   The consequence of the horizontal rule, and it is intended: the distance is
   measured to the edge the line meets, so the label extends OUTWARD from there
   and a distance smaller than the label is wide puts the text off the picture.
   A distance of 0 shows nothing at all. */

/* One rule per side, each saying the same three things: span from the target
   to the docking edge, centre the row on the target's other axis, and order
   the parts so the DOT is the end that sits on the target. No other rule below
   knows which side it is on. */
.zcLeadRow[data-side="right"]{
  left:calc(var(--lead-x) - var(--zc-lead-half)); right:var(--zc-dock-x); top:var(--lead-y);
  transform:translateY(-50%); flex-direction:row;
}
.zcLeadRow[data-side="left"]{
  left:var(--zc-dock-x); right:calc(100% - var(--lead-x) - var(--zc-lead-half)); top:var(--lead-y);
  transform:translateY(-50%); flex-direction:row-reverse;
}
.zcLeadRow[data-side="bottom"]{
  top:calc(var(--lead-y) - var(--zc-lead-half)); bottom:var(--zc-dock-y); left:var(--lead-x);
  transform:translateX(-50%); flex-direction:column;
}
.zcLeadRow[data-side="top"]{
  top:var(--zc-dock-y); bottom:calc(100% - var(--lead-y) - var(--zc-lead-half)); left:var(--lead-x);
  transform:translateX(-50%); flex-direction:column-reverse;
}
/* ---- THE LABEL A PERCENTAGE OF THE WAY IN ----

   The row already spans from the marker to the docking edge, and the label
   is packed at the docking end — so where the label sits IS where that end
   is, and a percentage only has to move it. Nothing else about the four
   sides changes, which is why these redefine the dock offset rather than
   the positions that read it.

   Measured from the marker to the VISIBLE edge: the overhang is added back
   so 100% lands where the picture ends rather than where the cropped image
   does, which is the edge somebody is looking at. At 100% the label is
   flush to it — no inset — because "at the edge" is what was asked for; a
   gap is 95%. At 0% it sits on its own marker.

   The attribute is set only when a percentage is given, so a leader without
   one is laid out by the rules above, untouched. */
.zcLeadRow[data-pct][data-side="right"]{
  --zc-dock-x:calc(var(--zc-overhang-x, 0px) +
    (100% - var(--lead-x) - var(--zc-overhang-x, 0px)) * (1 - var(--lead-pct, 1)));
}
.zcLeadRow[data-pct][data-side="left"]{
  --zc-dock-x:calc(var(--zc-overhang-x, 0px) +
    (var(--lead-x) - var(--zc-overhang-x, 0px)) * (1 - var(--lead-pct, 1)));
}
.zcLeadRow[data-pct][data-side="bottom"]{
  --zc-dock-y:calc(var(--zc-overhang-y, 0px) +
    (100% - var(--lead-y) - var(--zc-overhang-y, 0px)) * (1 - var(--lead-pct, 1)));
}
.zcLeadRow[data-pct][data-side="top"]{
  --zc-dock-y:calc(var(--zc-overhang-y, 0px) +
    (var(--lead-y) - var(--zc-overhang-y, 0px)) * (1 - var(--lead-pct, 1)));
}
.zcLeadDot{
  flex:none;
  width:calc(var(--lead-dot, 5px) * var(--scale));
  height:calc(var(--lead-dot, 5px) * var(--scale));
  border-radius:50%;
  background:var(--lead-color, var(--pin-color, var(--accent)));
}
/* min-width/min-height 0 because a flex item refuses to shrink below its
   content by default, and on a narrow window the row is shorter than the label */
.zcLeadLine{
  flex:1 1 auto;
  background:var(--lead-color, var(--pin-color, var(--accent)));
}
.zcLeadRow[data-side="left"] .zcLeadLine,
.zcLeadRow[data-side="right"] .zcLeadLine{
  min-width:0; height:calc(var(--lead-weight, 1px) * var(--scale));
}
.zcLeadRow[data-side="top"] .zcLeadLine,
.zcLeadRow[data-side="bottom"] .zcLeadLine{
  min-height:0; width:calc(var(--lead-weight, 1px) * var(--scale));
}
/* The label is an ordinary marker, so every colour, shape, padding and font
   setting in the Markers menu already applies to it. Two parts of one do not
   belong on a leader: the little tail underneath pointed at the thing, and a
   line does that now; and a pulse ring draws the eye to a label parked at the
   edge rather than to the target it names. */
.zcLeadRow .hotspot{ pointer-events:auto; flex:none; }
/* Out of the flow and hung off the row's docking end, so the row still spans
   exactly target-to-distance and the label starts where the row stops.
   Centred with auto margins rather than a translate, because animateIn and
   :hover both own this element's transform. */
.zcLeadRow[data-side="left"] .hotspot,
.zcLeadRow[data-side="right"] .hotspot{ position:absolute; top:0; bottom:0; margin-top:auto; margin-bottom:auto; }
.zcLeadRow[data-side="right"] .hotspot{ left:100%; }
.zcLeadRow[data-side="left"] .hotspot{ right:100%; }
/* The gap shortens the LINE rather than moving the label: the label's edge is
   the thing being positioned, so anything that moved it would undo the
   alignment the distance was set for. */
.zcLeadRow[data-side="right"] .zcLeadLine{ margin-right:calc(var(--lead-gap, 12px) * var(--scale)); }
.zcLeadRow[data-side="left"] .zcLeadLine{ margin-left:calc(var(--lead-gap, 12px) * var(--scale)); }
.zcLeadRow[data-side="bottom"] .zcLeadLine{ margin-bottom:calc(var(--lead-gap, 12px) * var(--scale)); }
.zcLeadRow[data-side="top"] .zcLeadLine{ margin-top:calc(var(--lead-gap, 12px) * var(--scale)); }
.zcLeadRow .hotspot.named::after{ display:none; }
.zcLeadRow .hotspot::before{ display:none; }
/* The line draws out FROM the target, which is the direction it is read in.
   The label scales in on its own through animateIn, as every marker does. */
.zcLeadRow[data-side="right"] .zcLeadLine{ transform-origin:left center; }
.zcLeadRow[data-side="left"] .zcLeadLine{ transform-origin:right center; }
.zcLeadRow[data-side="bottom"] .zcLeadLine{ transform-origin:center top; }
.zcLeadRow[data-side="top"] .zcLeadLine{ transform-origin:center bottom; }
.zcLeadRow[data-side="left"] .zcLeadLine,
.zcLeadRow[data-side="right"] .zcLeadLine{ animation:zcLeadDrawX var(--lead-draw, 420ms) ease-out both; }
.zcLeadRow[data-side="top"] .zcLeadLine,
.zcLeadRow[data-side="bottom"] .zcLeadLine{ animation:zcLeadDrawY var(--lead-draw, 420ms) ease-out both; }
@keyframes zcLeadDrawX{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }
@keyframes zcLeadDrawY{ from{ transform:scaleY(0); } to{ transform:scaleY(1); } }
@media (prefers-reduced-motion: reduce){
  .zcLeadLine{ animation:none; }
}

/* ---- custom marker: thought cloud ----
   Built from overlapping bordered lobes with unbordered fills laid over the
   top, which erases the internal borders and leaves one continuous outline.
   All geometry is set inline from JS as fractions of the bubble width, so the
   whole thing scales from a single number. */
.hotspotPos.custom{ transform:translate(-50%,-100%); }
.hotspot.custom{
  background:none; backdrop-filter:none; border:none; padding:0;
  box-shadow:none;
  display:block; cursor:pointer;
  transform:scale(1);
  transition:transform 150ms ease;
}
.hotspot.custom::before{ display:none; }   /* no pulse ring behind a cloud */
.hotspot.custom:hover{ transform:scale(1.03); }

/* A marker with nothing behind it — no panel content, no picture, no panorama,
   no target scene. It looks exactly as it did; it simply stops inviting a
   click it cannot answer. Placed after the three :hover rules above so it wins
   on equal specificity rather than by an !important.

   The editor overrides the cursor back: there, every marker is clickable
   because clicking one is how you edit it. */
/* A CONTROL MARKER THAT IS ON. Only the units one has a state to show — a
   filter and a list are doors rather than switches — and it borrows the flat
   chooser's own "on" colour so a button placed on the render matches the rail
   it replaces instead of inventing a second idea of pressed. */
.hotspot.control.on{
  background:var(--zfc-rail-bg-on, var(--accent));
  border-color:var(--zfc-rail-bg-on, var(--accent));
}
.hotspot.control.on .hsName,
.hotspot.control.on .hsIcon{ color:var(--zfc-rail-fg-on, #141410); }
.hotspot.inert{ cursor:default; }
.hotspot.inert:hover{ transform:scale(1); }
.zcCloud{ position:relative; filter:drop-shadow(var(--marker-shadow-flat, 0 0 0 transparent)); }
.zcLayer{
  position:absolute;
  transition:transform 0.15s cubic-bezier(0.25, 1, 0.5, 1);
}
.zcCloudLayer{ left:0; top:0; }
.zcFloat{
  position:relative; width:100%; height:100%;
  animation:zcFloat 3s ease-in-out infinite alternate 2.2s;
}
/* the whole union carries the background transparency ONCE — applying it to
   each lobe would compound wherever they overlap and look blotchy */
.zcBody{ display:block; position:absolute; inset:0; opacity:var(--zc-alpha, 1); }
.zcDot, .zcLobe{
  position:absolute;
  background:var(--zc-fill, #fff);
  border:var(--zc-stroke, 4px) solid var(--zc-border, #333);
  border-radius:50%;
  opacity:0; transform:scale(0);
  animation:zcPop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}
/* dots sit outside the body group and don't overlap each other, so they can
   carry the alpha themselves — via the keyframe, which would otherwise end at 1 */
.zcDot{ display:block; animation-name:zcPopDot; }
.zcFill{
  position:absolute;
  background:var(--zc-fill, #fff);
  border-radius:50%;
  opacity:0;
  animation:zcFade 0.1s forwards;
}
/* pictogram + name, centred on the cloud body */
.zcContent{
  position:absolute; inset:14% 14% 22%;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:0.35em;
  opacity:0;
  animation:zcFade 0.35s forwards;
  pointer-events:none;
  z-index:2;
}
.zcIcon{ color:var(--zc-text, #17170f); line-height:1; }
.zcText{
  display:block; max-width:100%;
  text-align:center; line-height:1.2;
  color:var(--zc-text, #17170f);
  font-family:var(--marker-font-family, inherit);
  font-weight:var(--marker-font-weight, 600);
  font-style:var(--marker-font-style, normal);
  overflow-wrap:break-word;
}
@keyframes zcPop{
  0%{ opacity:0; transform:scale(0); }
  100%{ opacity:1; transform:scale(1); }
}
@keyframes zcPopDot{
  0%{ opacity:0; transform:scale(0); }
  100%{ opacity:var(--zc-alpha, 1); transform:scale(1); }
}
@keyframes zcFade{ to{ opacity:1; } }
@keyframes zcFloat{
  0%{ transform:translateY(0); }
  100%{ transform:translateY(-6px); }
}
@media (prefers-reduced-motion: reduce){
  .zcFloat{ animation:none; }
  .zcDot, .zcLobe, .zcFill, .zcContent{ animation-duration:0.01s; }
}

/* ---- "bar" nav style ----
   Current scene centred, neighbours either side, arrow at each end. The three
   slots are absolutely positioned thirds so the relay can slide them between
   fixed positions without label widths shifting each other mid-animation. */
/* One slot width is the unit the whole strip is measured in, so a dead end can
   drop exactly one slot (and one arrow) off the capsule.

   The box around the capsule is pinned to the FULL strip width and never
   shrink-wraps its contents. It used to be width:auto, which deadlocked the
   collapse: the box sized itself to the capsule, the capsule (a flex item) was
   then clamped to the box, and the pair could only ever ratchet smaller — the
   capsule could shrink but never grow back. */
/* ---- the call button, sitting above the navigation ----

   Deliberately the same rules as .navBarShell rather than a copy of its
   look: same height, same radius, same ground, same blur. It reads as the
   bar's twin because it IS the bar's geometry, handed to it in the same
   custom properties. */
.zCallBtn{
  position:absolute;
  left:50%; transform:translateX(-50%);
  z-index:6;
  width:min(var(--bar-width, 520px), 92vw);
  height:var(--bar-height, 44px);
  padding:0 16px;
  background:var(--bar-bg, rgba(255,255,255,0.92));
  border:var(--bar-stroke-width, 0) solid var(--bar-stroke-color, transparent);
  border-radius:var(--bar-radius, 999px);
  backdrop-filter:blur(4px);
  box-sizing:border-box;
  display:flex; align-items:center; justify-content:center;
  /* the home colour, painted by a rule rather than inline, so the
     reduced-motion rule below can move it — see buildCallButton */
  color:var(--call-ink, #141410);
  text-align:center; line-height:1.15;
  /* one line of text, and a long one is cut rather than allowed to make the
     button a different height from the bar under it */
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  cursor:pointer;
  transition:background-color 220ms ease, color 220ms ease, opacity 220ms ease, filter 160ms ease;
}
.zCallBtn:hover{ filter:brightness(0.92); }
.zCallBtn:focus-visible{ outline:2px solid currentColor; outline-offset:3px; }

/* THE PING-PONG: out to the far end and back, for as long as the shapes are
   off. The animation is on a class rather than on the element, so turning the
   shapes on stops it where the transition above can carry the values home
   rather than snapping them.

   Three channels, one clock. Each far end falls back to its own home value,
   so a channel nobody has set animates from a value to itself — which is to
   say it holds still, without needing a keyframe of its own. */
@keyframes zCallPulse{
  0%, 100%{
    background:var(--bar-bg, rgba(255,255,255,0.92));
    color:var(--call-ink, #141410);
    opacity:1;
  }
  50%{
    background:var(--call-pulse, var(--bar-bg, rgba(255,255,255,0.92)));
    color:var(--call-pulse-ink, var(--call-ink, #141410));
    opacity:var(--call-pulse-opacity, 1);
  }
}
.zCallBtn.pulsing{
  animation:zCallPulse var(--call-pulse-ms, 2000ms) ease-in-out infinite;
}
/* Somebody who has asked their machine for less movement is not asking to
   miss the button, so it is held at the far end of the swing — all three
   channels, the same place the animation would have spent half its time. */
@media (prefers-reduced-motion: reduce){
  .zCallBtn.pulsing{
    animation:none;
    background:var(--call-pulse, var(--bar-bg, rgba(255,255,255,0.92)));
    color:var(--call-pulse-ink, var(--call-ink, #141410));
    opacity:var(--call-pulse-opacity, 1);
  }
}

/* ================= PICTURE ON TOP, CONTROLS UNDER IT =======================

   Below the project's aspect threshold the viewer stops being a fixed window
   with chrome floating on the render and becomes an ordinary page: the
   picture at its OWN shape across the top, and the navigation, the filter and
   the list flowing beneath it. The page scrolls, which it never has before.

   What stays on the picture is what belongs to the picture — the button rail,
   the title, the compass, the call button — so the phone switches already set
   go on meaning what they meant. What moves under it is what was competing
   with the render for the same pixels. */
/* overflow:VISIBLE, not auto. `auto` on body makes the body its own scroll
   container: the content is 866px tall inside an 812px box that scrolls
   internally, the document never grows, and none of the ordinary page
   behaviour — the address bar retracting, window.scrollTo, the scroll
   position on a reload — applies. Visible lets the content extend the
   document and the PAGE scroll, which is what a page under a picture is. */
html.zPanelMode, html.zPanelMode body{
  height:auto; overflow:visible;
  overscroll-behavior-y:contain;
}
/* The wrap KEEPS the window's height, deliberately. It is what syncStageWidth
   measures to decide this mode, so growing it to fit the panel would put the
   decision back inside the thing it decides — the same loop that measuring the
   stage caused, one level up. The content overflows it and the PAGE scrolls. */
.stageWrap.stagePanel{
  display:block;
  overflow:visible;
}
/* The stage is now exactly the picture. Its height comes from the render
   rather than from the window, so everything absolutely positioned inside it
   — the title, the crumb bar, the call button — keeps landing on the render
   and not on the panel below. */
.stagePanel .stage{ height:auto; }
/* min-width/min-height:100% is what CROPS the render to fill a window of a
   different shape. Off, and with the aspect-ratio it already has, the box
   becomes the whole picture at its own proportions. */
.stagePanel .stageInner{
  position:relative; top:auto; left:auto; transform:none;
  min-width:0; min-height:0;
  width:100%; height:auto;
}

/* ---- the panel: breadcrumb, then navigation, then the flats ---- */
#phonePanel{ display:none; }
.stagePanel #phonePanel{
  display:flex; flex-direction:column;
  background:var(--panel-ground, #14140f);
  color:var(--panel-ink, #eee9dd);
}
/* Each of the three arrives from somewhere it was absolutely positioned, so
   each has to be told to take part in the flow it has been moved into. */
.stagePanel #phonePanel > *{
  position:relative;
  inset:auto; left:auto; right:auto; top:auto; bottom:auto;
  transform:none;
  width:auto; max-width:none;
  margin:0;
}
/* THE BAR IS THE BAND, ON A PHONE HELD UPRIGHT.

   Everywhere else the bar is a capsule floating over a render, and the room
   around it is the picture. Down here there is no picture behind it — the
   render stops above — so the same capsule sat in a 375x64 hole of panel
   ground with 15px of nothing either side and 12px above and below: a shape
   cut out of the page rather than part of it.

   So it takes the whole width and the corners go. Nothing about what it DOES
   changes: the same three slots, the same two arrows, the same colours, the
   current scene still centred. It is the same bar, filling its row. */
/* THE BREADCRUMB IS A ROW DOWN HERE, not a pill floating on a render — the
   same trade the bar below makes, and for the same reason: there is nothing
   left to float over. It keeps its colours, its typeface and its segments;
   it gives up its corners and takes the width. A closed one still has
   display:none from .crumbBar, so it opens no gap in the column. */
.stagePanel #phonePanel .crumbBar{
  width:100%; max-width:none;
  justify-content:center;
  border-radius:0;
  backdrop-filter:none;
}
.stagePanel #phonePanel .navBox,
.phoneVPan .navBox{
  display:flex; justify-content:center;
  padding:0;
  background:none; backdrop-filter:none;
}
.stagePanel #phonePanel .navBox.barMode,
.phoneVPan .navBox.barMode{
  /* the three slots share whatever the two arrows leave of the full width,
     so every calc() written against a slot still holds */
  --bar-slot-w:calc((100% - 2 * var(--bar-arrow-w)) / 3);
  width:100%;
  /* STATIC, and that is the point of it. renderBarNav writes bottom: inline
     for the desktop, where the bar floats over the render — and inline beats
     the reset above, which position:relative then obeys: the band sat 10px
     off its own row, over the render at the top and leaving a strip of panel
     ground under it. Static ignores an offset rather than obeying one.

     Only in bar mode. The wheel puts an absolutely positioned SVG straight
     into this box and needs it to be the containing block; the bar keeps
     everything it positions inside .navBarTrack, which is relative itself. */
  position:static;
}
.stagePanel #phonePanel .navBarShell,
.phoneVPan .navBarShell{
  width:100%;
  border-radius:0;
  /* A stroke down the two edges of a full-width band is a line drawn on the
     edge of the screen; top and bottom still separate it from what it sits
     against, so those stay. */
  border-left-width:0; border-right-width:0;
  /* nothing behind it to blur */
  backdrop-filter:none;
}
/* AND IT DOES NOT COLLAPSE. The capsule shrinks at a dead end to show there
   is one direction left — it can, because it floats. A band has nowhere to
   shrink to: it would just open a hole at one end again. The arrow still
   goes; it keeps its width so nothing else moves. */
.stagePanel #phonePanel .navBarShell.deadPrev,
.stagePanel #phonePanel .navBarShell.deadNext,
.stagePanel #phonePanel .navBarShell.deadPrev.deadNext,
.phoneVPan .navBarShell.deadPrev,
.phoneVPan .navBarShell.deadNext,
.phoneVPan .navBarShell.deadPrev.deadNext{
  width:100%; transform:none;
}
.stagePanel #phonePanel .navBarShell.deadPrev .navBarTrack,
.stagePanel #phonePanel .navBarShell.deadNext .navBarTrack,
.phoneVPan .navBarShell.deadPrev .navBarTrack,
.phoneVPan .navBarShell.deadNext .navBarTrack{ margin:0; }
.stagePanel #phonePanel .navBarShell.deadPrev .navBarArrow.prev,
.stagePanel #phonePanel .navBarShell.deadNext .navBarArrow.next,
.phoneVPan .navBarShell.deadPrev .navBarArrow.prev,
.phoneVPan .navBarShell.deadNext .navBarArrow.next{
  width:var(--bar-arrow-w, 38px); opacity:0; cursor:default;
}
/* Three scene names in 345px of bar: at full size they run into each other,
   which is what the labels do on any narrow window. Down here there is no
   render to compete with, so the two you are not on give up their room to the
   one you are — and any name still too long is cut rather than overlapped. */
.stagePanel #phonePanel .navBarSlot,
.phoneVPan .navBarSlot{
  /* display:block, not the flex the slot normally is: text-overflow has
     nothing to act on in a flex container — the text there is an anonymous
     item, and the label goes on overflowing both sides of its third. */
  display:block; line-height:var(--bar-height, 44px);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  padding:0 4px;
}
.stagePanel #phonePanel .navBarSlot:not(.current),
.phoneVPan .navBarSlot:not(.current){ font-size:11px; }
/* THE FILTER IS STILL A POPUP DOWN HERE.

   It was a row of the page for a while: always open, sitting between the
   navigation and the list. Which is honest about where it belongs and wrong
   about what it is — a panel of sliders and tick boxes, most of them left
   alone, taking a screenful of a page whose whole point is the flats under
   it. Every visitor scrolled past it every time.

   So it goes back to being what it is on a monitor: a thing behind a button,
   over everything, dismissed by a tap anywhere else. FIXED rather than
   absolute, and centred: the button that opens it is down in the list, which
   may be a screen and a half below the render the panel used to hang off. */
.stagePanel .zFcFilter{
  position:fixed; top:50%; left:50%; right:auto; bottom:auto;
  width:min(320px, calc(100vw - 32px)); max-width:none;
  z-index:60;
  transform:translate(-50%, -50%) scale(0.97);
  transform-origin:center;
  box-shadow:0 18px 60px rgba(0,0,0,0.45);
}
.stagePanel .zFcFilter.open{ transform:translate(-50%, -50%); }
.stagePanel #phonePanel .zFcList{
  height:auto; max-height:none;
  box-shadow:none;
  border-top:1px solid var(--panel-rule, rgba(255,255,255,0.12));
}
/* The list scrolled inside itself because it was a panel over the render.
   Here the PAGE scrolls, so it should be as long as it is. */
.stagePanel #phonePanel .zFcRows{ overflow:visible; max-height:none; }
/* Except sideways, in a sheet. Its columns need about 500px and a phone has
   375 — left visible, that overflow reaches the PAGE, and the whole viewer
   scrolls sideways with the render sliding off the screen. The sheet scrolls
   in its own box; everything above it stays where it is. */
.stagePanel #phonePanel .zFcSheet .zFcRows{ overflow-x:auto; }
/* Its grip and close button belong to a panel that slides over a picture, and
   there is no such panel here. */
.stagePanel #phonePanel .zFcGrip,
.stagePanel #phonePanel .zFcListClose{ display:none; }
/* Both rail buttons go: the list is a row of the page rather than a panel to
   open, and the filter is opened from beside the list instead — see
   .zFcFilterBtn, which appears exactly where this disappears. */
.stagePanel .zFcRailBtn[data-act="filter"]{ display:none; }
.stagePanel .zFcRailBtn[data-act="list"]{ display:none; }

/* MARKERS OR FLATS, NEVER BOTH — see soloUnits in zoa-core.js.

   The class is put on whenever the shapes are showing on a phone whose
   configuration asked for this, by the one function every way of turning
   them on ends up in. All this rule does is take the pins away while they
   are: on a strip a third the size of a monitor's render, a label and a
   polygon land on each other and a fingertip covers both.

   The whole layer, so the leader lines go with their labels — a line drawn
   to a label that is not there is worse than either. */
.stageWrap.stagePhone.phoneUnitsSolo #hotspotLayer{ display:none; }

/* ---- TWO ANSWERS THAT ARE NOT A LAYOUT AT ALL ----

   A flat colour over everything, and nothing else. It exists to answer one
   question from across a room while a phone is being turned over in a hand:
   WHICH configuration is this screen using right now. A render cannot answer
   that — it looks much the same either way up — and a label would need
   reading. Red and blue can be seen from the doorway.

   Painted on the wrap rather than the stage so it covers the panel as well:
   upright the page is a column of things below the picture, and a colour
   that stopped at the picture would leave the very rows this is asking
   about on screen. */
/* .stagePhone as well, and not for tidiness: the class is put on by script,
   and a rule that can only fire on a phone is a second lock on the one thing
   in this file that can black out an entire screen. It went on a monitor
   once. */
/* NO RED IN THE FALLBACK, and this is the whole point of the line.

   --phone-solid is set by applyPhoneTreatment in the same statement that
   adds the class, so a missing variable should be impossible. But the
   fallback was #e02020, and what it falls back to is what a CLIENT'S SITE
   shows if that ever stops being true — a full-screen red flash on
   thearchie.zoa3d.com, from a colour that exists only to tell two test
   modes apart across a room.

   The two costs are not comparable. A wrong fallback of transparent shows
   the page carrying on; a wrong fallback of red shows the client something
   alarming. So the guard is free and the exposure is not.

   Kept as a var() with a fallback rather than a bare var() because a bare
   one resolves to the unset value and the declaration is thrown away,
   which would let the wrap's own background through — the same outcome,
   less obviously on purpose. */
.stageWrap.stagePhone.phoneSolid{ background:var(--phone-solid, transparent); }
.stageWrap.stagePhone.phoneSolid > *{ visibility:hidden; }

/* ================= THE INTRO SCREEN =================

   Black over everything while the frames that matter arrive, then the two
   gestures this viewer needs a visitor to know. It is one card with three
   panels in it and a class saying which is showing, rather than three things
   being added and removed — so a fade is a class change and the layout never
   reflows underneath a transition.

   z-index 60, above the splash's 40: only one of them is ever up (a splash
   means the phone is the wrong way round, and this waits for that to be
   fixed), but if that were ever wrong the intro is the one holding the black
   background and must not end up under it. */
.zPhoneIntro{
  position:absolute; inset:0; z-index:60;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:clamp(18px, 5vmin, 34px); padding:8vmin; text-align:center;
  background:#000;
  opacity:1; transition:opacity 420ms ease;
  /* it IS the screen while it is up: nothing behind it should take a tap */
  touch-action:manipulation; -webkit-tap-highlight-color:transparent;
  outline:none;
}
.zPhoneIntro.going{ opacity:0; pointer-events:none; }

/* Every line in the project's own typeface, which is the whole reason this
   does not look like a browser dialogue. */
.zIntroLine{
  margin:0;
  color:var(--zi-fg, #f2f2ed);
  font-family:var(--zi-font, inherit);
  font-weight:600;
  text-transform:uppercase;
  letter-spacing:.15em; text-indent:.15em;
  line-height:1.35;
}
.zIntroSwipeLine{ font-size:clamp(15px, 4.4vmin, 24px); }
/* The instruction, not the lesson: quieter, and it is the one that says a tap
   will do something. */
.zIntroTapLine{ font-size:clamp(11px, 3.2vmin, 15px); opacity:.62; }

/* ---- the three panels ---- */
.zIntroLoad, .zIntroReady, .zIntroMenu{ transition:opacity 340ms ease; }
.zIntroReady{
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(14px, 4vmin, 26px);
}
.zPhoneIntro.loading .zIntroReady,
.zPhoneIntro.loading .zIntroMenu,
.zPhoneIntro.loading .zIntroNav,
.zPhoneIntro.ready   .zIntroLoad,
.zPhoneIntro.ready   .zIntroMenu,
.zPhoneIntro.ready   .zIntroNav,
.zPhoneIntro.menu    .zIntroLoad,
.zPhoneIntro.menu    .zIntroReady,
.zPhoneIntro.menu    .zIntroNav,
.zPhoneIntro.nav     .zIntroLoad,
.zPhoneIntro.nav     .zIntroReady,
.zPhoneIntro.nav     .zIntroMenu{ opacity:0; pointer-events:none; }
/* Out of the flow as well as invisible, or the card would stay the height of
   whichever panel is tallest and the visible one would sit off centre. */
.zPhoneIntro.loading .zIntroReady,
.zPhoneIntro.ready   .zIntroLoad,
.zPhoneIntro.menu    .zIntroLoad,
.zPhoneIntro.nav     .zIntroLoad,
.zPhoneIntro.nav     .zIntroReady,
.zPhoneIntro.menu    .zIntroReady{ position:absolute; }

/* ---- the loader ---- */
/* A hairline, not a progress widget. It is answering "is anything happening",
   and a heavy bar on a black screen becomes the thing being looked at. */
.zIntroBar{
  width:min(56vw, 240px); height:2px; border-radius:2px;
  background:color-mix(in srgb, var(--zi-fg, #f2f2ed) 20%, transparent);
  overflow:hidden;
}
.zIntroBar i{
  display:block; height:100%; width:0;
  background:var(--zi-fg, #f2f2ed);
  transition:width 240ms linear;
}

/* ---- the hand ---- */
/* THE SUPPLIED ARTWORK, as two mask layers over one square.

   background is the paint and the mask is the stencil, so the drawing
   takes the project's own colour. Both masks carry the original's whole
   200x200 canvas, so inset:0 is the only positioning there is.

   Sized modestly on purpose: the source is 200px square, and asking for
   much more than this on a three-times screen is asking it to be sharper
   than it is. */
.zIntroHand{ position:relative; width:clamp(92px, 27vmin, 140px); aspect-ratio:1 / 1; }
.zIntroHand i{
  position:absolute; inset:0; display:block;
  background:var(--zi-fg, #f2f2ed);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-position:center; mask-position:center;
  -webkit-mask-size:100% 100%; mask-size:100% 100%;
}
/* THE PIVOT IS THE WRIST, and that is the whole idea: turning the hand
   about the bottom of the palm sweeps the fingertips along the arc drawn
   above them. Pivoting at the centre, or sliding sideways as this used to,
   draws one motion under an illustration of another.

   Measured off the artwork itself: the bottom of the hand, on its own
   centre line, as a percentage of the shared canvas. */
.zIntroTilt{
  -webkit-mask-image:url("data:image/png;base64,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");
  mask-image:url("data:image/png;base64,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");
  transform-origin:50.7% 82.5%;
  animation:zIntroTilt 3s ease-in-out infinite;
}
/* The arc is the guide and does not move with the hand. Quieter than the
   hand so it reads as the path rather than as a second object. */
.zIntroArc{
  -webkit-mask-image:url("data:image/png;base64,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");
  mask-image:url("data:image/png;base64,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");
  opacity:.55;
}

/* Small. It is a nudge along a curve, not a wave — and the arc it has to
   agree with only spans about twenty degrees. */
@keyframes zIntroTilt{
  0%, 100% { transform:rotate(11deg); }
  50%      { transform:rotate(-11deg); }
}

/* ---- the mouse, for a room with a pointer in it ---- */
/* The supplied drawing, as a mask, for the same reason the hand is one: the
   file is dark ink and this card is whatever colour the project says.

   IT SLIDES RATHER THAN TILTS. A wrist turns and a mouse does not; the
   gesture being taught is a drag across the desk, so the whole pictogram
   travels and the amount it travels is the amount a hand would move it.
   Sized by HEIGHT, with the drawing's own proportions as the aspect ratio,
   because the mouse is the tall one of the two and matching their widths
   would have made it tower over the hand.

   The left button is filled in the artwork, which happens to be exactly
   right here: it reads as a button held down, which is what a drag is. */
.zIntroMouse{
  position:relative; height:clamp(72px, 19vmin, 104px); aspect-ratio:171 / 256;
}
.zIntroMouse i{
  position:absolute; inset:0; display:block;
  background:var(--zi-fg, #f2f2ed);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-position:center; mask-position:center;
  -webkit-mask-size:100% 100%; mask-size:100% 100%;
  /* Both layers take the SAME slide: they are one object, and a body that
     travelled without its button would come apart. */
  animation:zIntroDrag 3.4s ease-in-out infinite;
}
/* TWO CLASSES, NOT ONE. `.zIntroMouse i` above is a class and a type, which
   out-specifies a lone `.zIntroMouseBtn` — so the shared slide replaced the
   button's two-animation shorthand and the click never happened. It looked
   right in the file and did nothing in the browser, which is the only kind
   of bug a stylesheet has. */
.zIntroMouse .zIntroMouseBody{
  -webkit-mask-image:url("data:image/png;base64,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");
  mask-image:url("data:image/png;base64,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");
}
/* THE CLICK, on top of the drag. Two animations rather than one keyframe
   doing both: they are different lengths on purpose — a click is quicker
   than the sweep it happens during, and locking them together would make
   the button appear to press once per journey, at the same point every
   time, which reads as a mechanism rather than as a hand.

   Opacity and transform, so the two never fight over a property. */
.zIntroMouse .zIntroMouseBtn{
  -webkit-mask-image:url("data:image/png;base64,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");
  mask-image:url("data:image/png;base64,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");
  animation:zIntroDrag 3.4s ease-in-out infinite,
            zIntroPress 1.4s ease-in-out infinite;
}
@keyframes zIntroPress{
  0%, 100% { opacity:1; }
  50%      { opacity:0.18; }
}
/* GENTLY. Far enough to read as a hand moving a mouse across a desk, slow
   enough that it is a drift rather than a shuttle — the card is teaching one
   thing calmly, not demonstrating a mechanism. */
@keyframes zIntroDrag{
  0%, 100% { transform:translateX(-26%); }
  50%      { transform:translateX(26%); }
}
/* The same drift, smaller and slower, for somebody who has asked for less
   motion. See the reduced-motion block at the end of this section. */
@keyframes zIntroDragGentle{
  0%, 100% { transform:translateX(-10%); }
  50%      { transform:translateX(10%); }
}

/* ---- the menu hint ---- */
/* Anchored to the corner the rail is in rather than centred, because it is
   pointing at something: a centred arrow would have to be aimed, and aiming
   it would mean measuring a button this card cannot see. */
/* Anchored under the rail it points at, at whatever height that rail turns
   out to be — the fallbacks are a one-button rail, which is what the fixed
   numbers used to assume for everybody. */
.zPhoneIntro .zIntroMenu{
  position:absolute;
  top:var(--zi-menu-top, 64px); left:var(--zi-menu-left, 20px);
  display:flex; flex-direction:column; align-items:flex-start; gap:6px;
  text-align:left;
}
/* NO INSET ANY MORE. The inset existed to shift the arrow from the rail's
   left edge to its middle, back when the block was anchored under the whole
   rail. It is anchored to the menu button's own bottom-right corner now, so
   the tip is already where it should be and 26px of nudge would push it off
   the button it is aiming at. */
.zIntroArrowArt{ width:34px; height:auto; overflow:visible; margin-left:0; }
.zIntroArrowArt path{
  fill:none; stroke:var(--zi-arrow, var(--zi-fg, #f2f2ed));
  stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round;
}
.zPhoneIntro .zIntroMenu .zIntroLine{
  font-size:var(--zi-menu-size, clamp(12px, 3.4vmin, 16px)); line-height:1.5;
}

/* ================= VERSION TWO: THE SAME LESSONS, OVER THE PAGE =========

   Same artwork, same words, same face. What goes is the black and the bar,
   and the order reverses: the menu first, the swipe second. It stops being
   a splash and becomes two coach marks on a page that is already running.

   Everything below is a subtraction or a legibility fix. There is no second
   set of rules for the panels, the hand, the arrow or the type — if there
   were, the two versions would drift apart the first time either was
   touched, and "the same thing, arranged differently" is the whole idea. */
/* AN 80% SCRIM, not the splash's solid black. The two are not the same
   thing arriving at different strengths: the black hides the page because
   there is nothing to see yet, and this dims it because there IS — the
   render stays legible underneath, and the one thing lifted above the scrim
   is the real button being pointed at, which is now the brightest thing on
   screen rather than one more object among many.

   It also gives the card a ground of its own. Everything else here
   (lettering, drawing, the shadow under both) was being asked to work over
   whatever render happened to be behind it, which is a different problem on
   every project. */
.zPhoneIntro.zIntroCoach{ background:rgba(0, 0, 0, 0.9); }
/* AND FROM THE FIRST MOMENT, INCLUDING WHILE IT WAITS.

   This used to be transparent until the rail appeared, on the reasoning that
   a card showing nothing should cover nothing. That was wrong, and wrong in
   a way only visible on a real connection: the render finished arriving, the
   visitor started looking at it, and THEN the page went dark. It read as a
   fault rather than as an introduction.

   The card is built in the same breath as the first frame's src, before the
   browser has painted anything, so leaving the scrim on from the start puts
   it in front of the load instead of on top of the result. What waits is the
   lesson, not the ground it stands on. */
/* nothing is being waited for: the visitor is already looking at the
   building, and a bar over it would be answering a question nobody asked */
.zPhoneIntro.zIntroCoach .zIntroLoad{ display:none; }
/* AND NO 'TAP TO CONTINUE'. In the splash that line is the way on to the
   menu lesson and the card would stall without it. Here the swipe lesson is
   the LAST thing: there is nothing to continue to, and the line would be
   promising a step that does not exist. */
.zPhoneIntro.zIntroCoach .zIntroTapLine{ display:none; }

/* BEFORE THE BUTTON EXISTS. The rail is mounted after the unit shapes have
   been fetched, and version two points at it as its FIRST act — so the
   LESSON waits while the ground it stands on is already there.

   It holds the tap as well now. It did not while it was invisible, which was
   right then and would be wrong here: a click that lands on something nobody
   can see through is a click into the dark. The splash spends its own wait
   the same way, and this is now the same kind of moment. */
.zPhoneIntro.waiting > *{ opacity:0; pointer-events:none; }

/* READABLE OVER A RENDER, which is the one thing the black was doing for
   free. A panel or a scrim would be a new visual element, so the type and
   the artwork carry their own shadow instead and the picture stays
   uncovered.

   A SWITCH RATHER THAN A PROPERTY OF VERSION TWO: it is wanted wherever the
   card sits on something busy, and unwanted on a project whose renders are
   dark or whose designer would rather it were clean. On the black splash it
   costs nothing and shows nothing, which is why it is simply on by default
   for both. */
/* THREE STOPS RATHER THAN TWO, and the near one opaque. The pair before it
   was tuned for light lettering, where a shadow only has to stop a bright
   sky washing the edges out. The colour is the project's to choose now, and
   a DARK line on a mid-toned render has the harder job: it needs a close,
   solid edge to sit on before the wide soft one does its work. */
.zIntroShadow .zIntroLine{
  text-shadow:0 1px 2px rgba(0,0,0,1), 0 2px 7px rgba(0,0,0,.92), 0 4px 26px rgba(0,0,0,.85);
}
/* AND ONE THE DRAWING ACTUALLY CASTS. The three stops above are a halo:
   they hold a thin letterform off a busy background, which is what they
   were tuned to do. A hundred-pixel solid shape has no thin edges for a
   two-pixel offset to separate, so the drawing had, in effect, no shadow at
   all — the middle stop is pushed down and out here until it reads as
   something the object is sitting above rather than a rim around it. */
.zIntroShadow .zIntroHand i,
.zIntroShadow .zIntroMouse i,
.zIntroShadow .zIntroArrowArt{
  filter:drop-shadow(0 1px 2px rgba(0,0,0,1))
         drop-shadow(0 7px 10px rgba(0,0,0,.65))
         drop-shadow(0 4px 22px rgba(0,0,0,.8));
}

/* ASKED FOR LESS MOTION: the hand holds still, mid-stroke, where it reads as
   a hand on the glass rather than one caught at the end of a movement. The
   loader keeps filling — that is information, not decoration, and stopping it
   would leave somebody watching a bar that never moves. */
@media (prefers-reduced-motion: reduce){
  .zIntroTilt{ animation:none; transform:rotate(0deg); }
  /* NO TRAVEL, BUT NOT NOTHING. What this setting is about is a shape
     crossing the screen; the click is not that, it is one element fading
     where it stands. Stripping it too left a still picture of a mouse with
     DRAG TO TURN beside it and nothing to say the mouse was the thing being
     talked about — which is less help, not less motion.

     Slower here than at full motion: nobody is being hurried. Both
     selectors are written out because the two-class one below out-specifies
     `.zIntroMouse i`, and half a rule would go on sliding for exactly the
     person who asked it not to. */
  /* SMALLER AND SLOWER, NOT STILL. What this setting is about is a shape
     crossing the screen — parallax, a panel flying in, anything that moves
     enough of the field of view to unsettle somebody. Seven pixels of drift
     under a seventy-pixel drawing, over five seconds, is not that.

     And here the movement IS the content: the whole card exists to say that
     the render turns when the mouse is dragged, and a mouse that never moves
     is the one thing this cannot afford to be. So it is reduced rather than
     removed, which is what the setting asks for by name.

     Both selectors written out because the two-class one below out-specifies
     `.zIntroMouse i`. */
  .zIntroMouse i,
  .zIntroMouse .zIntroMouseBody{
    animation:zIntroDragGentle 5s ease-in-out infinite; opacity:1;
  }
  .zIntroMouse .zIntroMouseBtn{
    animation:zIntroDragGentle 5s ease-in-out infinite,
              zIntroPress 2.6s ease-in-out infinite;
  }
}

/* ---- the three-step card, for the panned mode ---- */
/* A TENTH LIGHTER THAN THE COACH MARKS, and the difference is deliberate.

   Both scrims exist to stop the render competing with the words. This card
   carries more words than any other — a heading and a numbered step — so it
   wins that competition easily, and at 90% the picture behind it had gone
   dark enough to read as a loading screen rather than as a viewer with a
   note over it. Which matters here more than anywhere else: this one sits
   inside somebody else's property page, where the first impression is
   supposed to be the building. */
.zPhoneIntro.zIntroSteps{ background:var(--zi-bg, rgba(0, 0, 0, 0.65)); }
.zPhoneIntro.zIntroSteps.waiting{ background:transparent; }
/* AND THE LAST STEP HAS NO SCRIM AT ALL. By then the visitor has been told
   where the two controls are, and the only thing left to say is about the
   picture — which they should be looking at while they read it. Dimming it
   to explain it would be the card talking over its own subject. */
.zPhoneIntro.zIntroSteps.ready{ background:transparent; }
.zPhoneIntro.zIntroSteps .zIntroLoad{ display:none; }

/* ---- the heading on the three-step card ----

   Bigger than the steps under it and centred in what is left of the card,
   which on this card is the whole middle: the loader is off, and whichever
   step is showing is pinned to the control it points at — one at the foot of
   the frame, one in the top corner. So the heading has the centre to itself
   without anything being positioned by hand.

   AND IT GOES WHEN THE BLACK GOES. The last step drops the scrim on purpose,
   so the visitor can look at the picture while they read about moving it — a
   heading still standing across the middle of it would be the card talking
   over its own subject. Out of the flow as well as invisible, or the two
   symbols that step arrives with would be pushed off centre by a heading
   nobody can see. */
.zPhoneIntro .zIntroHead{
  font-size:var(--zi-title-size, clamp(18px, 5.4vmin, 28px));
  line-height:1.3;
  /* NO max-width IN em. It read 20em and it defeated the fit above: every
     pixel off the font took 20 pixels off the box the font had to fit into,
     so the heading shrank to its floor and stayed three lines. The card own
     padding is what holds it off the edges. */
  /* Tighter than the steps under it, which is what large type wants anyway --
     and it is worth two or three pixels of size at the width a phone has. */
  letter-spacing:.09em; text-indent:.09em;
  transition:opacity 340ms ease;
}
.zPhoneIntro.ready .zIntroHead{
  opacity:0; position:absolute; pointer-events:none;
}

/* ABOVE THE BAR, CENTRED. The navigation is the full width of the frame, so
   there is no side to stand beside — the words go over the middle of it and
   the arrow drops straight down. --zi-nav-bottom is measured from the real
   bar, whose height is whichever nav style the project chose. */
.zPhoneIntro .zIntroNav{
  position:absolute; left:0; right:0;
  bottom:var(--zi-nav-bottom, 84px);
  display:flex; flex-direction:column; align-items:center; gap:4px;
  text-align:center;
  transition:opacity 340ms ease;
}
.zPhoneIntro .zIntroNav .zIntroLine{
  font-size:var(--zi-nav-size, clamp(12px, 3.4vmin, 16px)); line-height:1.5;
}

/* ---- the same card on a monitor ----

   Everything above is shared: the steps class carries the order, the scrim,
   the heading and the two pointing blocks in both rooms. What is here is only
   what a monitor needs that a phone does not.

   THE HEADING IS PLACED RATHER THAN CENTRED. On the phone it is the only
   thing in the card's flow and lands in the middle by itself; on a monitor
   the project can say where it goes, and a percentage of the stage is the
   only coordinate that means the same thing on every screen the project will
   meet. 50/50 is the middle, which is where it starts. */
.zPhoneIntro.zIntroDesk .zIntroHead{
  position:absolute;
  left:var(--zi-title-x, 50%); top:var(--zi-title-y, 50%);
  transform:translate(-50%, -50%);
  /* Not max-content: the breaks in the sentence decide where it turns, and a
     box that never wrapped would let a line run off the sides instead of
     being caught and shrunk by the fit. */
  max-width:86%;
}
/* A BLOCK THAT WAS GIVEN A COORDINATE STOPS FOLLOWING ITS CONTROL. Both of
   these are otherwise measured onto the real thing they point at, which is
   why neither has a position of its own until it is told one — a hint that
   stayed where a form put it while the navigation moved would be pointing at
   the place the navigation used to be. */
.zPhoneIntro .zIntroNav.placed{
  left:var(--zi-nav-x, 50%); right:auto;
  top:var(--zi-nav-y, 80%); bottom:auto;
  transform:translate(-50%, -50%);
}
.zPhoneIntro .zIntroMenu.placed{
  left:var(--zi-menu-x, 20%); top:var(--zi-menu-y, 20%);
  transform:translate(-50%, -50%);
}
/* Mirrored, so the tip is at the bottom where the bar is. */
.zIntroArrowDown{ transform:scaleY(-1); }

/* The pair sits where the hand sat, on one row. Sized against the same
   viewport unit as the hand so the three cards stay siblings. */
.zIntroPair{
  display:flex; align-items:center; justify-content:center;
  gap:clamp(18px, 6vmin, 34px);
}
.zIntroIcon{
  width:clamp(38px, 11vmin, 54px); height:auto; overflow:visible;
  fill:none; stroke:var(--zi-fg, #f2f2ed);
  stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round;
}
/* The arrowheads are the one filled part: an outlined triangle at this size
   is a shape with a hole in it rather than a point. */
.zIntroIcon path:nth-child(2), .zIntroIcon path:nth-child(3){
  fill:var(--zi-fg, #f2f2ed); stroke:none;
}
/* Except in the glass, whose second and third paths are the handle and the
   plus — both strokes, both of which a fill would turn into blobs. */
.zIntroIcon:last-child path{ fill:none; stroke:var(--zi-fg, #f2f2ed); }
/* Still, and asked to stay still: this is a legend, not a demonstration. */
.zIntroPair *{ animation:none; }
.zIntroShadow .zIntroIcon{
  filter:drop-shadow(0 1px 2px rgba(0,0,0,1))
         drop-shadow(0 7px 10px rgba(0,0,0,.65))
         drop-shadow(0 4px 22px rgba(0,0,0,.8));
}

/* Two instructions of equal standing, not an instruction and an invitation.
   The second line is the smaller one everywhere else because it says "tap to
   continue"; here it says "pinch to zoom", which is half the lesson. */
.zPhoneIntro.zIntroSteps .zIntroTapLine{
  display:block;
  font-size:clamp(12px, 3.6vmin, 17px); opacity:0.9;
}

/* ================= THE PANNED PORTRAIT MODE =================

   For the viewer inside somebody else's portrait iframe. The picture fills
   the box and the visitor moves it; the navigation, not the drag, turns the
   building. Everything here follows from those two sentences.

   NOTHING IS RE-LAID-OUT. .stageInner is already centred and cropped to
   fill — that is what min-width/min-height:100% has always done off panel
   mode — so this mode gets the crop it wants by NOT being a panel, which
   stageStacks guarantees. What is left is where the chrome sits. */

/* The chrome is pinned to the FRAME, not to the picture. The picture slides
   under it; a navigation that slid with it would leave the visitor holding
   a control they had pushed off the edge. */
.phoneVPan .navBox{
  position:absolute; left:0; right:0; bottom:0; top:auto;
  transform:none; width:auto; max-width:none; margin:0;
  z-index:8;
}
/* AND IT HAS TO BE TAKEN OFF THE FLOW AGAIN.

   The band rules above are the panel's, shared on purpose — it is the same
   band and two copies would drift. But one of them is `position:static`,
   which is right there (the bar is a row in a column of rows) and wrong
   here (it has to be pinned to the foot of a frame the picture slides
   under). Static ignored the offsets and the bar rendered at the top.

   The !important is for an INLINE style, which is the one thing the cascade
   cannot outrank by being more specific: renderBarNav writes `bottom` on the
   element, for the capsule that floats ten pixels over a render on a
   monitor. Ten pixels here would leave a strip of render below the band —
   the very gap this mode exists to close. */
.phoneVPan .navBox.barMode{
  position:absolute; left:0; right:0;
  top:auto; bottom:0 !important;
}
/* AND IT KEEPS ITS OWN COLOURS. This block used to paint the shell with the
   panel ground, on the reasoning that a band over a render needs something
   behind it. The bar already HAS something behind it — --bar-bg, which this
   project sets to a near-white — and painting over it did two things at once:
   it threw away the colour scheme the bar was designed in, and it hid the two
   arrows, whose --bar-arrow is a dark ink meant for a light band. Dark on
   dark is why they looked missing rather than merely dim. */

/* NO FILTER AND NO LIST. Both are panels that take the screen, and this
   mode's screen belongs to somebody else's page — a widget that swallows
   the frame to show a table of flats has stopped being a widget. The rail
   keeps its first button, which is the one that shows the flats on the
   render itself.

   Done here rather than by forcing the two switches: the switches belong to
   the project and mean something in the other modes, and a mode that
   rewrote them would leave the editor showing a state nobody chose. */
.phoneVPan .zFcRailBtn[data-act="filter"],
.phoneVPan .zFcRailBtn[data-act="list"],
.phoneVPan .zFcFilter,
.phoneVPan .zFcList{ display:none !important; }

/* THE DRAG IS THE PICTURE'S NOW.

   touch-action:none, and this is the one place it is right: every axis is
   ours here, because the picture is bigger than the box in both of them
   once it is zoomed. The other modes leave pan-y to the page on purpose —
   there a finger going up is the visitor scrolling — but a widget in an
   iframe that let a vertical drag escape while the visitor was moving a
   zoomed render would scroll the host page out from under them mid-gesture.

   pinch-zoom is not given back either: the pinch is handled here, over the
   render, rather than by the browser over the whole embedded document. */
.stage.stagePan{ cursor:grab; touch-action:none; }
.stage.stagePan.panning{ cursor:grabbing; }
.stage.stagePan.panning, .stage.stagePan.panning *{ user-select:none; }
/* The transform is written by bindStagePan; the transition is not, so that
   a drag tracks the finger exactly and only a reset glides. */
.stagePan .stageInner{ will-change:transform; }

/* ---- the list beside the picture, on a phone ----

   The panel takes width here rather than covering, so the render has to be
   re-fitted into what is left of the screen — and the thing being protected
   is its WIDTH: a flat cropped off the side is not merely unseen, it is
   untappable, which is the same reason the panned mode exists.

   So the box is given the full width it has and takes its height from the
   render's own shape. min-height:100% is what has to go: that is the rule
   that crops a wide render into a narrow box by scaling it up until it fills,
   and filling is exactly what must not happen here. What is left is centred
   in the stage — cropped equally top and bottom when the render is taller
   than the space, banded equally when it is shorter, which is the render's
   proportions and the panel's width deciding between them rather than us.

   Everything inside stays put: markers, shapes and leader lines are
   positioned in percentages of this box, so re-fitting it moves them with the
   picture, exactly as a window resize does. */
.phoneHList .stageInner{
  min-width:0; min-height:0;
  width:100%; height:auto;
}

/* ================= THE SPLASH MODES =================

   A phone held the way this project is not for gets one sentence and the
   gesture that fixes it. Built like the solid colours above — the same two
   locks, .stagePhone and the mode's own class — but with one thing left
   visible, which is why the hiding rule here carries a :not() and the one
   above does not.

   Absolutely positioned rather than laid out: .stageWrap is a flex row and
   a fifth child would otherwise take a share of the width from the stage
   even while everything was hidden. */
.zPhoneSplash{ display:none; }
.stageWrap.stagePhone.phoneSplash{ background:var(--phone-splash-bg, #0d0d0a); }
.stageWrap.stagePhone.phoneSplash > *:not(.zPhoneSplash){ visibility:hidden; }
.stageWrap.stagePhone.phoneSplash > .zPhoneSplash{
  display:flex; position:absolute; inset:0; z-index:40;
  flex-direction:column; align-items:center; justify-content:center;
  gap:clamp(16px, 5vmin, 34px); padding:8vmin; text-align:center;
}

/* vmin, not the phone text multiplier: that multiplier is tuned against a
   render with marker labels on it, and there is no render here. This is the
   only thing on the screen, so it is sized against the screen. */
.zPhoneSplashArt{ width:clamp(104px, 34vmin, 190px); height:auto; overflow:visible; }
.zPhoneSplashArt rect, .zPhoneSplashArt line,
.zPhoneSplashArt circle, .zPhoneSplashArt path{
  fill:none; stroke:var(--phone-splash-fg, #f2f2ed);
  stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round;
}
.zPhoneSplashArt .zPhoneSplashScreen{ opacity:.34; stroke-width:1.8; }
.zPhoneSplashArrow{ opacity:.5; }
/* Mirrored for the other direction rather than drawn twice: the arc is
   symmetrical about the phone, so the same path read backwards is the
   other way round. */
.zPhoneSplash.toUp .zPhoneSplashArrow{ transform:scaleX(-1); transform-origin:70px 70px; }

.zPhoneSplashTurn{
  transform-origin:70px 70px;
  animation:zSplashToSide 3.4s cubic-bezier(.65,0,.35,1) infinite;
}
.zPhoneSplash.toUp .zPhoneSplashTurn{ animation-name:zSplashToUp; }

/* Held at each end long enough to be read as two states rather than as a
   spin: still, turn, still, turn back. */
@keyframes zSplashToSide{
  0%, 18%   { transform:rotate(0deg); }
  42%, 72%  { transform:rotate(90deg); }
  96%, 100% { transform:rotate(0deg); }
}
@keyframes zSplashToUp{
  0%, 18%   { transform:rotate(90deg); }
  42%, 72%  { transform:rotate(0deg); }
  96%, 100% { transform:rotate(90deg); }
}

.zPhoneSplashText{
  margin:0;
  color:var(--phone-splash-fg, #f2f2ed);
  font-family:var(--phone-splash-font, inherit);
  font-weight:var(--phone-splash-weight, 600);
  /* The strings are capitals already; this is so they stay capitals if
     somebody ever edits them in a hurry. */
  text-transform:uppercase;
  font-size:clamp(15px, 4.6vmin, 26px);
  letter-spacing:.15em; line-height:1.3;
  /* the last letter's tracking would otherwise push the line off centre */
  text-indent:.15em;
}

/* ASKED FOR LESS MOTION: hold the phone at the shape being ASKED FOR, not
   at the one it is in. Stopping it upright on a screen whose whole message
   is "turn it sideways" would illustrate the wrong half. */
@media (prefers-reduced-motion: reduce){
  .zPhoneSplashTurn{ animation:none; transform:rotate(90deg); }
  .zPhoneSplash.toUp .zPhoneSplashTurn{ transform:rotate(0deg); }
}

/* AND ONE THAT IS "DO NOTHING SPECIAL". A phone told to behave like a
   monitor: the shape classes stay, because the browser is still that size
   and the stylesheet still has to lay something out, but everything a phone
   changes is switched off. The multipliers go to 1 in applyPhoneTreatment;
   these are the parts that are a class rather than a number. */
.stageWrap.stagePhone.phoneAsDesktop{ /* nothing yet — the switches below read it */ }
.phoneAsDesktop .navBox,
.phoneAsDesktop .onImageTitle,
.phoneAsDesktop .zCompass,
.phoneAsDesktop .zCallBtn,
.phoneAsDesktop .zFcRailBtn{ display:revert; }

/* EVERY OTHER PIECE OF TEXT ON A PHONE.

   Not the markers — those have a scale of their own, because a marker sits ON
   the render and competes with it, while all of this sits beside it.

   TWO NAMES, AND THE DIFFERENCE MATTERS. --phone-text-scale is the SETTING,
   published on the root whatever screen this is, because that is where a
   setting lives. --text-scale is the multiplier IN FORCE HERE, which is 1
   everywhere and the setting only inside a phone-shaped wrap.

   Everything that multiplies must read the second one. The navigation and the
   call button set their sizes inline — a stylesheet cannot multiply an inline
   value afterwards, so they carry the calc() themselves, which means no
   .stagePhone selector gates them and reading the setting direct scaled the
   desktop too. The class does the gating instead, by choosing the value.

   Declared on the WRAP rather than the root so a second stage on the page
   (the editor's preview beside a real one) answers for its own shape; and
   the phone rule is written with both classes so it wins on specificity
   rather than on being further down the file. */
.stageWrap{ --text-scale:1; --tip-scale:1; }
.stageWrap.stagePhone{
  --text-scale:var(--phone-text-scale, 1);
  --tip-scale:var(--phone-tip-scale, 1);
}
.stagePhone .onImageTitle{ font-size:calc(var(--title-size, 17px) * var(--text-scale, 1)); }
.stagePhone .onImageFooter{ font-size:calc(var(--footer-size, 11px) * var(--text-scale, 1)); }
.stagePhone .crumbBar{ font-size:calc(var(--crumb-size, 12px) * var(--text-scale, 1)); }
.stagePhone .zSwipeText{ font-size:calc(13px * var(--text-scale, 1)); }
.stagePhone .zFcFilter{ font-size:calc(var(--zfc-filter-size, 11.5px) * var(--text-scale, 1)); }
.stagePhone .zFcList{ font-size:calc(var(--zfc-list-size, 12px) * var(--text-scale, 1)); }

/* THE CARD A FLAT RAISES, at a phone's size.

   Its own third scale, and not one of the two above: it is not text beside
   the render and it is not a marker on it, it is a panel over it — sized in
   the Units menu for a monitor, where 300px wide with a 150px plan inside is
   a quarter of the screen and upright on a phone is most of it, sitting over
   the very plan the tap was asking to see.

   Every --ut-* is written inline on the card by createUnitTip, so none of
   them can be redeclared here — a rule multiplies them into the properties
   instead, which is the same shape the navigation's sizes use and for the
   same reason. One number moves all of it together, so the card keeps its
   proportions rather than becoming a different card. */
.stagePhone .zUnitTip{
  width:calc(var(--ut-w, 300px) * var(--tip-scale, 1));
  min-height:calc(var(--ut-h, 0px) * var(--tip-scale, 1));
  padding:calc(var(--ut-pad, 16px) * var(--tip-scale, 1));
  border-radius:calc(var(--ut-radius, 14px) * var(--tip-scale, 1));
  gap:calc(10px * var(--tip-scale, 1));
}
.stagePhone .zUnitTipPlan{
  flex-basis:calc(var(--ut-plan-h, 150px) * var(--tip-scale, 1));
  min-height:calc(var(--ut-plan-h, 150px) * var(--tip-scale, 1));
  border-radius:calc(var(--ut-radius, 14px) * 0.5 * var(--tip-scale, 1));
}
.stagePhone .zUnitTipCode{ font-size:calc(var(--ut-code, 20px) * var(--tip-scale, 1)); }
.stagePhone .zUnitTipName,
.stagePhone .zUnitTipStatKey,
.stagePhone .zUnitTipNotes,
.stagePhone .zUnitTipStatus{ font-size:calc(var(--ut-label, 10.5px) * var(--tip-scale, 1)); }
.stagePhone .zUnitTipStatVal,
.stagePhone .zUnitTipHeadBit{ font-size:calc(var(--ut-value, 15px) * var(--tip-scale, 1)); }
.stagePhone .zUnitTipStatus{ padding:calc(5px * var(--tip-scale, 1)) calc(10px * var(--tip-scale, 1)); }
.stagePhone .zUnitTipStats{ gap:calc(12px * var(--tip-scale, 1)); }

/* ---- WHAT A PHONE GETS INSTEAD ----

   Markers keep their positions and lose their size. Done by re-declaring
   --scale on the marker layer alone rather than by editing the dozen rules
   that read it: they all inherit, so one declaration reaches every one of
   them and nothing outside the layer moves. --scale-base is the same number
   applyTheme puts on the root, kept referable so this can multiply it. */
/* A leader's label parks this far in from the edge of the picture, and on a
   phone the same number of pixels is a much bigger share of it. A fraction
   of the project's own inset, like the marker size. */
.stagePhone .zcLeadRow{
  --lead-used-x:calc(var(--lead-inset, 40px) * var(--phone-leader-x, 1));
  --lead-used-y:calc(var(--lead-inset, 40px) * var(--phone-leader-y, 1));
}

.stagePhone #hotspotLayer{
  --scale:calc(var(--scale-base, 1) * var(--phone-marker-scale, 1));
}

/* The bar goes only when the page has said there is another way to travel —
   see phoneNavHidden. Never on the class alone. */
.phoneNavOff .navBox{ display:none; }

/* Three more a phone can do without, each on its own switch. Hidden rather
   than shrunk: at 812 by 375 there is not a size at which a title, a compass
   and nine markers all fit and are worth reading, and a project that would
   rather have the render than the furniture should be able to say so. */
.phoneTitleOff .onImageTitle{ display:none; }
.phoneCompassOff .zCompass{ display:none; }
.phoneCallOff .zCallBtn{ display:none; }
/* The rail's other two. Never the overlay button: it is what turns the
   shapes on, and hiding it is hiding the flat chooser itself. */
.phoneFilterOff .zFcRailBtn[data-act="filter"]{ display:none; }
.phoneListOff .zFcRailBtn[data-act="list"]{ display:none; }
.phoneScrollAidOff .zScrollHint{ display:none; }

/* TAPPING A FLAT: the card has to be able to take the second tap, so here
   alone it stops being scenery. Everywhere else it must NOT take pointer
   events — a card that followed the mouse and also swallowed clicks would
   make the flat under it unclickable.

   .on IS LOAD-BEARING, and leaving it off did the very thing the sentence
   above warns about. The card is hidden by opacity alone — it has to be, so
   it can fade — and an element at opacity 0 is still laid out and still hit
   tested. So after the first card was dismissed, its rectangle went on
   swallowing every tap: on a phone held upright that is most of the render,
   and the flats under it could not be opened again. Nothing looked wrong,
   which is why it reads as the site having frozen rather than as a card
   that is still there. */
.phoneTapTip .zUnitTip.on{ pointer-events:auto; cursor:pointer; }

/* With the compass gone the title has nothing to make room for, and nothing
   to stack under either — so it takes the whole width back and returns to the
   first row. Joined selectors throughout: phoneCompassOff, hasCompass and the
   width classes all live on .stageWrap, and a space between any two of them
   asks for a wrap inside a wrap. */
.phoneCompassOff.hasCompass .onImageTitle{
  max-width:calc(100% - var(--title-left, 18px) - 18px);
}
.stageW700.hasCompass.phoneCompassOff .onImageTitle{ top:16px; }

/* ---- the gesture, taught once ----
   In the middle, because that is where the picture is and where a thumb
   already is. Nothing here takes a pointer event: it is a caption, not a
   control, and a visitor who swipes THROUGH it has understood it. */
.zSwipeHint{
  position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%);
  z-index:8; pointer-events:none;
  display:flex; flex-direction:column; align-items:center; gap:10px;
  padding:18px 26px; border-radius:14px;
  background:rgba(20,20,16,0.42);
  backdrop-filter:blur(6px);
  color:#f4f1ea;
  text-shadow:0 1px 4px rgba(0,0,0,0.5);
  opacity:0;
  animation:zSwipeIn 420ms ease forwards;
}
.zSwipeHint.leaving{ animation:zSwipeOut 420ms ease forwards; }
@keyframes zSwipeIn{ from{ opacity:0; transform:translate(-50%,-46%); }
                     to{ opacity:1; transform:translate(-50%,-50%); } }
@keyframes zSwipeOut{ from{ opacity:1; } to{ opacity:0; } }

.zSwipeArt{ display:flex; align-items:center; gap:14px; }
.zSwipeArrow{ font-size:26px; line-height:1; opacity:0.55; }
/* the arrows brighten in turn, in time with the hand, so the pair reads as
   one movement rather than two lights blinking */
.zSwipeArrow.left{ animation:zSwipeArrowL 2200ms ease-in-out infinite; }
.zSwipeArrow.right{ animation:zSwipeArrowR 2200ms ease-in-out infinite; }
@keyframes zSwipeArrowL{ 0%,55%,100%{ opacity:0.3; } 72%{ opacity:1; } }
@keyframes zSwipeArrowR{ 0%,100%{ opacity:0.3; } 22%{ opacity:1; } }

/* A thumb: a rounded rectangle with a smaller one for the tip. Two boxes
   rather than an SVG path, so it costs nothing and scales with the text. */
/* A HAND, not a mouse. The first drawing was a mouse body with a scroll
   wheel in it — which is exactly the wrong instrument to show somebody
   holding a phone. This is a palm with a thumb: a rounded rectangle, and a
   smaller one at the side of it, moving together. */
.zSwipeHand{
  position:relative; width:22px; height:26px;
  border-radius:7px 7px 9px 9px;
  background:currentColor;
  animation:zSwipeMove 2200ms ease-in-out infinite;
}
/* the thumb, tucked against the left edge and angled off it */
.zSwipeThumb{
  position:absolute; left:-7px; top:9px;
  width:11px; height:8px; border-radius:5px;
  background:currentColor;
  transform:rotate(-24deg);
}
/* the three fingers, as notches in the top edge */
.zSwipeHand::after{
  content:''; position:absolute; left:4px; top:-1px;
  width:14px; height:5px;
  background:
    repeating-linear-gradient(90deg,
      transparent 0 3px, rgba(0,0,0,0.32) 3px 4px);
  border-radius:5px 5px 0 0;
}
@keyframes zSwipeMove{
  0%, 100%{ transform:translateX(0); }
  25%     { transform:translateX(13px); }
  75%     { transform:translateX(-13px); }
}
.zSwipeText{ margin:0; font-size:13px; letter-spacing:0.04em; }

/* Someone who asked for less movement still needs telling how to navigate —
   they get the words and the arrows, held still. */
@media (prefers-reduced-motion: reduce){
  .zSwipeHint{ animation:none; opacity:1; }
  .zSwipeHand, .zSwipeArrow{ animation:none; }
  .zSwipeArrow{ opacity:0.8; }
}

.navBox.barMode{
  --bar-arrow-w:38px;
  --bar-slot-w:calc((min(var(--bar-width, 520px), 92vw) - 2 * var(--bar-arrow-w)) / 3);
  background:none; backdrop-filter:none; padding:0;
  width:calc(var(--bar-slot-w) * 3 + var(--bar-arrow-w) * 2);
  justify-content:center;
  /* the box stays full width even when the capsule is collapsed, so it must
     not swallow clicks in the empty margin either side */
  pointer-events:none;
}
.navBarShell{
  flex:0 0 auto;
  pointer-events:auto;
  display:flex; align-items:center;
  width:calc(var(--bar-slot-w) * 3 + var(--bar-arrow-w) * 2);
  height:var(--bar-height, 44px);
  background:var(--bar-bg, rgba(255,255,255,0.92));
  border:var(--bar-stroke-width, 0) solid var(--bar-stroke-color, transparent);
  border-radius:var(--bar-radius, 999px);
  backdrop-filter:blur(4px);
  box-sizing:border-box;
  overflow:hidden;
  transition:width var(--bar-anim, 600ms) cubic-bezier(.3,.9,.3,1),
             transform var(--bar-anim, 600ms) cubic-bezier(.3,.9,.3,1);
}
/* The track never changes size — a negative margin slides it under the shell's
   clip so the dead side's slot disappears while the surviving slots keep both
   their width and their position. */
.navBarTrack{
  position:relative; flex:0 0 auto; height:100%;
  width:calc(var(--bar-slot-w) * 3);
  transition:margin var(--bar-anim, 600ms) cubic-bezier(.3,.9,.3,1);
}
.navBarSlot{
  position:absolute; top:0;
  width:33.3333%; height:100%;
  display:flex; align-items:center; justify-content:center;
  padding:0 6px;
  background:none; border:none;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  cursor:pointer;
  transition:color var(--bar-anim, 600ms) ease, font-size var(--bar-anim, 600ms) ease;
  box-sizing:border-box;
}
.navBarSlot.current{ cursor:default; }
.navBarSlot.isEmpty{ cursor:default; opacity:0; pointer-events:none; }
.navBarSlot:not(.current):not(.isEmpty):hover{ filter:brightness(0.75); }
.navBarDivider{
  position:absolute; top:22%; bottom:22%;
  width:1px;
  background:var(--bar-divider, #c9c9c4);
  pointer-events:none;
  transition:opacity var(--bar-anim, 600ms) ease;
}
.navBarArrow{
  flex:0 0 auto;
  width:var(--bar-arrow-w, 38px); height:100%;
  min-width:0;
  display:flex; align-items:center; justify-content:center;
  background:none; border:none; padding:0;
  cursor:pointer;
  overflow:hidden;
  color:var(--bar-arrow, #141410);
  transition:width var(--bar-anim, 600ms) cubic-bezier(.3,.9,.3,1),
             opacity var(--bar-anim, 600ms) ease;
}
.navBarArrow svg{ width:18px; height:18px; flex:0 0 18px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.navBarArrow:not(.isEmpty):hover{ filter:brightness(0.6); }
/* Dead end of a non-looping sequence: the arrow, its divider and the empty
   slot all go, and the capsule closes up against the current scene — so there
   is visibly only one direction left to travel. */
/* THE CAPSULE'S THREE WIDTHS. barApplyEnds used to write these inline as the
   same calc() its two classes already imply. Here instead, because an inline
   width is the one thing no stylesheet can answer — which is what kept the
   bar a floating pill on a phone held upright, where it should be a band. */
.navBarShell{ width:calc(var(--bar-slot-w) * 3 + var(--bar-arrow-w) * 2); }
.navBarShell.deadPrev, .navBarShell.deadNext{
  width:calc(var(--bar-slot-w) * 2 + var(--bar-arrow-w));
}
.navBarShell.deadPrev.deadNext{ width:var(--bar-slot-w); }
/* The track keeps its size and slides under the shell's clip, so the slots
   that survive keep both their width and their place. */
.navBarShell.deadPrev .navBarTrack{ margin-left:calc(var(--bar-slot-w) * -1); }
.navBarShell.deadNext .navBarTrack{ margin-right:calc(var(--bar-slot-w) * -1); }
.navBarShell.deadPrev .navBarArrow.prev,
.navBarShell.deadNext .navBarArrow.next{ width:0; opacity:0; cursor:default; }
.navBarShell.deadPrev .navBarDivider.d1,
.navBarShell.deadNext .navBarDivider.d2{ opacity:0; }
/* The capsule is centred in the box, so shrinking it alone would drag the
   current scene sideways. This offset by half of what was removed pins the
   current scene where it was: only the dead edge travels. */
.navBarShell.deadPrev{ transform:translateX(calc((var(--bar-slot-w) + var(--bar-arrow-w)) / 2)); }
.navBarShell.deadNext{ transform:translateX(calc((var(--bar-slot-w) + var(--bar-arrow-w)) / -2)); }
/* a two-scene, non-looping sequence can be dead on both sides at once */
.navBarShell.deadPrev.deadNext{ transform:none; }

@media (prefers-reduced-motion: reduce){
  .navBarSlot, .navBarShell, .navBarTrack, .navBarArrow, .navBarDivider{ transition:none; }
}

/* ---- scroll hint ---- */
.zScrollHint{
  position:absolute; left:50%; transform:translateX(-50%);
  z-index:6;
  pointer-events:none;
  opacity:var(--zsh-opacity, 0.85);
  filter:var(--zsh-shadow, none);
  transition:opacity 420ms ease;
  animation:zshBob 2.4s ease-in-out infinite;
}
.zScrollHint.retired{ opacity:0; }
.zScrollHint svg{
  display:block;
  /* 55/46: the artwork grew a second chevron and got taller, the MOUSE did
     not. Scaling by the same ratio the box grew keeps `size` meaning the
     rendered mouse it has always meant, so no stored setting silently shrank. */
  height:calc(var(--zsh-size, 34px) * 55 / 46); width:auto;
  fill:none;
  stroke:var(--zsh-color, #eee9dd);
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
}
.zScrollHint .zshDot{
  fill:var(--zsh-color, #eee9dd); stroke:none;
  animation:zshDot 1.7s ease-in-out infinite;
}
/* Both chevrons pulse at once, each leaning the way it points. Alternating
   them would read as an instruction to go that way FIRST; together they read
   as "either way", which is what the gesture actually offers. */
.zScrollHint .zshChev.down{ animation:zshChevDown 1.7s ease-in-out infinite; }
.zScrollHint .zshChev.up{ animation:zshChevUp 1.7s ease-in-out infinite; }
@keyframes zshDot{
  0%,100%{ transform:translateY(0); opacity:1; }
  55%{ transform:translateY(7px); opacity:0.15; }
}
@keyframes zshChevDown{
  0%,100%{ opacity:0.35; transform:translateY(0); }
  55%{ opacity:1; transform:translateY(3px); }
}
@keyframes zshChevUp{
  0%,100%{ opacity:0.35; transform:translateY(0); }
  55%{ opacity:1; transform:translateY(-3px); }
}
@keyframes zshBob{
  0%,100%{ transform:translateX(-50%) translateY(0); }
  50%{ transform:translateX(-50%) translateY(-3px); }
}
/* ---- the same hint on its side, for a free-spin strip ----
   Rotating the artwork by -90deg sends "down" to "right" and "up" to "left",
   and takes the dot and both chevrons with it — so the drop and the two nudges
   that teach a scroll become the drag that turns a building either way, with
   no second drawing to keep in step with the first. The bob turns sideways to
   match. */
.zScrollHint.horizontal svg{ transform:rotate(-90deg); }
.zScrollHint.horizontal{ animation-name:zshBobX; }
@keyframes zshBobX{
  0%,100%{ transform:translateX(-50%); }
  50%{ transform:translateX(calc(-50% + 3px)); }
}
@media (prefers-reduced-motion: reduce){
  .zScrollHint{ animation:none; }
  .zScrollHint .zshDot, .zScrollHint .zshChev{ animation:none; }
}

.pendingPin{
  position:absolute; width:14px; height:14px; transform:translate(-50%,-50%);
  border-radius:50%; border:2px dashed var(--accent); pointer-events:none;
}

#transitionVideo{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  z-index:7;
  opacity:0;
  pointer-events:none;
}
#transitionVideo.playing{ opacity:1; }

/* A scene whose still view is replaced by a short film once it has arrived.

   Not #transitionVideo's job: that one covers the JOURNEY between two scenes
   and is gone by the time you get there. This plays while the visitor stands
   still, so it has to sit exactly where the render sits — same box, same
   object-fit — or the swap would nudge the picture.

   No z-index on purpose. It is inserted after the render and before
   #hotspotLayer, so DOM order puts it over the frame it replaces while the
   unit shapes (z-index 1) and the markers (2) stay on top of both. */
#sceneVideo{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  pointer-events:none;
  opacity:0;
  transition:opacity 300ms ease;
}
#sceneVideo.on{ opacity:1; }

.stageOverlay{
  position:absolute; inset:0; background:rgba(10,10,8,0.32);
  opacity:0; pointer-events:none; transition:opacity 220ms ease; z-index:9;
}
.stageOverlay.open{ opacity:1; pointer-events:auto; }

.panel{
  position:absolute;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:6px;
  box-shadow:0 12px 32px rgba(0,0,0,0.45);
  z-index:10;
  opacity:0;
  pointer-events:none;
  transform:scale(0.18);
  transition:transform 240ms cubic-bezier(.2,.9,.3,1.2), opacity 180ms ease;
  overflow:hidden;
}
.panel.open{ opacity:1; pointer-events:auto; transform:scale(1); }
.panel .panelInner{ padding:14px; }
.panel .pimgWrap{ margin:-14px -14px 12px; position:relative; }
.panel .pimgWrap img{ width:100%; display:block; }
.panel .pimgWrap.clickable{ cursor:zoom-in; }
/* pre-line so a newline typed into the title survives; a title without one
   wraps and collapses spaces exactly as before. */
.panel .ptitle{ white-space:pre-line; font-size:calc(var(--panel-title-font-size-base, 14.5px)); font-weight:var(--panel-title-font-weight, 600); font-style:var(--panel-title-font-style, normal); text-decoration:var(--panel-title-text-decoration, none); margin:0 0 5px; font-family:var(--panel-title-font-family, inherit); color:var(--panel-title-color, var(--text)); }
/* white-space:pre-wrap keeps the line breaks the author typed in the editor's
   textarea — the newlines are already in the DOM via textContent, but the
   default `white-space:normal` collapses them into single spaces. */
.panel .pbody{ font-size:var(--panel-text-font-size-base, 13px); line-height:1.5; color:var(--panel-text-color, var(--text-muted)); margin:0; font-family:var(--panel-text-font-family, inherit); font-weight:var(--panel-text-font-weight, 400); font-style:var(--panel-text-font-style, normal); text-decoration:var(--panel-text-text-decoration, none); white-space:pre-wrap; overflow-wrap:break-word; }
.panel .close{
  position:absolute; top:8px; right:8px;
  width:24px; height:24px;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface-2);
  border:1px solid var(--border);
  border-radius:3px;
  color:var(--text-muted);
  font-family:'IBM Plex Mono',monospace; font-size:13px; line-height:1;
  cursor:pointer;
  z-index:2;
  padding:0;
}
.panel .close:hover{ color:var(--accent); border-color:var(--accent); }
.panel .moreInfoBtn{
  display:block; width:calc(100% + 28px);
  margin:12px -14px -14px;
  padding:10px 14px;
  border:none;
  background:var(--panel-more-bg, var(--accent));
  color:var(--panel-more-color, #141410);
  font-family:var(--panel-more-font-family, inherit);
  font-size:var(--panel-more-font-size-base, 12px);
  font-weight:var(--panel-more-font-weight, 600);
  font-style:var(--panel-more-font-style, normal);
  text-align:left;
  text-decoration:var(--panel-more-text-decoration, none);
  cursor:pointer;
  letter-spacing:0.02em;
}
.panel .moreInfoBtn:hover{ filter:brightness(1.08); }
/* inset variant — sits inside the box's padding as a normal button instead
   of bleeding out to the box's edges */
.panel .moreInfoBtn.inset{
  display:inline-block; width:auto;
  margin:12px 0 0;
  padding:8px 16px;
  border-radius:5px;
}

.lightbox{
  position:absolute; inset:0;
  z-index:20;
  background:rgba(8,8,6,0.44);
  display:flex; align-items:center; justify-content:center;
  opacity:0; pointer-events:none;
  transition:opacity 200ms ease;
  cursor:zoom-out;
}
.lightbox.open{ opacity:1; pointer-events:auto; }
.lightbox img{ max-width:92%; max-height:92%; object-fit:contain; border-radius:4px; box-shadow:0 20px 60px rgba(0,0,0,0.6); }

/* ---- 360° panorama viewer (vr hotspots) ----
   .vrBackdrop is the click-outside-to-close surface; .vrWindow is the framed
   box whose bounds that refers to, so clicks inside it must not bubble. */
.vrBackdrop{
  position:absolute; inset:0;
  z-index:25;
  background:rgba(8,8,6,0.82);
  opacity:0; pointer-events:none;
  transition:opacity 200ms ease;
}
.vrBackdrop.open{ opacity:1; pointer-events:auto; }
/* absolutely positioned (not flex-centred) because left/top/width/height are
   driven from JS once the window can be dragged and resized — openVr() sets
   the initial centred geometry */
.vrWindow{
  position:absolute;
  background:#12120f;
  border:1px solid var(--border);
  border-radius:8px;
  overflow:hidden;
  box-shadow:0 18px 48px rgba(0,0,0,0.55);
}
.vrStage{ position:absolute; inset:0; cursor:grab; }
.vrStage:active{ cursor:grabbing; }
.vrBtn{
  position:absolute; top:10px; z-index:4;
  width:28px; height:28px;
  display:flex; align-items:center; justify-content:center;
  background:rgba(20,20,16,0.85);
  border:1px solid var(--border);
  border-radius:4px;
  color:var(--text-muted);
  font-family:'IBM Plex Mono',monospace; font-size:14px; line-height:1;
  cursor:pointer; padding:0;
}
.vrBtn:hover{ color:var(--accent); border-color:var(--accent); }
.vrClose{ right:10px; }
.vrMove{ right:44px; cursor:grab; }
.vrMove:active{ cursor:grabbing; }

/* Resize grips — one per edge and corner, above the panorama canvas so a drag
   here resizes instead of panning. Kept fully inside the box: .vrWindow has
   overflow:hidden (to clip the canvas to the rounded corners), so anything
   pushed outside its bounds would be clipped away and lose grab area. */
.vrGrip{ position:absolute; z-index:3; }
.vrGrip.n{ top:0; left:16px; right:16px; height:8px; cursor:ns-resize; }
.vrGrip.s{ bottom:0; left:16px; right:16px; height:8px; cursor:ns-resize; }
.vrGrip.w{ left:0; top:16px; bottom:16px; width:8px; cursor:ew-resize; }
.vrGrip.e{ right:0; top:16px; bottom:16px; width:8px; cursor:ew-resize; }
.vrGrip.nw{ top:0; left:0; width:16px; height:16px; cursor:nwse-resize; }
.vrGrip.se{ bottom:0; right:0; width:16px; height:16px; cursor:nwse-resize; }
.vrGrip.ne{ top:0; right:0; width:16px; height:16px; cursor:nesw-resize; }
.vrGrip.sw{ bottom:0; left:0; width:16px; height:16px; cursor:nesw-resize; }
.vrMsg{
  position:absolute; left:0; right:0; top:50%; transform:translateY(-50%);
  margin:0; padding:0 24px; text-align:center;
  color:var(--text-muted);
  font-family:'IBM Plex Mono',monospace; font-size:13px; line-height:1.6;
  pointer-events:none; z-index:2;
}
/* pannellum ships a light-grey loading backdrop — keep it in the dark theme */
.vrStage .pnlm-container{ background:#12120f; }
.vrStage .pnlm-load-box{ display:none; }


/* ── the compass ──────────────────────────────────────────────────────────
   A window onto a strip three turns wide. Only the window is visible; the
   strip slides behind it, so the marks never move relative to each other and
   the whole thing is one transform per frame. */
.zCompass{
  position:absolute;
  top:var(--zc-top, 16px);
  left:50%;
  transform:translateX(-50%);
  /* Published as a variable rather than computed here, because the TITLE has
     to subtract it — see .onImageTitle. 62% of the stage, not 62vw: in a
     phone-sized preview inside a wide editor, vw is the editor. */
  width:var(--zc-used);
  height:22px;
  z-index:5;
  overflow:hidden;
  pointer-events:none;
  /* the ends fade out rather than being cut, so the strip reads as continuing
     past the window instead of stopping at a hard edge */
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  mask-image:linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
  transition:opacity 160ms ease;
  background:var(--zc-bg, transparent);
  border-radius:var(--zc-radius, 4px);
}
/* a plate wants its ends square, or the fade eats the corners */
.zCompass.noFade{ -webkit-mask-image:none; mask-image:none; }
.zCompass.off{ opacity:0; }
/* the same distance, measured from the other edge */
.zCompass.atBottom{ top:auto; bottom:var(--zc-top, 16px); }
/* A compass at the bottom is not in the title's row at all, so the title gets
   the whole width back. */
.stageWrap:has(> .zCompass.atBottom) .onImageTitle{ max-width:calc(100% - var(--title-left, 18px) - 18px); }
/* nothing in the top row to stack under, so a centred title has it back */
.stageWrap:has(> .zCompass.atBottom) .onImageTitle[data-align="center"]{
  top:var(--title-top, 16px);
  max-width:calc(100% - 36px);
}

.zCompassStrip{
  position:absolute; inset:0 auto 0 0;
  width:var(--zc-strip, 900%);
  will-change:transform;
}
.zCompassMark{
  position:absolute; top:50%;
  transform:translate(-50%, -50%);
  color:var(--zc-color, #fff);
  opacity:var(--zc-dim, 0.55);
  font-family:var(--zc-family, 'IBM Plex Mono', ui-monospace, monospace);
  font-size:calc(var(--zc-size, 11px) * var(--zc-minor, 0.8));
  line-height:1;
  text-shadow:var(--zc-shadow, 0 1px 4px rgba(0,0,0,0.6));
  white-space:nowrap;
}
.zCompassMark.named{
  font-size:var(--zc-size, 11px);
  opacity:calc(var(--zc-dim, 0.55) + 0.2);
  letter-spacing:0.08em;
}
.zCompassMark.cardinal{ opacity:1; font-weight:var(--zc-weight, 600); }

/* the middle: where you are actually looking */
.zCompassTick{
  position:absolute; left:50%; top:0; bottom:0;
  width:var(--zc-tickw, 1px);
  transform:translateX(-50%);
  background:var(--zc-tick, var(--zc-color, #fff));
  opacity:0.9;
}
.zCompassTick::after{
  content:''; position:absolute; left:50%; top:-1px;
  width:0; height:0; margin-left:-4px;
  border-left:4px solid transparent;
  border-right:4px solid transparent;
  border-top:5px solid var(--zc-tick, var(--zc-color, #fff));
}

/* 78% of the STAGE, not of the window: in a phone-sized preview inside a big
   editor 78vw was wider than the whole picture. */
.stageW700 .zCompass{ height:20px; }
