/* ==========================================================================
   Nordu configurator
   Brand palette: #ffffff #323232 (ink) #e8502a (accent orange), per the logo
   Typeface: Host Grotesk, Bold headings and Regular body
   ========================================================================== */

:root{
  --paper:#dfdfdf;
  --surface:#ffffff;
  --surface-2:#ececec;
  --ink:#323232;
  --ink-soft:#5a5a5a;
  /* #8a8a8a measured 3.45:1 on --surface and 2.59:1 on --paper, both short of
     the 4.5:1 AA text floor. This is the darkest grey that still reads as a
     step below --ink-soft while clearing AA against both. */
  --ink-muted:#636363;
  --line:#d6d6d6;
  --line-strong:#b8b8b8;

  --accent:#e8502a;
  --accent-soft:rgba(232,80,42,0.10);
  --accent-line:rgba(232,80,42,0.45);
  /* The brand orange measures 3.74:1 on white and 2.81:1 on the page, so as
     small text or as a fill behind white text it fails AA. --accent-text
     covers the former (an option label, a text link) and is redefined to
     plain --accent in dark mode, where the same hue already clears AA as
     text. --accent-strong covers the latter (a filled tab, a badge) and is
     not redefined for dark, because white-on-fill is the same failing pair
     in either theme: the fill itself doesn't change colour with the page. */
  --accent-text:#b63414;
  --accent-strong:#b63414;
  --accent-strong-hover:#a93012;
  --danger:#c0392b;
  --danger-soft:rgba(192,57,43,0.12);

  --rail-w:420px;
  --radius:10px;
  --shadow:0 1px 2px rgba(0,0,0,0.04), 0 8px 24px -12px rgba(0,0,0,0.14);

  /* Scrollbars, form control chrome and the caret, which this stylesheet does
     not paint. Flipped in the dark mode block at the foot of the file. */
  color-scheme:light;
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  background:var(--paper);
  color:var(--ink);
  font-family:'Host Grotesk',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  font-size:16px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  min-height:100vh;
}
[hidden]{display:none !important;}
::selection{background:var(--accent-soft);}

button{font-family:inherit;}
:focus-visible{outline:2px solid var(--accent-strong);outline-offset:3px;border-radius:4px;}

.eyebrow{
  font-size:11px;
  font-weight:600;
  letter-spacing:0.18em;
  text-transform:uppercase;
  color:var(--ink-muted);
  margin:0;
}

/* ---------- header ----------
   Deliberately identical to the marketing site's header in site/site.css so
   the two read as one product. If you change one, change the other. */
/* No ink rescoping: the bar inherits the page's, so it follows the theme
   rather than being maintained as its own little scheme. */
.site-head{
  position:sticky;top:0;z-index:60;
  background:transparent;
  color:var(--ink);
}
/* Frosted white glass. The alpha is set by the marketing site's hero
   photographs, where the nav has to stay legible over dark foliage: see the
   measurement note in site/site.css. There is no hero here, but the two
   headers are deliberately identical. */
.site-head::before{
  content:'';position:absolute;inset:0;
  -webkit-backdrop-filter:blur(20px) saturate(160%);
  backdrop-filter:blur(20px) saturate(160%);
  background:rgba(255,255,255,0.85);
  box-shadow:inset 0 -1px 0 rgba(0,0,0,0.09);
  pointer-events:none;
}
.head-inner{
  max-width:1360px;margin:0 auto;padding:16px 24px;
  display:flex;align-items:center;justify-content:space-between;gap:24px;
  position:relative;z-index:1;
}
/* color, not just fill: the mark's outer path is stroked with currentColor,
   and without this the anchor's default link blue wins over the inherited
   white. The marketing site gets this from its global `a{color:inherit}`,
   which this file does not have. */
.logo{display:flex;align-items:center;flex:none;color:var(--ink);}
.logo svg{height:22px;width:auto;display:block;}
.logo-ink{fill:var(--ink);}
.logo-accent{fill:var(--accent);}

.nav{display:flex;align-items:center;gap:30px;margin-left:auto;}
.nav a{
  font-size:14px;font-weight:600;text-decoration:none;color:var(--ink-soft);
  transition:color .15s;white-space:nowrap;
}
.nav a:hover{color:var(--ink);}
.nav a[aria-current="page"]{color:var(--ink);}
/* Two things need restating here. `.nav a` outweighs `.btn-primary` on
   specificity, and this file's `.btn-primary` fills with var(--ink), which in
   the dark theme resolves to near-white and would put a white button on a
   white label. Use the accent, as the site does. */
.nav a.btn-primary,
.nav a.btn-primary[aria-current="page"]{
  background:var(--accent-strong);border-color:var(--accent-strong);color:#fff;
  text-decoration:none;
}
.nav a.btn-primary:hover{background:var(--accent-strong-hover);border-color:var(--accent-strong-hover);}
.nav .btn{margin-left:4px;}
.nav-toggle{
  display:none;background:none;border:1px solid var(--line-strong);
  border-radius:999px;padding:9px 16px;color:var(--ink);cursor:pointer;
  font-size:13px;font-weight:600;
  order:2;
}
/* Theme toggle, after the Enquire button. Same rule as the header it sits in:
   this is a copy of the site's, and if you change one, change the other. The
   order values only matter on mobile, where the nav goes out of flow and this
   would otherwise land after the Menu button. */
.theme-toggle{
  display:none;order:1;
  align-items:center;justify-content:center;
  width:38px;height:38px;flex:none;
  background:none;border:1px solid var(--line-strong);border-radius:999px;
  color:var(--ink);cursor:pointer;
  transition:border-color .15s,background .15s;
}
.js .theme-toggle{display:inline-flex;}
/* A dark wash now the bar is light. */
.theme-toggle:hover{border-color:var(--ink);background:rgba(0,0,0,0.05);}
.theme-toggle svg{width:17px;height:17px;display:block;}
.theme-toggle .icon-light{display:none;}
.btn-sm{padding:10px 18px;font-size:14px;}

@media (max-width:860px){
  .nav{
    display:none;position:absolute;left:0;right:0;top:100%;
    flex-direction:column;align-items:stretch;gap:0;
    /* Follows the bar above it rather than carrying its own dark scheme.
       Solid rather than glass because page content scrolls underneath. */
    background:var(--surface);
    border-bottom:1px solid var(--line);
    box-shadow:0 12px 24px -14px rgba(0,0,0,0.30);
    padding:12px 24px 22px;
  }
  .nav.open{display:flex;}
  .nav a{padding:13px 0;font-size:16px;border-bottom:1px solid var(--line);}
  .nav .btn{margin-top:16px;border-bottom:0;justify-content:center;}
  .nav-toggle{display:block;}
  /* The nav is out of flow here, so pushing the right hand group across falls
     to this instead. */
  .theme-toggle{margin-left:auto;}
  .site-head{position:relative;}
}

/* ---------- layout ---------- */
.layout{
  max-width:1360px;margin:0 auto;padding:36px 24px 140px;
  display:grid;grid-template-columns:1fr var(--rail-w);gap:48px;align-items:start;
}
/* once the lead is sent there's no rail left, so drop to a single column */
.layout.submitted{grid-template-columns:1fr;}
.layout.submitted .rail{display:none;}

/* ---------- panels ---------- */
.panel{animation:rise .3s ease both;}
@keyframes rise{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:none;}}
.panel-head{margin-bottom:26px;max-width:640px;}
.panel-head h2{
  font-size:34px;font-weight:800;letter-spacing:-0.015em;line-height:1.1;
  margin:8px 0 10px;
}
.panel-head p{margin:0;color:var(--ink-soft);font-size:15.5px;}

/* Where a configuration came from, when it came from a card on Inspiration.
   A line under the step heading rather than a banner above it: it explains
   why the steps behind this one are already answered, which is a footnote to
   the question being asked, not a louder thing than it. Same hairline box as
   .design-note on step 1 -- both are the panel telling the customer something
   about what they are looking at rather than asking them for anything. */
/* .panel-head p, not .from-design, is what sets the margin and the size on
   this element otherwise: one class plus one type outweighs the lone class. */
.panel-head .from-design{
  display:flex;gap:11px;align-items:baseline;flex-wrap:wrap;
  margin:16px 0 0;padding:12px 14px;
  background:var(--surface);border:1px solid var(--line);border-radius:10px;
  font-size:14px;
}
.from-design-tag{
  flex:none;font-size:11px;font-weight:700;letter-spacing:0.12em;
  text-transform:uppercase;color:var(--accent);
}
.from-design b{color:var(--ink);font-weight:700;}

/* ---------- stage bar ----------
   Stages + progress, full width at the top of the page but on the page
   background, deliberately outside the header above it. */
.stage-bar{
  max-width:1360px;margin:0 auto;padding:22px 24px 0;
}
.stage-tabs{
  display:flex;gap:4px;overflow-x:auto;scrollbar-width:none;
  margin-bottom:14px;
}
.stage-tabs::-webkit-scrollbar{display:none;}
.stage-tab{
  background:none;border:none;padding:0;margin-right:26px;
  font-size:12.5px;font-weight:600;letter-spacing:0.04em;
  color:var(--ink-muted);cursor:pointer;white-space:nowrap;
  transition:color .15s;
}
.stage-tab:disabled{cursor:default;opacity:0.45;}
.stage-tab:not(:disabled):hover{color:var(--ink);}
.stage-tab.active{color:var(--ink);}
.stage-tab .num{
  display:inline-flex;align-items:center;justify-content:center;
  width:19px;height:19px;border-radius:50%;margin-right:7px;
  border:1px solid var(--line-strong);font-size:10.5px;font-weight:700;
  vertical-align:1px;
}
.stage-tab.active .num{background:var(--accent-strong);border-color:var(--accent-strong);color:#fff;}
.stage-tab.done .num{background:var(--ink);border-color:var(--ink);color:#fff;}
.stage-bar .progress-track{
  height:3px;position:relative;background:var(--line);
}
.stage-bar .progress-fill{
  height:100%;background:var(--accent);width:20%;
  transition:width .35s ease;
}
.stage-bar .progress-bulb{
  position:absolute;top:50%;left:20%;transform:translate(-50%,-50%);
  background:var(--accent-strong);color:#fff;
  font-size:10.5px;font-weight:700;letter-spacing:0.02em;
  padding:3px 8px;border-radius:999px;white-space:nowrap;
  box-shadow:0 1px 4px rgba(0,0,0,0.18);
  transition:left .35s ease;
  font-variant-numeric:tabular-nums;
}
.stage-bar:empty{display:none;}

/* ---------- option cards ---------- */
.opts{display:grid;gap:14px;}
.opts.two{grid-template-columns:repeat(2,1fr);}
.opts.three{grid-template-columns:repeat(3,1fr);}

.opt{
  position:relative;text-align:left;width:100%;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius);
  padding:22px;
  cursor:pointer;
  transition:border-color .15s, box-shadow .15s, transform .1s;
}
.opt:hover{border-color:var(--line-strong);box-shadow:var(--shadow);}
.opt:active{transform:scale(0.995);}
.opt.selected{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent);}
.opt.selected::after{
  content:'';position:absolute;top:16px;right:16px;
  width:18px;height:18px;border-radius:50%;
  background:var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/11px no-repeat;
}
.opt-title{font-size:20px;font-weight:700;letter-spacing:-0.01em;line-height:1.2;}
.opt-nordu{font-size:12.5px;color:var(--accent-text);font-weight:600;margin-top:3px;}
.opt-eg{font-size:12.5px;color:var(--ink-muted);margin-top:12px;line-height:1.4;}
.opt-blurb{font-size:13.5px;color:var(--ink-soft);margin-top:8px;line-height:1.45;}
.opt-price{
  font-size:13px;color:var(--ink-soft);margin-top:16px;
  padding-top:12px;border-top:1px solid var(--line);
  font-variant-numeric:tabular-nums;
}
.opt-price b{font-weight:700;color:var(--ink);}
/* Keeps "£14,000 + VAT" together so a narrow card breaks after "from" rather
   than orphaning the VAT on a line of its own. */
.opt-amount{white-space:nowrap;}

/* line choice (step 1): taller cards with an illustration */

/* display:flex is what keeps the photograph against the top of the card, and
   it is not cosmetic. .opt is a <button>, and a button centres its content
   vertically when it is taller than that content. The two cards are grid
   siblings, so both stretch to the taller one — and at any width where
   "Everything from an office for one to garden living." wraps to two lines
   while "A porch, a sunroom, or a full extension." stays on one, the shorter
   card's whole contents drop by half the difference. That was a ~10px band of
   card background above the photograph on one card and not the other, which
   reads as a misaligned image rather than as centred text. */
.opt.line-card{padding:0;overflow:hidden;display:flex;flex-direction:column;}
.line-art{display:block;width:100%;height:auto;background:var(--surface-2);}
.line-art img{display:block;width:100%;height:100%;aspect-ratio:3/2;object-fit:cover;}
.line-copy{padding:22px;}

/* material swatch */
.swatch{
  display:block;width:100%;height:96px;border-radius:6px;margin-bottom:16px;
  border:1px solid var(--line);
}

/* add-on rows */
.addons{display:grid;gap:12px;}
.addon{
  display:flex;align-items:flex-start;gap:16px;text-align:left;width:100%;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:18px 20px;cursor:pointer;
  transition:border-color .15s, box-shadow .15s;
}
.addon:hover{border-color:var(--line-strong);box-shadow:var(--shadow);}
.addon.selected{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent);}
.addon-check{
  flex:none;width:22px;height:22px;border-radius:5px;margin-top:2px;
  border:1.5px solid var(--line-strong);background:var(--surface);
  transition:background .15s,border-color .15s;
}
.addon.selected .addon-check{
  background:var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/13px no-repeat;
  border-color:var(--accent);
}
.addon-icon{flex:none;color:var(--accent);margin-top:1px;}
.addon-body{flex:1;min-width:0;}
.addon-title{display:block;font-size:16.5px;font-weight:700;}
.addon-blurb{display:block;font-size:13px;color:var(--ink-muted);margin-top:3px;line-height:1.4;}
/* Holds a phrase ("Priced to specification") rather than only a figure, so it
   is allowed to wrap and to give way to the title instead of pushing it out of
   the row. `nowrap` here crushed .addon-body on a 390px screen. */
.addon-price{
  flex:0 1 auto;max-width:9.5em;text-align:right;
  font-size:13.5px;font-weight:600;color:var(--ink-soft);
  line-height:1.3;margin-top:2px;
}
/* Narrow enough and there is no room for a title and a phrase side by side, so
   the phrase drops to its own line rather than wrapping every title in two.
   Below this width .addon-blurb is already hidden, so it takes that slot. */
@media (max-width:900px){
  .addon{flex-wrap:wrap;}
  .addon-price{
    flex:1 0 100%;max-width:none;text-align:left;
    margin-top:6px;padding-left:38px;
  }
}
/* ---------- detail panel (revealed on selection) ---------- */
/* A <details>, closed by default. On a 390px screen the size step's detail ran
   to 1215px and pushed Continue 2.5 screens down; collapsed, the whole step
   fits in about one. The padding lives on the summary and the body rather than
   on the card, so the closed state is a compact bar rather than a mostly empty
   box. */
.detail{
  margin-top:28px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius);
  box-shadow:var(--shadow);
  animation:reveal .32s ease both;
}
@keyframes reveal{from{opacity:0;transform:translateY(-6px);}to{opacity:1;transform:none;}}

.detail > summary{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:17px 22px;cursor:pointer;
  font-size:15px;font-weight:600;color:var(--ink);
  /* Both are needed: list-style kills the marker everywhere modern, the
     pseudo-element is what Safari still reads. */
  list-style:none;
  border-radius:var(--radius);
  transition:color .15s;
}
.detail > summary::-webkit-details-marker{display:none;}
.detail > summary:hover{color:var(--accent-text);}
.detail-chev{flex:none;color:var(--ink-muted);transition:transform .2s ease;}
.detail[open] > summary .detail-chev{transform:rotate(180deg);}
/* Only once open, so the closed bar reads as a single unbroken control. */
.detail[open] > summary{border-bottom:1px solid var(--line);border-radius:var(--radius) var(--radius) 0 0;}
.detail-body{padding:24px 22px 26px;}

.detail-head{max-width:660px;}
.detail-head h3{
  font-size:27px;font-weight:800;letter-spacing:-0.015em;line-height:1.15;
  margin:6px 0 0;
}
.detail-lede{margin:12px 0 0;color:var(--ink-soft);font-size:15.5px;line-height:1.55;}
.detail-dims{margin:12px 0 0;font-size:14.5px;color:var(--ink-soft);}
.detail-price{
  font-size:26px;font-weight:800;letter-spacing:-0.02em;
  margin:14px 0 0;font-variant-numeric:tabular-nums;
}
.detail-note{
  margin:18px 0 0;font-size:12.5px;color:var(--ink-muted);
  line-height:1.5;max-width:620px;
}

.detail-points{
  display:grid;grid-template-columns:repeat(2,1fr);gap:20px 32px;
  margin-top:26px;max-width:720px;
}
.point h4{
  font-size:11px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;
  color:var(--ink);margin:0 0 6px;
}
.point p{margin:0;font-size:13.5px;color:var(--ink-soft);line-height:1.5;}

/* material detail: big swatch + spec table */
.detail-material{
  display:grid;grid-template-columns:200px 1fr;gap:32px;
  margin-top:26px;max-width:720px;align-items:start;
}
.swatch-lg{
  display:block;width:100%;height:150px;border-radius:8px;
  border:1px solid var(--line);
}
.spec-list{
  display:grid;grid-template-columns:auto 1fr;gap:0 20px;margin:0;
}
.spec-list dt{
  font-size:11px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;
  color:var(--ink-muted);padding:9px 0;border-top:1px solid var(--line);
}
.spec-list dd{
  margin:0;font-size:14px;color:var(--ink);padding:9px 0;
  border-top:1px solid var(--line);
}
.spec-list dt:first-of-type,.spec-list dd:first-of-type{border-top:none;padding-top:0;}

/* colour picker: sits under the swatch + specs, within the same finish detail */
.color-picker{margin-top:24px;padding-top:20px;border-top:1px solid var(--line);}
.color-picker-label{
  font-size:11px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;
  color:var(--ink-muted);margin:0 0 12px;
}
.color-picker-label b{
  font-size:13px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--ink);
}
.color-swatches{display:flex;gap:10px;flex-wrap:wrap;}
.color-swatch{
  width:34px;height:34px;border-radius:50%;flex:none;cursor:pointer;
  border:1px solid var(--line-strong);box-shadow:inset 0 0 0 2px var(--surface);
  transition:box-shadow .15s,transform .1s;
}
.color-swatch:hover{transform:scale(1.08);}
.color-swatch.selected{
  box-shadow:inset 0 0 0 2px var(--surface), 0 0 0 2px var(--accent);
}

/* ---------- floor plans ---------- */
.plans{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:18px;margin-top:26px;max-width:900px;align-items:start;
}
.plan{
  margin:0;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:18px;box-shadow:var(--shadow);
}
.plan figcaption{
  font-size:10.5px;font-weight:700;letter-spacing:0.14em;text-transform:uppercase;
  color:var(--ink-muted);margin-bottom:12px;
}
/* height is auto from each layout's own aspect ratio, not squashed to match
   its neighbours: a portrait plan and a landscape plan of the same area
   should read as genuinely different shapes. */
.plan svg{display:block;width:100%;height:auto;}

/* ---------- small print ---------- */
.smallprint{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:24px;margin-top:28px;
}
.smallprint h3{font-size:13px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;margin:0 0 14px;}
.sp-cols{display:grid;grid-template-columns:1fr 1fr;gap:28px;}
.sp-cols ul{margin:0;padding:0;list-style:none;}
.sp-cols li{
  font-size:13.5px;color:var(--ink-soft);line-height:1.45;
  padding-left:20px;position:relative;margin-bottom:7px;
}
.sp-inc li::before{
  content:'';position:absolute;left:0;top:6px;width:11px;height:8px;
  background:var(--accent);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/contain no-repeat;
}
.sp-note{
  margin:20px 0 0;padding-top:16px;border-top:1px solid var(--line);
  font-size:12.5px;color:var(--ink-muted);line-height:1.5;
}

/* The note under the first step's cards, saying the imagery and the window
   layouts are indicative and the real design comes from a designer.

   Carded and iconed rather than set as fine print, because a customer who
   misses it goes on to read every photograph and every plan as a promise. The
   accent is carried by the icon alone rather than by a tinted fill or an edge:
   a filled orange panel on the opening screen reads as an error, and this is
   information rather than a warning. */
.design-note{
  display:flex;gap:13px;align-items:flex-start;
  max-width:64ch;margin:26px 0 0;padding:16px 18px;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:10px;
}
.design-note-icon{flex:none;color:var(--accent);display:flex;margin-top:1px;}
.design-note-copy{
  font-size:14px;color:var(--ink-soft);line-height:1.55;
}
.design-note-copy b{color:var(--ink);font-weight:700;}

/* The same list inside the price card on the last step. Narrower column, so
   one column and a size down, and it borrows the tick from .sp-inc rather
   than restating the mask. */
.rail-inc{margin-top:20px;padding-top:18px;border-top:1px solid var(--line);}
.rail-inc h3{
  font-size:11.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;
  margin:0 0 12px;color:var(--ink);
}
.rail-inc ul{margin:0;padding:0;list-style:none;}
.rail-inc li{
  font-size:12.5px;color:var(--ink-soft);line-height:1.4;
  padding-left:18px;position:relative;margin-bottom:6px;
}
.rail-inc li::before{
  content:'';position:absolute;left:0;top:5px;width:10px;height:7px;
  background:var(--accent);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/contain no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/contain no-repeat;
}
/* --ink-soft, not --ink-muted. Muted measures 3.09:1 on this card in light
   mode, and this is the paragraph that says the figure is not a quotation, so
   it is the last thing on the page that should be hard to read. Soft measures
   6.9:1. */
.rail-inc-note{
  margin:14px 0 0;padding-top:12px;border-top:1px solid var(--line);
  font-size:11.5px;color:var(--ink-soft);line-height:1.45;
}

/* ---------- capture form ---------- */
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;max-width:640px;}
.field{display:flex;flex-direction:column;}
.field.wide{grid-column:1 / -1;}
.field label{
  font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;
  color:var(--ink-muted);margin-bottom:7px;
}
.field input,.field textarea{
  background:var(--surface);border:1px solid var(--line-strong);border-radius:6px;
  padding:12px 14px;font-family:inherit;font-size:15px;color:var(--ink);
  outline:none;transition:border-color .15s, box-shadow .15s;
}
.field textarea{resize:vertical;min-height:88px;line-height:1.45;}
.field input:focus,.field textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);}
.field input[aria-invalid="true"]{border-color:var(--danger);}
.field-err{font-size:12px;color:var(--danger);margin-top:6px;min-height:0;}

.consent{
  display:flex;gap:11px;align-items:flex-start;max-width:640px;margin-top:20px;
  font-size:13px;color:var(--ink-soft);line-height:1.45;
}
.consent input{margin:2px 0 0;width:17px;height:17px;accent-color:var(--accent);flex:none;}

.cta-row{display:flex;gap:12px;flex-wrap:wrap;margin-top:26px;}
.form-status{font-size:13.5px;margin-top:16px;min-height:20px;}

.form-status.err{color:var(--danger);}

/* ---------- buttons ---------- */
/* Pill, matching the marketing site's .btn in site/site.css. If you change one,
   change the other: the two are meant to read as one product. */
.btn{
  display:inline-flex;align-items:center;gap:8px;
  font-size:15px;font-weight:600;letter-spacing:0.01em;
  padding:14px 28px;border-radius:999px;border:1px solid transparent;
  cursor:pointer;transition:background .15s,border-color .15s,color .15s,transform .1s;
}
.btn:active{transform:scale(0.98);}
.btn:disabled{opacity:0.45;cursor:default;}
.btn-primary{background:var(--ink);color:#fff;}
.btn-primary:not(:disabled):hover{background:var(--accent-strong);}
.btn-ghost{background:transparent;border-color:var(--line-strong);color:var(--ink);}
.btn-ghost:not(:disabled):hover{border-color:var(--ink);}
.btn-quiet{
  background:none;border:none;padding:14px 4px;color:var(--ink-muted);
  font-size:14px;font-weight:600;cursor:pointer;
}
.btn-quiet:hover{color:var(--ink);}

.nav-row{display:flex;align-items:center;gap:16px;margin-top:32px;}

/* ---------- summary rail ---------- */
.rail{position:sticky;top:112px;}
/* See renderRail: too tall to stick without hiding its own bottom. */
.rail-long{position:static;}
.rail-card{
  background:rgba(255,255,255,0.6);
  -webkit-backdrop-filter:blur(18px) saturate(160%);
  backdrop-filter:blur(18px) saturate(160%);
  border:1px solid rgba(255,255,255,0.5);
  border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);
}
.hero{
  position:relative;background:var(--surface-2);
  border-bottom:1px solid var(--line);aspect-ratio:8/5;
}
.hero svg,.hero img{display:block;width:100%;height:100%;object-fit:cover;}
.hero-tag{
  position:absolute;left:12px;top:12px;
  background:rgba(255,255,255,0.92);backdrop-filter:blur(4px);
  border:1px solid var(--line);border-radius:5px;
  padding:5px 9px;font-size:10.5px;font-weight:600;
  letter-spacing:0.08em;text-transform:uppercase;color:var(--ink-muted);
}
.hero-empty{
  display:flex;align-items:center;justify-content:center;height:100%;
  color:var(--ink-muted);font-size:13.5px;text-align:center;padding:24px;
}

.rail-body{padding:20px 22px 22px;}
.spec-row{
  display:flex;justify-content:space-between;gap:14px;
  font-size:14px;padding:9px 0;border-bottom:1px solid var(--line);
}
.spec-row:first-of-type{padding-top:0;}
.spec-k{color:var(--ink-muted);flex:none;}
.spec-v{text-align:right;font-weight:600;}
.spec-v small{display:block;font-weight:400;color:var(--ink-muted);font-size:12px;margin-top:1px;}
.spec-v .pending{font-weight:400;color:var(--line-strong);}

.rail-addons{padding:12px 0 2px;border-bottom:1px solid var(--line);}
.rail-addon{
  display:flex;align-items:center;gap:9px;font-size:13.5px;
  color:var(--ink-soft);margin-bottom:8px;
}
.rail-addon svg{flex:none;color:var(--accent);}
.rail-addon span{flex:1;}
/* Same reasoning as .addon-price: a phrase, in a rail only ~420px wide. */
.rail-addon b{
  flex:0 1 auto;max-width:8.5em;text-align:right;
  font-weight:600;font-size:12px;line-height:1.3;color:var(--ink-soft);
}

.price-block{padding-top:18px;}
.price-label{font-size:11px;font-weight:600;letter-spacing:0.14em;text-transform:uppercase;color:var(--ink-muted);}
.price-value{
  font-size:42px;font-weight:800;letter-spacing:-0.03em;line-height:1.05;
  margin-top:4px;font-variant-numeric:tabular-nums;
}
/* Sits under the figure rather than beside it: at 42px the number is the
   thing being read, and a suffix on the same line either shrinks it or wraps.
   Close enough underneath that it is plainly part of the price. */
.price-vat{
  font-size:13px;font-weight:600;color:var(--ink-soft);
  margin-top:2px;letter-spacing:0.01em;
}
.price-sub{font-size:12.5px;color:var(--ink-muted);margin-top:10px;line-height:1.45;}


/* ---------- mobile price bar ---------- */
.mobile-bar{
  display:none;position:fixed;left:0;right:0;bottom:0;z-index:40;
  background:var(--surface);border-top:1px solid var(--line);
  padding:12px 20px;
  box-shadow:0 -6px 20px -12px rgba(0,0,0,0.3);
}
.mobile-bar-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;}
.mobile-price{
  font-size:22px;font-weight:800;letter-spacing:-0.02em;font-variant-numeric:tabular-nums;
  /* One line, whatever the figure: the buttons beside it take the rest. */
  white-space:nowrap;
}
.mobile-price small{display:block;font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--ink-muted);}
/* Small and beside the figure rather than under it: the bar is one line and
   the buttons want the rest of it. */
.mobile-vat{font-size:12px;font-weight:600;color:var(--ink-soft);margin-left:5px;}
/* The step buttons, beside the price. Tighter than the panel's own pair --
   this bar is 20px of padding and a 22px figure, and full size buttons would
   make it the tallest thing on a small screen. min-width keeps Continue a
   thumb target rather than sizing to its label. */
.mobile-nav{display:flex;align-items:center;gap:10px;flex:none;}
.mobile-nav .btn{padding:11px 20px;font-size:14.5px;min-width:112px;}
.mobile-nav .btn-quiet{padding:11px 6px;font-size:14.5px;}

/* ---------- confirmation ---------- */
.confirm{
  max-width:620px;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:36px;box-shadow:var(--shadow);
}
.confirm-mark{
  width:46px;height:46px;border-radius:50%;background:var(--accent);
  display:flex;align-items:center;justify-content:center;margin-bottom:20px;
}
.confirm h2{font-size:28px;font-weight:800;letter-spacing:-0.015em;margin:0 0 12px;}
.confirm p{color:var(--ink-soft);margin:0 0 10px;font-size:15.5px;}
.confirm-report{margin-top:18px;font-size:15px;}
.confirm-download{margin-top:8px;}
.confirm-payload{margin-top:26px;}
.confirm-payload summary{font-size:12.5px;color:var(--ink-muted);cursor:pointer;}
.confirm-payload summary:hover{color:var(--ink);}
.confirm-payload pre{
  background:var(--surface-2);border:1px solid var(--line);border-radius:6px;
  padding:16px;font-size:12px;line-height:1.5;overflow-x:auto;margin-top:12px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
}

/* Splits stage 5 into its two halves: the preview card above, the form card
   below. The gap between them is the cards' own margin, so the heading needs
   no top margin of its own. */
.form-heading{
  font-size:22px;font-weight:800;letter-spacing:-0.015em;
  margin:0 0 8px;
}
.form-lede{margin:0 0 22px;color:var(--ink-soft);font-size:15.5px;max-width:640px;}

/* The privacy line, above the file picker rather than below it: someone
   deciding whether to hand over a photograph of their home should be able to
   read what happens to it before they choose the file, not after. */
.gp-privacy{
  margin:14px 0 0;padding:13px 15px;
  background:var(--surface-2);border-radius:8px;
  font-size:13px;line-height:1.55;color:var(--ink-soft);
}
.gp-privacy b{color:var(--ink);font-weight:700;}

/* ---------- see it in a garden ----------
   Mounted inline in two places, never both at once: above the form on stage
   5, and under the confirmation card once the flow is done. Same width as
   that card, so the two read as one column rather than as the page changing
   shape underneath the customer. */





.gp,
.form-card{
  max-width:620px;margin-top:var(--radius);
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:36px;box-shadow:var(--shadow);
}
.gp h3{font-size:24px;font-weight:800;letter-spacing:-0.015em;margin:6px 0 10px;}
.gp-lede{color:var(--ink-soft);font-size:15.5px;margin:0 0 24px;}
.gp-note{font-size:12.5px;color:var(--ink-muted);margin:12px 0 0;line-height:1.5;}

/* Two routes in. Radio behaviour rather than a filter: picking one replaces
   the panel below rather than adding to it. */
.gp-tabs{display:grid;grid-template-columns:1fr 1fr;gap:10px;}
.gp-tab{
  display:flex;align-items:center;gap:10px;text-align:left;
  background:var(--surface);border:1px solid var(--line);border-radius:8px;
  padding:14px 16px;cursor:pointer;font-family:inherit;
  font-size:14px;font-weight:600;color:var(--ink-soft);
  transition:border-color .15s,color .15s,box-shadow .15s;
}
.gp-tab:hover{border-color:var(--line-strong);color:var(--ink);}
.gp-tab.active{
  border-color:var(--accent);color:var(--ink);box-shadow:0 0 0 1px var(--accent);
}
.gp-tab svg{width:19px;height:19px;flex:none;color:var(--accent);}

.gp-panel{margin-top:18px;}

/* The upload target is the label, so the whole box is the file picker and
   there is no separate button to miss. */
.gp-drop{
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;
  border:1px dashed var(--line-strong);border-radius:8px;
  padding:32px 24px;cursor:pointer;background:var(--surface-2);
  transition:border-color .15s,background .15s;
}
.gp-drop:hover{border-color:var(--accent);}
.gp-drop svg{width:26px;height:26px;color:var(--ink-muted);margin-bottom:4px;}
.gp-drop b{font-size:15px;font-weight:700;}
.gp-drop span{font-size:13px;color:var(--ink-muted);max-width:34ch;line-height:1.45;}

.gp-file{display:flex;gap:16px;align-items:flex-start;}
.gp-file img{
  width:132px;height:99px;object-fit:cover;flex:none;
  border-radius:6px;border:1px solid var(--line);
}
.gp-file-meta{display:flex;flex-direction:column;gap:5px;min-width:0;}
.gp-file-meta b{font-size:14px;font-weight:700;word-break:break-word;}
.gp-file-meta span{font-size:12.5px;color:var(--ink-muted);line-height:1.5;}
.gp-link{
  align-self:flex-start;background:none;border:0;padding:0;margin-top:3px;
  font-family:inherit;font-size:13px;font-weight:600;color:var(--accent-text);
  cursor:pointer;text-decoration:underline;text-underline-offset:2px;
}

.gp-styles{display:flex;flex-wrap:wrap;gap:8px;}
.gp-style{
  background:none;border:1px solid var(--line-strong);border-radius:999px;
  padding:9px 16px;font-family:inherit;font-size:13.5px;font-weight:600;
  color:var(--ink-soft);cursor:pointer;
  transition:border-color .15s,color .15s,background .15s;
}
.gp-style:hover{border-color:var(--ink);color:var(--ink);}
.gp-style.active{
  border-color:var(--accent);color:var(--ink);background:var(--accent-soft);
}

/* What the room is for. Applies to both paths, because it drives the interior
   shot whether or not a photograph was uploaded. */
.gp-use{margin-top:20px;padding-top:18px;border-top:1px solid var(--line);}
.gp-use-label{
  margin:0 0 10px;font-size:11px;font-weight:700;letter-spacing:0.1em;
  text-transform:uppercase;color:var(--ink-muted);
}

.gp-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:22px;}
.gp-save{padding:12px 20px;font-size:14px;}
.gp-save svg{width:16px;height:16px;}

/* Three views. Two up on desktop with the interior spanning the full width
   beneath, since it is the one that rewards being seen large. */
.gp-shots{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
.gp-shots .gp-result:last-child:nth-child(odd){grid-column:1 / -1;}
.gp-caption{
  margin:14px 0 0;font-size:12.5px;font-weight:600;color:var(--ink-muted);
}
@media (max-width:620px){
  .gp-shots{grid-template-columns:1fr;}
}
.gp-out:not(:empty){margin-top:26px;}

.gp-working{display:flex;align-items:center;gap:14px;}
/* The same row, but under a set that is half arrived rather than instead of
   one that has not started. */
.gp-working-more{
  margin:14px 0 0;font-size:13.5px;color:var(--ink-soft);
}
.gp-working-more .gp-spinner{width:15px;height:15px;}
.gp-working p{margin:0;font-size:14.5px;color:var(--ink-soft);}
.gp-spinner{
  width:20px;height:20px;flex:none;border-radius:50%;
  border:2px solid var(--line-strong);border-top-color:var(--accent);
  animation:gp-spin .8s linear infinite;
}
@keyframes gp-spin{to{transform:rotate(360deg);}}

/* The retry path. Errors keep whatever the customer already picked, so this
   block replaces the generate button rather than sitting under it. */
.gp-error{
  border-left:2px solid var(--danger);background:var(--danger-soft);
  border-radius:0 6px 6px 0;padding:16px 18px;
}
.gp-error p{margin:0 0 12px;font-size:14.5px;color:var(--ink);}
/* Quiet, but readable out loud down a phone. */
.gp-code{color:var(--ink-muted);font-size:13px;font-variant-numeric:tabular-nums;}
.btn-retry{padding:9px 18px;font-size:13.5px;}

.gp-result{margin:0;}
.gp-result img{
  display:block;width:100%;height:auto;border-radius:8px;border:1px solid var(--line);
}
.gp-result figcaption{
  margin-top:10px;font-size:12.5px;font-weight:600;color:var(--ink-muted);
}

@media (max-width:620px){
  .gp,
  .form-card{padding:24px 20px;}
  .gp-tabs{grid-template-columns:1fr;}
  .gp-file img{width:96px;height:72px;}
}

/* ---------- responsive ---------- */
@media (max-width:1080px){
  :root{--rail-w:340px;}
  .layout{gap:32px;}
  .panel-head h2{font-size:30px;}
}
@media (max-width:900px){
  .layout{grid-template-columns:1fr;padding-bottom:96px;}
  .rail{position:static;}
  .rail .rail-body .price-block{display:none;}
  /* The price block is what used to sit between the add-on rows and the
     included list, and each of them draws a rule against it. With it hidden
     the two rules end up 20px apart with nothing between them, which reads as
     a mistake. The list keeps its own, since it is also the one that shows
     when there are no add-ons at all. */
  .rail-addons{border-bottom:0;}
  .rail-inc{margin-top:4px;}
  .mobile-bar{display:block;}
  /* The bar carries these now. Hidden rather than removed: the last step's row
     is the one that has no Continue in it, and .nav-row is still what spaces
     the panel above the bar. */
  .nav-row{display:none;}
  .opts.three{grid-template-columns:1fr;}
  .sp-cols{grid-template-columns:1fr;gap:22px;}
}
@media (max-width:620px){
  .opts.two{grid-template-columns:1fr;}
  .form-grid{grid-template-columns:1fr;}
  .detail > summary{padding:15px 17px;font-size:14.5px;}
  .detail-body{padding:20px 17px 22px;}
  .detail-points{grid-template-columns:1fr;gap:18px;}
  .detail-material{grid-template-columns:1fr;gap:20px;}
  .swatch-lg{height:110px;}
  .plans{grid-template-columns:1fr;gap:22px;}
  .detail-head h3{font-size:23px;}
  .panel-head h2{font-size:26px;}
  .head-inner{padding:14px 18px;}
  .layout{padding:26px 18px 96px;}
  .stage-bar{padding:18px 18px 0;}
  .addon{gap:12px;padding:16px;}
  .addon-blurb{display:none;}
}
@media (max-width:360px){
  /* The tightest row on the site. It gives ground before it wraps. */
  .mobile-bar{padding:12px 14px;}
  .mobile-price{font-size:19px;}
  .mobile-nav{gap:6px;}
  .mobile-nav .btn{min-width:92px;padding:11px 14px;}
  .mobile-nav .btn-quiet{padding:11px 2px;}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:0.01ms !important;transition-duration:0.01ms !important;}
}

/* ==========================================================================
   DARK MODE
   The token values are the site's, deliberately, so a visitor moving between
   the two never sees the greys shift. The preference is stored under the same
   localStorage key and resolved by the same inline <head> script, so the
   choice carries across. See site/site.css for the reasoning behind each tone
   and for why there is no prefers-color-scheme copy of this block.
   ========================================================================== */
:root[data-theme="dark"]{
  --paper:#121212;
  --surface:#1c1c1c;
  --surface-2:#262626;
  --ink:#f2f0ee;
  --ink-soft:#b8b5b1;
  --ink-muted:#8f8c88;
  --line:#2f2f2f;
  --line-strong:#454545;

  --accent-soft:rgba(232,80,42,0.16);
  --accent-line:rgba(232,80,42,0.55);
  /* --accent-strong is not redefined here: see the note on it in the light
     :root. --accent-text reverts to the plain accent, which already clears
     AA as text against both dark surfaces. */
  --accent-text:var(--accent);
  /* Lifted off the light page value. #c0392b measures 3.4:1 on --surface,
     which fails AA for the validation messages it carries. */
  --danger:#ff7a68;
  --danger-soft:rgba(255,122,104,0.16);

  --shadow:0 1px 2px rgba(0,0,0,0.5), 0 10px 28px -14px rgba(0,0,0,0.9);

  color-scheme:dark;
}

:root[data-theme="dark"] .theme-toggle .icon-dark{display:none;}
:root[data-theme="dark"] .theme-toggle .icon-light{display:block;}

/* Dark glass rather than white. The ink needs no override: the bar inherits
   the page's, which has already flipped by this point. */
:root[data-theme="dark"] .site-head::before{
  background:rgba(18,18,18,0.85);
  box-shadow:inset 0 -1px 0 rgba(255,255,255,0.12);
}
:root[data-theme="dark"] .theme-toggle:hover{background:rgba(255,255,255,0.08);}

/* The summary rail is frosted white glass over a light page. Inverted it has
   to be frosted dark glass, or it turns into a bright slab beside the panel. */
:root[data-theme="dark"] .rail-card{
  background:rgba(255,255,255,0.05);
  border-color:rgba(255,255,255,0.10);
}
:root[data-theme="dark"] .hero-tag{
  background:rgba(18,18,18,0.86);
}

/* plans.js draws black line work on a white sheet, and those colours are baked
   into the SVG. The drawing keeps its own sheet rather than the drawing code
   being forked for a theme. */
:root[data-theme="dark"] .plan svg{
  background:#e6e4e1;border-radius:6px;
}

/* The colour chips are real material tones and do not move. Only the ring
   around them does, so the darkest timbers still read as a chip on a dark
   card the way the palest render does on a light one. */
:root[data-theme="dark"] .color-swatch{
  border-color:rgba(255,255,255,0.24);
}

/* Two fills that are ink rather than accent, so they invert with it. Both hard
   coded a white label for a near black fill, and without this the Continue
   button and the completed stage markers come out white on white. */
:root[data-theme="dark"] .btn-primary,
:root[data-theme="dark"] .stage-tab.done .num{
  color:var(--paper);
}
/* The one exception. Inside the header .btn-primary is filled with the accent,
   not with ink, so its label stays white and matches the site's Enquire
   button. Restated because the rule above outweighs the header's own. */
:root[data-theme="dark"] .nav a.btn-primary,
:root[data-theme="dark"] .nav a.btn-primary[aria-current="page"]{
  color:#fff;
}

/* ---------- first visit hint ----------
   A one-off nudge at the theme button, shown to someone who has never set a
   preference. Built by script and never present otherwise, so it cannot be
   left behind in the markup.

   Blue on purpose, and the only blue on either site. It reads as chrome
   rather than as content, which is what a one-off onboarding nudge is: in the
   brand orange it would look like a promotion. Change --hint-bg if you would
   rather it were the accent.

   Positioned against .head-inner, which is already position:relative, with
   the exact offsets set by script. The button sits at the end of the row on
   desktop and in the middle of it on mobile, so a fixed right value cannot
   serve both. */
.theme-hint{
  /* Measured, not picked. At #1d6fe0 the softer second line came out at
     3.96:1 and failed AA. This tone puts the heading at 6.0:1, the second
     line at 4.9:1 and the close control at 4.1:1, all clear. */
  --hint-bg:#1a5fc4;
  position:absolute;top:100%;z-index:70;
  margin-top:11px;max-width:min(268px,calc(100vw - 32px));
  background:var(--hint-bg);color:#fff;
  border-radius:11px;padding:13px 15px;
  box-shadow:0 10px 28px -10px rgba(0,0,0,0.45);
  font-size:13.5px;line-height:1.45;
  display:flex;gap:11px;align-items:flex-start;
  animation:hint-in .28s ease both;
}
@keyframes hint-in{from{opacity:0;transform:translateY(-5px);}to{opacity:1;transform:none;}}
/* The pointer. --arrow is the distance from the bubble's right edge to the
   centre of the button, set by script. */
.theme-hint::before{
  content:'';position:absolute;top:-6px;right:var(--arrow,20px);
  width:12px;height:12px;background:var(--hint-bg);
  transform:rotate(45deg);border-radius:2px;
}
.theme-hint b{display:block;font-weight:700;margin-bottom:2px;}
.theme-hint span{color:rgba(255,255,255,0.86);}
.theme-hint-close{
  flex:none;background:none;border:0;padding:2px;margin:-2px -3px 0 0;
  color:rgba(255,255,255,0.75);cursor:pointer;line-height:0;
  border-radius:4px;
}
.theme-hint-close:hover{color:#fff;}
.theme-hint-close svg{width:14px;height:14px;display:block;}
@media (prefers-reduced-motion:reduce){
  .theme-hint{animation:none;}
}
