/* UCD Order Form — styled to match the UCD Portal design language. */
:root{
  --purple:#5b2c83; --purple2:#7a45a7; --purple-soft:#f1eaf6;
  --ink:#1b2230; --muted:#5c6678; --muted2:#7a8395; --label:#7a8395;
  --panel:rgba(255,255,255,.82); --panel-solid:#ffffff;
  --line:rgba(27,34,48,.08); --line2:rgba(27,34,48,.12);
  --shadow:0 10px 30px rgba(27,34,48,.08);
  --shadow-lg:0 16px 35px rgba(91,44,131,.35);
  --radius:22px; --radius-sm:16px;
  --stage-a:#efe9f2; --stage-b:#e7e0d6;
  --ok:#2f7a4c;
  --safe:env(safe-area-inset-bottom,0px);
  font-synthesis:none;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%}
body{
  font-family:Arial,Helvetica,sans-serif;color:var(--ink);min-height:100vh;
  display:flex;flex-direction:column;overflow-x:hidden;
  /* the viewport meta stops the pinch; this stops the double-tap zoom, which otherwise fires on a
     quick second tap on a card and leaves the customer zoomed in mid-order */
  touch-action:manipulation;
  background:url("../assets/portal_background.jpg") center center / cover no-repeat fixed;
}
.overlay{position:fixed;inset:0;z-index:0;background:linear-gradient(135deg,
  rgba(248,246,243,.93) 0%,rgba(248,246,243,.88) 34%,rgba(243,241,237,.84) 100%)}
img{max-width:100%;display:block}
button{font:inherit;cursor:pointer}
.app{position:relative;z-index:1;width:min(1280px,calc(100% - 32px));margin:0 auto;
  display:flex;flex-direction:column;min-height:100vh;padding-bottom:96px}
/* Header */
.hdr{margin:12px 0 0;padding:12px 20px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);display:flex;align-items:center;justify-content:space-between;gap:12px;
  backdrop-filter:blur(12px);box-shadow:var(--shadow);position:sticky;top:8px;z-index:20;
  transition:background .28s ease, box-shadow .28s ease, border-color .28s ease,
             -webkit-backdrop-filter .28s ease, backdrop-filter .28s ease}
/* Once it is actually pinned, the bar becomes glass: thinner, more transparent, and blurring what
   passes underneath, with a lit top edge and a shadow that only exists while it is floating. At
   rest it stays the plain panel — the effect has to read as "this is on top of the page now",
   which it cannot do if it looks the same when nothing is behind it. */
.hdr.stuck{
  background:linear-gradient(155deg,rgba(255,255,255,.60),rgba(255,255,255,.40));
  -webkit-backdrop-filter:blur(24px) saturate(180%);
  backdrop-filter:blur(24px) saturate(180%);
  border-color:rgba(255,255,255,.55);
  box-shadow:0 12px 34px rgba(27,34,48,.13), inset 0 1px 0 rgba(255,255,255,.8)}
.hdr .brand{display:flex;align-items:center;gap:10px;min-width:0}
.hdr .brand img{height:46px;width:auto}
.hdr .center{text-align:center}
.hdr .center .eyebrow{font-size:11px;font-weight:700;letter-spacing:1.8px;text-transform:uppercase;color:var(--purple)}
.hdr .center h1{font-size:17px;font-weight:800;color:var(--ink)}
.hdr .right{display:flex;align-items:center;gap:14px}
/* "Show door" — the way to actually look at the door on a phone, where the preview is a thumbnail */
.btn-door{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 15px;border-radius:999px;
  border:1px solid var(--line2);background:#fff;color:var(--purple);font-size:13px;font-weight:800;
  box-shadow:var(--shadow);white-space:nowrap}
.btn-door:hover{background:var(--purple-soft);border-color:var(--purple)}
.btn-door .di{font-size:15px;line-height:1}
/* The step guide. A round "?" beside the button that leaves the step — asked at the moment it is
   wanted, which is while looking at the question you cannot answer. */
.btn-help{width:44px;height:44px;flex:none;border-radius:50%;border:1px solid var(--line2);
  background:#fff;color:var(--purple);font-size:17px;font-weight:800;line-height:1}
.btn-help:hover{background:var(--purple-soft);border-color:var(--purple)}
.help-in{gap:12px;max-height:100%;overflow-y:auto}
.help-eyebrow{display:block;font-size:10.5px;font-weight:800;letter-spacing:1.3px;text-transform:uppercase;
  color:var(--purple)}
.help-head b{display:block;font-size:18px;font-weight:800;margin-top:5px;padding-right:44px;line-height:1.3}
.help-body{display:flex;flex-direction:column;gap:11px}
.help-pt{border-left:2px solid var(--purple-soft);padding-left:12px}
.help-pt b{display:block;font-size:13.5px;font-weight:800;color:var(--ink)}
.help-pt p{font-size:13px;line-height:1.55;color:var(--muted);margin-top:3px}
/* the half that is actually about this customer's order rather than about the form */
.help-todo{background:var(--purple-soft);border-radius:12px;padding:11px 13px}
.help-todo b{display:block;font-size:11px;font-weight:800;letter-spacing:.6px;text-transform:uppercase;
  color:var(--purple);margin-bottom:6px}
.help-todo span{display:block;font-size:13px;color:#463f52;line-height:1.5}
.help-todo span+span{margin-top:3px}
/* "Report a problem" — quieter than Show door, because it is a safety net rather than a thing to
   do; but present on every step and on a phone, since the step you are on when something looks
   wrong is the one worth reporting from. On a narrow screen the label goes and the flag stays. */
.btn-report{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 14px;border-radius:999px;
  border:1px solid var(--line2);background:rgba(255,255,255,.7);color:var(--muted);
  font-size:13px;font-weight:800;white-space:nowrap}
.btn-report:hover{background:#fff;border-color:var(--purple);color:var(--purple)}
.btn-report .di{font-size:14px;line-height:1}
/* the report dialog */
.issue-in{gap:10px;max-height:100%;overflow-y:auto}
.issue-head b{display:block;font-size:17px;font-weight:800;padding-right:44px}
.issue-head p{font-size:13px;color:var(--muted);line-height:1.5;margin-top:6px}
.issue-lab{display:block;font-size:11px;font-weight:800;letter-spacing:.6px;text-transform:uppercase;
  color:var(--label);margin-top:4px}
.issue-lab span{text-transform:none;letter-spacing:0;font-weight:700;color:var(--muted2)}
#issueText{width:100%;border:1px solid var(--line2);border-radius:12px;padding:10px 12px;
  font:inherit;font-size:14px;color:var(--ink);background:#fff;resize:vertical}
#issueText:focus{outline:none;border-color:var(--purple);
  box-shadow:0 0 0 2px rgba(91,44,131,.16)}
/* The way out, said before they need it: nobody should abandon an order because one option would
   not behave. */
.issue-tip{font-size:12.5px;line-height:1.55;color:var(--muted);background:var(--purple-soft);
  border-radius:12px;padding:10px 12px;margin-top:2px}
.issue-tip b{color:var(--ink)}
.issue-sent{background:#eaf4ee;border-radius:12px;padding:10px 12px}
.issue-sent b{display:block;font-size:13.5px;font-weight:800;color:var(--ok)}
.issue-sent p{font-size:12.5px;line-height:1.55;color:var(--muted);margin-top:4px}
.issue-sent p b{display:inline;color:var(--ink);font-size:inherit}
.issue-btns{display:flex;gap:10px;justify-content:flex-end;margin-top:2px}
/* On a phone the label goes and the flag becomes a small round button — which, sitting between the
   logo and "Show door", read as part of the brand mark rather than as a control. Swapped: the thing
   they came to press stays next to the logo, the flag goes to the edge. */
@media (max-width:820px){
  .btn-report .dt{display:none}
  .btn-report{padding:0;width:40px;justify-content:center}
  .hdr .right{flex-direction:row-reverse;gap:10px}
}
/* full-screen door */
.dmodal{position:fixed;inset:0;z-index:100;background:rgba(27,34,48,.62);backdrop-filter:blur(6px);
  display:grid;place-items:center;padding:16px;padding-bottom:calc(16px + var(--safe))}
.dmodal[hidden]{display:none}
.dmodal-in{position:relative;width:min(560px,100%);max-height:100%;display:flex;flex-direction:column;
  background:var(--panel-solid);border-radius:var(--radius);box-shadow:0 24px 60px rgba(27,34,48,.4);
  padding:16px;gap:14px}
.dmodal-x{position:absolute;top:10px;right:10px;z-index:2;width:38px;height:38px;border-radius:50%;
  border:1px solid var(--line2);background:rgba(255,255,255,.92);color:var(--ink);font-size:15px;line-height:1}
.dmodal-x:hover{background:var(--purple-soft);border-color:var(--purple);color:var(--purple)}
.dmodal-stage{height:min(64vh,520px);border-radius:var(--radius-sm);overflow:hidden;display:grid;place-items:center;
  background:linear-gradient(160deg,var(--stage-a),var(--stage-b))}
.dmodal-stage .misvg{width:100%;height:100%;min-width:0;min-height:0;display:grid;place-items:center;padding:14px}
.dmodal-stage .misvg svg{width:100%;height:100%;max-width:100%;max-height:100%;
  filter:drop-shadow(0 16px 24px rgba(0,0,0,.24))}
.dmodal-stage .misvg+.ph{display:none}
.dmodal-stage .ph{color:var(--muted2);font-size:13px;text-align:center;padding:0 16px}
.dmodal-meta b{display:block;font-size:17px;font-weight:800}
.dmodal-rows{display:flex;flex-wrap:wrap;gap:7px;margin-top:9px}
.dmodal-rows span{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--muted);
  background:var(--purple-soft);border-radius:999px;padding:5px 11px}
.dmodal-rows .dot{width:13px;height:13px;border-radius:50%;border:1px solid rgba(0,0,0,.15)}
/* The thank-you. The one screen in the form that is not asking for anything, so it is allowed to be
   quiet: a tick, a sentence, the door they built, and the two things anyone wants at that moment. */
.done-in{width:min(460px,100%);text-align:center;align-items:center;padding:26px 20px 20px;gap:10px}
.done-tick{width:62px;height:62px;border-radius:50%;display:grid;place-items:center;
  font-size:31px;font-weight:800;color:#fff;background:linear-gradient(135deg,var(--purple),var(--purple2));
  box-shadow:0 12px 28px rgba(91,44,131,.36);animation:doneTick .42s cubic-bezier(.2,1.4,.4,1)}
@keyframes doneTick{0%{transform:scale(.5);opacity:0}100%{transform:scale(1);opacity:1}}
.done-in>b{font-size:21px;font-weight:800;letter-spacing:-.2px}
.done-lead{font-size:13.5px;line-height:1.5;color:var(--muted);margin:0;max-width:34em}
.done-card{width:100%;display:flex;align-items:center;gap:14px;text-align:left;margin-top:4px;
  background:linear-gradient(160deg,var(--stage-a),var(--stage-b));border-radius:var(--radius-sm);padding:12px 14px}
.done-door{flex:none;width:74px;height:104px;position:relative;display:grid;place-items:center}
.done-door .misvg{position:absolute;inset:0;display:grid;place-items:center}
.done-door .misvg svg{width:100%;height:100%;filter:drop-shadow(0 5px 9px rgba(0,0,0,.18))}
.done-meta{min-width:0;display:flex;flex-direction:column;gap:3px}
.done-ref{font-size:10.5px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:var(--purple)}
.done-meta b{font-size:13.5px;font-weight:800;line-height:1.35}
.done-meta span:last-child{font-size:12px;color:var(--muted)}
.done-in .issue-btns{width:100%;margin-top:6px}
body.modal-open{overflow:hidden}
/* Progress line under header */
.progress{height:4px;border-radius:4px;background:var(--line);margin:12px 4px 0;overflow:hidden}
/* Main layout */
.main{display:grid;grid-template-columns:280px 1fr 360px;gap:16px;margin-top:16px;align-items:start}
/* Step rail */
.rail{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  backdrop-filter:blur(12px);box-shadow:var(--shadow);padding:20px 16px;position:sticky;top:78px}
.rail h2{font-size:24px;font-weight:800;letter-spacing:-.01em;line-height:1.1}
.rail .sub{color:var(--muted);font-size:14px;margin:8px 0 16px;line-height:1.5}
.steps{display:flex;flex-direction:column;gap:4px}
.step{display:flex;align-items:center;gap:12px;padding:11px 10px;border-radius:14px;cursor:pointer;
  transition:.15s;border:1px solid transparent}
.step:hover{background:rgba(91,44,131,.05)}
.step.active{background:var(--purple-soft);border-color:rgba(91,44,131,.14)}
.step .ic{width:34px;height:34px;border-radius:11px;display:grid;place-items:center;flex:none;
  background:#fff;border:1px solid var(--line);color:var(--muted2);font-size:15px}
.step.active .ic{background:linear-gradient(135deg,var(--purple),var(--purple2));color:#fff;border-color:transparent}
.step.done .ic{background:#eaf4ee;color:var(--ok);border-color:transparent}
.step .tx b{display:block;font-size:14px;font-weight:800;color:var(--ink)}
.step .tx span{font-size:11.5px;color:var(--muted2)}
.step.active .tx b{color:var(--purple)}
.step .no{margin-left:auto;font-size:11px;color:var(--muted2);font-weight:800}
.help{margin-top:16px;background:rgba(255,255,255,.6);border:1px solid var(--line);border-radius:16px;padding:14px;display:flex;gap:12px}
.help .ic{width:34px;height:34px;border-radius:11px;background:var(--purple-soft);color:var(--purple);display:grid;place-items:center;flex:none}
.help b{font-size:13px}.help p{font-size:12px;color:var(--muted);margin-top:2px}
/* Preview stage */
.stage{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  backdrop-filter:blur(12px);box-shadow:var(--shadow);padding:14px;position:sticky;top:78px;min-height:520px;
  display:flex;flex-direction:column}
.seg{display:inline-flex;background:rgba(255,255,255,.7);border:1px solid var(--line);border-radius:12px;padding:3px}
.seg button{padding:7px 14px;border:none;background:transparent;border-radius:9px;font-weight:800;font-size:13px;color:var(--muted);display:inline-flex;align-items:center;gap:6px}
.seg button[aria-pressed="true"]{background:var(--ink);color:#fff}
.zoom{display:inline-flex;gap:6px}
.zoom button{width:34px;height:34px;border-radius:11px;border:1px solid var(--line);background:rgba(255,255,255,.8);color:var(--muted);display:grid;place-items:center}
/* Option panel */
.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  backdrop-filter:blur(12px);box-shadow:var(--shadow);padding:20px;position:sticky;top:78px}
/* Choice cards (type/design) */
.cards{display:grid;gap:10px}
/* Door-type cards (step 1 — clean text cards, left accent) */
/* The type cards used to be stretched to fill the panel height, which on a desktop left five
   mostly-empty boxes the height of a hand. They are sized by their content now and laid out across
   the width instead — the art carries the meaning, so the card only has to be big enough to read
   it. (The .fill class that did the stretching is gone entirely.) */
/* The door is the card. Photograph on top on its own stage, name and description beneath — the
   same shape as the design gallery further on, so the two steps feel like one product rather than
   two screens. Sized by content: the cards used to be stretched to the panel height, which on a
   desktop left five near-empty boxes the height of a hand. */
.tcards{display:grid;grid-template-columns:repeat(auto-fit,minmax(212px,1fr));gap:12px;align-content:start}
/* Five products, so from the width where all five fit they go in one row rather than
   four-and-a-stray. Below that auto-fit takes over; the phone case is set in the mobile block near
   the end of this file, which comes later and would override anything written here. */
@media (min-width:1080px){ .tcards{grid-template-columns:repeat(5,1fr)} }
.tcard{position:relative;display:flex;flex-direction:column;text-align:left;border:1px solid var(--line2);
  background:rgba(255,255,255,.75);border-radius:16px;padding:0;overflow:hidden;transition:.15s}
.tcard .tart{display:block;aspect-ratio:1/1.05;background:linear-gradient(160deg,#f4f0f8,#ebe6ef);
  position:relative;transition:.15s}
.tcard .tart img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:14px 12px 10px}
/* the live render takes the photograph's place, and must take its geometry too: absolute, so the
   tile keeps its own 1/1.05 and five cards stay the same height as each other */
.tcard .tart .misvg{position:absolute;inset:0;padding:14px 12px 10px;display:block}
.tcard .tart .misvg svg{width:100%;height:100%;display:block;
  filter:drop-shadow(0 10px 16px rgba(0,0,0,.18))}
.tcard[aria-pressed="true"] .tart{background:linear-gradient(160deg,#f3ecf9,#e7dff0)}
.tcard .tbody{padding:12px 14px 14px;min-width:0}
/* The spec used to be a purple pill beside the name — "44MM", "1 + ½ LEAF". Five of those in a row
   read as five badges competing with the five titles, when the spec is really just the first half of
   the sentence underneath. Same size, same colour, same line as the description; italic is enough to
   keep it a label. */
.tcard .tbody b{display:block;font-size:16px;font-weight:800;color:var(--ink)}
.tcard[aria-pressed="true"] .tbody b{color:var(--purple)}
.tcard .tbody small{display:block;font-size:12px;color:var(--muted);margin-top:5px;line-height:1.45}
.tcard .tbody small i{font-style:normal;font-weight:800;color:var(--muted)}
/* The intro sat top-left over a void, ragged across two lines at the default 52ch. Given the room
   to finish on one line and closed up against the cards, it reads as the head of the block rather
   than something stranded above it. */
.form-body.shortstep .lead{max-width:none;margin-bottom:14px}
/* The panel stretches to the window on every step, which on the short ones left a tall white void
   under the content. Centring the void only moved it, and growing the cards into it made them 224
   wide by 600 tall — a stage that shape fits its picture by WIDTH, so every door came out smaller,
   and the twin, being the widest, smallest of all. The height is what has to give: on these steps
   the panel ends where its content ends and the page background carries on below. */
.form:has(.form-body.shortstep){flex:0 0 auto}
.tcard:hover{border-color:rgba(91,44,131,.35);border-left-color:rgba(91,44,131,.5);transform:translateY(-1px);box-shadow:var(--shadow)}
.tcard[aria-pressed="true"]{border-color:var(--purple);border-left-color:var(--purple);
  box-shadow:0 0 0 2px rgba(91,44,131,.22);background:#fff}
/* The badge sits on the artwork tile, and the tile's gradient is almost exactly --purple-soft — so
   a soft-purple pill on it had no edge at all and read as a smudge rather than a label. Solid white
   with a hairline and a small lift separates it from anything behind it, on every card. */
.tcard .badge{position:absolute;z-index:1;top:6px;right:8px;
  background:#fff;color:var(--purple);border:1px solid rgba(91,44,131,.22);
  box-shadow:0 1px 3px rgba(27,34,48,.10);
  font-size:9.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  padding:4px 9px;border-radius:999px;white-space:nowrap;max-width:calc(100% - 20px);
  overflow:hidden;text-overflow:ellipsis}
.tcard .tcard-top{display:flex;align-items:center;gap:9px}
.tcard b{font-size:16px;font-weight:800;color:var(--ink)}
.tcard[aria-pressed="true"] b{color:var(--purple)}
.tcard .spec{font-size:10px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;
  color:var(--purple);background:var(--purple-soft);padding:3px 9px;border-radius:999px;white-space:nowrap}
.tcard small{display:block;font-size:12px;color:var(--muted);margin-top:6px;line-height:1.45}
/* Profile cards — the manufacturer's own colour, the way the UCD portal configurator does it
   (configurator.css .profile-pill): each card carries a --brand and everything is mixed from it, so
   the card IS the brand rather than sitting white beside a tinted logo. At rest a wash; chosen, the
   card fills with the colour and the text turns white.
   The logo keeps its own white chip precisely because of that last state — a mark printed in the
   brand colour disappears against a background of the same colour. */
.pfcard{--brand:var(--purple);
  display:flex;flex-direction:row;align-items:center;justify-content:center;text-align:center;
  gap:14px;padding:13px 16px;border-left-width:1px;
  transition:.18s}
.pfcard[data-profile="Veka"]{--brand:#1a72b6}
.pfcard[data-profile="Liniar"]{--brand:#74a730}
/* Unchosen stays plain. A brand wash sitting under both of them read as "one of these is already
   picked" — the colour has to mean the choice, so nothing carries it until something is chosen.
   Hover is the exception: it is momentary and follows the cursor, so it cannot be mistaken for state. */
.pfcard b{font-size:17px;letter-spacing:-.01em;color:var(--ink)}
.pfcard:hover{border-color:var(--brand);background:color-mix(in srgb,var(--brand) 8%,#fff);
  transform:none;box-shadow:none}
.pfcard:hover b{color:color-mix(in srgb,var(--brand) 80%,black)}
.pfcard[aria-pressed="true"]{border-color:var(--brand);background:var(--brand);
  box-shadow:0 8px 22px color-mix(in srgb,var(--brand) 34%,transparent)}
.pfcard[aria-pressed="true"] b{color:#fff}
/* A fixed height squashed the wide one: Veka is nearly square (0.90) and Liniar more than twice as
   wide as it is tall (2.31), so at a common height Liniar overran its chip and the global
   img{max-width:100%} then crushed its width while the height stayed put. Bounded on BOTH sides
   instead, so whichever dimension runs out first is the one that limits it. */
.pf-logo{width:56px;height:56px;flex:none;border-radius:14px;display:grid;place-items:center;
  background:#fff;box-shadow:0 1px 4px rgba(15,22,36,.16)}
.pf-logo img{width:auto;height:auto;max-width:82%;max-height:68%;object-fit:contain}
/* Veka's mark is a near-square diamond with the wordmark set INSIDE it, so at a height that suits a
   wide logo it collapses to an unreadable dot. It gets most of the chip instead — the same
   allowance the portal makes for it. */
.pfcard[data-profile="Veka"] .pf-logo img{max-height:88%}
/* Security and core cards (profile step).
   The step read as three unrelated bands: two brand cards, then two wide text bars, then three
   narrower ones — nothing lined up and nothing but words. All three rows sit on ONE six-column grid
   now (profile 3+3, security 3+3, core 2+2+2) so the columns agree down the page, and every card
   carries a line-art mark in its own accent, the way the portal's core cards do. */
.tcards.pf,.tcards.sec{grid-template-columns:repeat(6,1fr)}
.tcards.pf>*{grid-column:span 3}
.tcards.sec>*{grid-column:span 3}
.tcards.sec#coreBlock>*{grid-column:span 2}
.seccard{--accent:var(--purple);
  display:flex;flex-direction:row;align-items:center;gap:13px;text-align:left;
  padding:14px 16px;min-height:0}
.seccard .cimark{width:42px;height:42px;flex:none;border-radius:12px;display:grid;place-items:center;
  color:var(--accent);background:color-mix(in srgb,var(--accent) 9%,#fff);
  border:1px solid color-mix(in srgb,var(--accent) 20%,transparent);transition:.18s}
/* 22px was too small for the glyphs that carry real drawing — a door leaf with an arrow either side
   of it lost its arrows entirely at that size. The mark grows with it so the icon still has air. */
.seccard .cimark{width:54px;height:54px}
.seccard .cimark .ci{width:32px;height:32px}
/* the swing pair is drawn 34 units wide against everyone else's 24: same height, more room across,
   so the arrows are arrows rather than ticks squeezed against the door */
.seccard .cimark .ci-wide{width:45px;height:32px}
.seccard .citext{min-width:0;display:flex;flex-direction:column;gap:4px}
.seccard b{font-size:15.5px}
.seccard[aria-pressed="true"]{border-color:var(--accent);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 22%,transparent)}
.seccard[aria-pressed="true"] b{color:var(--accent)}
.seccard[aria-pressed="true"] .cimark{background:var(--accent);color:#fff;border-color:var(--accent)}
.seccard small{font-size:12px;color:var(--muted);line-height:1.4}
/* Handing cards (LH/RH with door glyph) */
.tcards.hand2{grid-template-columns:1fr 1fr}
.handcard{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;text-align:center;
  gap:10px;padding:20px 16px 18px;min-height:0}
/* tach's own door schematic fills the card — the drawn glyph is only the fallback.
   Kept short enough that the opening-direction question below it stays on screen: at 300px the two
   cards came to 406px each and pushed the Open In / Open Out chips past the bottom of the panel,
   where they read as the end of the step rather than a question still to answer. */
.handcard .hs{display:block;width:100%;height:min(24vh,225px);min-height:150px;position:relative}
.handcard .hs img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
.handcard .hglyph{position:absolute;inset:0;width:100%;height:100%;color:var(--purple);opacity:.85}
.handcard b{font-size:16px}
/* The chosen hand only turned its caption purple, so beside a near-identical white card the answer
   was a single word. What it was actually missing is the mark every other card on the step carries:
   Standard Security and Sapphire each have a tinted glyph that fills solid purple when picked, and
   this card had nothing to fill. It sits in the corner rather than in a row, because the artwork
   already owns the middle. */
.handcard{--accent:var(--purple)}
.handcard .cimark{position:absolute;top:12px;left:12px;width:36px;height:36px;border-radius:11px;
  display:grid;place-items:center;color:var(--accent);
  background:color-mix(in srgb,var(--accent) 9%,#fff);
  border:1px solid color-mix(in srgb,var(--accent) 20%,transparent);transition:.18s}
.handcard .cimark .ci{width:20px;height:20px}
.handcard[aria-pressed="true"] .cimark{background:var(--accent);color:#fff;border-color:var(--accent)}
.handcard[aria-pressed="true"] b{color:var(--purple)}
.handcard[aria-pressed="true"] .hs{filter:drop-shadow(0 6px 16px rgba(91,44,131,.22))}
.handcard small{font-size:12px;color:var(--muted)}
@media (max-width:520px){ .handcard .hs{height:22vh;min-height:130px} .handcard{padding:16px 10px 14px} }
/* Short screens, not narrow ones: a 1366x768 laptop has width to spare and no height, and that is
   where the opening-direction question fell off the bottom of the panel. Everything on this step
   gets tighter so both questions stay on screen together — a question you have to scroll to find
   reads as though the step is over. */
@media (max-height:820px){
  .handcard .hs{height:min(18vh,150px);min-height:104px}
  .handcard{padding:12px 12px 10px;gap:6px}
  .handcard b{font-size:14px}
  .handcard small{font-size:11px}
  .form-body.handstep .lead{margin-bottom:10px}
  .form-body.handstep .panel-label{margin-top:12px!important}
}
/* Sidelight cards (door + optional side panels) */
.tcards.sl{grid-template-columns:repeat(4,1fr)}
/* Dropdown fields (hardware & frame steps) */
.fsection{font-size:12px;font-weight:800;letter-spacing:1.2px;text-transform:uppercase;color:var(--purple);
  margin:22px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line)}
.fsection:first-of-type{margin-top:4px}
/* running total of a span group, e.g. "1300 / 1300 mm" — red while it doesn't add up */
.fsection{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.fsum{font-size:11px;font-weight:800;letter-spacing:.3px;text-transform:none;color:var(--muted);
  background:var(--purple-soft);padding:2px 9px;border-radius:999px;white-space:nowrap}
/* "Standard 850" reset next to the midrail field */
.stdchip{margin-left:8px;font-size:10px;font-weight:800;letter-spacing:.3px;text-transform:none;cursor:pointer;
  color:var(--purple);background:var(--purple-soft);border:1px solid transparent;padding:2px 8px;border-radius:999px}
.stdchip:hover{border-color:rgba(91,44,131,.35)}
.stdchip.on{color:#fff;background:var(--purple);cursor:default}
.fgrid{display:grid;grid-template-columns:1fr 1fr;gap:13px 16px}
.field{display:flex;flex-direction:column;gap:6px;min-width:0}
.field .fl{font-size:12px;font-weight:800;color:#3b4556}
.field .sel{position:relative;display:block}
.field .sel::after{content:'▾';position:absolute;right:13px;top:50%;transform:translateY(-50%);
  pointer-events:none;color:var(--muted2);font-size:12px}
.field select,.field input[type=text],.field input[type=date]{width:100%;height:46px;border:1px solid var(--line2);border-radius:12px;
  padding:0 34px 0 13px;font:inherit;font-size:14px;font-weight:700;color:var(--ink);
  background:rgba(255,255,255,.85);appearance:none;-webkit-appearance:none;cursor:pointer;text-overflow:ellipsis}
.field input[type=text]{padding-right:13px;cursor:text}
/* The date field came out as a bare browser control next to the styled selects, with our own ▾
   sitting on top of the native calendar button. It gets the same shell as everything else, and the
   chevron steps aside for the picker icon that actually opens something. */
.field .sel.date::after{content:none}
.field input[type=date]{padding-right:13px}
.field input[type=date]::-webkit-calendar-picker-indicator{
  cursor:pointer;opacity:.5;padding:4px;margin-right:-2px}
.field input[type=date]:hover::-webkit-calendar-picker-indicator{opacity:.9}
.field select:focus,.field input:focus{outline:none;border-color:var(--purple);box-shadow:0 0 0 2px rgba(91,44,131,.18)}
.field .fhint{font-size:11px;color:var(--muted);line-height:1.35}
/* a standalone hint line under a block — the compact sibling of .note.
   (inline hints inside .field keep the tighter rule above, which out-specifies this one) */
p.fhint,.fhint:not(.field .fhint){font-size:12.5px;line-height:1.5;color:var(--muted);font-weight:600}
p.fhint{position:relative;margin-top:12px;padding:2px 0 2px 24px}
p.fhint::before{content:"";position:absolute;left:8px;top:3px;bottom:3px;width:2px;border-radius:2px;
  background:linear-gradient(180deg,var(--purple),var(--purple2));opacity:.35}
/* Sizes step — live frame diagram + summary */
.framebox{background:linear-gradient(160deg,#f4eef8,#efe8f2);border:1px solid rgba(91,44,131,.12);
  border-radius:16px;padding:10px;display:grid;place-items:center;margin-bottom:12px}
.framesvg{width:100%;max-width:340px;max-height:330px;height:auto;display:block;pointer-events:auto}
/* the numbers are the tap target — keep them generous on touch */
.dimlab{pointer-events:auto;cursor:pointer;touch-action:manipulation}
/* a handful of glazing patterns have no artwork in tach — a frosted tile stands in for them */
.dcard.glass .th.noswatch{background:
  repeating-linear-gradient(135deg,rgba(91,44,131,.07) 0 6px,rgba(91,44,131,.02) 6px 12px),
  linear-gradient(160deg,#fbf9fd,#f1ecf6);
  display:grid;place-items:center;padding:6px}
.dcard.glass .th.noswatch .ph{position:static;font-size:11px;font-weight:800;line-height:1.25;
  color:var(--purple);text-align:center;opacity:1}
/* every number on the drawing is editable in place */
.framebox{position:relative}
.dimlab rect{transition:fill .12s ease}
.dimlab:hover rect{fill:var(--purple-soft)}
.dimlab:hover text{fill:var(--purple2)}
.dim-edit{position:absolute;transform:translate(-50%,-50%);z-index:5;width:74px;text-align:center;
  font:800 14px/1 Arial,Helvetica,sans-serif;color:var(--purple);background:#fff;
  border:2px solid var(--purple);border-radius:9px;padding:7px 4px;box-shadow:0 6px 18px rgba(91,44,131,.28)}
.dim-edit:focus{outline:none}
.ovbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 15px;
  background:var(--purple-soft);border:1px solid rgba(91,44,131,.14);border-radius:12px}
.ovbar span{font-size:11px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:var(--muted2)}
.ovbar b{font-size:17px;font-weight:800;color:var(--purple)}
.sizeerr{margin-top:8px;font-size:12.5px;font-weight:700;color:#b23b2e;background:rgba(192,57,43,.07);
  border:1px solid rgba(192,57,43,.2);border-radius:10px;padding:8px 12px}
.sizeok{margin-top:8px;font-size:12.5px;font-weight:700;color:var(--ok)}
/* number/dimension inputs — clean, with mm unit, no browser spinners */
.field .num{position:relative;display:block}
.field .num input{width:100%;height:52px;border:1px solid var(--line2);border-radius:12px;
  padding:0 48px 0 15px;font:inherit;font-size:17px;font-weight:800;color:var(--ink);letter-spacing:.3px;
  background:rgba(255,255,255,.9)}
.field .num input:focus{outline:none;border-color:var(--purple);box-shadow:0 0 0 3px rgba(91,44,131,.16);background:#fff}
.field .num .unit{position:absolute;right:14px;top:50%;transform:translateY(-50%);pointer-events:none;
  font-size:11.5px;font-weight:800;color:var(--muted2);text-transform:uppercase;letter-spacing:.6px}
.field .num input::-webkit-outer-spin-button,
.field .num input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.field .num input[type=number]{-moz-appearance:textfield;appearance:textfield}
/* Toggle chips (veka/liniar/hinge) */
.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.chip{border:1px solid var(--line2);background:rgba(255,255,255,.8);border-radius:999px;padding:9px 15px;font-weight:800;font-size:13px;color:#3b4556;display:inline-flex;align-items:center;gap:8px;min-height:42px}
.chip img{height:18px;width:auto}
/* A chosen chip is filled, not tinted. Beside the option cards — which go purple border, purple
   icon, purple title when picked — a pale lilac pill read as "hovered" rather than "this is the
   answer", and Open In / Open Out is a real answer with consequences two steps later. */
.chip[aria-pressed="true"]{border-color:var(--purple);background:var(--purple);color:#fff;
  box-shadow:0 1px 3px rgba(91,44,131,.28)}
.chip[aria-pressed="true"] .aot{color:rgba(255,255,255,.78)}
/* compact two-line glass chip (sidelight / toplight glazing) */
.glasschips{gap:10px}
.glasschip{flex-direction:column;align-items:flex-start;gap:1px;border-radius:12px;padding:9px 14px;min-height:52px;line-height:1.25}
.glasschip b{font-size:13px}
.glasschip span{font-size:11px;font-weight:600;color:var(--muted)}
.glasschip[aria-pressed="true"] span{color:rgba(255,255,255,.82);opacity:1}
/* required-field affordances */
.reqtag{display:inline-block;margin-left:8px;font-size:10px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;
  color:#dc2626;background:rgba(220,38,38,.1);padding:2px 8px;border-radius:999px;vertical-align:middle}
.reqtag.ok{color:var(--purple);background:var(--purple-soft)}
.flash-req{border-radius:14px;animation:flashReq 1.7s ease}
@keyframes flashReq{0%{box-shadow:0 0 0 0 rgba(220,38,38,.55)}60%{box-shadow:0 0 0 9px rgba(220,38,38,0)}100%{box-shadow:0 0 0 0 rgba(220,38,38,0)}}
.toast{position:fixed;left:50%;bottom:90px;transform:translateX(-50%) translateY(18px);z-index:70;
  background:#dc2626;color:#fff;font-weight:800;font-size:13.5px;padding:11px 18px;border-radius:999px;
  box-shadow:0 14px 34px rgba(0,0,0,.28);opacity:0;pointer-events:none;transition:.24s ease}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
.toast.ok{background:var(--ok)}
/* ---- review: every line is a shortcut back to the step that owns it ---- */
.rvlist{border:1px solid var(--line);border-radius:14px;overflow:hidden}
.rvrow{display:grid;grid-template-columns:minmax(84px,.9fr) minmax(0,1.6fr) auto;gap:10px;align-items:center;
  width:100%;padding:10px 12px;font-size:13.5px;text-align:left;background:none;border:0;cursor:pointer;
  border-bottom:1px solid var(--line2);transition:background .15s ease}
.rvrow:last-child{border-bottom:0}
.rvrow.alt{background:rgba(255,255,255,.4)}
.rvrow:hover{background:var(--purple-soft)}
.rvrow .rl{color:var(--muted)}
.rvrow .rv{text-align:right;font-weight:800;min-width:0;overflow-wrap:anywhere}
.rvrow .redit{font-size:10px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:var(--purple);
  background:var(--purple-soft);padding:3px 8px;border-radius:999px;opacity:0;transition:opacity .15s ease}
.rvrow:hover .redit,.rvrow:focus-visible .redit{opacity:1}
.rvrow.miss{background:rgba(220,38,38,.07)}
.rvrow.miss .rv{color:#dc2626}
.rvrow.miss .redit{opacity:1;color:#fff;background:#dc2626;width:18px;height:18px;padding:0;display:grid;place-items:center;font-size:12px}
.rvalert{display:flex;flex-wrap:wrap;gap:2px 10px;align-items:baseline;margin:0 0 14px;padding:11px 14px;border-radius:12px;
  border:1px solid rgba(220,38,38,.3);background:rgba(220,38,38,.07);font-size:13px}
/* `hidden` is only display:none in the browser's own stylesheet, and the rule above outranks it —
   so hiding the banner from script left an empty red bar sitting there (Misha, 2026-08-13). */
.rvalert[hidden]{display:none}
.rvalert b{color:#dc2626}
.rvalert span{color:var(--muted)}
/* ---- the "you came here from the review" return bar ---- */
.rvbar{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center;justify-content:space-between;
  margin:0 0 18px;padding:9px 9px 9px 15px;border-radius:12px;background:var(--purple-soft);
  border:1px solid rgba(91,44,131,.22);font-size:12.5px;font-weight:700;color:var(--purple)}
.rvback{border:1px solid rgba(91,44,131,.3);background:#fff;color:var(--purple);font-weight:800;font-size:12.5px;
  padding:7px 14px;border-radius:999px;cursor:pointer}
.rvback:hover{background:var(--purple);color:#fff}
@media (max-width:640px){
  .rvrow{grid-template-columns:minmax(0,1fr) auto;row-gap:2px;padding:11px 12px}
  .rvrow .rl{grid-column:1;font-size:11.5px;text-transform:uppercase;letter-spacing:.5px;font-weight:800}
  .rvrow .rv{grid-column:1;text-align:left}
  .rvrow .redit{grid-column:2;grid-row:1 / span 2;opacity:1}
}
/* the chosen sidelight/toplight layout + its glazing choices, pinned above the layout grid */
.chosen-layout{display:grid;grid-template-columns:132px minmax(0,1fr);gap:18px;align-items:start;
  margin:4px 0 22px;padding:16px;border:1px solid rgba(91,44,131,.22);background:var(--purple-soft);border-radius:16px}
.chosen-layout .cl-img{width:132px;height:150px;background:#fff;border:1px solid var(--line2);
  border-radius:12px;position:relative;overflow:hidden}          /* clip: the schematic SVG has no intrinsic size */
.chosen-layout .cl-img img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:9px}
/* the real opening replaces the schematic once it has been composed; the <img> stays underneath as
   the fallback for when it cannot be */
.chosen-layout .cl-img .misvg{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:7px}
.chosen-layout .cl-img .misvg svg{width:100%;height:100%;filter:drop-shadow(0 6px 10px rgba(0,0,0,.18))}
.chosen-layout .cl-img:has(.misvg) img{display:none}
.chosen-layout .cl-body{min-width:0}
.chosen-layout .cl-head{margin-bottom:12px}
.chosen-layout .cl-eyebrow{display:block;font-size:10px;font-weight:800;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--purple);opacity:.75;margin-bottom:2px}
.chosen-layout .cl-head b{font-size:16px;color:var(--ink)}
.chosen-layout .panel-label{margin:0 0 8px}
/* glass options as an even grid so they never wrap into ragged rows */
.glasschips{display:grid;grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:8px;margin-top:0}
.glasschip{width:100%;justify-content:flex-start}
@media (max-width:640px){
  /* stack, but keep the schematic portrait — a full-width strip squashes the door */
  .chosen-layout{grid-template-columns:1fr;justify-items:center;padding:14px}
  .chosen-layout .cl-img{width:118px;height:138px}
  .chosen-layout .cl-body{width:100%}
  .chosen-layout .cl-head{text-align:center}
  .glasschips{grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}
}
/* layout schematics (coupled sidelight/toplight variants) — show the whole diagram, not a crop */
/* A short row of layouts stops at its own width instead of spreading across the panel. --n is the
   number of cards, set on the element; the fallback keeps the old full-width behaviour if it is
   ever rendered without one. auto-fill still wraps below this, so a phone is unaffected. */
.dgrid.layouts{max-width:calc(var(--n,99) * 190px)}
/* the square is a fallback only — fitLayoutTiles() replaces it with the drawing's own shape as soon
   as the viewBox is read, so the schematic fills the tile instead of being letterboxed in white */
/* ONE box for every layout, at the middle of the range these schematics span (0.385 for a door with
   a toplight, 0.813 for a pair of sidelights — median 0.626). Per-card shapes gave every card in a
   row a different height and turned the grid into rubble; a common box also puts every drawing on
   one scale, so a wider opening is drawn wider, which is what is being compared. The cards stretch
   to the row so a caption that wraps onto two lines does not drag its tile out of line. */
.dgrid.layouts .dcard{display:flex;flex-direction:column}
.dgrid.layouts .th{aspect-ratio:.62/1;background:linear-gradient(160deg,#f7f4fa,#f1edf5)}
.dgrid.layouts .cap{flex:1}
.dgrid.layouts .th img{padding:10px}          /* keep the base absolute inset:0 + object-fit:contain */
.dgrid.layouts .th .slglyph{height:78%;width:auto;color:var(--purple);opacity:.55}
.dgrid.layouts .cap{font-size:11.5px;line-height:1.3}
.sel.unset{box-shadow:inset 0 0 0 1.5px rgba(220,38,38,.4)}
.sel.unset select{color:#b91c1c}
/* Design grid */
.dgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
.dcard{border:1px solid var(--line2);background:#fff;border-radius:14px;overflow:hidden;padding:0;text-align:left;transition:.15s}
.dcard:hover{border-color:rgba(91,44,131,.3)}
.dcard[aria-pressed="true"]{border-color:var(--purple);box-shadow:0 0 0 2px rgba(91,44,131,.22)}
.dcard .th{aspect-ratio:1/1.55;background:linear-gradient(160deg,#f2eef6,#e9e2d8);display:grid;place-items:center;overflow:hidden;position:relative}
/* absolutely fill the tile — the full photo always fits, never cropped or spilling */
.dcard .th img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:6px}
.dcard .th .ph{position:absolute;color:var(--muted2);font-size:11px;font-weight:700;text-align:center;padding:0 6px}
/* sizes — one block per part of the door, so the door's numbers are never mixed with the toplight's */
.partblk{border:1px solid var(--line);border-radius:14px;padding:12px 13px;margin-bottom:10px;
  background:rgba(255,255,255,.55)}
.parthd{font-size:12px;font-weight:800;letter-spacing:1.1px;text-transform:uppercase;
  color:var(--purple);margin-bottom:9px}
.balance{display:flex;gap:8px;flex-wrap:wrap;margin-left:auto}
.balance .fsum{font-size:11.5px}
.fsum.ok{color:var(--ok)}
/* an opening a touch too big for the frame — the add-ons that would make it work */
.aoff{margin-top:10px;font-size:13px;line-height:1.5;color:var(--ink);
  border-left:3px solid var(--purple);padding:8px 0 8px 11px}
.aoff b{color:var(--purple)}
.aorow{display:flex;align-items:center;gap:12px;margin-top:9px;flex-wrap:wrap}
.aomm{font-size:13px;font-weight:800;color:var(--ink);min-width:56px}
.aorow .chips{margin:0;gap:7px}
.chip.ao{border-color:var(--purple);color:var(--purple);font-weight:800;text-transform:capitalize}
.chip.ao .aot{color:var(--muted2);font-weight:700;margin-left:5px;text-transform:none}
/* letterplate position — picked by looking at the door, so the cards stay big enough to compare
   even on a phone, and the plate itself is what differs between them */
.dgrid.lppos{grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}
.dgrid.lppos .cap{display:flex;flex-direction:column;gap:2px}
.dgrid.lppos .cap small{font-weight:700;font-size:10.5px;color:var(--muted2);text-transform:uppercase;letter-spacing:.6px}
/* a position that was never a choice: shown, settled, not greyed out — it is an answer, not a
   disabled control, so it keeps the chosen border and full contrast */
.dcard.fixed{cursor:default;opacity:1}
.dcard.fixed:hover{transform:none;box-shadow:none}
/* the plates themselves — three different shapes, so the part is shown rather than named. A plate is
   a wide flat thing, not a door, so these tiles are landscape rather than inheriting the door card's
   portrait crop, which left the photo swimming in empty space. */
/* A letterplate is a long thin strip. In a 1.6:1 tile it sat as a small bar across the middle of a
   mostly empty box, and the three cards read as three empty boxes with captions. The tile is cut to
   the shape of the thing in it, and the plate is given the width — which is the dimension that
   actually differs between an All Metal, a TS008 and an Era. */
.dgrid.lpplate{grid-template-columns:repeat(auto-fill,minmax(190px,1fr))}
.dgrid.lpplate .th{aspect-ratio:3.1/1;background:linear-gradient(160deg,#f6f4fa,#eceaf3)}
.dgrid.lpplate .th img{padding:0 10px;object-fit:contain}
/* "None" is a choice, not a blank tile: say so in the middle of it, in the same voice as the caption
   underneath rather than as grey placeholder text in an empty box */
.dgrid.lpplate .th .ph{font-size:12px;font-weight:800;letter-spacing:.3px;color:var(--muted2);
  text-transform:uppercase}
.dgrid.lpplate .dcard[aria-pressed="true"] .th .ph{color:var(--purple)}
/* The side panel's two cards.
   A side panel is 347 wide and 1966 tall — a shape nothing else on the step has, and dropped into
   the door card's 1/1.55 tile it sat as a thin strip in the middle of a lot of nothing. The tile
   keeps the door card's HEIGHT, so the two questions read as one step, and loses the width it has no
   use for: 120 wide against the door's 150, with only two of them. */
.dgrid.sidepanel{grid-template-columns:repeat(auto-fill,minmax(120px,1fr));max-width:380px}
.dgrid.sidepanel .th{aspect-ratio:.52/1}
.dgrid.sidepanel .th .misvg{padding:4px 2px}
/* the real render, once the card has scrolled into view */
.dcard .th .misvg{position:absolute;inset:0;min-width:0;min-height:0;display:grid;place-items:center;padding:6px}
.dcard .th .misvg svg{width:100%;height:100%;max-width:100%;max-height:100%;
  filter:drop-shadow(0 5px 8px rgba(0,0,0,.16))}
/* glass swatches fill the tile (they're textures); real BM Touch photos */
.dcard.glass .th img{object-fit:cover;padding:0}
.dcard .cap{padding:8px 9px;font-size:12px;font-weight:800;color:var(--ink)}
.dcard[aria-pressed="true"] .cap{color:var(--purple)}
/* Colour swatches — tappable cards: big colour circle + name, ✓ when chosen */
.swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(82px,1fr));gap:10px;margin-top:10px}
.sw{appearance:none;border:1px solid var(--line2);background:rgba(255,255,255,.8);border-radius:14px;
  padding:12px 6px 10px;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:8px;transition:.15s}
.sw:hover{border-color:rgba(91,44,131,.35);transform:translateY(-1px);box-shadow:var(--shadow)}
.sw[aria-pressed="true"]{border-color:var(--purple);box-shadow:0 0 0 2px rgba(91,44,131,.22);background:#fff}
.sw .dot{display:block;width:46px;height:46px;border-radius:50%;position:relative;flex:none;overflow:hidden;
  border:1px solid rgba(0,0,0,.14);
  box-shadow:inset 0 3px 7px rgba(255,255,255,.35),inset 0 -3px 7px rgba(0,0,0,.14)}
.sw .dot img{width:100%;height:100%;object-fit:cover;display:block}
/* real photo swatches look better without the inner gloss */
.sw .dot:has(img){box-shadow:inset 0 0 0 1px rgba(0,0,0,.06)}
/* near-white finishes: a firmer edge so the dot still reads on the white card */
.sw .dot.light{border-color:rgba(27,34,48,.28);
  box-shadow:inset 0 3px 7px rgba(255,255,255,.5),inset 0 -4px 8px rgba(27,34,48,.10)}
.sw .dot.ral{background:conic-gradient(from 20deg,#e05c5c,#e0a95c,#e6df5c,#5fce6a,#5cb0e0,#8a5ce0,#e05ccf,#e05c5c)}
.sw-ral .nm{color:var(--purple)}
.sw[aria-pressed="true"] .dot::after{content:'✓';position:absolute;inset:0;display:grid;place-items:center;
  color:#fff;font-weight:800;font-size:17px;text-shadow:0 1px 4px rgba(0,0,0,.55)}
.sw .nm{display:block;font-size:10.5px;font-weight:700;color:var(--muted);line-height:1.25;text-align:center}
.sw[aria-pressed="true"] .nm{color:var(--purple)}
/* Summary bar */
.summary{position:fixed;left:0;right:0;bottom:0;z-index:40;background:rgba(255,255,255,.9);
  border-top:1px solid var(--line);backdrop-filter:blur(14px);box-shadow:0 -8px 30px rgba(27,34,48,.08)}
.btn-primary{padding:14px 28px;font-size:15px;font-weight:800;border:none;border-radius:16px;color:#fff;
  background:linear-gradient(135deg,var(--purple),var(--purple2));box-shadow:var(--shadow-lg);display:inline-flex;align-items:center;gap:8px;transition:.2s}
.btn-primary:hover{transform:translateY(-2px)}
.btn-primary:disabled{opacity:.5;filter:grayscale(.3);box-shadow:none;transform:none;cursor:not-allowed}
/* Continue, on a step that has one more thing to choose.
   Not an error and it must not look like one. The first version was red with an exclamation mark in
   front of the name, and it read as a telling-off — the customer is part-way through a form, which is
   the normal state of a form (UCD, 2026-08-13). Amber, the word "Next", and the same arrow the
   finished button carries: it is still the way forward, it just names where it is going.
   Not disabled either — pressing it is the fastest way to the field, and a dead button teaches the
   customer to stop pressing the one control that helps.
   Width-capped because one of the names is a sentence: the obscure glass block is headed "Pattern
   behind the obscure glass" and the button carries that heading word for word, so past the cap it
   ellipsises rather than stretching the footer bar. */
.btn-primary.gap{background:#fffaf0;color:#8a5a12;border:1.5px solid rgba(217,150,50,.5);
  box-shadow:0 8px 20px rgba(180,120,30,.13);padding:12.5px 18px;gap:9px;max-width:min(48vw,340px)}
.btn-primary.gap:hover{transform:translateY(-2px);border-color:rgba(217,150,50,.85);
  box-shadow:0 10px 24px rgba(180,120,30,.2)}
.btn-primary.gap .gapnext{flex:none;font-size:10px;font-weight:800;letter-spacing:.9px;
  text-transform:uppercase;color:#a9772b;background:rgba(217,150,50,.16);padding:3px 8px;border-radius:999px}
/* the name can be long ("Hardware colour") and the bar is narrow on a phone — it gets the room */
.btn-primary.gap .gaptext{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.btn-primary.gap .gapmore{flex:none;font-size:11.5px;font-weight:800;color:#b98536}
.btn-primary.gap .gaparrow{flex:none;font-weight:800;opacity:.75}
@media (max-width:430px){ .btn-primary.gap .gapnext,.btn-primary.gap .gapmore{display:none} }
.btn-ghost{padding:0 18px;height:48px;border-radius:16px;border:1px solid var(--line2);background:rgba(255,255,255,.9);color:#3b4556;font-weight:800;display:inline-flex;align-items:center;gap:7px}
/* Chooser modal */
.scrim{position:fixed;inset:0;z-index:60;background:rgba(27,20,40,.5);display:grid;place-items:center;padding:20px;backdrop-filter:blur(4px)}
.coming{padding:40px 10px;text-align:center;color:var(--muted2)}
.coming .ic{font-size:28px;margin-bottom:10px}
/* Responsive */
@media (max-width:1080px){
  .main{grid-template-columns:1fr;gap:12px}
  .dgrid{max-height:none}
}
@media (max-width:560px){
  .hdr .center{display:none}
}
/* ===== Form layout (form-first, portal-styled) ===== */
.app{padding-bottom:28px}
.stepper{margin-top:14px;padding:10px 12px;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius);backdrop-filter:blur(12px);box-shadow:var(--shadow);
  display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;
  /* Centred when the ten steps fit, which on a wide screen left the whole right half of the bar
     empty. `safe` is the important word: plain `center` on an overflowing flex row pushes the first
     items past the scroll origin, where nothing can reach them — on a narrow window "Door type"
     would be unreachable. With `safe`, it falls back to start alignment the moment it overflows. */
  justify-content:center;justify-content:safe center}
.stepper::-webkit-scrollbar{display:none}
.stepper .step{display:flex;align-items:center;gap:9px;padding:8px 12px;border-radius:13px;cursor:pointer;
  white-space:nowrap;flex:none;border:1px solid transparent;transition:.15s}
.stepper .step:hover{background:rgba(91,44,131,.05)}
.stepper .step.active{background:var(--purple-soft);border-color:rgba(91,44,131,.14)}
.stepper .step .ic{width:26px;height:26px;border-radius:9px;display:grid;place-items:center;flex:none;
  background:#fff;border:1px solid var(--line);color:var(--muted2);font-size:13px}
.stepper .step.active .ic{background:linear-gradient(135deg,var(--purple),var(--purple2));color:#fff;border-color:transparent}
.stepper .step.done .ic{background:#eaf4ee;color:var(--ok);border-color:transparent}
.stepper .step b{font-size:13px;font-weight:800;color:var(--ink)}
.stepper .step.active b{color:var(--purple)}
.stepper .step .sub{display:none}
.form{margin-top:16px;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  backdrop-filter:blur(12px);box-shadow:var(--shadow);overflow:hidden;
  flex:1;display:flex;flex-direction:column}
.form-grid{display:grid;grid-template-columns:1fr 260px;flex:1;min-height:0}
/* type step has no meaningful preview — drop the side strip, go full width */
.form.no-mini .mini{display:none}
.form.no-mini .form-grid{grid-template-columns:1fr}
.form-body{padding:26px 28px;overflow-y:auto;min-height:0}
.form-body .eyebrow{font-size:11px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;color:var(--purple)}
.form-body h3{font-size:23px;font-weight:800;margin:6px 0 6px;letter-spacing:-.01em}
.form-body .lead{color:var(--muted);font-size:14px;line-height:1.55;margin-bottom:18px;max-width:52ch}
/* a short aside inside a step — the heavier sibling of p.fhint */
.form-body .note{margin-top:16px;background:var(--purple-soft);border:1px solid rgba(91,44,131,.18);
  border-radius:12px;padding:12px 15px;font-size:13px;line-height:1.5;color:#463f52;
  display:flex;gap:10px;align-items:flex-start;font-weight:600;max-width:52ch}
.mini{border-left:1px solid var(--line);padding:22px 20px;background:rgba(255,255,255,.4)}
.mini-card{position:sticky;top:14px}
.mini-img{aspect-ratio:1/1.4;border-radius:16px;overflow:hidden;display:grid;place-items:center;position:relative;
  isolation:isolate;background:radial-gradient(120% 90% at 50% 10%,var(--stage-a),var(--stage-b))}
.mini-img img{height:100%;width:auto;object-fit:contain;filter:drop-shadow(0 14px 20px rgba(0,0,0,.22))}
.mini-img .tint{position:absolute;inset:0;mix-blend-mode:multiply;opacity:.88;pointer-events:none;transition:background .2s}
.mini-img .ph{color:var(--muted2);font-size:12px;text-align:center;line-height:1.6;padding:0 10px}
.mini-img .ph-retry{margin-top:8px;border:1px solid var(--line2);background:#fff;color:var(--purple);
  font-weight:800;font-size:11.5px;padding:6px 12px;border-radius:999px}
/* min-height/min-width auto on a grid item lets the SVG push the wrapper past the frame, and
   overflow:hidden then clips the top and bottom off the door — pin them to 0 so height:100% wins */
.mini-img .misvg{width:100%;height:100%;min-width:0;min-height:0;
  display:flex;align-items:center;justify-content:center;padding:6px}
.mini-img .misvg svg{width:100%;height:100%;max-width:100%;max-height:100%;
  filter:drop-shadow(0 12px 18px rgba(0,0,0,.22))}
.mini-img .misvg+.ph{display:none}
.mini-meta{margin-top:12px}
.mini-meta b{font-size:15px;display:block}
.mini-row{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted);margin-top:5px}
.mini-row .dot{width:15px;height:15px;border-radius:50%;border:1px solid rgba(0,0,0,.15)}
.form-foot{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:14px 20px;border-top:1px solid var(--line);background:rgba(255,255,255,.55)}
.form-foot .build{min-width:0;display:flex;flex-direction:column}
.form-foot .build .k{font-size:10px;font-weight:800;letter-spacing:1.2px;text-transform:uppercase;color:var(--muted2)}
.form-foot .build #sumBuild{font-size:13.5px;font-weight:700;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:42ch}
.foot-btns{display:flex;gap:10px;flex:none}
@media (max-width:820px){
  /* let the document grow & scroll normally (base rule pins body to 100vh) */
  html,body{height:auto;min-height:100%}
  body{display:block;overflow-y:auto;-webkit-overflow-scrolling:touch}
  .app{width:100%;min-height:100dvh;padding:0 10px calc(12px + var(--safe))}
  /* The header travels with the scroll: "Show door" is the only way to see the door properly on a
     phone, and it was scrolling out of reach exactly when the customer is deep in the options and
     most wants to look. The stepper does NOT follow — pinning both ate a fifth of the screen and
     the half-scrolled chips looked like a mistake rather than a design. */
  .hdr{position:sticky;top:0;z-index:25;margin-top:8px;padding:9px 14px;border-radius:16px}
  .hdr .center{display:none}
  .hdr .brand img{height:38px}
  .stepper{margin-top:10px;padding:7px;border-radius:16px}
  .stepper .step{padding:8px 12px;min-height:42px}
  .stepper .step b{font-size:13px}
  /* form fills the screen: short steps push the footer to the bottom,
     long steps scroll the PAGE (no inner clipping) with the footer stuck */
  .form{margin-top:10px;border-radius:18px;flex:1;overflow:visible}
  .form-grid{grid-template-columns:1fr;flex:1;min-height:auto;align-content:start}
  .form-body{overflow:visible}
  /* compact sticky door preview strip */
  .mini{order:-1;border-left:none;border-bottom:1px solid var(--line);
    display:flex;gap:14px;align-items:center;padding:11px 16px;
    background:rgba(255,255,255,.6)}
  .mini-card{position:static;display:flex;gap:14px;align-items:center;flex:1;min-width:0}
  .mini-img{width:52px;height:76px;aspect-ratio:auto;flex:none;border-radius:10px}
  .mini-meta{min-width:0}
  .mini-meta b{font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .tcards{grid-template-columns:1fr 1fr;gap:10px}
  .tcard{padding:13px 15px}
  .fgrid{grid-template-columns:1fr;gap:11px}
  /* the six-column grid collapses: the brand cards are short enough to stay two-up, the security
     and core cards carry a mark and a sentence and need the full width */
  .tcards.pf{grid-template-columns:1fr 1fr}
  .tcards.sec{grid-template-columns:1fr}
  .tcards.pf>*,.tcards.sec>*,.tcards.sec#coreBlock>*{grid-column:auto}
  .tcards.sl{grid-template-columns:1fr 1fr}
  .field select,.field input[type=text]{height:48px}   /* bigger tap target */
  /* profile: same row layout as the desktop, just tighter */
  .pfcard{padding:12px 13px;gap:11px}
  .pf-logo{width:50px;height:50px;border-radius:13px}
  .pfcard b{font-size:16px}
  .form-body{padding:16px 15px}
  .form-body h3{font-size:20px}
  .form-body .lead{font-size:13.5px;margin-bottom:14px}
  /* 3-up compact grid — 24 styles shouldn't be a kilometre of scroll */
  .dgrid{grid-template-columns:repeat(3,1fr);gap:8px;max-height:none}
  /* …but the letterplate height has to be visible, so those cards get half the screen each */
  .dgrid.lppos{grid-template-columns:repeat(2,1fr);gap:10px}
  .dgrid.lpplate{grid-template-columns:repeat(2,1fr);gap:10px}
  .dgrid.lpplate .th img{padding:10px}
  .dcard .cap{padding:6px 7px;font-size:10.5px}
  .swatches{grid-template-columns:repeat(auto-fill,minmax(76px,1fr));gap:8px}
  .chip{min-height:44px;padding:11px 16px}
  /* Sticky bottom action bar. It DOES overlap — that is what sticky means — and at 94% white the
     content passing under it stayed visible: a letterplate tile scrolling past showed through the
     bar and read as a picture escaping its card. Opaque, so what is behind it is behind it. */
  .form-foot{position:sticky;bottom:0;z-index:40;margin-top:auto;
    background:var(--panel-solid);border-top:1px solid var(--line);
    padding:10px 14px calc(10px + var(--safe));box-shadow:0 -8px 24px rgba(27,34,48,.10)}
  /* and room to scroll the last row clear of the bar, instead of it ending half-hidden behind it */
  .form-body{padding-bottom:76px}
  .form-foot .build{flex:1}
  .form-foot .build #sumBuild{max-width:none}
  .foot-btns .btn-primary{padding:13px 22px}
  .foot-btns .btn-ghost{height:46px;padding:0 15px}
}
@media (max-width:430px){
  .form-foot .build{display:none}
  .foot-btns{flex:1}
  .foot-btns .btn-ghost{flex:none}
  .foot-btns .btn-primary{flex:1;justify-content:center}
  /* The cap is for the desktop bar, where a long name would push the footer wide. Here the button is
     told to FILL the row, and the two fought: on step one, where there is no Back button beside it,
     the cap won and left a hole to its right (Misha, 2026-08-13). The name ellipsises anyway. */
  .foot-btns .btn-primary.gap{max-width:none}
  /* ...but filling the row is only right while Back is there to share it. Alone, a full-width button
     reads as a banner rather than as a control, so on step one it takes its own width and sits at
     the end of the row like every other Continue. */
  .foot-btns.solo{justify-content:flex-end}
  .foot-btns.solo .btn-primary{flex:none;max-width:min(70vw,340px)}
  .stepper .step:not(.active) b{display:none}
  .mini-img{width:46px;height:66px}
}

/* ---- a step you can't open yet ---- */
.stepper .step.locked{opacity:.45;cursor:not-allowed}
.stepper .step.locked:hover{background:transparent}
/* ---- the review's summary card ---- */
.rvhead{display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:center;margin:0 0 16px;
  padding:14px;border:1px solid var(--line2);border-radius:16px;background:rgba(255,255,255,.55)}
/* the summary photo is the same live render as the side preview, just smaller */
.rvhead{grid-template-columns:auto minmax(0,1fr)}
.rvhead-img{width:104px;height:146px;border-radius:12px;position:relative;overflow:hidden;
  display:grid;place-items:center;aspect-ratio:auto;
  background:radial-gradient(120% 90% at 50% 10%,var(--stage-a),var(--stage-b))}
.rvhead-tx b{font-size:17px}
/* the reference is the first thing asked for on the review */
.field.rvref{margin:0 0 16px;max-width:420px}
.rvhead-tx{min-width:0}
.rvhead-tx b{display:block;font-size:16px;line-height:1.25}
.rvhead-tx span{display:block;font-size:12.5px;color:var(--muted);line-height:1.5;overflow-wrap:anywhere}
/* ---- notes ---- */
/* The ▾ belongs to a <select>; on a textarea it was a chevron pointing at nothing, and it sat over
   the resize grip. */
.field .sel.area::after{content:none}
.field textarea{width:100%;border:1px solid var(--line2);border-radius:12px;padding:10px 12px;
  font:inherit;font-size:14px;color:var(--ink);background:#fff;
  min-height:46px;overflow:hidden;resize:none;display:block}
.field textarea:focus{outline:none;border-color:var(--purple);box-shadow:0 0 0 3px rgba(91,44,131,.12)}


/* ============================================================================
   Tablet & desktop. The base rules are the phone layout; these widen it in two
   stages so a big screen actually gains room instead of just stretching.
   ============================================================================ */

/* --- tablet in landscape and small laptops: the side preview earns its place --- */
@media (min-width:900px){
  .form-grid{grid-template-columns:minmax(0,1fr) 320px}
  .form-body{padding:26px 30px}
}
/* On a desktop the preview is the whole point of the side column and it was being shown at barely
   more than the phone's thumbnail size. Let it use the room — but only the room it has.
   A grid item's min-height is auto, so the preview column grew to whatever the door wanted and
   pushed straight out of its cell; the form being a flex column with the footer after it, the door
   came down over the Continue button. The cap was `max-height: 100vh - 210px`, and 210 is a guess at
   the header, the stepper, the footer, the column's padding and the caption under the picture. On a
   short window the guess is too small and it pushed through anyway — measured at 1300x620: a 503px
   column in a 333px cell, 148px of it lying across the footer.
   No guess now. The column may not exceed its cell, and the picture takes what is left after the
   caption, which is a thing the browser can work out on its own at any window size. */
@media (min-width:821px){
  .mini{min-height:0;overflow:hidden;display:flex;flex-direction:column}
  .mini-card{flex:1;min-height:0;display:flex;flex-direction:column}
  .mini-img{flex:1 1 auto;min-height:0}
  .mini-meta{flex:none}
}

/* --- desktop: use the width for more columns, not longer lines --- */
@media (min-width:1200px){
  .app{width:min(1400px,calc(100% - 40px))}
  .form-grid{grid-template-columns:minmax(0,1fr) 400px}
  .chosen-layout{grid-template-columns:170px minmax(0,1fr)}
  .chosen-layout .cl-img{width:170px;height:194px}
  .form-body{padding:30px 36px}
  /* short answers don't need a 460px control each */
  .fgrid{grid-template-columns:repeat(3,minmax(0,1fr));gap:14px 18px}
  /* a dimension label and its value 950px apart is unreadable — keep the pair together.
     The reference/date pair and the notes box belong in the same measure: capped separately, the
     boxes the customer types into ran wider than the table beneath them and the column edges
     disagreed down the page. */
  .rvlist,.rvhead,.form-body .note,.rvalert,.rvtop,.rvnotes{max-width:860px}
  .rvrow{grid-template-columns:minmax(150px,260px) minmax(0,1fr) auto}
  .dgrid{grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:12px}
  .swatches{grid-template-columns:repeat(auto-fill,minmax(92px,1fr));gap:12px}
  .mini-card{top:20px}
}

/* --- very wide: cap the reading column, let the preview grow --- */
@media (min-width:1600px){
  .form-grid{grid-template-columns:minmax(0,1fr) 470px}
  .fgrid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .dgrid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
}

/* --- tablet portrait (the stacked layout, but with room to breathe) --- */
@media (min-width:640px) and (max-width:899px){
  .form-body{padding:22px 24px}
  .fgrid{grid-template-columns:1fr 1fr;gap:13px 16px}
  .dgrid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
  /* the preview strip becomes a proper card instead of a thin band */
  .mini{padding:16px 24px}
  .mini-card{display:grid;grid-template-columns:88px minmax(0,1fr);gap:16px;align-items:center}
  .mini-img{width:88px;height:124px;aspect-ratio:auto}
  }

/* a height that doesn't fit has two ways out — the threshold is the one that costs nothing */
.thfix{margin:8px 0 10px;font-size:13px;line-height:1.5;color:var(--muted)}
.thfix .chips{margin-top:7px}

/* special RAL — the typed colour, and what gets written into the notes for it */
.ralbox{display:flex;align-items:flex-start;gap:12px;margin-top:12px}
.ralbox .field{flex:1;min-width:0}
.ralchip{width:46px;height:46px;border-radius:12px;flex:none;margin-top:26px;
  border:1px solid rgba(0,0,0,.15);box-shadow:var(--shadow)}
.reqtag.ok{color:var(--ok);background:#eaf4ee}
.autonotes{border-left:3px solid var(--purple);padding:8px 0 8px 11px;margin-bottom:10px;
  display:flex;flex-direction:column;gap:3px}
.autonotes b{font-size:11.5px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:var(--purple)}
.autonotes span{font-size:13px;color:var(--muted)}

/* a RAL that doesn't exist — said plainly, with the nearest ones to pick from */
.ralbox.bad .fl{color:#b3261e}
.reqtag.bad{color:#b3261e;background:#fdecea}
.ralbad{color:#b3261e;display:block;margin-bottom:2px}
.ralchip.none{display:grid;place-items:center;background:repeating-linear-gradient(45deg,
  #f3f4f6 0 6px,#e5e7eb 6px 12px);color:#b3261e;font-weight:800;font-size:18px}
.ralnear{flex-basis:100%;display:flex;align-items:center;flex-wrap:wrap;gap:7px;margin-top:2px}
.ralnear>span{font-size:12.5px;color:var(--muted);font-weight:700}
.ralnear .chip.ao{text-transform:none;font-weight:700}
.ralnear .dot{width:13px;height:13px;border-radius:50%;border:1px solid rgba(0,0,0,.15);margin-right:6px}
.ralbox{flex-wrap:wrap}

/* A plate we hold no photograph of, shown as the same plate in the nearest metal. Only Stainless
   Steel needs it. Cooled and slightly dulled so it reads as brushed steel rather than mirror chrome
   — near enough to recognise the part, not pretending to be the photograph. */
.lpplate .th img.lent{filter:saturate(.55) brightness(.97) hue-rotate(-6deg)}

/* The metal, under the model. Quiet — it is a label on a choice already made higher up the step,
   not a second question. */
.lpplate .cap span{display:block;margin-top:2px;font-weight:600;font-size:11px;color:var(--muted)}
.lpplate .dcard[aria-pressed="true"] .cap span{color:var(--purple)}

/* Master leaf picker — the double door's replacement for the hinge side.
   It IS the hinge card (.handcard/.hs give it the size, the badge and the chosen-state mark); all
   that is added here is the drawing's own shape, so that half the box is exactly one leaf, and the
   wash and the M that mark it. Without the shape both landed in the white space beside the door. */
.mcard .ms{margin:0 auto;width:auto;max-width:100%}
/* Both cards mark a leaf, so if both marks are purple the colour stops meaning "chosen" and starts
   meaning "this side" — which is why picking one barely looked different from not picking it.
   So the mark is GREY until the card is the answer, and only then does it light up. */
/* The wash marks a LEAF, not half the picture. Measured off the drawing itself
   (assets/handing/twin_ln_lh.svg, viewBox -20 -20 1840 2340): the leaves are x 54..900 and 900..1746,
   so they meet exactly halfway, but each one stops 74 units short of the edge — that is the outer
   frame. Running the wash to the edge of the box put it over the frame as well as the leaf, which is
   the mismatch. So it is inset by the frame: 4% at the sides, 3.2% top and bottom. */
.mcard .mtint{position:absolute;top:3.2%;bottom:3.2%;pointer-events:none;border-radius:5px;
  background:rgba(20,20,30,.07);border:2px solid rgba(20,20,30,.18);
  transition:background .15s ease,border-color .15s ease}
.mcard .ms-l .mtint{left:4%;right:50%}
.mcard .ms-r .mtint{left:50%;right:4%}
.mcard .mbadge{position:absolute;top:50%;transform:translate(-50%,-50%);
  width:30px;height:30px;border-radius:50%;display:grid;place-items:center;
  font:800 15px/1 Arial,Helvetica,sans-serif;color:#fff;background:#9aa0a6;
  box-shadow:0 2px 6px rgba(0,0,0,.18);pointer-events:none;
  transition:background .15s ease,box-shadow .15s ease,transform .15s ease}
.mcard .ms-l .mbadge{left:27%}
.mcard .ms-r .mbadge{left:73%}
/* The Door & Half's two leaves are not halves, so the wash cannot be one. Its card gets its own
   drawing per side and the geometry comes with it, measured off the drawing and passed inline
   (HALF_LEAF) — all this rule does is stop the twin's 50% defaults applying to it. */
.mcard.mhalf .mtint{left:auto;right:auto}
.mcard.mhalf .mbadge{left:auto}
.mcard.mhalf small{font-size:12px;color:var(--muted);margin-top:-2px}
/* chosen: the leaf, its mark and the caption all turn together, so there is one obvious answer */
.mcard[aria-pressed="true"] .mtint{background:rgba(91,44,131,.34);border-color:var(--purple)}
.mcard[aria-pressed="true"] .mbadge{background:var(--purple);
  box-shadow:0 3px 12px rgba(91,44,131,.55);transform:translate(-50%,-50%) scale(1.12)}
.mcard[aria-pressed="true"] b{color:var(--purple)}
.mcard b{font-size:16px}
/* On a phone the card is barely wider than the drawing, so the corner mark ends up sitting on the
   door instead of beside it. The M on the leaf already says which card this is, so the corner one
   is the redundant of the two and it goes. Kept on the single-door cards, which are not this narrow
   relative to their artwork. */
@media (max-width:560px){ .mcard .cimark{display:none} }

/* Left | Right — which leaf a fitting goes on.
   It rides on the field's own label line, at the size of a label rather than the size of the field:
   as a full-height control beside the select it was the loudest thing on the step, and the step is
   not about which leaf a spyhole goes on. The label line is the right home for it — it belongs to
   the fitting's NAME ("Knocker, on the left one"), not to its value. */
.field.hasaside .fl{display:flex;align-items:center;gap:8px}
.field.hasaside .lswitch{margin-left:auto}
/* Two little doors, the chosen one filled in — the pair on screen is the pair on the doorstep, so
   the question answers itself without being read. Square halves rather than a text pill: the glyph
   is the label. */
.lswitch{display:inline-flex;flex:0 0 auto;border:1px solid var(--line2);border-radius:9px;
  overflow:hidden;background:#fff;height:38px}
.lsw{width:38px;padding:0;border:0;background:transparent;cursor:pointer;color:var(--muted2);
  display:grid;place-items:center;transition:background .12s,color .12s}
.lsw+.lsw{border-left:1px solid var(--line2)}
/* big enough that the mark ON the door — the slot, the ring, the digits — is legible; that mark is
   what tells the customer what the two doors are asking about */
.lsw .lsg{width:21px;height:30px;display:block}
.lsw:hover{background:var(--purple-soft);color:var(--purple)}
.lsw.on{background:var(--purple);color:#fff}
/* "Only one leaf takes a plate — this one: [Left|Right]" — the switch ends the sentence it answers */
.lpleafline{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:-2px 0 12px}
/* the preview cannot draw this plate on this design. Stated plainly and without alarm: nothing is
   wrong with the order, only with the picture, and saying so beats a door that comes back unchanged */
.lpnote{margin:12px 0 0;padding:9px 12px;border-radius:10px;background:#fbf7ec;border:1px solid #e8dcbe;
  font-size:12.5px;line-height:1.5;font-weight:600;color:#6b5a2e}
/* a 26px half is a fine target for a mouse and a poor one for a thumb. It sits on the label line, so
   growing it costs the field nothing. */
@media (max-width:820px){
  .lswitch{height:44px}
  .lsw{width:44px}
  .lsw .lsg{width:24px;height:34px}
}
