/* ── Top-down scene connector (CS-TOUR-05) ─────────────────────────────────
 *
 * /listings/:slug/tour/board — the plane where the top-down scan tiles are
 * matched to each other and the ways between rooms are read off the result.
 *
 * PORTED FROM THE APPROVED PROTOTYPE, value for value. Every colour, size,
 * radius, gap and easing below is the one Dom signed off on; nothing here is
 * a new invention and nothing here reaches outside `.tdb`.
 *
 * SCOPE. The prototype was a standalone document, so it styled `*`, `html`
 * and `body`. Those three selectors are the only things that could not be
 * carried over as written: they are re-hung under `.tdb` so the board can
 * never restyle a page it shares the document with. The board's own surface
 * is one fixed layer that makes its own stacking context, which is why every
 * z-index in here is a small local number and none of them can climb over the
 * toast host or a type-to-confirm dialog.
 *
 * DARK IN BOTH THEMES, deliberately. The tiles are photographic top-down
 * renders and the plane is the dark table they are laid on; a cream plane
 * would wash them out. Same precedent as the two forced-dark cockpit screens.
 *
 * Laws: glass, no frames, no shadows, no outlines. Radius 6. Geist, then
 * Inter, then system-ui. Bright grey #d9dce1 for an active state only, CS
 * yellow #fff083 for selection. Red only ever means danger, and the one place
 * it appears is a save that did not land. There is no emerald and no teal on
 * this board, in any form, at any opacity.
 *
 * GLASS HERE IS THE CAPITAL SHOTS STACK BLUR, never one `backdrop-filter`:
 * six masked rungs from about 2 px to about 28 px under a dark wash, injected
 * by the board's own script. See "THE CAPITAL SHOTS STACK BLUR" below.
 *
 * ONE EXCEPTION TO "no outlines", ASKED FOR (CS-TOUR-05, eighth pass): the
 * NODES on the plane wear one. The whole disc is what is grabbed now, so the
 * whole disc has to say so — and it is drawn in the plane's own svg at the
 * node's true metric radius rather than in CSS, because a border on a
 * top-down render would eat into the picture and change its scale. See
 * `render()`. Nothing else on this board gains an outline.
 */

.tdb {
  --tdb-plane: #08090b;
  /* ── THE BUYER PAGE'S CHROME, LIFTED (CS-TOUR-05, tenth pass) ──────────
   * Dom, twice: the editor must wear the buyer page's chrome. Every value in
   * this block is read off the live pointer viewer's own stylesheet
   * (cs-booking/src/handlers/tours.ts, tourPointerCss, default "glass" skin):
   *
   *   ink          #f6f5f3                     --pk-ink
   *   body line    .80 of the ink              .pk-card p, opacity .88
   *   quiet line   .60 of the ink              .pk-credit, opacity .72
   *   glass fill   rgba(16,15,20,.44)          --pk-fill
   *   glass blur   blur(18px) saturate(1.35)   --pk-blur
   *   round icon   a white wash, .12 to .20    .pk-mm-close / .pk-mm-hide
   *   spacing      16 edge, 8 between, 14/16 in a panel, 10 between cards
   *
   * WHAT DOES NOT COME OVER: the viewer's 1 px hairline, its inset highlight,
   * its drop shadow and its 20 px radius. This board's law is no frames, no
   * shadows, no outlines and a 6 px corner, and the law wins over the lift. */
  --tdb-ink: #f6f5f3;
  --tdb-ink-2: rgba(246, 245, 243, .80);
  --tdb-ink-3: rgba(246, 245, 243, .60);
  /* ── THE WASH UNDER THE STACK BLUR (twelfth pass) ─────────────────────
   * The viewer's own fill is rgba(16,15,20,.44), and it was carried over
   * value for value. Over a WHITE KITCHEN that composites to about #9b9a9d
   * and the quiet ink on it reads 1.9:1, which is the washed-out label Dom
   * has now raised twice. The material is unchanged — it is still one dark
   * wash over a blur — but the wash is taken down to where the worst case
   * on this board is legible, in BOTH views rather than in the sphere only:
   * the plane carries photographic floor renders and little planets, and a
   * white kitchen is a white kitchen whichever view it is under.
   *
   *   .76 over pure white  →  #434343   ink 9.2:1   quiet ink 4.4:1
   *   .88 over pure white  →  #26272a   ink 12.6:1  quiet ink 5.6:1
   *
   * `-deep` is for the surfaces that carry the most reading and the least
   * chrome around them: the card menu, the peek, and the two full screens. */
  --tdb-glass: rgba(8, 9, 11, .76);
  --tdb-glass-deep: rgba(8, 9, 11, .88);
  --tdb-glass-2: rgba(255, 255, 255, .085);
  --tdb-glass-3: rgba(255, 255, 255, .12);
  --tdb-ico: rgba(255, 255, 255, .12);
  --tdb-ico-2: rgba(255, 255, 255, .20);
  --tdb-edge: 16px;
  --tdb-gap: 8px;
  --tdb-pad: 14px 16px;
  /* A panel title, a body line and a quiet line, at the viewer's own sizes. */
  --tdb-fs-title: 14.5px;
  --tdb-fs-body: 12.8px;
  --tdb-fs-quiet: 11.5px;
  /* The band under the floating top controls. */
  --tdb-top2: 66px;
  /* HOW TALL THE DOCK IS RIGHT NOW, measured by the board's own script. The
     dock runs edge to edge along the bottom, so the preview and the floor
     proposal float ABOVE it rather than cutting a notch out of it, and both
     read their clearance from this one number. It is measured and never
     assumed: the dock is a row per floor and it folds. */
  --tdb-dockh: 128px;
  --tdb-active: #d9dce1;
  --tdb-sel: #fff083;
  /* ── EVERY LIVE STATE, NAMED (twelfth pass) ───────────────────────────
   * Focus, active, checked, hovered and the caret in a field are SET here
   * and nowhere else, so no browser default can put its own colour on this
   * board. There is no emerald and no teal in this file at any opacity, and
   * `accent-color` is declared explicitly on the board and on every input
   * rather than left to inherit from the shell. */
  --tdb-focus: rgba(217, 220, 225, .22);
  --tdb-track: rgba(255, 255, 255, .14);
  --tdb-line: #e6ebf2;
  --tdb-line-cut: #8b95a3;
  --tdb-line-dash: #96a0ad;
  --tdb-info: #7fb4ff;
  --tdb-r: 6px;
  /* THE BUYER PAGE'S THUMBNAIL AT THE BUYER PAGE'S SIZE, and the EDITOR'S OWN
     CORNER (ninth pass). The card is still 132 wide with a 62 tall picture and
     10 between cards, value for value from the pointer viewer — but the 20 px
     radius came over with the size and made this editor look like the buyer
     page. Every corner on this board is the board's own 6 now, thumbnails
     included. Only the radius changed; not one size did. */
  --tdb-th-w: 132px;
  --tdb-th-h: 62px;
  --tdb-th-r: var(--tdb-r);
  --tdb-th-gap: 10px;
  /* ── HOW TALL THE DOCK MAY GET ────────────────────────────────────────
     Three floors of cards took about 400 px, close to half a laptop screen.
     The dock is capped at TWO ROWS and scrolls past that, so moving between
     floors is a scroll inside the dock rather than a dropdown.
     A row is the card (1 + 62 + 26 + 1 = 90) plus its own scrollbar and
     padding (6), plus the floor head (24), plus the row's padding (6). */
  --tdb-cardh: 90px;
  --tdb-rowh: 126px;
  --tdb-dockrows: 2;
  /* ── A GROUND FOR TEXT OVER A PHOTOGRAPH ──────────────────────────────
     In the sphere every panel floats over a 360, and a 360 of a white kitchen
     is white. Light type straight onto it cannot be read, which is a
     legibility bug and not a matter of taste. Every label that sits over a
     picture wears this instead of trusting the picture underneath it. */
  --tdb-under: rgba(8, 9, 11, .82);
  --tdb-under-2: rgba(8, 9, 11, .62);
  --tdb-font: "Geist", "Geist Sans", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  position: fixed;
  inset: 0;
  z-index: 50;
  isolation: isolate;
  background: var(--tdb-plane);
  color: var(--tdb-ink);
  font-family: var(--tdb-font);
  font-size: 12px;
  line-height: 1.45;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  user-select: none;
}
.tdb *,
.tdb *::before,
.tdb *::after { box-sizing: border-box; margin: 0; padding: 0; border: 0; outline: 0; box-shadow: none; }
.tdb button,
.tdb select,
.tdb input { font: inherit; color: inherit; background: none; border-radius: var(--tdb-r); }
.tdb button { cursor: pointer; }

/* ---------- stage ---------- */
.tdb-stage { position: absolute; inset: 0; overflow: hidden; cursor: default; touch-action: none; }
.tdb-stage.is-pan { cursor: grab; }
.tdb-stage.is-panning { cursor: grabbing; }
.tdb-grid { position: absolute; inset: 0; pointer-events: none; opacity: .5; }
.tdb-tiles { position: absolute; inset: 0; pointer-events: none; }
.tdb-tiles > img,
.tdb-tiles > .tdb-disc { position: absolute; pointer-events: none; will-change: transform; image-rendering: auto; }
/* SPHERE draws the node from the scan's own 360 instead of its floor render:
   the panorama projected about the nadir, so the floor is in the middle of the
   disc, the walls wrap around it and the ceiling runs out to the rim. The
   picture is made in the board's own script and carries its own transparency,
   so everything outside the disc is simply not there. No ring, no frame and no
   shadow — there is nothing here to draw one with. */
.tdb-planet { border-radius: 50%; }
/* The needle's "N" is the only text drawn into the plane. A font-family
   ATTRIBUTE holding var(--font) does not parse in SVG and silently leaves the
   default serif, so the family is set here, where a custom property works. */
.tdb-gfx { position: absolute; inset: 0; pointer-events: none; font-family: var(--tdb-font); }
.tdb-marquee { position: absolute; background: rgba(255, 240, 131, .10); border-radius: var(--tdb-r); pointer-events: none; display: none; }

/* A scan with no top-down render is still placeable: a plain dark disc at the
   median radius of the tiles that do exist, carrying the room's own thumbnail
   and its name. Never a blank surface. It does not turn with the heading —
   there is no top-down geometry in it to align — the pin's tick shows that. */
.tdb-disc {
  border-radius: 50%;
  background: rgba(8, 9, 11, .88);
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.tdb-disc img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .40; border-radius: 50%; }
/* The name sits ON the room's own thumbnail, so it carries its own dark
   ground: a light name straight onto a bright kitchen is not a name. */
.tdb-disc b {
  position: relative; z-index: 1; font-weight: 500; font-size: 10px; color: var(--tdb-ink);
  padding: 2px 7px; margin-bottom: 12%; max-width: calc(100% - 16px); text-align: center;
  background: var(--tdb-under); border-radius: var(--tdb-r);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- generic glass ----------
   THE MATERIAL IS THE STACK, not one filter. See "THE CAPITAL SHOTS STACK
   BLUR" at the foot of this file: every surface here carries a wash and a
   run of six masked blur rungs from 2 px to 28 px, injected once by the
   board's own script as `.tdb-blur`. There is no `backdrop-filter` on this
   class, or on any other piece of board chrome. */
.tdb-glass {
  background: var(--tdb-glass);
  border-radius: var(--tdb-r);
}

/* ---------- the floating controls, top ----------
   NOT A STRIP ANY MORE. The bar was one full-width slab across the top of the
   window, and the buyer page has no such thing on it. What it has is small
   glass clusters floating 16 from the edges with 8 between them (.pk-tools),
   so that is what this is: the same controls, in the same order, doing the
   same things, gathered into groups that each wear the viewer's glass.
   The layer itself is nothing — it paints no ground and takes no presses;
   only the groups inside it do. */
.tdb-topbar {
  position: absolute; top: var(--tdb-edge); left: var(--tdb-edge); right: var(--tdb-edge);
  display: flex; align-items: center; gap: var(--tdb-gap); z-index: 40;
  background: none; pointer-events: none;
}
.tdb-grp {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px;
  min-width: 0; pointer-events: auto;
}
/* The address group is the one thing in the row that can be any length, so it
   is the one thing that gives way; every control keeps its full label. */
.tdb-grp.tdb-idgrp { flex: 0 1 auto; }
.tdb-topbar .tdb-sep { display: none; }
.tdb-btn {
  height: 26px; padding: 0 12px; border-radius: var(--tdb-r);
  background: var(--tdb-glass-2); color: var(--tdb-ink-2);
  font-size: 12px; font-weight: 600; letter-spacing: .01em;
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  transition: background .12s, color .12s;
}
.tdb-btn:hover { background: var(--tdb-glass-3); color: var(--tdb-ink); }
.tdb-btn[disabled] { opacity: .32; cursor: default; }
.tdb-btn[disabled]:hover { background: var(--tdb-glass-2); color: var(--tdb-ink-2); }
.tdb-btn.is-on { background: rgba(217, 220, 225, .14); color: var(--tdb-active); }
.tdb-btn.is-warn { color: var(--tdb-sel); }
.tdb-seg { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--tdb-r); background: rgba(255, 255, 255, .04); }
.tdb-seg .tdb-btn { background: transparent; height: 22px; padding: 0 10px; font-size: 11.5px; }
.tdb-seg .tdb-btn:hover { background: var(--tdb-glass-2); }
.tdb-seg .tdb-btn.is-on { background: rgba(217, 220, 225, .14); color: var(--tdb-active); }
/* The address is the one thing in the bar that can be any length, so it is the
   one thing that gives way — every control keeps its full label. */
/* THE PROPERTY TAG. On the buyer page the address sits lower left in the
   display face at 600 and -.01em, with a quieter fact line riding above it
   (.pk-addr and .pk-facts). The editor has no lower left left to give it — the
   dock runs the full width down there — so it wears that treatment in its own
   group at the top: the name in the ink at the tag's weight and tracking, the
   address beside it in the quiet line. */
.tdb-brand {
  color: var(--tdb-ink-3); padding: 0 8px 0 4px; font-size: var(--tdb-fs-quiet);
  letter-spacing: .02em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tdb-gfx text { user-select: none; }
.tdb-brand b { color: var(--tdb-ink); font-weight: 600; font-size: 13px; letter-spacing: -.01em; }
/* The turn grip. Drag it left and right to turn the whole plan on screen,
   double press it to straighten it again. The reading beside it is set the way
   BLEND and NORTH set theirs, so the three read as one family. Nothing here is
   a new value: the grip is a .tdb-btn wearing a different cursor. */
.tdb-grip { cursor: ew-resize; touch-action: none; }
.tdb-grip.is-drag { cursor: grabbing; }
.tdb-topbar .tdb-val {
  color: var(--tdb-ink); font-size: var(--tdb-fs-quiet); font-variant-numeric: tabular-nums;
  padding: 0 2px; min-width: 34px; white-space: nowrap;
}
.tdb-spacer { flex: 1; }
/* The save state, in the portal's own words: Saving… / Saved just now /
   Not saved. Nothing on this board saves silently and nothing fails quietly. */
.tdb-save { color: var(--tdb-ink-3); font-size: var(--tdb-fs-quiet); white-space: nowrap; padding: 0 6px 0 2px; font-variant-numeric: tabular-nums; }
.tdb-save.is-err { color: var(--err, #ff6b6b); }
.tdb-lvl {
  height: 26px; padding: 0 10px; font-size: 12px; font-weight: 600; background: var(--tdb-glass-2); color: var(--tdb-ink);
  border-radius: var(--tdb-r); appearance: none; padding-right: 22px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path d='M2 4l3 3 3-3' fill='none' stroke='%239aa3ae' stroke-width='1.2'/></svg>");
  background-repeat: no-repeat; background-position: right 7px center;
}
.tdb-lvl option { background: #14161a; color: #e9edf2; }

/* ---------- info clouds ---------- */
/* THE ROUND ICON BUTTON, the viewer's own. Top right on the buyer page a
   control that is only a mark is a circle with a white wash on the glass, no
   line and no shadow: .pk-mm-hide is 19 px at rgba(255,255,255,.18) and
   .pk-mm-close is 32 px at .12. The board's 6 px corner is for the controls
   that carry words; a mark stays round here as it does there. */
.tdb-q {
  width: 18px; height: 18px; flex: 0 0 18px; border-radius: 50%;
  background: var(--tdb-ico); color: var(--tdb-info);
  font-size: 10px; line-height: 18px; text-align: center; padding: 0;
  transition: background .12s;
}
.tdb-q:hover { background: var(--tdb-ico-2); }
/* The cloud is the viewer's tag card (.pk-card): its padding, its title, its
   body line. Only the blue ground is the board's, because an info cloud has to
   be told apart from a panel at a glance. */
.tdb-cloud {
  position: absolute; z-index: 60; max-width: 300px; padding: var(--tdb-pad); border-radius: var(--tdb-r);
  background: rgba(24, 43, 74, .90);
  color: #dce9ff; display: none; font-size: var(--tdb-fs-body); line-height: 1.6;
}
.tdb-cloud h4 { font-size: var(--tdb-fs-title); font-weight: 600; color: #fff; margin-bottom: 6px; }
.tdb-cloud p { margin-top: 8px; color: #c9dcff; }

/* ---------- right rail ---------- */
/* Blend alone. The column that once stacked it against the north dial went with
   the dial: a wrapper whose only job was to hold one control is not a control. */
.tdb-rail {
  position: absolute; right: var(--tdb-edge); top: var(--tdb-top2); width: 176px; z-index: 35;
  padding: var(--tdb-pad);
}
.tdb-rowhead { display: flex; align-items: center; gap: var(--tdb-gap); color: var(--tdb-ink-3); letter-spacing: .06em; text-transform: uppercase; font-size: 10px; }
.tdb-rowhead .tdb-val { margin-left: auto; color: var(--tdb-ink); text-transform: none; letter-spacing: 0; font-size: var(--tdb-fs-quiet); font-variant-numeric: tabular-nums; }
.tdb input[type=range] {
  -webkit-appearance: none; appearance: none; width: 100%; height: 16px; background: transparent; margin-top: 6px; cursor: pointer;
}
.tdb input[type=range]::-webkit-slider-runnable-track { height: 3px; border-radius: 2px; background: rgba(255, 255, 255, .14); }
.tdb input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 11px; height: 11px; border-radius: 50%; background: var(--tdb-ink); margin-top: -4px;
}
.tdb input[type=range]::-moz-range-track { height: 3px; border-radius: 2px; background: rgba(255, 255, 255, .14); }
.tdb input[type=range]::-moz-range-thumb { width: 11px; height: 11px; border-radius: 50%; background: var(--tdb-ink); border: 0; }

/* ---------- the needle's own reading, on the plane ----------
   ONE compass, not two. The needle is the north control, and its reading and
   its question ride beside it in the same quiet type the rest of the board
   uses. The tag is placed by the render, which knows where the needle is. */
.tdb-northtag {
  position: absolute; z-index: 34; display: flex; align-items: center; gap: 5px;
  height: 18px; pointer-events: auto;
}
.tdb-northtag .tdb-val {
  color: var(--tdb-ink); font-size: var(--tdb-fs-quiet); font-variant-numeric: tabular-nums;
  line-height: 18px; white-space: nowrap;
}
/* Bright grey says the same thing the needle itself says: this floor has been
   given its own needle instead of following the plan. */
.tdb-northtag .tdb-val.is-own { color: var(--tdb-active); }

/* ---------- THE DOCK, EDGE TO EDGE ----------
   The buyer page's strip runs the full width of the window and sits ON the
   bottom edge: no inset, no corner, no gap on the right, 10 above and 16 in
   from the sides, 10 between cards, on a ground that is a fall to black rather
   than a panel (.pk-strip). That is this, value for value. The preview no
   longer stands beside it; it floats above it, and the dock has the whole
   width back. */
.tdb-strip {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 35;
  padding: 10px var(--tdb-edge) 12px; border-radius: 0;
  /* THE DOCK OVER A PHOTOGRAPH. The buyer page's fall to black is enough for
     a page that is only looked at; room names are read off these cards fifty
     times a day and what is behind them is a 360 or a floor render, either of
     which can be a white kitchen. Same gradient, same direction, taken down to
     where the head line and the floor names hold up. The cards keep their own
     dark ground on top of it, and the stack blur rides underneath. */
  background: linear-gradient(to top, rgba(8, 9, 11, .94), rgba(8, 9, 11, .82) 62%, rgba(8, 9, 11, .34));
}
.tdb-striphead { display: flex; align-items: center; gap: var(--tdb-gap); margin-bottom: 9px; color: var(--tdb-ink-3); font-size: var(--tdb-fs-quiet); }
.tdb-striphead .tdb-count { color: var(--tdb-ink); font-weight: 600; }
.tdb-items { display: flex; gap: var(--tdb-th-gap); overflow-x: auto; overflow-y: hidden; padding-bottom: 2px; scrollbar-width: thin; }
.tdb-items::-webkit-scrollbar { height: 5px; }
.tdb-items::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); border-radius: 3px; }
/* THE CARD IS THE BUYER PAGE'S CARD. 132 wide, the picture 62 tall and flush
   to the corners, the name on one line beneath it, the short id riding in
   front of the name exactly where the buyer page rides its walk number. The
   1px of padding stands in for the buyer card's 1px line, so the box and the
   picture inside it come out at the same size without drawing a frame. */
/* THE CARD'S OWN GROUND IS DARK, not the picture behind it. In the sphere the
   dock floats over the 360 and a translucent white card over a white kitchen
   is a white card with grey writing on it. Nothing about the card's SIZE
   changes here; what changes is what the type stands on. */
.tdb-item {
  flex: 0 0 var(--tdb-th-w); width: var(--tdb-th-w);
  border-radius: var(--tdb-th-r); background: var(--tdb-under-2);
  padding: 1px; overflow: hidden; text-align: left;
  cursor: grab; position: relative; transition: background .12s, transform .2s ease;
}
.tdb-item:hover { background: rgba(8, 9, 11, .78); transform: translateY(-2px); }
.tdb-item .tdb-th { width: 100%; height: var(--tdb-th-h); background: rgba(255, 255, 255, .06); display: block; object-fit: cover; }
.tdb-item .tdb-nm {
  padding: 6px 9px; font-size: 11.5px; font-weight: 600; color: var(--tdb-ink);
  /* The buyer page's caption inherits a plain normal line-height; the board
     runs 1.45 everywhere, which would make the card three pixels taller than
     the one Dom approved. 14px is that caption's line box, held whatever font
     loads, so the card comes out at the buyer page's 90. */
  line-height: 14px;
  /* AND ITS OWN GROUND. The caption is the one line of this card that has to
     be read at a glance fifty times a day. */
  background: var(--tdb-under);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tdb-item .tdb-id {
  display: inline-block; margin-right: 5px; font-weight: 700; opacity: .6;
  color: inherit; font-variant-numeric: tabular-nums;
}
/* RENAME IN PLACE. The caption becomes the field, in the caption's own box, so
   the words being typed are the words that will be read. Same padding, same
   size, same line box: the card does not change height while it is edited. */
.tdb-item .tdb-nmin {
  display: block; width: 100%; height: 26px;
  padding: 6px 9px; font-size: 11.5px; font-weight: 600; color: var(--tdb-ink);
  line-height: 14px; background: var(--tdb-glass-3); border-radius: 0;
}
.tdb-item .tdb-nmin::placeholder { color: var(--tdb-ink-3); }
.tdb-item.is-placed { cursor: grab; opacity: .42; }
.tdb-item.is-placed:hover { background: rgba(8, 9, 11, .78); transform: none; }
.tdb-item.is-inview { background: rgba(255, 240, 131, .16); }
/* Hidden on the tour. The card is still here and still does everything a card
   does; the picture says it is out of sight. */
.tdb-item.is-off .tdb-th { opacity: .38; }
.tdb-item.is-editing { cursor: default; }
/* The state marks, top left, out of the way of the menu. Editor-only, and
   inside the buyer card's proportions: a dark ground with the board's own
   bright grey on it, so they hold up over the brightest room in the tour. */
.tdb-marks { position: absolute; top: 7px; left: 7px; display: flex; gap: 4px; max-width: calc(100% - 44px); }
.tdb-badge {
  padding: 2px 6px; border-radius: var(--tdb-r);
  background: var(--tdb-under); color: var(--tdb-active); font-size: 9.5px; letter-spacing: .02em;
  max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tdb-badge.is-other { color: var(--tdb-ink-2); }
/* ONE MENU ON THE CARD, and one only. Everything the rooms rail did to a room
   is behind it, so the dock does not become a soup of icons. */
.tdb-cardmenu {
  position: absolute; top: 7px; right: 7px; width: 20px; height: 18px; padding: 0;
  border-radius: var(--tdb-r); background: var(--tdb-under); color: var(--tdb-ink-2);
  font-size: 12px; line-height: 10px; opacity: 0; transition: opacity .12s, color .12s;
}
.tdb-item:hover .tdb-cardmenu,
.tdb-item.is-inview .tdb-cardmenu,
.tdb-cardmenu[aria-expanded="true"] { opacity: 1; }
.tdb-cardmenu:hover { color: var(--tdb-ink); }
/* Drawn on the board, never inside the card: a row of cards scrolls and clips,
   and a menu inside one would be cut in half by its own row. */
.tdb-cardpop {
  position: absolute; z-index: 46; display: none; min-width: 140px;
  padding: 5px; flex-direction: column;
  background: var(--tdb-glass-deep);
}
.tdb-cardpop.is-on { display: flex; }
.tdb-cardpop button {
  text-align: left; height: 24px; padding: 0 9px; border-radius: var(--tdb-r);
  color: var(--tdb-ink-2); font-size: 11.5px; font-weight: 600; transition: background .12s, color .12s;
}
.tdb-cardpop button:hover { background: var(--tdb-glass-2); color: var(--tdb-ink); }
.tdb-ghost {
  position: absolute; z-index: 55; pointer-events: none; border-radius: 50%; opacity: .75; display: none;
  object-fit: cover; background: rgba(8, 9, 11, .88);
}

/* ---------- preview ----------
   IT FLOATS ABOVE THE DOCK and does not reserve space beside it. The dock is
   edge to edge now, so the preview stands clear of the dock's own measured
   height and rides up and down with it as the floors are folded away. */
.tdb-preview {
  position: absolute; right: var(--tdb-edge); bottom: calc(var(--tdb-dockh) + 12px);
  width: 158px; z-index: 36; padding: 10px;
}
.tdb-preview .tdb-pv { width: 100%; height: 82px; object-fit: cover; border-radius: var(--tdb-r); display: block; background: #111; }
.tdb-preview .tdb-cap { margin-top: 8px; display: flex; align-items: baseline; gap: 6px; }
.tdb-preview .tdb-cap b { font-weight: 600; font-size: var(--tdb-fs-quiet); color: var(--tdb-ink); }
.tdb-preview .tdb-cap span { font-size: 10px; color: var(--tdb-ink-3); margin-left: auto; font-variant-numeric: tabular-nums; }
.tdb-preview .tdb-tag { margin-top: 3px; font-size: 10px; color: var(--tdb-ink-3); }
/* The panel is dragged to size from its top-left corner. No button, no menu and
   no handle drawn on the glass: an invisible grab zone in the corner and the
   cursor over it are the whole affordance. The caption stays one legible line
   at every width, so a long room name gives way rather than the reading. */
.tdb-preview { touch-action: none; }
.tdb-preview .tdb-grab {
  position: absolute; left: 0; top: 0; width: 18px; height: 18px; z-index: 1;
  cursor: nwse-resize; touch-action: none;
}
.tdb-preview .tdb-cap b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tdb-preview .tdb-cap span { flex: 0 0 auto; }
.tdb-preview .tdb-tag { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- readout ---------- */
.tdb-readout {
  position: absolute; left: var(--tdb-edge); top: var(--tdb-top2); z-index: 35;
  padding: var(--tdb-pad); max-width: 260px;
}
.tdb-readout .tdb-t { color: var(--tdb-ink-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.tdb-readout .tdb-n { font-size: var(--tdb-fs-title); font-weight: 600; letter-spacing: -.01em; margin-top: 4px; }
.tdb-readout .tdb-l { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; font-size: var(--tdb-fs-body); line-height: 1.6; color: var(--tdb-ink-2); }
.tdb-readout .tdb-l i { font-style: normal; display: inline-block; width: 24px; height: 1px; vertical-align: middle; margin-right: 7px; position: relative; top: -3px; }
.tdb-readout .tdb-empty { color: var(--tdb-ink-3); font-size: var(--tdb-fs-quiet); margin-top: 4px; }
/* More than one scene picked, said plainly. A shared ring is easy to miss, and
   the fact that the selection moves and turns together is the point of it. */
.tdb-readout .tdb-sel { margin-top: 7px; display: none; font-size: var(--tdb-fs-quiet); }
.tdb-readout .tdb-sel b { display: block; font-weight: 500; color: var(--tdb-sel); }
.tdb-readout .tdb-sel span { display: block; color: var(--tdb-ink-3); }

/* ═══════════════════════════════════════════════════════════════════════════
 * THE SPHERE VIEW (CS-TOUR-05, fourth pass)
 *
 * The second of the board's two views: the scan in view, full viewport, in its
 * own 360, where a way out is placed by hand on the doorway.
 *
 * Same laws, same tokens, not one new colour: glass, no frames, no shadows, no
 * outlines, radius 6, Geist. Bright grey #d9dce1 is the active state — the view
 * that is open, the tool that is armed, the pointer waiting for a room. CS
 * yellow #fff083 is selection, and it means here exactly what it means on the
 * plane: this is the thing your controls are pointed at. Red never appears.
 *
 * ONE EXCEPTION, DELIBERATE. The pointer markers carry a ring. They are not
 * board chrome; they are the tour's own approved pointer drawn over a
 * photograph, and the geometry below is the tour editor's, value for value —
 * the same 36 px body, the same inset mark, the same styles. Without a ring a
 * light marker over a bright window is invisible, which is the one thing a
 * marker may never be.
 * ═════════════════════════════════════════════════════════════════════════ */

/* Which controls exist at all. A control that belongs to one view is not shown
   in the other, rather than sitting there doing nothing. */
.tdb.is-sphere .tdb-planonly { display: none; }
.tdb:not(.is-sphere) .tdb-sphonly { display: none; }

/* ---------- the 360 itself ---------- */
.tdb-sphere { position: absolute; inset: 0; z-index: 1; background: var(--tdb-plane); }
.tdb-vh { position: absolute; inset: 0; }
.tdb-vh canvas { display: block; }
.tdb-vfail { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); max-width: 340px; text-align: center; color: var(--tdb-ink-2); font-size: 12px; line-height: 1.6; }

/* The room in view, and how many ways lead out of it. The plane's readout is
   not on screen here, so this stands where it stood. */
.tdb-vtag { position: absolute; left: var(--tdb-edge); top: var(--tdb-top2); z-index: 35; padding: 10px 16px; display: flex; align-items: baseline; gap: var(--tdb-gap); max-width: 280px; }
.tdb-vtag b { font-weight: 600; font-size: var(--tdb-fs-title); letter-spacing: -.01em; color: var(--tdb-ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tdb-vtag em { font-style: normal; font-size: var(--tdb-fs-quiet); color: var(--tdb-ink-3); flex: 0 0 auto; }

/* In the sphere every scan can be opened, placed or not, so the strip stops
   dimming the ones already on the plane. */
.tdb.is-sphere .tdb-item.is-placed { opacity: 1; cursor: pointer; }
.tdb.is-sphere .tdb-item.is-placed:hover { background: rgba(8, 9, 11, .78); }

/* ---------- the inspector ---------- */
/* THE PANEL DOES NOT SCROLL; ITS INSIDE DOES (twelfth pass). The stack blur
   is a child of the panel, and a child of a scrolling box scrolls with it —
   the blur would slide off the bottom of its own panel. So the panel holds
   the ground and the rungs, and `.tdb-inspin` holds the reading. */
.tdb-insp {
  position: absolute; right: var(--tdb-edge); top: var(--tdb-top2); width: 238px; z-index: 35;
  padding: var(--tdb-pad); overflow: hidden;
  /* It stops clear of the dock, which is edge to edge and changes height. */
  max-height: calc(100% - var(--tdb-dockh) - var(--tdb-top2) - 24px);
}
.tdb-inspin {
  /* The board is fixed to the viewport, so its 100% and 100vh are the same
     number; a percentage would resolve against an auto-height parent and be
     ignored, which is why this one is written in vh. 52 = the 24 the panel
     always stood clear of the dock by, plus the panel's own 14 + 14. */
  max-height: calc(100vh - var(--tdb-dockh) - var(--tdb-top2) - 52px);
  overflow-y: auto; scrollbar-width: thin;
}
.tdb-inspin::-webkit-scrollbar { width: 5px; }
.tdb-inspin::-webkit-scrollbar-thumb { background: var(--tdb-track); border-radius: 3px; }
.tdb-insp .tdb-t { color: var(--tdb-ink-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.tdb-insp .tdb-t.tdb-mt { margin-top: 12px; }
.tdb-insp .tdb-rowhead.tdb-mt { margin-top: 12px; }
.tdb-hint { color: var(--tdb-ink-3); font-size: var(--tdb-fs-quiet); line-height: 1.6; margin-top: 6px; }
.tdb-goes { font-size: var(--tdb-fs-body); line-height: 1.6; color: var(--tdb-ink-2); margin: 4px 0 10px; }
.tdb-goes b { font-weight: 600; color: var(--tdb-ink); }

/* The name filter over the rooms. Glass, like everything else on this board. */
.tdb-filter {
  width: 100%; height: 26px; margin-top: 6px; padding: 0 10px;
  background: var(--tdb-glass-2); color: var(--tdb-ink); font-size: var(--tdb-fs-quiet);
}
.tdb-filter::placeholder { color: var(--tdb-ink-3); }
.tdb-filter:focus { background: var(--tdb-glass-3); }

/* The rooms, as their own pictures. Picking one is the whole linking gesture. */
.tdb-picks { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 7px; }
/* Its own dark ground, like every other name that stands over a picture on
   this board: the grid sits on glass, the glass sits on the 360, and the 360
   can be a white kitchen. */
.tdb-pick {
  position: relative; padding: 0; border-radius: var(--tdb-r); overflow: hidden;
  background: var(--tdb-under-2); transition: background .12s;
}
.tdb-pick:hover { background: rgba(8, 9, 11, .78); }
.tdb-pick img { display: block; width: 100%; height: 46px; object-fit: cover; background: rgba(0, 0, 0, .35); }
.tdb-pick span {
  display: block; padding: 3px 5px 4px; font-size: 9.5px; color: var(--tdb-ink-2);
  background: var(--tdb-under);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Where it goes today. Yellow is selection here as it is everywhere else. */
.tdb-pick.is-on { background: rgba(255, 240, 131, .16); }
.tdb-pick.is-on span { color: var(--tdb-sel); }

/* ---------- chips: the look, the size, which ways ---------- */
.tdb-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.tdb-chip {
  height: 24px; padding: 0 11px; border-radius: var(--tdb-r);
  background: var(--tdb-glass-2); color: var(--tdb-ink-2); font-size: 11px; font-weight: 600;
  transition: background .12s, color .12s;
}
.tdb-chip:hover { background: var(--tdb-glass-3); color: var(--tdb-ink); }
.tdb-chip.is-on { background: rgba(217, 220, 225, .14); color: var(--tdb-active); }
.tdb-acts { margin-top: 12px; display: flex; gap: 6px; }
.tdb-acts .tdb-btn { flex: 1; justify-content: center; }

/* ---------- the pointer markers, in the photo ----------
   The tour editor's own geometry. One size variable drives the body, so the
   three named sizes are three numbers and not three copies of the rule. */
/* THE TURN aims the GLYPH BODY, exactly as the buyer page does: its tap circle
   and its label are its siblings on the hotspot, so neither of them turns. */
.tdb-hs {
  --hs: 36px;
  transform: rotate(var(--grot, 0deg));
  position: relative; width: var(--hs); height: var(--hs);
  margin: calc(var(--hs) / -2) 0 0 calc(var(--hs) / -2);
  border-radius: 50%; cursor: grab; touch-action: none;
  /* NO BLUR ON THE MARKER, and none on its toolbar either. The buyer page's
     own pointer face carries `blur(6px)` — but the Worker's stylesheet also
     rules, on the surface right beside it, that a hotspot is INSIDE A 3D
     TRANSFORM, "where backdrop blur is unreliable across browsers and costs a
     full-frame readback per ring". A marker is a 22% white disc inside a 92%
     white ring: what carries it is the fill and the ring, not six pixels of
     blur behind it that half the browsers never draw. So this board has NO
     `backdrop-filter` anywhere in it — the stack is the only blur on it. */
  background: rgba(233, 237, 242, .22);
  border: 2px solid rgba(233, 237, 242, .92);
}
.tdb-hs:active { cursor: grabbing; }
.tdb-hs::after { content: ""; position: absolute; inset: 30%; border-radius: 50%; background: rgba(233, 237, 242, .92); }
.tdb-hs--s { --hs: 28px; }
.tdb-hs--m { --hs: 36px; }
.tdb-hs--l { --hs: 46px; }
.tdb-hs--chevron { border-radius: var(--tdb-r); }
.tdb-hs--chevron::after { inset: 25%; border-radius: 3px; transform: rotate(45deg); }
.tdb-hs--pill {
  width: calc(var(--hs) * 1.45); height: calc(var(--hs) * .72); border-radius: 999px;
  margin: calc(var(--hs) * -.36) 0 0 calc(var(--hs) * -.725);
}
.tdb-hs--pill::after { inset: 22% 42%; border-radius: 999px; }
.tdb-hs--minimal { background: none; border-color: rgba(233, 237, 242, .75); }
.tdb-hs--minimal::after { inset: 38%; }
/* Selected: the same yellow a selected tile wears on the plane. */
.tdb-hs.is-sel { border-color: var(--tdb-sel); }
.tdb-hs.is-sel::after { background: var(--tdb-sel); }
/* Placed, waiting to be told which room. Bright grey, because it is the live tool. */
.tdb-hs.is-new { cursor: default; border-style: dashed; border-color: var(--tdb-active); animation: tdb-blink 1s ease-in-out infinite; }
.tdb-hs.is-new::after { background: var(--tdb-active); }
@keyframes tdb-blink { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .tdb-hs.is-new { animation: none; } }
/* THE ROOM NAME OVER THE PHOTOGRAPH. It never trusts the picture underneath
   it: a name in light type on a white kitchen is a name nobody can read, and
   this is the tag that says where a pointer goes. Its own dark ground, the
   board's own corner. */
.tdb-hs-lbl {
  position: absolute; left: 0; bottom: calc(var(--hs, 36px) / 2 + 9px); transform: translateX(-50%);
  white-space: nowrap; pointer-events: none;
  font-size: 10.5px; color: var(--tdb-ink); padding: 3px 8px; border-radius: var(--tdb-r);
  /* A SOLID GROUND, NOT A BLUR. This label is the one tag on the screen that
     says where a way out goes, it is small, and it stands directly on the
     photograph. A blurred white kitchen is still a white kitchen: what makes
     it readable is the ground, so it wears the ground alone. */
  background: var(--tdb-glass-deep);
}
/* THE TAP CIRCLE, drawn only here. The buyer page never draws it — this ring
   exists so the person setting it can see the size he is setting. */
.tdb-hs-hit {
  position: absolute; left: 50%; top: 50%;
  width: calc(10px * var(--hit, 6)); height: calc(10px * var(--hit, 6));
  transform: translate(-50%, -50%);
  border: 1px dashed rgba(233, 237, 242, .45);
  border-radius: 50%; pointer-events: none;
}


/* ═══════════════════════════════════════════════════════════════════════════
 * CS-TOUR-05, SIXTH PASS
 *
 * The sphere doing the whole everyday job: a toolbar on the selected pointer,
 * the inspector filled out, and the rooms rail down the left.
 *
 * Same laws and not one new token: glass, no frames, no shadows, no outlines,
 * radius 6, Geist. Bright grey #d9dce1 is the active state only, CS yellow #fff083
 * is selection, and red is danger only — which is why an armed Remove here
 * wears the same yellow the plane's own armed controls wear.
 * ═════════════════════════════════════════════════════════════════════════ */

/* ---------- one hotspot, four parts ----------
   The marker, its tap circle, its label and its toolbar all hang off ONE
   element pinned at the pointer's yaw and pitch, so every one of them follows
   it as it is dragged with no code to make them. The wrapper has no size of
   its own: its origin IS the point on the photo. */
.tdb-hswrap { position: relative; width: 0; height: 0; }

/* ---------- the toolbar on the selected pointer ----------
   Three small round glass buttons, under the marker, following it. The fast
   path to the three things done most; the inspector is the full one. */
.tdb-hsbar {
  position: absolute; left: 0; top: calc(var(--hs, 36px) / 2 + 10px);
  transform: translateX(-50%);
  display: flex; gap: 5px; padding: 4px; border-radius: 999px;
  /* The wash alone — see the note on `.tdb-hs` above: this is inside a hotspot,
     which is inside a 3D transform, and the Worker already ruled that blur out
     on that surface. Deep enough to read over the brightest room in the tour. */
  background: var(--tdb-glass-deep);
}
.tdb-mini {
  width: 22px; height: 22px; flex: 0 0 22px; padding: 0; border-radius: 50%;
  background: var(--tdb-ico); color: var(--tdb-ink-2);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s, color .12s;
}
.tdb-mini:hover { background: var(--tdb-ico-2); color: var(--tdb-ink); }
.tdb-mini svg { width: 13px; height: 13px; display: block; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* Armed, the same yellow every armed control on this board wears. */
.tdb-mini.is-warn { background: rgba(255, 240, 131, .18); color: var(--tdb-sel); }

/* ---------- style, as the styles themselves ----------
   Four glyphs instead of four words: each one is the tour's own marker drawn
   small, from the same rules that draw it in the photo. */
.tdb-glyphs { display: flex; gap: 5px; margin-top: 6px; }
.tdb-glyph {
  flex: 1; height: 34px; border-radius: var(--tdb-r);
  background: var(--tdb-glass-2);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s;
}
.tdb-glyph:hover { background: var(--tdb-glass-3); }
.tdb-glyph.is-on { background: rgba(217, 220, 225, .14); }
/* The marker inside a glyph is a picture of a marker: it does not centre
   itself on a point, it does not take a press, and it does not turn. */
.tdb-glyph .tdb-hs {
  --hs: 17px; --grot: 0deg;
  position: relative; margin: 0; pointer-events: none; cursor: inherit;
}
.tdb-glyph.is-on .tdb-hs { border-color: var(--tdb-active); }
.tdb-glyph.is-on .tdb-hs::after { background: var(--tdb-active); }

/* ---------- the label switch ---------- */
.tdb-check { display: flex; align-items: center; gap: 7px; margin-top: 7px; cursor: pointer; font-size: 11px; color: var(--tdb-ink-2); }
.tdb-check input { width: 13px; height: 13px; flex: 0 0 13px; accent-color: var(--tdb-active); cursor: pointer; }
.tdb-check span { min-width: 0; }
.tdb-lbtext { margin-top: 6px; }

/* The rotate reading doubles as its own reset: press it and the glyph is
   straight again. It is a button wearing the reading, not a second control. */
.tdb-zero { padding: 0 2px; cursor: pointer; transition: color .12s; }
.tdb-zero:hover { color: var(--tdb-active); }

/* ---------- the rooms rail is GONE (CS-TOUR-05, ninth pass) ----------
   It said every room twice: once down the left and once in the dock below.
   Rename, reorder, hide, show and open all live on the dock thumbnail now,
   behind one small menu, in both views — see `.tdb-item` above. The rail's
   whole stylesheet went with it, and the sphere's left corner is clear
   again, so the tag up there no longer has to give it room. */

/* A plain block inside the inspector needs the same air the headed ones get. */
.tdb-insp .tdb-mt { margin-top: 12px; }
/* Where the mini toolbar just sent you, for a beat. Bright grey, because it is
   the live thing on the panel, and it fades out on its own. */
.tdb-insp [data-blk].is-lit { border-radius: var(--tdb-r); background: rgba(217, 220, 225, .10); }
@media (prefers-reduced-motion: no-preference) {
  .tdb-insp [data-blk] { transition: background .35s; }
}

/* ═══════════════════════════════════════════════════════════════════════════
 * CS-TOUR-05, EIGHTH PASS
 *
 * The plan as what Dom says it is: a tool for saying where the tripod stood.
 * A row of the strip per floor, cards dragged between them, the heights
 * clustered into a proposal, a card that grows under the pointer, and a
 * pointer walked through in the sphere.
 *
 * Not one new colour and not one new radius. Bright grey #d9dce1 is the
 * active state, CS yellow #fff083 is selection and the thing in your hand,
 * red is danger and appears nowhere here. No emerald, no teal, no frames, no
 * shadows. The one outline this pass adds is on the nodes, and it is drawn in
 * the plane's own svg — see the note at the top of this file.
 * ═════════════════════════════════════════════════════════════════════════ */

/* ---------- the strip, one row per floor ----------
   Every storey on screen at once, named, each row scrolling on its own and
   collapsing on its own. The rows run downwards, top of the house at the top,
   which is how a house is drawn in section. The whole stack scrolls when a
   tall house needs more room than the strip may take from the plane. */
.tdb-striphead .tdb-hinttext { color: var(--tdb-ink-3); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tdb-striphead .tdb-spacer { flex: 1; }
.tdb-striphead .tdb-btn { height: 24px; font-size: 11.5px; flex: 0 0 auto; }

/* ---------- the dock does not eat the screen (ninth pass) ----------
   TWO ROWS TALL, and it scrolls past that. Three floors used to stack to
   about 400 px, close to half a laptop screen; capped, the whole dock is a
   little over 250 and moving between storeys is a scroll INSIDE it rather
   than a trip to the dropdown. The active floor is always the row scrolled
   into view — see `revealFloor()`.

   AND ONE PRESS FOLDS THE LOT. Folded, the head is all that is left: the
   count, the hint, Suggest floors, and the caret to open it again. The state
   is remembered, like every other collapsible surface here. */
.tdb-docktog {
  width: 16px; height: 16px; flex: 0 0 16px; padding: 0; border-radius: var(--tdb-r);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--tdb-ink-3); transition: background .12s, color .12s;
}
.tdb-docktog:hover { background: var(--tdb-glass-2); color: var(--tdb-ink); }
.tdb-strip.is-closed .tdb-docktog .tdb-caret { transform: rotate(0deg); }
.tdb-strip.is-closed .tdb-striphead { margin-bottom: 0; }
.tdb-strip.is-closed .tdb-floors { display: none; }
.tdb-floors {
  max-height: calc(var(--tdb-rowh) * var(--tdb-dockrows) + 4px);
  overflow-y: auto; overflow-x: hidden; scrollbar-width: thin;
}
.tdb-floors::-webkit-scrollbar { width: 5px; }
.tdb-floors::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); border-radius: 3px; }

.tdb-floor { border-radius: var(--tdb-r); padding: 2px 4px 4px; transition: background .12s; }
.tdb-floor + .tdb-floor { margin-top: 4px; }
/* The floor the plane is showing. The same bright grey every active thing on
   this board wears, and the same thing the dropdown in the bar is saying. */
.tdb-floor.is-active { background: rgba(217, 220, 225, .07); }
/* The row a card is being dragged onto. Yellow, because it is the thing the
   hand is pointed at, which is what yellow means everywhere here. */
.tdb-floor.is-drop { background: rgba(255, 240, 131, .12); }
.tdb-flhead { display: flex; align-items: center; gap: 6px; padding: 2px 2px 4px; }
.tdb-fltog {
  width: 16px; height: 16px; flex: 0 0 16px; padding: 0; border-radius: var(--tdb-r);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--tdb-ink-3); transition: background .12s, color .12s;
}
.tdb-fltog:hover { background: var(--tdb-glass-2); color: var(--tdb-ink); }
.tdb-floor.is-closed .tdb-caret { transform: rotate(0deg); }
.tdb-flname {
  padding: 0 4px; height: 18px; border-radius: var(--tdb-r);
  color: var(--tdb-ink-2); font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  transition: background .12s, color .12s;
}
.tdb-flname:hover { background: var(--tdb-glass-2); color: var(--tdb-ink); }
.tdb-floor.is-active .tdb-flname { color: var(--tdb-active); }
.tdb-fln { color: var(--tdb-ink-3); font-size: 9.5px; font-variant-numeric: tabular-nums; }
.tdb-floor.is-closed .tdb-items { display: none; }
.tdb-flempty { color: var(--tdb-ink-3); font-size: 10.5px; padding: 22px 6px; }
.tdb-item.is-drag { opacity: .38; }
/* Every card is draggable now — to the plane, or to another floor — so every
   card says so, and answers the hand the way the others do. A card already on
   the plane keeps the dimming that says so, and lifts far enough out of it to
   be read while it is being picked up. */
.tdb-item.is-placed { cursor: grab; }
.tdb-item.is-placed:hover { background: rgba(8, 9, 11, .78); transform: translateY(-2px); opacity: .72; }

/* ---------- the enlarged thumbnail ----------
   The same picture, bigger, floating beside the pointer. It is out of the
   strip's flow and takes no presses, so nothing moves and nothing under it
   becomes unreachable. */
.tdb-peek {
  position: absolute; z-index: 45; width: 300px; padding: 8px;
  border-radius: var(--tdb-r); pointer-events: none; display: none;
  background: var(--tdb-glass-deep);
}
.tdb-peek.is-on { display: block; }
.tdb-peek img {
  display: block; width: 100%; height: 141px; object-fit: cover;
  border-radius: var(--tdb-r); background: rgba(255, 255, 255, .06);
}
/* The caption rides right under a photograph that fills the panel, so it
   carries its own ground rather than the picture's spill. */
.tdb-peek b {
  display: block; margin-top: 5px; padding: 2px 6px; font-weight: 500; font-size: 11px;
  background: var(--tdb-under); border-radius: var(--tdb-r);
  color: var(--tdb-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- the floor proposal ----------
   A question, not an action. It names every scan that would move and where it
   would move from, and nothing is written until it is answered. */
/* It floats above the dock too, and rides with it. */
.tdb-propose {
  /* CENTRED WITHOUT A TRANSFORM (twelfth pass). `left: 50%` plus a translate
     puts the panel inside a transformed box, and a `backdrop-filter` under a
     transform is the one place browsers disagree — which is exactly where the
     stack blur now lives. Two zero offsets and automatic margins centre it the
     same way, in the same place, with nothing transformed. */
  position: absolute; left: 0; right: 0; margin: 0 auto;
  bottom: calc(var(--tdb-dockh) + var(--tdb-edge));
  z-index: 44; width: 440px; max-width: calc(100% - 32px);
  padding: var(--tdb-pad); display: none;
}
.tdb-propose.is-on { display: block; }
.tdb-propose .tdb-t { color: var(--tdb-ink-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.tdb-prolist { margin-top: 8px; max-height: 34vh; overflow-y: auto; scrollbar-width: thin; }
.tdb-prolist::-webkit-scrollbar { width: 5px; }
.tdb-prolist::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); border-radius: 3px; }
.tdb-prorow + .tdb-prorow { margin-top: 9px; }
.tdb-prohead { display: flex; align-items: baseline; gap: 8px; }
.tdb-prohead b { font-weight: 500; font-size: 11.5px; color: var(--tdb-ink); }
.tdb-prohead span { color: var(--tdb-ink-3); font-size: 9.5px; margin-left: auto; font-variant-numeric: tabular-nums; }
.tdb-pronames { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.tdb-proname {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 2px 7px; border-radius: var(--tdb-r); background: rgba(255, 255, 255, .05);
  font-size: 10.5px; color: var(--tdb-ink-2); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tdb-proname em { font-style: normal; color: var(--tdb-ink-3); font-size: 9px; }

/* ---------- walking ----------
   Walking, a press on a pointer goes through it. Every pointer keeps its own
   toolbar so editing one is still a single press: it is there under the hand
   the moment the pointer is under the hand, and out of the way otherwise. */
.tdb-vtag .tdb-walking { color: var(--tdb-active); }
.tdb.is-walk .tdb-hs { cursor: pointer; }
.tdb-hswrap.is-walk .tdb-hsbar { opacity: 0; transition: opacity .12s; }
.tdb-hswrap.is-walk:hover .tdb-hsbar,
.tdb-hswrap.is-walk.is-sel .tdb-hsbar { opacity: 1; }
/* The toolbar hangs ten pixels clear of the marker, and a hand crossing that
   gap must not put it away again on the way to it. This is that gap and
   nothing else: it is transparent, it is behind the marker, and it is no wider
   than the toolbar it is bridging to. */
.tdb-hswrap.is-walk::before {
  content: ""; position: absolute; left: 50%; transform: translateX(-50%);
  top: calc(var(--hs, 36px) / 2 - 2px); width: 88px; height: 16px;
}
@media (prefers-reduced-motion: reduce) { .tdb-hswrap.is-walk .tdb-hsbar { transition: none; } }


/* ═══════════════════════════════════════════════════════════════════════════
 * CS-TOUR-05, NINTH PASS
 *
 * Legibility over a photograph, the names on the plane, and the discs shrunk
 * by Option. Not one new colour and not one new radius: bright grey #d9dce1
 * is still the only active state, CS yellow #fff083 is still selection, red is
 * still danger and appears nowhere here.
 * ═════════════════════════════════════════════════════════════════════════ */

/* ---------- text over a 360 ----------
   IN THE SPHERE EVERY PANEL FLOATS OVER A PHOTOGRAPH. The plane is a dark
   table and a pale glass reads beautifully on it; a white kitchen is not a
   dark table, and the same pale glass over it becomes a white panel with grey
   writing on it. So in the sphere the glass keeps its blur and loses its
   brightness: the SAME material, given its own ground instead of borrowing the
   picture's. No frame, no shadow, no outline — only what the type stands on. */
.tdb.is-sphere .tdb-glass,
.tdb.is-sphere .tdb-grp { background: var(--tdb-glass-deep); }
/* THE DOCK OVER A 360. The buyer page's fall to black is enough for a page
   that is only looked at; the editor reads room names off these cards fifty
   times a day and the picture behind them can be a white kitchen. Same
   gradient, same direction, deeper — and the cards keep their own dark ground
   on top of it. */
.tdb.is-sphere .tdb-strip {
  background: linear-gradient(to top, rgba(8, 9, 11, .96), rgba(8, 9, 11, .88) 62%, rgba(8, 9, 11, .46));
}
/* HOW DARK, AND WHY THAT DARK. Measured against the worst case there is, a
   pure white room: at .82 the panel composites to about #343434, which puts
   the board's own ink at better than 10:1 and #9aa3ae at 4.9:1. At the .46
   this started on, the same panel came out mid-grey and the ink read 2.8:1,
   which is the bug Dom hit.
   The quiet third ink is the one tone that cannot be carried by a ground
   alone, so in the sphere it steps up to #8b95a3 — a value already on this
   board, not a new colour — and lands at 4.2:1, which is what it already
   reads on the dark plane. */
/* MEASURED AGAIN (twelfth pass). #8b95a3 was put here to lift the quiet ink
   off a bright panel, and on the deeper wash it is now the WORSE of the two:
   over the .88 ground it reads 3.5:1, while the board's own translucent white
   composites toward the ground it stands on and reads 6.2:1 at .68. A
   translucent ink cannot be washed out by a bright picture, because it never
   touches the picture — it touches the ground. So the quiet ink stays white
   and simply carries more of itself in the sphere. */
.tdb.is-sphere { --tdb-ink-3: rgba(246, 245, 243, .68); }
/* The info cloud is not inside a panel: it floats straight over the photo, so
   it carries its own ground rather than borrowing one. Same blue, taken down
   to where the copy on it reads over a white kitchen. */
.tdb.is-sphere .tdb-cloud { background: rgba(24, 43, 74, .92); }

/* ---------- the names on the plane ----------
   A chip under the pin saying which room it is: every node while Option is
   held, and the one under the pointer the rest of the time, which is what the
   wheel cycling a pile has to say out loud. Its own dark ground, like every
   other label on this board that stands over a picture. */
.tdb-tags { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.tdb-tag-name {
  position: absolute; transform: translateX(-50%);
  max-width: 148px; padding: 2px 7px; border-radius: var(--tdb-r);
  background: var(--tdb-under); color: var(--tdb-ink);
  font-weight: 500; font-size: 10px; line-height: 14px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* ---------- Option shrinks the discs ----------
   A display transform and nothing else: the pictures scale about their own
   centres, the pins do not move, and nothing on the record changes. It is done
   in the board's own script, on the same `transform` that already carries the
   heading and the turn of the plan, and DELIBERATELY WITHOUT A TRANSITION —
   the same property turns the plan under the grip, and a tile that eased into
   place would lag the hand turning it. There is nothing for CSS to add. */


/* ═══════════════════════════════════════════════════════════════════════════
 * CS-TOUR-05, ELEVENTH PASS: NAMING THE SCANS
 *
 * Fast rename, Sort and Manage scans: the three surfaces that carry the
 * everyday cost of a tour of fifty-odd scans. Not one new colour, not one new
 * radius, not one new font. Bright grey #d9dce1 is the active state and the
 * thing with the keyboard on it, CS yellow #fff083 is what is picked, red is
 * danger and appears nowhere here. Glass, no frames, no shadows, no outlines,
 * one 6 px corner.
 *
 * KEYBOARD FOCUS HAS TO BE VISIBLE, and `outline: 0` is set on everything on
 * this board. So a focused control here wears the SAME bright-grey wash an
 * active one wears, one step brighter. It is the board's own active state,
 * used for the one thing it already means: this is the live control.
 * ═════════════════════════════════════════════════════════════════════════ */

/* ---------- the bar makes room for itself ----------
   The floating groups have been growing with every pass and the row can now
   run past the right of a laptop, which puts the level dropdown out of reach.
   It is allowed to wrap, and the band every panel hangs from (--tdb-top2) is
   MEASURED off the bar by the board's own script rather than assumed. A bar
   that still fits on one line measures the same 34 it always did and the band
   comes out at exactly the 66 it has always been. */
.tdb-topbar { flex-wrap: wrap; row-gap: var(--tdb-gap); }

/* ---------- the round close ----------
   The buyer page's round icon button, the same one `.tdb-q` is: a white wash
   on the glass, no line and no shadow. Bigger, because it closes a whole
   screen rather than opening a paragraph. */
.tdb-x {
  width: 26px; height: 26px; flex: 0 0 26px; padding: 0; border-radius: 50%;
  background: var(--tdb-ico); color: var(--tdb-ink-2);
  font-size: 15px; line-height: 26px; text-align: center;
  transition: background .12s, color .12s;
}
.tdb-x:hover,
.tdb-x:focus-visible { background: var(--tdb-ico-2); color: var(--tdb-ink); }

/* ═══════════════════════════════ FAST RENAME ═══════════════════════════════
   One scan, big, in the middle of the screen. Over the plane, the dock and the
   bar, because while it is up it IS the screen. */
.tdb-fast {
  position: absolute; inset: 0; z-index: 47; display: none;
  align-items: center; justify-content: center; padding: 24px;
  background: rgba(8, 9, 11, .92);
}
.tdb-fast.is-on { display: flex; }
.tdb-fastbox {
  width: min(820px, 100%); max-height: 100%; overflow-y: auto;
  scrollbar-width: thin;
}
.tdb-fastbox::-webkit-scrollbar { width: 5px; }
.tdb-fastbox::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); border-radius: 3px; }
.tdb-fasttop { display: flex; align-items: center; gap: var(--tdb-gap); margin-bottom: 10px; }
.tdb-fastcount { font-size: var(--tdb-fs-title); font-weight: 600; letter-spacing: -.01em; color: var(--tdb-ink); }
.tdb-fastfloor { font-size: var(--tdb-fs-quiet); color: var(--tdb-ink-2); }
.tdb-fastid { font-size: 10px; color: var(--tdb-ink-3); font-variant-numeric: tabular-nums; }
/* The room, at a size a room can be told apart at. The card's own 132:62, so
   nothing about the shot is stretched to fill the space. */
.tdb-fastpic {
  display: block; width: 100%; aspect-ratio: 132 / 62; max-height: 44vh;
  object-fit: cover; border-radius: var(--tdb-r);
  background: rgba(255, 255, 255, .06);
}
.tdb-fastnow { margin-top: 10px; font-size: var(--tdb-fs-body); color: var(--tdb-ink-3); }
.tdb-fastnow b { font-weight: 600; color: var(--tdb-ink); }
.tdb-fastchips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
.tdb-fastchips .tdb-chip { height: 28px; font-size: 11.5px; }
/* The field is the fast path and it says so by being the biggest thing under
   the picture. It is the board's own glass at the board's own corner. */
.tdb-fastin {
  display: block; width: 100%; height: 40px; margin-top: 12px; padding: 0 13px;
  background: var(--tdb-glass-2); color: var(--tdb-ink);
  font-size: 15px; font-weight: 600; border-radius: var(--tdb-r);
}
.tdb-fastin::placeholder { color: var(--tdb-ink-3); font-weight: 500; }
.tdb-fastin:focus { background: var(--tdb-glass-3); }
/* The names this tour already uses. Down out of the field steps into them. */
.tdb-fastsug { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.tdb-fastsug:empty { margin-top: 0; }
.tdb-fastsugb { height: 24px; font-size: 11px; }
.tdb-fastacts { display: flex; align-items: center; gap: 6px; margin-top: 14px; }
.tdb-fasthint { color: var(--tdb-ink-3); font-size: var(--tdb-fs-quiet); line-height: 1.6; }
/* Focus, without an outline: the board's own active wash, one step up. */
.tdb-fast .tdb-chip:focus-visible,
.tdb-fast .tdb-btn:focus-visible { background: rgba(217, 220, 225, .22); color: var(--tdb-active); }

/* ═══════════════════════════════ SORT ══════════════════════════════════════
   The floor proposal's panel, in the floor proposal's place, for the same
   reason: it is a question, it shows the whole answer before it is given, and
   nothing is written until it is answered. */
.tdb-sort {
  position: absolute; left: 0; right: 0; margin: 0 auto;
  bottom: calc(var(--tdb-dockh) + var(--tdb-edge));
  z-index: 44; width: 520px; max-width: calc(100% - 32px);
  padding: var(--tdb-pad); display: none;
}
.tdb-sort.is-on { display: block; }
.tdb-sort .tdb-t { color: var(--tdb-ink-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.tdb-sortlist {
  margin-top: 9px; max-height: 30vh; overflow-y: auto; scrollbar-width: thin;
  display: flex; flex-wrap: wrap; gap: 4px; align-content: flex-start;
}
.tdb-sortlist::-webkit-scrollbar { width: 5px; }
.tdb-sortlist::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); border-radius: 3px; }
.tdb-sortname {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 2px 7px; border-radius: var(--tdb-r); background: rgba(255, 255, 255, .05);
  font-size: 10.5px; color: var(--tdb-ink-3); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tdb-sortname em { font-style: normal; font-size: 9px; color: var(--tdb-ink-3); font-variant-numeric: tabular-nums; }
/* The ones that would actually move. Bright grey, because they are what the
   panel is about; the ones staying put are quiet on purpose. */
.tdb-sortname.is-moved { background: rgba(217, 220, 225, .12); color: var(--tdb-ink); }
.tdb-sortname.is-moved em { color: var(--tdb-active); }

/* ═══════════════════════════ MANAGE SCANS ══════════════════════════════════
   Every scan on one screen, by floor, at a size a room can be read at. It sits
   BELOW the card menu in the stack, which is drawn on the board, so the one
   menu serves the dock and the grid without a second copy of it. */
.tdb-manage {
  position: absolute; inset: 0; z-index: 45; display: none;
  flex-direction: column;
  background: rgba(8, 9, 11, .92);
}
.tdb-manage.is-on { display: flex; }
.tdb-mgtop {
  display: flex; align-items: center; gap: var(--tdb-gap);
  padding: var(--tdb-edge) var(--tdb-edge) 10px; flex: 0 0 auto;
}
.tdb-mgtitle { font-size: var(--tdb-fs-title); font-weight: 600; letter-spacing: -.01em; color: var(--tdb-ink); }
.tdb-mgcount { font-size: var(--tdb-fs-quiet); color: var(--tdb-ink-3); font-variant-numeric: tabular-nums; }
.tdb-mgq { width: 200px; margin-top: 0; flex: 0 0 auto; }
.tdb-mgbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
  padding: 0 var(--tdb-edge) 10px; flex: 0 0 auto;
}
.tdb-mgbar .tdb-btn { height: 24px; font-size: 11.5px; }
.tdb-mgbar .tdb-chip { height: 24px; }
.tdb-mgpick { font-size: var(--tdb-fs-quiet); font-weight: 600; color: var(--tdb-sel); }
.tdb-mgmove { font-size: 10px; color: var(--tdb-ink-3); letter-spacing: .06em; text-transform: uppercase; padding: 0 3px 0 5px; }
.tdb-mghint { font-size: var(--tdb-fs-quiet); color: var(--tdb-ink-3); }
.tdb-mgbody {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: thin;
  padding: 0 var(--tdb-edge) var(--tdb-edge);
}
.tdb-mgbody::-webkit-scrollbar { width: 5px; }
.tdb-mgbody::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); border-radius: 3px; }
.tdb-mgempty { color: var(--tdb-ink-3); font-size: var(--tdb-fs-body); padding: 40px 4px; }
.tdb-mgfloor + .tdb-mgfloor { margin-top: 16px; }
.tdb-mghead { display: flex; align-items: center; gap: var(--tdb-gap); padding: 4px 2px 8px; }
.tdb-mghead b {
  font-weight: 500; font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--tdb-ink-2);
}
.tdb-mghead span { color: var(--tdb-ink-3); font-size: 9.5px; font-variant-numeric: tabular-nums; }
.tdb-mghead .tdb-btn { height: 22px; font-size: 11px; }
.tdb-mggrid {
  display: grid; gap: var(--tdb-th-gap);
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
/* THE DOCK'S CARD, BIGGER. Same proportion, same dark ground, same caption
   with the short id riding in front of the name, same one menu top right. */
.tdb-mgcard {
  position: relative; border-radius: var(--tdb-th-r);
  background: var(--tdb-under-2); padding: 1px; overflow: hidden;
  text-align: left; cursor: pointer;
  transition: background .12s, transform .2s ease;
}
.tdb-mgcard:hover { background: rgba(8, 9, 11, .78); transform: translateY(-2px); }
.tdb-mgcard:focus-visible { background: rgba(217, 220, 225, .14); }
.tdb-mgth {
  display: block; width: 100%; aspect-ratio: 132 / 62; object-fit: cover;
  background: rgba(255, 255, 255, .06);
}
.tdb-mgcard .tdb-nm {
  padding: 7px 10px; font-size: 12px; font-weight: 600; color: var(--tdb-ink);
  line-height: 15px; background: var(--tdb-under);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tdb-mgcard .tdb-id {
  display: inline-block; margin-right: 5px; font-weight: 700; opacity: .6;
  color: inherit; font-variant-numeric: tabular-nums;
}
.tdb-mgcard .tdb-nmin {
  display: block; width: 100%; height: 29px;
  padding: 7px 10px; font-size: 12px; font-weight: 600; color: var(--tdb-ink);
  line-height: 15px; background: var(--tdb-glass-3); border-radius: 0;
}
.tdb-mgcard .tdb-nmin::placeholder { color: var(--tdb-ink-3); }
.tdb-mgcard.is-editing { cursor: default; }
.tdb-mgcard.is-off .tdb-mgth { opacity: .38; }
/* PICKED. CS yellow, because that is what yellow means everywhere here: this
   is the thing your controls are pointed at. */
.tdb-mgcard.is-sel { background: rgba(255, 240, 131, .16); }
.tdb-mgcard.is-sel .tdb-nm { color: var(--tdb-sel); }
.tdb-mgcard:hover .tdb-cardmenu,
.tdb-mgcard.is-sel .tdb-cardmenu,
.tdb-mgcard:focus-within .tdb-cardmenu { opacity: 1; }
.tdb-manage .tdb-btn:focus-visible,
.tdb-manage .tdb-chip:focus-visible { background: rgba(217, 220, 225, .22); color: var(--tdb-active); }


/* ═══════════════════════════════════════════════════════════════════════════
 * CS-TOUR-05, TWELFTH PASS — DOM'S OWN LIST, IN HIS ORDER
 *
 * 1 contrast everywhere   2 no teal, inherited included   3 the dock is one row
 * 4 stack blur            5 rotate snaps                  6 levelling
 * 7 walk is the default   8 a live preview                9 hover peek
 *
 * Not one new colour. Bright grey #d9dce1 is the active state, CS yellow
 * #fff083 is selection, red is danger and appears nowhere here. No frames, no
 * shadows, no outlines, one 6 px corner, Geist.
 * ═════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════ 4 · THE CAPITAL SHOTS STACK BLUR ══════════════════
 * Glass on this board is NOT one `backdrop-filter`. It is the house material:
 * a run of masked blur rungs climbing from about 2 px to about 28 px, laid
 * under a light dark wash, so the picture behind a panel falls away smoothly
 * instead of ending on one flat sheet of frosting. Same construction as the
 * site's navbar skirt (`.cs-pblur-1…10` in cs-shell.css), sized for a panel
 * rather than a bar: the rungs are masked to bands that overlap down the
 * panel, so the edges keep the picture and the core loses it.
 *
 * WHY IT IS SIX SIBLING LAYERS AND NOT TWO PSEUDO-ELEMENTS. An element that
 * carries a `backdrop-filter` becomes a BACKDROP ROOT: a filter on anything
 * inside it then samples that element's own contents instead of the page
 * behind it, and the rungs would stack up to nothing. Siblings do not nest,
 * so each rung samples the same picture — and because each one paints over
 * the one before it, the blurs compound down the stack exactly as they do in
 * the navbar. The layers are inserted once, by the board's own script
 * (`glassify()`), so no markup in this file had to change and nothing that
 * rewrites a panel's contents can lose them.
 *
 * The wash lives on the panel itself and is painted BEFORE the rungs, so a
 * rung blurs the picture and the wash together. The wash is flat, so blurring
 * it changes nothing, and there is no doubling.
 *
 * NO EXCEPTIONS, AND NO SINGLE `backdrop-filter` LEFT ANYWHERE. The pointer
 * marker and its toolbar carried one; both are inside a Marzipano hotspot,
 * which is inside a 3D transform, and the Worker's own stylesheet already
 * ruled on exactly that surface: "No backdrop-filter: this element is inside a
 * 3D transform, where backdrop blur is unreliable across browsers and costs a
 * full-frame readback per ring." With walking on there is a toolbar under
 * every way out, so that readback would be paid several times a frame over a
 * live 360 to draw something half the browsers never drew. Both wear the wash
 * alone, deep enough to read on a white kitchen, which is the only thing the
 * blur was ever doing for them.
 *
 * So: every panel on this board is the stack, and the only `backdrop-filter`
 * declarations in this file are the six rungs below.                        */
.tdb-blur {
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; overflow: hidden; pointer-events: none;
}
.tdb-blur > i { position: absolute; inset: 0; display: block; }
/* Six rungs. Each one blurs less than the last and is masked to a wider band,
   so the stack fades out to the panel's edge instead of stopping on a line. */
.tdb-blur-1 { -webkit-backdrop-filter: blur(2px) saturate(1.06);  backdrop-filter: blur(2px) saturate(1.06); }
.tdb-blur-2 {
  -webkit-backdrop-filter: blur(4px) saturate(1.10); backdrop-filter: blur(4px) saturate(1.10);
  -webkit-mask-image: linear-gradient(#0000 0%, #000 7%, #000 93%, #0000 100%);
  mask-image: linear-gradient(#0000 0%, #000 7%, #000 93%, #0000 100%);
}
.tdb-blur-3 {
  -webkit-backdrop-filter: blur(7px) saturate(1.14); backdrop-filter: blur(7px) saturate(1.14);
  -webkit-mask-image: linear-gradient(#0000 4%, #000 15%, #000 85%, #0000 96%);
  mask-image: linear-gradient(#0000 4%, #000 15%, #000 85%, #0000 96%);
}
.tdb-blur-4 {
  -webkit-backdrop-filter: blur(12px) saturate(1.20); backdrop-filter: blur(12px) saturate(1.20);
  -webkit-mask-image: linear-gradient(#0000 10%, #000 25%, #000 75%, #0000 90%);
  mask-image: linear-gradient(#0000 10%, #000 25%, #000 75%, #0000 90%);
}
.tdb-blur-5 {
  -webkit-backdrop-filter: blur(19px) saturate(1.26); backdrop-filter: blur(19px) saturate(1.26);
  -webkit-mask-image: linear-gradient(#0000 18%, #000 35%, #000 65%, #0000 82%);
  mask-image: linear-gradient(#0000 18%, #000 35%, #000 65%, #0000 82%);
}
.tdb-blur-6 {
  -webkit-backdrop-filter: blur(28px) saturate(1.32); backdrop-filter: blur(28px) saturate(1.32);
  -webkit-mask-image: linear-gradient(#0000 28%, #000 45%, #000 55%, #0000 72%);
  mask-image: linear-gradient(#0000 28%, #000 45%, #000 55%, #0000 72%);
}
/* A negative-z layer only stays inside its own panel where that panel is a
   stacking context. Every panel on this board already carries a z-index; the
   floating control groups did not, so they are given the one number that makes
   them a context without moving them: zero. */
.tdb-grp { position: relative; z-index: 0; }
/* No backdrop-filter at all (an older engine, or it is switched off), and the
   reader who has asked for less transparency: the wash was always there and it
   is enough on its own, so the rungs simply go. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tdb-blur { display: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .tdb-blur { display: none; }
}


/* ═══════════════════════ 2 · NO TEAL, INHERITED INCLUDED ═══════════════════
 * A slider, a checkbox and a focus ring take their colour from `accent-color`,
 * which INHERITS. Left unset, this board wears whatever the shell sets — which
 * is the site emerald — and Dom watched a rotate slider draw a green ring.
 * So it is declared here on the board itself and again on every control that
 * can read it, and every focus, active and checked state is written out.
 * There is no #3EE092 and no rgb(62, 224, 146) in this file, at any opacity. */
.tdb,
.tdb input,
.tdb select,
.tdb button,
.tdb textarea,
.tdb progress,
.tdb input[type=range],
.tdb input[type=checkbox],
.tdb input[type=radio] {
  accent-color: var(--tdb-active);
  caret-color: var(--tdb-ink);
  color-scheme: dark;
}
.tdb ::selection { background: rgba(255, 240, 131, .28); color: var(--tdb-ink); }
/* FOCUS, WITHOUT AN OUTLINE. `outline: 0` is set on everything here, so a
   focused control wears the board's own active wash one step brighter — the
   convention the eleventh pass set for Fast rename and Manage scans, now
   applied to every control on the board rather than to those two screens. */
.tdb .tdb-btn:focus-visible,
.tdb .tdb-chip:focus-visible,
.tdb .tdb-glyph:focus-visible,
.tdb .tdb-pick:focus-visible,
.tdb .tdb-cardpop button:focus-visible,
.tdb .tdb-flname:focus-visible,
.tdb .tdb-fltog:focus-visible,
.tdb .tdb-docktog:focus-visible,
.tdb .tdb-mini:focus-visible,
.tdb .tdb-zero:focus-visible,
.tdb .tdb-pvtog:focus-visible { background: var(--tdb-focus); color: var(--tdb-active); }
.tdb .tdb-q:focus-visible { background: var(--tdb-ico-2); }
.tdb .tdb-filter:focus-visible,
.tdb .tdb-fastin:focus-visible,
.tdb .tdb-nmin:focus-visible,
.tdb .tdb-lvl:focus-visible { background: var(--tdb-focus); color: var(--tdb-ink); }
.tdb .tdb-lvl:focus { background: var(--tdb-focus); }
/* The three states a control can be in, all written. Active is bright grey,
   picked is CS yellow, and neither of them is ever a browser default. */
.tdb .tdb-btn:active,
.tdb .tdb-chip:active,
.tdb .tdb-mini:active { background: var(--tdb-focus); color: var(--tdb-active); }
/* ── THE SLIDERS ──────────────────────────────────────────────────────────
 * Every part of a range control is drawn here: the track, the part of the
 * track behind the thumb (Firefox fills that with `accent-color` on its own),
 * the thumb, and what the thumb looks like under the hand and under the
 * keyboard. Nothing is left for a browser to colour in. */
.tdb input[type=range] { accent-color: var(--tdb-active); }
.tdb input[type=range]::-webkit-slider-runnable-track { background: var(--tdb-track); }
.tdb input[type=range]::-moz-range-track { background: var(--tdb-track); }
.tdb input[type=range]::-moz-range-progress { height: 3px; border-radius: 2px; background: rgba(217, 220, 225, .45); }
.tdb input[type=range]::-webkit-slider-thumb { background: var(--tdb-ink); }
.tdb input[type=range]::-moz-range-thumb { background: var(--tdb-ink); }
.tdb input[type=range]:hover::-webkit-slider-thumb { background: var(--tdb-active); }
.tdb input[type=range]:hover::-moz-range-thumb { background: var(--tdb-active); }
/* Focus is the thumb growing into the active grey, not a ring around it. */
.tdb input[type=range]:focus-visible::-webkit-slider-thumb { width: 14px; height: 14px; margin-top: -5.5px; background: var(--tdb-active); }
.tdb input[type=range]:focus-visible::-moz-range-thumb { width: 14px; height: 14px; background: var(--tdb-active); }
/* Under the hand, the thumb is the thing being moved, so it is the selection
   colour for as long as it is being moved. */
.tdb input[type=range]:active::-webkit-slider-thumb { background: var(--tdb-sel); }
.tdb input[type=range]:active::-moz-range-thumb { background: var(--tdb-sel); }
/* The checkbox, said out loud in both states. */
.tdb input[type=checkbox] { accent-color: var(--tdb-active); }
.tdb input[type=checkbox]:checked { accent-color: var(--tdb-active); }
.tdb input[type=checkbox]:focus-visible { accent-color: var(--tdb-sel); }
.tdb .tdb-check input:focus-visible { accent-color: var(--tdb-sel); }
/* A select's own list is drawn by the browser; both ends of it are set. */
.tdb-lvl option:checked { background: #14161a; color: var(--tdb-active); }


/* ═══════════════════════ 1 · CONTRAST, EVERYWHERE ══════════════════════════
 * Every piece of type that can end up over a photograph gets its OWN GROUND.
 * The worst case on this board is a white kitchen — as a dock thumbnail, as a
 * top-down floor render, as a little planet, and as the whole 360 behind the
 * sphere — and light type straight onto it is not type. This is a legibility
 * bug and it has been raised twice.
 *
 * WHAT WAS WASHED OUT AND WHAT IT WEARS NOW:
 *   the dock head line and its hint   their own dark chips
 *   the floor name and its count      their own dark chips
 *   the needle's reading              its own dark chip
 *   the needle's own "N"              a dark casing, drawn in the plane's svg
 *   the ring around every disc        a dark casing ring under the light one
 *   every panel                       a deeper wash under the stack blur      */
.tdb-striphead .tdb-count,
.tdb-striphead .tdb-hinttext {
  padding: 2px 8px; border-radius: var(--tdb-r); background: var(--tdb-under);
}
.tdb-striphead .tdb-hinttext { color: var(--tdb-ink-2); }
.tdb-flname,
.tdb-fln {
  background: var(--tdb-under);
  padding: 2px 7px; border-radius: var(--tdb-r);
}
.tdb-flname:hover { background: var(--tdb-glass-3); color: var(--tdb-ink); }
.tdb-floor.is-active .tdb-flname { background: rgba(217, 220, 225, .20); color: var(--tdb-active); }
.tdb-flempty {
  background: var(--tdb-under); border-radius: var(--tdb-r);
  padding: 22px 10px; color: var(--tdb-ink-2);
}
.tdb-northtag .tdb-val {
  padding: 1px 7px; border-radius: var(--tdb-r); background: var(--tdb-under);
}
/* The board's own text drawn INTO the plane's svg — the needle's "N" — cannot
   wear a CSS ground, so it wears a dark casing painted under the letter. Set
   here because `paint-order` is honoured on the element and the attribute for
   it is easy to lose in a render string. */
.tdb-gfx text { paint-order: stroke fill; stroke-linejoin: round; }
/* THE SAVE STATE is the one line on the board that must never be missed. */
.tdb-save { padding: 1px 8px; border-radius: var(--tdb-r); background: var(--tdb-under); }
.tdb-save:empty { background: none; padding: 0; }


/* ═══════════════════════ 3 · THE DOCK IS ONE ROW ═══════════════════════════
 * It opens showing ONLY the floor you are standing on. One press stacks every
 * floor, which is when a scan is dragged onto another one to re-file it, and
 * a second press folds them away again. Which of the two it was left in is
 * remembered, like every other collapsible surface here.
 *
 * The caret that folds the WHOLE dock to a thin bar is untouched and still
 * sits where it always did; this is a second, smaller control beside it, and
 * the dragging between floors is the same drag it has always been.           */
.tdb-dockall {
  height: 18px; padding: 0 8px; flex: 0 0 auto; border-radius: var(--tdb-r);
  background: var(--tdb-under); color: var(--tdb-ink-2);
  font-size: 10px; letter-spacing: .04em; text-transform: uppercase; font-weight: 600;
  display: inline-flex; align-items: center; gap: 5px;
  transition: background .12s, color .12s;
}
.tdb-dockall:hover { background: var(--tdb-glass-3); color: var(--tdb-ink); }
.tdb-dockall.is-on { background: rgba(217, 220, 225, .20); color: var(--tdb-active); }
/* Showing one floor, the row head has nothing left to say that the dropdown in
   the bar is not already saying, so the row loses its own ground and its
   caret. Nothing is removed: stack the floors and both are back. */
.tdb-strip.is-onefloor .tdb-floor { background: none; }
.tdb-strip.is-onefloor .tdb-fltog { display: none; }


/* ═══════════════════════ 5 · ROTATE SNAPS ══════════════════════════════════
 * The angular controls move in 15° steps so the angles stay clean, and Shift
 * gives fine control. The reading beside a snapped control says which of the
 * two it is on, so there is never a doubt about why a number jumped.         */
.tdb-fine {
  margin-left: auto; font-size: 9px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--tdb-ink-3);
}
.tdb-fine.is-on { color: var(--tdb-active); }


/* ═══════════════════════ 6 · LEVELLING, IN THE SPHERE ══════════════════════
 * A crooked scan, put straight. A horizon guide is laid across the picture and
 * turned until it lies along the room's own horizon; the two numbers under it
 * are the scan's own pitch and roll and they are written to the scan's own
 * fields. The guide takes no presses and the photograph is never transformed:
 * what is on screen is always the real 360.                                  */
.tdb-horizon { position: absolute; inset: 0; z-index: 2; pointer-events: none; display: none; }
.tdb-sphere.is-level .tdb-horizon { display: block; }
/* It stands where the floor proposal and the sort panel stand on the plan:
   bottom centre, clear of the dock and clear of the peek's own corner, because
   it is the same kind of thing — a tool that is open while it is being used
   and gone the moment it is not. */
.tdb-level {
  position: absolute; left: 0; right: 0; margin: 0 auto;
  bottom: calc(var(--tdb-dockh) + var(--tdb-edge));
  z-index: 37; width: 300px; max-width: calc(100% - 32px);
  padding: var(--tdb-pad); display: none;
}
.tdb-level.is-on { display: block; }
.tdb-level .tdb-t { color: var(--tdb-ink-3); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.tdb-level .tdb-rowhead { margin-top: 12px; }
.tdb-level .tdb-acts { margin-top: 14px; }


/* ═══════════════════════ 8 · THE LIVE PREVIEW ══════════════════════════════
 * The panel stops being a thumbnail and becomes the BUYER PAGE, keyed, opened
 * on the room in view and following it. Same panel, same corner, same drag to
 * size it and the same remembered width.
 *
 * IT CANNOT TAKE THE KEYBOARD. A live page in a frame that can be clicked can
 * take focus, and every key on this board would then be going somewhere else.
 * So the frame takes no pointer at all — `pointer-events: none`, `tabindex`
 * out of the order, and the board's own script pushes focus back out of it if
 * it ever lands there. It is a LOOK. Hands on the real tour is Preview, which
 * opens it in its own tab, and that control already exists.                  */
.tdb-pvframe {
  display: none; width: 100%; border: 0; border-radius: var(--tdb-r);
  background: #111; pointer-events: none;
}
.tdb-preview.is-live .tdb-pvframe { display: block; }
.tdb-preview.is-live .tdb-pv { display: none; }
.tdb-pvbar { display: flex; align-items: center; gap: 5px; margin-top: 7px; }
.tdb-pvtog {
  height: 19px; padding: 0 8px; border-radius: var(--tdb-r);
  background: var(--tdb-glass-2); color: var(--tdb-ink-2);
  font-size: 10px; letter-spacing: .04em; text-transform: uppercase; font-weight: 600;
  transition: background .12s, color .12s;
}
.tdb-pvtog:hover { background: var(--tdb-glass-3); color: var(--tdb-ink); }
.tdb-pvtog.is-on { background: rgba(217, 220, 225, .20); color: var(--tdb-active); }
.tdb-pvnote { font-size: 9.5px; color: var(--tdb-ink-3); margin-left: auto; }


/* ═══════════════════════ 9 · THE PEEK, ON A WAY OUT ════════════════════════
 * ONE PANEL, TWO JOBS. Rest on a way out in the sphere and it shows the ROOM
 * IT LEADS TO; rest on a card in the dock and it shows that scan. Either way
 * what it shows is the 360 ITSELF — the whole room in one picture — and not
 * the thumbnail, which is the crop that made him open the scan to find out.
 *
 * It is dragged where he wants it and sized how he wants it, and it opens
 * there next time. The picture holds 2:1 at every width because that is what
 * an equirectangular 360 is; the thumbnail stands behind it until the big
 * picture lands, so the panel is never blank.                                */
.tdb-peek {
  width: 300px; padding: 8px; pointer-events: auto; cursor: move; touch-action: none;
}
.tdb-peek img {
  display: block; width: 100%; height: auto; aspect-ratio: 2 / 1; object-fit: cover;
  border-radius: var(--tdb-r); background: rgba(255, 255, 255, .06) center / cover no-repeat;
}
.tdb-peek b {
  display: block; margin-top: 6px; padding: 2px 7px; font-weight: 500; font-size: 11px;
  background: var(--tdb-under); border-radius: var(--tdb-r);
  color: var(--tdb-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tdb-peek .tdb-pksub {
  display: block; margin-top: 3px; padding: 1px 7px; font-size: 9.5px;
  background: var(--tdb-under); border-radius: var(--tdb-r); color: var(--tdb-ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tdb-peek .tdb-pksub:empty { display: none; }
/* Sized from the bottom right, the way the preview is sized from the top left:
   an invisible grab zone in the corner and the cursor over it, nothing drawn. */
.tdb-peek .tdb-pkgrab {
  position: absolute; right: 0; bottom: 0; width: 18px; height: 18px; z-index: 2;
  cursor: nwse-resize; touch-action: none;
}
/* Resting ON a way out is what puts the panel up, so the marker says so. */
.tdb-hs.is-peek { border-color: var(--tdb-active); }


/* ── THE CARET WAS NEVER DRAWN ──────────────────────────────────────────────
 * `<i class="tdb-caret">` has been in the dock's fold control and on every
 * floor head since the eighth pass, and this sheet has only ever said how to
 * TURN it — `.is-closed .tdb-caret { transform: rotate(0deg) }` — never what it
 * looks like. An empty inline element has no size, so both controls have been
 * invisible boxes: a real dead control, and exactly the kind of thing the
 * contrast sweep is for.
 *
 * It is a chevron cut out of a solid block with `clip-path`, not a border
 * triangle: the board's own reset sets `border: 0` on everything, and a shape
 * made of borders would be a frame by another name. `currentColor` means it
 * takes the control's own ink and its hover with it, so nothing new is
 * declared. It points DOWN at rest, which is what the two existing rules
 * already assume: they turn it back to zero when the surface is closed, so the
 * open state is the turned one. */
.tdb-caret {
  display: block; width: 9px; height: 9px; flex: 0 0 9px;
  background: currentColor;
  clip-path: polygon(12% 30%, 50% 62%, 88% 30%, 88% 48%, 50% 80%, 12% 48%);
  transform: rotate(180deg);
  transition: transform .12s;
}
@media (prefers-reduced-motion: reduce) { .tdb-caret { transition: none; } }


/* ═══════════ 10 · EVERY LOOK THE VIEWER DRAWS, AND A PEEK THAT IS A ROOM ═══
 * CS-TOUR-05, thirteenth pass. Two things Dom could not reach from the editor:
 * the arrow set and the room glyphs the buyer page has been drawing since
 * CS-TOUR-07, and a peek that shows a room instead of a bent picture of one.
 *
 * Same laws and not one new token: the stack blur is the only blur, no frames,
 * no shadows, no outlines, radius 6, Geist. Bright grey #d9dce1 is the active
 * state, CS yellow #fff083 is selection, red is danger only, and there is no
 * emerald and no teal anywhere in it.
 * ═════════════════════════════════════════════════════════════════════════ */

/* ---------- the style picker, in three rows ----------
   Nineteen looks will not stretch across one row, so they are grouped the way
   they are chosen: the arrows, then the rooms, then the classic marks. Each
   glyph is a fixed square rather than a share of the row, so a row of two and a
   row of thirteen draw the same size of marker. */
.tdb-glyphset { margin-top: 2px; }
.tdb-glyphgrp + .tdb-glyphgrp { margin-top: 10px; }
.tdb-glyphhead {
  display: block; color: var(--tdb-ink-3);
  font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 600;
}
.tdb-glyphs { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.tdb-glyph { flex: 0 0 34px; width: 34px; height: 34px; }

/* ---------- a line glyph, in the picker and in the photograph ----------
   The arrow set and the room glyphs are DRAWN, not filled: one stroke weight,
   rounded ends, no fill, exactly as the buyer page draws them. The glass mark's
   own fill and its inner dot are turned off under them so nothing shows
   through the drawing.

   OVER A PHOTOGRAPH IT CARRIES ITS OWN GROUND. A thin white line on a white
   kitchen is a line nobody can see, and this is the marker that says where a
   way out goes. The ground is a soft wash that fades to nothing at the rim,
   which is what the buyer page puts under the same glyph, and never a shadow
   and never a frame. */
.tdb-hs--line {
  border-color: transparent;
  background: radial-gradient(circle at 50% 50%,
    rgba(8, 9, 11, .62) 0%, rgba(8, 9, 11, .44) 56%, rgba(8, 9, 11, 0) 78%);
}
.tdb-hs--line::after { display: none; }
.tdb-hs--line svg {
  position: absolute; left: 12%; top: 12%; width: 76%; height: 76%; display: block;
  fill: none; stroke: rgba(233, 237, 242, .95);
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
/* Selected: the same yellow every selected thing on this board wears. On a line
   glyph the colour IS the stroke, because there is no fill to carry it. */
.tdb-hs--line.is-sel { border-color: transparent; }
.tdb-hs--line.is-sel svg { stroke: var(--tdb-sel); }
/* Resting on it, the same bright grey the glass marker takes. */
.tdb-hs--line.is-peek { border-color: transparent; }
.tdb-hs--line.is-peek svg { stroke: var(--tdb-active); }
/* In the picker it stands on the button's own glass, so it needs no ground of
   its own and can be drawn larger: a room glyph at 17 px is a smudge. */
.tdb-glyph .tdb-hs--line { --hs: 25px; background: none; }
.tdb-glyph.is-on .tdb-hs--line { border-color: transparent; }
.tdb-glyph.is-on .tdb-hs--line svg { stroke: var(--tdb-active); }

/* ---------- the peek is a little 360 ----------
   The thumbnail is the panel's GROUND and the live panorama is drawn over it,
   so the panel is never blank while the room lands and never a broken box if it
   will not land at all. The picture holds 2:1 at every width because that is
   the shape an equirectangular file is stored in, and the panorama fills it.

   NO FRAME AND NO SHADOW. The panel is the board's own stack blur under its own
   wash, which it already wears as `.tdb-glass`. */
.tdb-pkpic { position: relative; }
.tdb-pkview {
  position: absolute; inset: 0; z-index: 1;
  border-radius: var(--tdb-r); overflow: hidden; touch-action: none;
}
/* The room is only draggable once there is a room in there to drag. */
.tdb-peek.is-live .tdb-pkview { cursor: grab; }
.tdb-peek.is-live .tdb-pkview:active { cursor: grabbing; }

/* ---------- the x that puts it down ----------
   It STAYS until this is pressed, so this is the control that has to be there.
   Its own ground, because it sits on the photograph. */
.tdb-pkx {
  position: absolute; right: 6px; top: 6px; z-index: 3;
  width: 20px; height: 20px; padding: 0; border-radius: var(--tdb-r);
  background: var(--tdb-under); color: var(--tdb-ink-2);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s, color .12s;
}
.tdb-pkx:hover { background: var(--tdb-glass-deep); color: var(--tdb-ink); }
.tdb-pkx svg {
  width: 12px; height: 12px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round;
}
.tdb .tdb-pkx:focus-visible { background: var(--tdb-focus); color: var(--tdb-active); }
