.btn.fin-btn{display:inline-flex;align-items:center;gap:7px;padding-left:9px}
.fin-btn i{width:15px;height:15px;border-radius:50%;background:var(--c,#888);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.18)}
.fin-btn .rf-nm{color:var(--ink)}
.fin-btn .rf-nm:not(:empty)::before{content:"·";margin:0 6px 0 0;color:var(--line);font-weight:400}
/* --muted2 is a decorative tone only: at 3.2-3.7:1 on these panels it fails
   WCAG AA for small text, and the type it was used on (product code, variant,
   counts, the hint) is exactly the type people need to read. Informational
   small text uses --muted, which measures 5.7-6.6:1. */
:root{
  --bg:#0d0e10; --panel:#16171b; --panel2:#1c1e23; --line:rgba(198,161,91,.22);
  --line-soft:rgba(255,255,255,.08); --gold:#c6a15b; --gold2:#e3c98f; --ink:#f4f1ea;
  --muted:#9a978f; --muted2:#6f6d67; --serif:"Cormorant Garamond",Georgia,serif;
  --sans:"Manrope",Arial,sans-serif;
  /* one type scale — every size in the UI comes from here */
  --t-micro:9.5px; --t-label:10.5px; --t-small:11.5px; --t-body:12.5px;
  --t-lead:14px; --t-title:17px;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{font-family:var(--sans);background:var(--bg);color:var(--ink);overflow:hidden;-webkit-font-smoothing:antialiased}
button{font-family:inherit;color:inherit;cursor:pointer;border:none;background:none}
input{font-family:inherit}
/* display:flex/grid on .menu and .modal would otherwise beat the hidden attribute */
[hidden]{display:none !important}

/* keyboard focus is visible everywhere, and only for keyboard users */
:focus{outline:none}
:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:6px}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

#app{display:grid;grid-template-columns:288px minmax(0,1fr);grid-template-rows:60px 1fr;height:100vh;height:100dvh}

/* ── topbar ──────────────────────────────────────────────────────────────
   Three groups with air between them: Room · View · Design. Everything that
   isn't a per-session choice (auto-arrange, clear) lives behind the ··· menu,
   so nothing in the bar competes with picking a product.                   */
.topbar{grid-column:1/-1;display:flex;align-items:center;gap:8px;padding:0 20px;
  background:rgba(13,14,16,.82);border-bottom:1px solid var(--line-soft);backdrop-filter:blur(12px);z-index:20}
.brand{display:flex;align-items:center;gap:11px}
/* the mark is a wide lockup (707x268); it is sized by HEIGHT so the bar's rhythm
   holds, and the width follows. `display:block` on the img kills the baseline gap. */
.brand .logo{display:block;line-height:0}
.brand .logo img{display:block;height:30px;width:auto}
.brand .sub{font-size:9px;letter-spacing:.36em;color:var(--muted);text-transform:uppercase;
  padding-top:2px}
.topbar .spacer{flex:1}

.group{display:flex;align-items:center;gap:7px;padding-left:18px;position:relative}
.group + .group::before{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);
  width:1px;height:22px;background:var(--line-soft)}
.group.is-empty{display:none}
.glabel{font-size:9px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);
  margin-right:2px;white-space:nowrap}

.wall-tabs{display:flex;background:var(--panel);border:1px solid var(--line-soft);border-radius:30px;padding:3px}
.wall-tabs button{padding:6px 13px;border-radius:24px;font-size:var(--t-body);color:var(--muted);transition:.25s}
.wall-tabs button:hover{color:var(--ink)}
.wall-tabs button.on{background:rgba(255,255,255,.07);color:var(--ink);font-weight:600;
  box-shadow:inset 0 0 0 1px var(--line)}

/* room finish switcher */
.themes{display:flex;gap:3px;background:var(--panel);border:1px solid var(--line-soft);
  border-radius:30px;padding:3px}
.themes button{display:flex;align-items:center;gap:6px;padding:5px 12px 5px 6px;border-radius:24px;
  font-size:var(--t-body);color:var(--muted);transition:.25s;white-space:nowrap}
.themes button i{width:15px;height:15px;border-radius:50%;background:var(--c);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22),0 1px 3px rgba(0,0,0,.5);flex:none}
.themes button:hover{color:var(--ink)}
.themes button.on{background:rgba(255,255,255,.07);color:var(--ink);font-weight:600;
  box-shadow:inset 0 0 0 1px var(--line)}

/* ceiling colour: swatches only. The dots ARE the choice — a row of six names
   would have doubled the width of the bar to say what the colour already says.
   Each one still carries its name for a hover title and a screen reader. */
.ceil{display:flex;align-items:center;gap:2px;background:var(--panel);
  border:1px solid var(--line-soft);border-radius:30px;padding:3px}
.ceil .ct-lbl{display:none}
.ceil button{display:flex;align-items:center;padding:3px;border-radius:50%;transition:.25s}
.ceil button i{width:13px;height:13px;border-radius:50%;background:var(--c);transition:transform .2s;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22),0 1px 3px rgba(0,0,0,.5);flex:none}
.ceil button:hover i{transform:scale(1.1)}
.ceil button.on{box-shadow:inset 0 0 0 1.5px var(--gold)}
.ceil button:focus-visible{outline:2px solid var(--gold);outline-offset:1px}

.btn{padding:8px 15px;border-radius:24px;font-size:var(--t-body);letter-spacing:.03em;transition:.25s;white-space:nowrap}
.btn.ghost{border:1px solid var(--line-soft);color:var(--muted)}
.btn.ghost:hover{color:var(--ink);border-color:var(--line)}
.btn.ghost.on{color:var(--ink);border-color:var(--line);background:rgba(198,161,91,.13)}
.btn.gold{background:linear-gradient(135deg,var(--gold),#a9803c);color:#1a140a;font-weight:600}
/* Reset all. It empties the room, so it should not look like Spec sheet — but it
   is also the documented way out of the finish lock, so it must not look scary
   enough to avoid either. Quiet until you reach for it. */
.btn.warn{border:1px solid var(--line-soft);color:var(--muted)}
.btn.warn:hover{color:#e9b4ac;border-color:rgba(201,106,92,.55);background:rgba(201,106,92,.10)}
.btn.icon{padding:8px 12px;letter-spacing:.12em;line-height:1}
/* The room's finish, worn on the button that changes it. The dot is the answer
   to "what colour is this room?" — the same 15px dot the room tabs use, so the
   two read as the same kind of control.
   IT SAYS "FINISH" AND THEN THE COLOUR, AND THE WORD IS NOT OPTIONAL. Wearing
   the colour name alone ("Brushed Bronze", sat between Cursor turn and Reset
   all) read as a label saying what the room IS, not as a control that changes
   it — reported on the day it shipped: "I can't see any option to change the
   colour". The noun is what makes it findable; the colour is the value. */

.menu{position:absolute;right:0;top:calc(100% + 8px);min-width:212px;padding:6px;
  background:rgba(18,19,22,.97);border:1px solid var(--line);border-radius:12px;
  box-shadow:0 24px 60px -20px rgba(0,0,0,.85);z-index:30;display:flex;flex-direction:column;gap:2px}
.menu button{padding:10px 12px;border-radius:8px;font-size:var(--t-body);text-align:left;color:var(--ink);transition:.15s}
.menu button:hover{background:var(--panel2)}
.menu button.danger{color:#e88}
.menu button.danger:hover{background:rgba(220,60,90,.16)}
/* controls that move into the menu on a phone keep their own handlers and state,
   so they only need to be re-dressed as menu rows */
.menu .in-menu{width:100%;padding:10px 12px;border:none;border-radius:8px;background:none;
  text-align:left;font-size:var(--t-body);color:var(--ink);letter-spacing:0}
.menu .in-menu:hover{background:var(--panel2)}
.menu .in-menu[aria-pressed="true"]::after{content:"✓";float:right;color:var(--gold)}
.menu .wall-tabs.in-menu{display:flex;justify-content:space-between;gap:4px;padding:4px;
  background:var(--panel2);border:1px solid var(--line-soft);border-radius:24px;margin-bottom:2px}
.menu .wall-tabs.in-menu button{flex:1;text-align:center;padding:7px 0}
.menu .ceil.in-menu{display:flex;align-items:center;gap:3px;padding:6px 10px;margin-bottom:2px;
  background:var(--panel2);border:1px solid var(--line-soft);border-radius:24px}
.menu .ceil.in-menu .ct-lbl{display:block;flex:1;font-size:var(--t-body);color:var(--ink);letter-spacing:0}
.menu .ceil.in-menu button{padding:4px}
.menu .ceil.in-menu button i{width:15px;height:15px}

/* what the customer has chosen, listed where they can see it */
.chosen{flex:none;margin:0 14px 10px;border:1px solid var(--line);border-radius:11px;
  background:rgba(198,161,91,.06);overflow:hidden}
.ch-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  padding:9px 11px 7px;font-size:var(--t-micro);letter-spacing:.16em;text-transform:uppercase;
  color:var(--gold2)}
.ch-n{font-variant-numeric:tabular-nums;color:var(--muted)}
.ch-row{display:flex;align-items:stretch;border-top:1px solid var(--line-soft)}
.ch-go{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:8px 4px 8px 11px;
  text-align:left;color:var(--ink);transition:.15s}
.ch-go:hover{background:rgba(255,255,255,.04)}
.ch-go i{width:12px;height:12px;border-radius:50%;flex:none;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.25)}
.ch-nm{flex:1;min-width:0;font-size:var(--t-small);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ch-fin{font-size:var(--t-micro);color:var(--muted);white-space:nowrap}
.ch-x{padding:0 11px;color:var(--muted);font-size:15px;line-height:1;transition:.15s}
.ch-x:hover{color:#e88;background:rgba(220,60,90,.12)}

/* ── rail ────────────────────────────────────────────────────────────────── */
.rail{background:linear-gradient(180deg,var(--panel),#101115);border-right:1px solid var(--line-soft);
  display:flex;flex-direction:column;min-height:0;z-index:10}
.rail-body{display:flex;flex-direction:column;min-height:0;flex:1}
.sheet-grip{display:none}

.rail-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:15px 18px 9px}
.rh-title{font-size:var(--t-label);letter-spacing:.24em;text-transform:uppercase;color:var(--muted)}
.rh-count{font-size:var(--t-micro);letter-spacing:.1em;color:var(--muted);font-variant-numeric:tabular-nums}

.rail-tools{padding:0 14px 10px;display:flex;flex-direction:column;gap:9px}
.srch{display:flex;align-items:center;gap:8px;padding:8px 11px;background:var(--panel2);
  border:1px solid var(--line-soft);border-radius:10px;transition:.2s}
.srch:focus-within{border-color:var(--line)}
.srch svg{width:14px;height:14px;color:var(--muted2);flex:none}
.srch input{flex:1;min-width:0;background:none;border:none;color:var(--ink);font-size:var(--t-body);outline:none}
.srch input::placeholder{color:var(--muted)}
.srch input::-webkit-search-cancel-button{filter:invert(.6)}

.cats{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:2px 12px 12px}
.cat-group{margin-bottom:5px}
.cat-title{display:flex;align-items:center;gap:11px;width:100%;padding:10px 12px;border-radius:11px;
  color:var(--ink);text-align:left;transition:.2s}
.cat-title:hover{background:var(--panel2)}
.cat-title .ic{width:32px;height:32px;flex:none;border-radius:9px;background:var(--bg);
  border:1px solid var(--line-soft);display:grid;place-items:center;overflow:hidden}
.cat-title .ic img{max-width:80%;max-height:80%;object-fit:contain}
.cat-title b{font-size:var(--t-lead);font-weight:600;flex:1;line-height:1.25}
/* the step number the client reads the four groups by — small, muted, and
   set as a run-in before the name so the header still reads as one line */
.cat-title b .step{display:inline-block;margin-right:.55em;font-size:var(--t-micro);font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--gold, #c9a961);vertical-align:.08em}
.cat-title .n{font-size:var(--t-micro);color:var(--muted);font-variant-numeric:tabular-nums}
.cat-title .chev{color:var(--muted2);transition:.25s;font-size:10px}
.cat-group.open .cat-title .chev{transform:rotate(90deg)}
.cat-items{display:none;grid-template-columns:1fr 1fr;gap:8px;padding:6px 4px 8px}
.cat-group.open .cat-items{display:grid}

.pcard{background:var(--panel2);border:1px solid var(--line-soft);border-radius:11px;
  transition:.2s;overflow:hidden;display:flex;flex-direction:column}
.pcard:hover{border-color:var(--gold);transform:translateY(-1px)}
.pcard.placed{border-color:var(--gold);background:linear-gradient(180deg,rgba(198,161,91,.14),transparent)}
.pc-main{display:block;width:100%;padding:9px 8px 10px;text-align:center;color:inherit}
.pcard .pic{display:flex;align-items:center;justify-content:center;height:58px}
.pcard .pic img{max-width:100%;max-height:100%;object-fit:contain}
.pcard .nm{display:block;font-size:var(--t-small);color:var(--ink);margin-top:6px;line-height:1.25;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.pcard .sub{display:block;font-size:var(--t-micro);line-height:1.3;color:var(--muted);
  margin-top:3px;letter-spacing:.02em}
/* ── the valve card: functions, finish tray, and why a card is refused ──────
   A valve is picked differently from everything else (finish first, and it
   commits the room), so its card carries more than a thumbnail. */
.pcard .fn{display:block;font-size:var(--t-micro);letter-spacing:.06em;text-transform:uppercase;
  color:var(--gold);margin-top:4px}
/* a product the room can no longer take — still legible, plainly not on offer */
.pcard.blocked{opacity:.5}
.pcard.blocked:hover{border-color:var(--line-soft);transform:none}
.pcard.blocked .pc-main{cursor:not-allowed}
.pc-why{display:block;padding:0 8px 9px;font-size:var(--t-micro);line-height:1.35;
  color:var(--muted);text-align:center}
/* ── the finish chooser ────────────────────────────────────────────────────
   Full size, because it decides the finish of the whole room. Each tile shows
   the product itself in that finish — a swatch dot alone tells you nothing
   about what Champagne does to a brushed panel. */
/* 100%, not 94vw: the overlay already keeps 22px of padding each side, and a
   card measured against the VIEWPORT does not know about it. On a 375 px phone
   94vw is 353 against a 331 content box, so the card started at the left
   padding and hung 22px off the right edge — it read as clipped. */
.modal-card.wide{width:min(100%,660px)}
.fin-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:10px;
  margin-bottom:18px;max-height:min(58vh,430px);overflow:auto}
.fin-tile{display:block;width:100%;padding:10px 8px 11px;border:1px solid var(--line-soft);
  border-radius:12px;background:var(--panel2);color:inherit;cursor:pointer;transition:.16s;text-align:center}
.fin-tile:hover,.fin-tile:focus-visible{border-color:var(--gold);transform:translateY(-2px);
  background:linear-gradient(180deg,rgba(198,161,91,.12),transparent)}
.ft-pic{display:flex;align-items:center;justify-content:center;height:74px}
.ft-pic img{max-width:100%;max-height:100%;object-fit:contain}
.ft-sw{display:block;width:26px;height:26px;border-radius:50%;margin:9px auto 7px;
  border:1px solid rgba(255,255,255,.16)}
.ft-nm{display:block;font-size:var(--t-small);color:var(--ink);line-height:1.3}
.ft-no{display:block;font-size:var(--t-micro);color:var(--muted);margin-top:5px;line-height:1.35}
.fin-tile.no{opacity:.4;cursor:not-allowed}
.fin-tile.no:hover{border-color:var(--line-soft);transform:none;background:var(--panel2)}
/* the finish the room is already in — marked, because the room chooser opens on
   a grid where one tile is the answer to "what am I looking at now?" */
.fin-tile.on{border-color:var(--gold);background:rgba(198,161,91,.10)}
.fin-tile.on .ft-nm{color:var(--gold)}
@media (max-width:860px){ .fin-grid{grid-template-columns:repeat(auto-fit,minmax(112px,1fr))} }

/* the tool card, once the valve has committed the room */
.fin-lock{display:block;font-size:var(--t-micro);line-height:1.5;color:var(--muted)}
.fin-lock b{color:var(--ink);font-weight:500}
/* the way out of the lock, said where the lock is said */
.lnk{font:inherit;color:var(--gold);text-decoration:underline;text-underline-offset:2px;
  cursor:pointer;padding:0;border:0;background:none}
.lnk:hover{color:var(--ink)}
.lnk:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-radius:3px}

.rail-empty{padding:26px 16px;text-align:center;color:var(--muted);font-size:var(--t-body);line-height:1.6}
.rail-foot{padding:12px 16px;border-top:1px solid var(--line-soft)}
.rail-foot .hint{font-size:var(--t-small);color:var(--muted);line-height:1.55}

/* ── stage ───────────────────────────────────────────────────────────────── */
.stage3d{position:relative;overflow:hidden;min-width:0;min-height:0;
  background:radial-gradient(60% 60% at 50% 30%,#1a1c20,#0b0c0e)}
#canvasHolder{position:absolute;inset:0}
#canvasHolder canvas{display:block;width:100%;height:100%;outline:none}
#canvasHolder.over-product canvas{cursor:pointer}
.loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:var(--muted);font-size:var(--t-lead);letter-spacing:.05em;pointer-events:none;transition:opacity .5s}
.loading.hide{opacity:0}

/* ── selected-product tool ───────────────────────────────────────────────
   Docked bottom-RIGHT, not bottom-centre: centred it sat straight over the
   vanity and over any low fitting (waste, bib tap) the moment you selected one. */
.tool{position:absolute;right:22px;bottom:22px;
  background:rgba(13,14,16,.92);border:1px solid var(--line);border-radius:16px;padding:13px 16px;
  backdrop-filter:blur(10px);box-shadow:0 20px 50px -18px rgba(0,0,0,.8);z-index:8;width:250px}
.tool-name{font-family:var(--serif);font-size:var(--t-title);line-height:1.2;margin-bottom:9px}
.tool-name em{display:block;font-family:var(--sans);font-style:normal;font-size:var(--t-micro);
  letter-spacing:.04em;color:var(--muted);margin-top:3px}
.tool-fins{display:flex;gap:7px;flex-wrap:wrap;margin-bottom:11px}
.tool-fins .fin{width:22px;height:22px;border-radius:50%;border:2px solid transparent;cursor:pointer;transition:.15s}
.tool-fins .fin.on{border-color:var(--gold);transform:scale(1.12)}
.tool-row{display:flex;gap:7px}
.tool-row button{padding:8px 13px;border-radius:10px;background:var(--panel2);border:1px solid var(--line-soft);
  font-size:var(--t-body);min-width:40px;transition:.2s}
.tool-row button:hover{border-color:var(--gold)}
.tool-row button.danger{margin-left:auto;color:#e88;border-color:transparent;background:rgba(220,60,90,.14)}
.tool-row button.danger:hover{background:rgba(220,60,90,.24)}

/* ── empty state ─────────────────────────────────────────────────────────
   Sits over the room until the first fitting is placed. It offers the first
   move; it never places anything on its own.                               */
.empty{position:absolute;left:50%;bottom:34px;transform:translateX(-50%);width:min(92vw,430px);
  background:rgba(13,14,16,.9);border:1px solid var(--line);border-radius:16px;padding:20px 22px 18px;
  backdrop-filter:blur(12px);box-shadow:0 24px 60px -24px rgba(0,0,0,.9);z-index:7;text-align:left}
.empty .e-kicker{font-size:9px;letter-spacing:.24em;text-transform:uppercase;color:var(--gold);margin-bottom:8px}
.empty h2{font-family:var(--serif);font-size:26px;font-weight:600;line-height:1.15;margin-bottom:8px}
.empty .e-body{font-size:var(--t-body);color:var(--muted);line-height:1.55;margin-bottom:15px}
.empty .e-row{display:flex;gap:8px;flex-wrap:wrap}
.empty button{padding:9px 15px;border-radius:22px;font-size:var(--t-body);border:1px solid var(--line-soft);
  color:var(--muted);transition:.2s}
.empty button:first-child{background:linear-gradient(135deg,var(--gold),#a9803c);color:#1a140a;
  font-weight:600;border-color:transparent}
.empty button:hover{color:var(--ink);border-color:var(--line)}
.empty button:first-child:hover{color:#1a140a;filter:brightness(1.06)}
@media (max-width:860px){ .empty{bottom:70px;padding:16px 17px 15px} .empty h2{font-size:21px} }

/* ── toast (with undo) ───────────────────────────────────────────────────── */
.toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);display:flex;align-items:center;gap:14px;
  background:rgba(18,18,20,.95);color:var(--ink);font-size:var(--t-body);font-weight:500;
  padding:11px 14px 11px 18px;border:1px solid var(--line);border-radius:11px;z-index:60;
  opacity:0;transition:opacity .2s;pointer-events:none;box-shadow:0 8px 24px rgba(0,0,0,.45)}
.toast.show{opacity:1;pointer-events:auto}
.toast button{padding:5px 11px;border-radius:7px;border:1px solid var(--line);
  font-size:var(--t-small);color:var(--gold2);transition:.15s}
.toast button:hover{background:rgba(198,161,91,.15)}

/* ── confirm dialog ──────────────────────────────────────────────────────── */
.modal{position:fixed;inset:0;display:grid;place-items:center;background:rgba(6,6,8,.66);
  backdrop-filter:blur(3px);z-index:80;padding:22px}
.modal-card{width:min(100%,380px);background:var(--panel);border:1px solid var(--line);
  border-radius:16px;padding:22px 22px 18px;box-shadow:0 30px 70px -24px #000}
.modal-card h2{font-family:var(--serif);font-size:23px;font-weight:600;margin-bottom:8px}
.modal-card p{font-size:var(--t-body);color:var(--muted);line-height:1.55;margin-bottom:18px}
.modal-row{display:flex;gap:9px;justify-content:flex-end}

/* Below this the bar has no room for a sixth control, so syncToolbar() moves the
   swatches into the ··· menu — and the caption + divider they leave behind in the
   bar have to go with them. Keep this width in step with CEIL_MQ in planner.js.
   1500 because the bar measures 1492 px with the swatches in it: at the old 1420
   a 1440-wide laptop overflowed, hanging "Spec sheet" and the ··· button off the
   right edge with no way to reach them. */
@media (max-width:1500px){ #ceilGroup{display:none} }

/* ── phone ───────────────────────────────────────────────────────────────
   The room is the product, so it keeps the screen. Products live in a sheet
   that slides up over it, and the toolbar is one scrolling row.            */
@media (max-width:860px){
  body{--sheet-h:min(62vh,520px)}
  #app{grid-template-columns:minmax(0,1fr);grid-template-rows:52px minmax(0,1fr)}
  /* the secondary controls move into the ... menu on a phone, so the bar fits
     and can stay overflow:visible — with overflow:auto the backdrop-filter on
     this bar clipped the open menu to a sliver */
  .topbar{gap:6px;overflow:visible;
    padding:0 max(11px,env(safe-area-inset-right)) 0 max(11px,env(safe-area-inset-left))}
  .menu{right:0;min-width:min(84vw,260px)}
  .brand .logo img{height:22px}
  .brand .sub,.glabel{display:none}
  .group{padding-left:10px;gap:5px}
  .group + .group::before{height:18px}
  .btn{padding:6px 11px;font-size:var(--t-small)}
  .wall-tabs button{padding:5px 10px;font-size:var(--t-small)}
  .themes button{padding:4px 9px 4px 5px;font-size:var(--t-small);gap:5px}
  .themes button i{width:12px;height:12px}
  .themes button span{display:none}
  /* The colour name goes on a phone; the WORD stays. A lone dot in a crowded
     bar is not a control anyone finds — the dot says what the room is, and
     "Finish" is the only part that says it can be changed. */
  .btn.fin-btn{padding-left:7px;gap:5px}
  .fin-btn i{width:13px;height:13px}
  .fin-btn .rf-nm{display:none}

  .stage3d{grid-row:2;grid-column:1}

  /* rail → bottom sheet over the room */
  .rail{position:fixed;left:0;right:0;bottom:0;z-index:40;border-right:none;
    border-top:1px solid var(--line);border-radius:16px 16px 0 0;
    background:rgba(18,19,22,.97);backdrop-filter:blur(14px);
    box-shadow:0 -18px 44px -22px rgba(0,0,0,.9);
    /* keep the grip clear of the home indicator on a notched phone */
    padding-bottom:env(safe-area-inset-bottom);
    transform:translateY(calc(100% - 54px - env(safe-area-inset-bottom)));
    transition:transform .3s cubic-bezier(.4,0,.2,1);
    max-height:var(--sheet-h)}
  .rail.open{transform:translateY(0)}
  .sheet-grip{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;height:54px;
    padding-bottom:0;
    flex:none;color:var(--ink);font-size:var(--t-body);letter-spacing:.16em;text-transform:uppercase}
  .sheet-grip i{width:34px;height:3px;border-radius:2px;background:var(--muted2);flex:none}
  .rail-body{max-height:calc(var(--sheet-h) - 54px)}
  .rail-head{padding:2px 16px 8px}
  .rail-foot{display:none}
  .cats{padding:0 10px 14px}
  .cat-items{grid-template-columns:1fr 1fr 1fr;gap:7px}
  .pcard .pic{height:48px}
  .pcard .nm{-webkit-line-clamp:2;font-size:var(--t-small)}
  .pcard .sub{display:none}          /* code+variant move into the tool panel on a phone */

  .tool{right:12px;left:12px;width:auto;bottom:calc(66px + env(safe-area-inset-bottom))}
  /* finger-sized controls: 22px swatches and 34px-high buttons are fine for a
     mouse and too small for a thumb */
  .tool-fins{gap:10px}
  .tool-fins .fin{width:30px;height:30px}
  .tool-row button{padding:11px 15px;min-width:48px}
  .toast{width:max-content;max-width:calc(100vw - 32px);
    bottom:calc(70px + env(safe-area-inset-bottom))}
  body.has-tool .toast{bottom:calc(78px + var(--tool-h,0px) + env(safe-area-inset-bottom))}
  /* with the products sheet up, the toast has to clear it */
  body.sheet-open .toast{bottom:calc(var(--sheet-h) + 14px)}
}

/* phone in landscape: short viewport, so the sheet and the opening card have to
   give the room back its height */
@media (max-width:900px) and (max-height:520px){
  body{--sheet-h:82vh}
  /* nothing is left between the sheet and the bar, so the toast hangs under it */
  body.sheet-open .toast{top:58px;bottom:auto}
  .cat-items{grid-template-columns:repeat(4,1fr)}
  /* A LEFT GUTTER FOR THE ROTATOR. Landscape is 375 tall: the stage is ~320 of
     that, so there is no height to dock the control clear of the panels the way
     portrait does. The panels give it the width instead — the dock ends at x 84,
     so they start at 96. Measured before this: the tool card ran x 12-800 and
     the dock 8-84 sat inside it, over the thumbnail, the code and the + button. */
  .empty{width:min(70vw,420px);left:96px;transform:none;bottom:64px;padding:12px 14px}
  .tool{left:96px}
  .empty h2{font-size:17px;margin-bottom:5px}
  .empty .e-body{display:none}
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms !important;transition-duration:.001ms !important}
}

/* ---- rotator dock (LEFT edge of the stage) -------------------------------
   Turning the room by dragging across it works, but it fights product picking
   and there is nothing to grab on a touchpad. This gives a fixed handle.

   WHY LEFT, ON EVERY SIZE. It docked right, vertically centred, and that is
   exactly where the fittings are: everything the planner places hangs on the
   RIGHT wall or in the back wall's right corner, so in the opening view the
   dock sat on top of them. Measured in the hero framing of a full set, the
   dock spanned x 1022-1118 / y 313-561 while the thermostatic bar spanned
   928-1127 / 381-441 and the four body jets 925-1141 / 302-530 — the control
   you reach for was parked over the products you came to look at, and the
   client saw the room that way before touching anything. The left third is
   the back wall and the vanity: room dressing, never a placed product. The
   phone rule below reached this conclusion first; it belongs at every width. */
.rotator{position:absolute;left:16px;top:50%;transform:translateY(-50%);z-index:8;
  display:flex;flex-direction:column;align-items:center;gap:8px;padding:12px 10px;
  background:rgba(13,14,16,.82);border:1px solid var(--line-soft);border-radius:20px;
  backdrop-filter:blur(10px);box-shadow:0 18px 44px -20px rgba(0,0,0,.85)}
.rot-dial{width:74px;height:74px;cursor:grab;touch-action:none;border-radius:50%;outline:none}
.rot-dial:active{cursor:grabbing}
.rot-dial:focus-visible{box-shadow:0 0 0 2px var(--gold)}
.rot-dial svg{width:100%;height:100%;display:block}
.rd-face{fill:rgba(255,255,255,.045);stroke:rgba(255,255,255,.1);stroke-width:1}
.rd-arc{fill:none;stroke:rgba(198,161,91,.45);stroke-width:3;stroke-linecap:round}
.rd-room{fill:none;stroke:rgba(255,255,255,.22);stroke-width:2}
.rd-dot{fill:var(--gold);stroke:#14100a;stroke-width:1.5}
.rot-pair{display:flex;gap:6px}
.rot-btn{width:32px;height:30px;border-radius:9px;background:var(--panel2);
  border:1px solid var(--line-soft);color:var(--muted);font-size:14px;line-height:1;
  display:grid;place-items:center;transition:.18s}
.rot-btn:hover{color:var(--ink);border-color:var(--line)}
.rot-btn.wide{width:70px;height:26px;font-size:11px;letter-spacing:.04em}
@media (max-width:820px){
  /* Same left dock as everywhere else, just tighter — a phone has less room to
     give the control and the fittings crowd further into frame.
     AND NOT VERTICALLY CENTRED. On a desktop stage the middle of the left edge
     is empty; on a phone it is where the panels are. Centred, the dock sat at
     y 333-532 of an 812 screen and the opening card runs 488-742, so the first
     thing the client saw was "…OUR BATHROOM" with the step heading behind a
     control. Docked under the top bar it clears both the opening card and the
     selected-piece tool, which start at 488 and 549. */
  .rotator{left:8px;top:62px;transform:none;gap:6px;padding:8px 7px;border-radius:16px}
  .rot-dial{width:56px;height:56px}
  .rot-btn{width:27px;height:26px;font-size:12.5px}
  .rot-btn.wide{width:60px;height:23px;font-size:10px}
}

/* the selected-piece tool is now the only place a finish is chosen */
.tool-finrow{display:flex;align-items:center;justify-content:center;gap:8px;margin:2px 0 8px}
.tool-finrow .lbl{font-size:var(--t-micro);letter-spacing:.18em;text-transform:uppercase;color:var(--muted)}
.tool-finrow .val{font-size:var(--t-small);color:var(--ink)}

/* selected-piece preview: shows the product in the finish you are pointing at */
.tool-head{display:flex;align-items:center;gap:11px;margin-bottom:9px}
.tool-head .tool-name{margin-bottom:0;min-width:0}
.tool-prev{width:58px;height:58px;flex:none;border-radius:10px;overflow:hidden;
  border:1px solid var(--line-soft);background:var(--panel2);display:grid;place-items:center}
.tool-prev img{max-width:86%;max-height:86%;object-fit:contain}
.tool-fins .fin{padding:0}
@media (max-width:820px){ .tool-prev{width:46px;height:46px} .tool-head{gap:9px} }
