/* Southeast Closet Company — shared site styles.
   Used by every product page + the site chrome web components in /assets/site-chrome.js.
   Product-specific styles stay inline in each product HTML. */

/* Palette: /assets/theme.css, linked ahead of this file on every page. */
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;background:var(--bg);color-scheme:var(--scheme)}
body{font-family:var(--font-sans);color:var(--ink);background:transparent;line-height:1.62;
  -webkit-font-smoothing:antialiased}
/* Two very soft blooms, fixed to the viewport, so the page reads as one
   continuous surface. Fixed rather than background-attachment:fixed, which
   janks on iOS; -1 puts it under every section but over html's base color. */
body::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(1100px 620px at 84% -6%,var(--wash-1),transparent 62%),
             radial-gradient(920px 560px at -12% 16%,var(--wash-2),transparent 58%)}
::selection{background:var(--brand-soft);color:var(--ink)}
h1,h2,h3,.serif{font-family:var(--font-serif);font-weight:600;line-height:1.08;letter-spacing:-.015em}
a{color:inherit}
img{max-width:100%;display:block}
.wrap{max-width:var(--wrap-max);margin:0 auto;padding:0 22px}
button:focus-visible,a:focus-visible,input:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;border-radius:8px}

/* Site chrome — the web components render into light DOM so these rules apply.

   site-header is display:CONTENTS, and that is load-bearing rather than tidy.
   header.site is position:sticky, and a sticky element sticks only inside its
   parent's box. As a display:block wrapper, <site-header> was exactly as tall
   as the header itself — its other children (the skip link, the scrim, the
   drawer) are all out of flow — so the sticky range was zero pixels: the header
   scrolled off the top like static content and would not come back until you
   reached the top of the page again. The peeking behavior in site-chrome.js was
   running the whole time, dutifully un-tucking a bar that was no longer on
   screen. Dropping the box makes header.site a child of <body> for layout, so
   it sticks against the full page.

   site-footer has nothing sticky in it and stays a plain block. */
site-header{display:contents}
site-footer{display:block}
/* Bypass blocks. Off-screen until focused, then it lands over the sticky
   header — which is z-index:50, so this has to clear it. The href is resolved
   against the page's real <main> in site-chrome.js; a page with no landmark
   never renders this at all. */
.skip-link{position:absolute;left:12px;top:-100px;z-index:60;
  background:var(--btn-bg);color:var(--btn-ink);font-weight:600;font-size:14.5px;
  padding:12px 20px;border-radius:0 0 var(--radius-sm) var(--radius-sm);text-decoration:none;
  box-shadow:var(--shadow-lg);transition:top .18s ease}
.skip-link:focus{top:0}
@media(prefers-reduced-motion:reduce){.skip-link{transition:none}}
main:focus{outline:none}

/* THE HEADER PEEKS. Scrolling down tucks it away, scrolling up brings it back,
   and a beat after you stop scrolling up it leaves again — so the page keeps
   its full height and the nav is always one flick away. Driven by
   site-chrome.js, which is also where every rule about when NOT to tuck lives.

   translateY, not `top`: transform is composited, and this element carries a
   backdrop-filter that would otherwise re-rasterise on every frame. */
header.site.is-tucked{transform:translateY(-100%)}
@media(prefers-reduced-motion:reduce){header.site{transition:none}}
header.site{position:sticky;top:0;z-index:50;background:var(--header-bg);
  transition:transform .3s cubic-bezier(.16,1,.3,1);will-change:transform;
  backdrop-filter:saturate(1.6) blur(14px);-webkit-backdrop-filter:saturate(1.6) blur(14px);
  border-bottom:1px solid rgba(var(--hair-rgb),.07);
  box-shadow:0 1px 0 rgba(255,255,255,.5) inset,0 16px 30px -26px rgba(var(--shade-rgb),.55)}
/* A brand hairline along the bottom edge, fading out at both ends. The brown
   topbar that used to cap the page is gone, so the header has to draw its own
   line under itself rather than borrow one. */
header.site::after{content:"";position:absolute;left:8%;right:8%;bottom:-1px;height:1px;pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(var(--brand-rgb),.16),transparent)}
/* THE BAR SINCE 2026-09-28 ("A" of the header mocks): the wordmark, the
   links as plain text with a hairline that draws in under the one you are on,
   then the number and the cart at the right end. A fixed height rather than
   padding, so the links can fill it top to bottom - the pointer then never
   crosses dead space on its way from a link down into its menu. */
header.site .wrap{display:flex;align-items:center;gap:14px;min-height:72px;padding:0 22px}
.logo{display:flex;align-items:center;gap:12px;text-decoration:none}
.logo .name{font-family:var(--font-serif);font-weight:700;font-size:20px;line-height:1;letter-spacing:-.012em;color:var(--ink);white-space:nowrap}
.logo .name span{display:block;font-family:var(--font-sans);font-weight:500;font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-soft);margin-top:4px}
.nav{display:flex;align-self:stretch;align-items:stretch;margin-left:32px}
.nav>a,.nav-group>a{position:relative;display:inline-flex;align-items:center;gap:5px;padding:0 12px;
  text-decoration:none;font:500 13.5px var(--font-sans);letter-spacing:.005em;color:var(--ink);white-space:nowrap}
.nav>a::after,.nav-group>a::after{content:"";position:absolute;left:12px;right:12px;bottom:calc(50% - 15px);height:1px;
  background:var(--brand);transform:scaleX(0);transform-origin:left;transition:transform .3s cubic-bezier(.16,1,.3,1)}
.nav>a:hover::after,.nav-group:hover>a::after,.nav-group:focus-within>a::after{transform:scaleX(1)}
.nav-group{display:flex;align-items:stretch}
.nav-caret{width:10px;height:10px;flex:none;opacity:.5;transition:transform .2s ease,opacity .2s ease}
.nav-group:hover .nav-caret,.nav-group:focus-within .nav-caret{transform:rotate(180deg);opacity:.9}

/* The photo menu: a full-width panel hung from the bottom of the bar (the bar
   is its containing block - .nav-group is left unpositioned on purpose), the
   builder and the collections on the left, two room photographs on the right.
   Hover AND focus-within, so it opens for a keyboard as well as a pointer. */
.mega{position:absolute;left:0;right:0;top:100%;z-index:60;background:var(--bg-card);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);box-shadow:0 40px 60px -40px rgba(var(--shade-rgb),.4);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .2s ease,transform .28s cubic-bezier(.16,1,.3,1),visibility .2s}
.nav-group:hover .mega,.nav-group:focus-within .mega{opacity:1;visibility:visible;transform:none}
.mega-in{max-width:var(--wrap-max);margin:0 auto;padding:30px 22px 32px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.7fr);gap:48px}
.mega-k{font:700 10.5px var(--font-sans);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-soft);margin-bottom:10px}
.mega-list a{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:2px 12px;padding:12px 0;
  border-top:1px solid var(--line);text-decoration:none;color:var(--ink)}
.mega-list a:last-child{border-bottom:1px solid var(--line)}
.mega-list b{font-family:var(--font-serif);font-weight:600;font-size:17px;letter-spacing:-.012em;transition:color .16s}
.mega-list em{grid-column:1;font-style:normal;font-size:12.5px;color:var(--ink-soft)}
.mega-list .nav-arrow{grid-row:1/3;grid-column:2;width:14px;height:14px;color:var(--brand);transition:transform .28s cubic-bezier(.16,1,.3,1)}
.mega-list a:hover b{color:var(--brand-deep)}
.mega-list a:hover .nav-arrow{transform:translateX(3px)}
.mega-tag{display:inline-block;margin-left:8px;padding:2px 6px;border-radius:2px;background:var(--accent-soft);color:var(--accent);
  font:700 9.5px var(--font-sans);letter-spacing:.12em;text-transform:uppercase;vertical-align:3px}
.mega-pics{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.mega-pics a{text-decoration:none;color:var(--ink)}
.mega-ph{display:block;aspect-ratio:4/3;overflow:hidden;border-radius:var(--r-hard);background:var(--surface-sunk)}
.mega-ph img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .9s cubic-bezier(.16,1,.3,1)}
.mega-pics a:hover img{transform:scale(1.04)}
.mega-pics p{display:flex;justify-content:space-between;gap:10px;margin-top:10px;font-size:13.5px;font-weight:600}
.mega-pics small{font-size:12px;font-weight:500;color:var(--ink-soft)}
.nav a:focus-visible,.mega a:focus-visible{outline:2px solid var(--brand);outline-offset:3px}

/* The number, at the right end of the bar from 1100px. */
.tel-link{display:none;align-items:center;gap:9px;margin-left:auto;margin-right:4px;padding-right:18px;height:24px;
  border-right:1px solid var(--line);color:var(--ink);text-decoration:none;font:600 13.5px var(--font-sans);white-space:nowrap;transition:color .16s}
.tel-link:hover{color:var(--brand-deep)}
.tel-link svg{width:14px;height:14px;flex:none;color:var(--brand)}
@media(min-width:1100px){.tel-link{display:inline-flex}}
@media(prefers-reduced-motion:reduce){
  .mega,.mega-ph img,.nav>a::after,.nav-group>a::after{transition:none}
  .mega-pics a:hover img{transform:none}
}
.nav a.btn-primary{color:var(--btn-ink)}
.btn{cursor:pointer;border:none;font-family:inherit;font-weight:600;border-radius:999px;transition:transform .18s,box-shadow .18s,background .18s,border-color .18s;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:8px}
/* A button set display:inline-flex above, and an author rule beats the browser's
   own [hidden]{display:none} — so without this `btn.hidden = true` would leave
   the button on screen, beside the "pricing unavailable" note it replaces. */
.btn[hidden]{display:none}
.btn-primary{background:var(--btn-bg);color:var(--btn-ink);padding:12px 22px;font-size:15px;box-shadow:0 1px 0 rgba(255,255,255,.18) inset,0 8px 20px rgba(var(--brand-rgb),.32)}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 1px 0 rgba(255,255,255,.18) inset,0 12px 26px rgba(var(--brand-rgb),.4)}
.btn-primary:active{transform:translateY(0)}
.btn-primary:disabled{background:var(--btn-off);box-shadow:none;cursor:wait;transform:none}
.btn-ghost{background:transparent;color:var(--ink);border:1.5px solid var(--line);padding:11px 20px}
.btn-ghost:hover{border-color:var(--brand);color:var(--brand)}
.btn-block{width:100%;padding:17px;font-size:16.5px;letter-spacing:.01em}
/* -- Mobile nav drawer ------------------------------------------------
   A full-height slide-in panel. The scrim and the panel live OUTSIDE
   <header class="site"> on purpose: the header sets backdrop-filter, which
   makes it the containing block for position:fixed descendants, so a drawer
   nested inside it would be pinned to the header rather than to the viewport.

   Since 2026-09-28 it reads as a page of its own: the four collections as
   photographs, the design-online links, the rest, then the shop's card - the
   same card, open dot and all, that the footer carries. The whole panel
   scrolls as one, the card included. */
.nav-toggle{display:none;position:relative;width:44px;height:44px;border:none;
  background:transparent;cursor:pointer;padding:0}
.nav-toggle span{position:absolute;right:10px;width:22px;height:1.6px;border-radius:2px;
  background:var(--ink);transition:transform .3s cubic-bezier(.32,.72,0,1),opacity .18s ease,width .18s ease}
.nav-toggle span:nth-child(1){top:16px}
.nav-toggle span:nth-child(2){top:21.5px;width:15px}
.nav-toggle span:nth-child(3){top:27px}
.nav-toggle.open span:nth-child(1){transform:translateY(5.5px) rotate(45deg)}
.nav-toggle.open span:nth-child(2){opacity:0;width:22px}
.nav-toggle.open span:nth-child(3){transform:translateY(-5.5px) rotate(-45deg)}

.mm-scrim{position:fixed;inset:0;z-index:65;background:rgba(var(--scrim-rgb),.42);
  opacity:0;visibility:hidden;transition:opacity .34s ease,visibility .34s;
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.mm-scrim.open{opacity:1;visibility:visible}

/* visibility, not display, so the panel can slide - and so it drops out of the
   tab order while it is closed without needing inert. */
.mobile-menu{position:fixed;top:0;right:0;bottom:0;z-index:70;width:min(90vw,400px);
  display:flex;flex-direction:column;background:var(--bg);
  border-left:1px solid var(--line);
  box-shadow:-30px 0 70px -28px rgba(var(--shade-rgb),.5);
  transform:translateX(101%);visibility:hidden;
  transition:transform .38s cubic-bezier(.32,.72,0,1),visibility .38s}
.mobile-menu.open{transform:none;visibility:visible}
body.mm-open{overflow:hidden}
/* The chat launcher floats above everything and would sit on the drawer's
   last rows and its card; the drawer has the number and the email anyway. */
body.mm-open #cb-widget{visibility:hidden}

.mm-top{flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:12px 10px 12px 20px;border-bottom:1px solid var(--line)}
.mm-brand{font-family:var(--font-serif);font-weight:700;font-size:17px;line-height:1.1;letter-spacing:-.012em;color:var(--ink)}
.mm-brand span{display:block;margin-top:4px;font:500 9.5px var(--font-sans);letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-soft)}
.mm-close{flex:none;display:grid;place-items:center;width:44px;height:44px;border-radius:var(--r-hard);
  cursor:pointer;border:0;background:transparent;color:var(--ink);transition:color .16s ease}
.mm-close:hover{color:var(--brand-deep)}
.mm-close svg{width:18px;height:18px}

.mm-body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;padding:20px 20px calc(20px + env(safe-area-inset-bottom,0px))}
.mm-eyebrow{margin:6px 0 12px;font:700 10.5px var(--font-sans);letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-soft)}
.mm-tiles,.mm-rows{margin-bottom:22px}
.mm-tiles{display:grid;grid-template-columns:1fr 1fr;gap:14px 12px}
.mm-tiles a{text-decoration:none;color:var(--ink)}
.mm-ph{display:block;aspect-ratio:1/1;overflow:hidden;border-radius:var(--r-hard);background:var(--surface-sunk)}
.mm-ph img{width:100%;height:100%;object-fit:cover;display:block}
.mm-tiles b{display:block;margin-top:8px;font-size:13.5px;font-weight:600;line-height:1.3}
.mm-tiles em{display:block;font-style:normal;font-size:12px;color:var(--ink-soft)}
.mm-rows a{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:48px;
  border-top:1px solid var(--line);text-decoration:none;color:var(--ink);font:500 15px var(--font-sans)}
.mm-rows a:last-child{border-bottom:1px solid var(--line)}
.mm-rows-big a{min-height:52px;font-family:var(--font-serif);font-weight:600;font-size:17px;letter-spacing:-.01em}
.mm-rows .nav-arrow{flex:none;width:14px;height:14px;color:var(--brand)}
.mm-go{flex:none;width:14px;height:14px;color:var(--ink-soft);opacity:.6}

/* The shop's card: the letter's paper and brown top edge, as in the footer. */
.mm-shop{background:var(--bg-card);border:1px solid var(--line);border-top:3px solid var(--brand);border-radius:3px;padding:14px 16px 4px}
.mm-status{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;font-weight:500;color:var(--ink-soft)}
.mm-status::before{content:"";flex:none;width:7px;height:7px;border-radius:50%;background:rgba(var(--ink-rgb),.3)}
.mm-status.is-open::before{background:var(--accent);box-shadow:0 0 0 3px rgba(var(--accent-rgb),.16)}
.mm-shop a{display:flex;align-items:center;gap:10px;min-height:46px;border-top:1px solid var(--line);text-decoration:none;
  color:var(--ink);font-family:var(--font-serif);font-weight:500;font-size:15px;white-space:nowrap}
.mm-shop a.mm-tel{margin-top:10px;font-weight:600;font-size:19px}
.mm-shop svg{flex:none;width:15px;height:15px;color:var(--brand)}
@media(max-width:380px){.mm-shop{padding:14px 12px 4px}.mm-shop a{font-size:14px}}

/* The call button: a phone icon wherever the number is not written out. */
.call-link{display:none;position:relative;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:var(--r-hard);color:var(--ink);text-decoration:none;flex:none;transition:color .16s}
.call-link:hover{color:var(--brand-deep)}
.call-link svg{width:19px;height:19px}
/* The phone bar: the wordmark on the left, then call, cart and the menu. */
@media(max-width:880px){
  .nav{display:none}
  header.site .wrap{display:grid;grid-template-columns:minmax(0,1fr) 40px 40px 44px;gap:2px;min-height:64px;padding:0 8px 0 18px}
  .logo{min-width:0}
  .logo .name{font-size:clamp(15px,4.4vw,18px)}
  .logo .name span{font-size:clamp(8.5px,2.45vw,10px);letter-spacing:.17em;margin-top:3px}
  .nav-toggle{display:block}
  .call-link{display:inline-flex}
}
/* Between the phone bar and 1100px the number does not fit in full, so the
   call button stands in for it. */
@media(min-width:881px) and (max-width:1099px){.call-link{display:inline-flex;margin-left:auto}}
@media(min-width:881px){.mobile-menu,.mm-scrim{display:none !important}}
@media(prefers-reduced-motion:reduce){
  .mobile-menu,.mm-scrim,.nav-toggle span{transition-duration:.01ms}
}

.crumb{font-size:12.5px;color:var(--ink-soft);padding:20px 0 0;letter-spacing:.01em}
.crumb a{text-decoration:none;color:var(--brand-deep);font-weight:600}
.crumb a:hover{text-decoration:underline}

/* Product-detail page layout */
.pdp{display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);gap:48px;padding:26px 22px 24px;align-items:start}
@media(max-width:900px){.pdp{grid-template-columns:1fr;gap:26px}}
@media(max-width:520px){.pdp{padding:20px 16px 20px;gap:22px}}

.gallery{position:sticky;top:92px;min-width:0}
@media(max-width:900px){.gallery{position:static}}
.gviewport{position:relative;border-radius:var(--r-hard);overflow:hidden;box-shadow:var(--shadow-lg);border:1px solid rgba(var(--hair-rgb),.5);aspect-ratio:1/1;background:linear-gradient(160deg,var(--stage-a),var(--stage-b))}
.gviewport::after{content:"";position:absolute;inset:0;border-radius:inherit;box-shadow:inset 0 0 0 1px rgba(var(--shade-rgb),.06);pointer-events:none;z-index:2}
/* The 3D visualiser is a seamless white sweep — see SWEEP_WHITE in
   assets/closet-product.js. The background is what shows behind the canvas
   before it mounts and on a browser with no WebGL, so it has to be the same
   white or the frame flashes a themed stage and then snaps to paper.

   AND IT IS FRAMED LIKE EVERY OTHER CARD ON THE PAGE: 1px `--line` at
   `--r-hard`, no shadow — the buy box beside it, the spec card under it, the
   four tiles at the foot. What it does NOT keep is the treatment `.gviewport`
   gives a themed stage: a near-black `--hair-rgb` hairline at 50% and a second
   inset ring inside it, the pair of them made to stop a dark cyclorama from
   bleeding into the page. Over paper white that reads as a box ruled around
   the photograph, in a weight nothing else on the page uses. */
.gviewport.sweep{background:var(--sweep);border-color:var(--line);box-shadow:none}
.gviewport.sweep::after{display:none}
.gviewport .preview3d{position:absolute;inset:0}
.gviewport .preview3d canvas{display:block;width:100%!important;height:100%!important}
/* THE PRODUCT PAGES' PHOTO GALLERY (assets/render-gallery.js, 2026-09-26).
   A 4:3 stage; a square studio cut-out sits in it on the paper it was
   rendered on, so it is not cropped. Two stacked
   <img>s cross-fade, so a thumbnail click never flashes blank. The 3D viewer is
   the last tile, its own .gviewport filling the stage when chosen. */
/* .sweep too: .gviewport.sweep's white outranks a lone .rg-stage, and a
   square studio photo in the 4:3 stage showed white bands either side. */
.rg-stage,.rg-stage.sweep{aspect-ratio:4/3;background:#f7f6f3}
.rg-img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;opacity:0;transition:opacity .28s ease}
.rg-img.on{opacity:1}
.rg-stage.is-3d .rg-img{opacity:0}
.rg-3d{position:absolute;inset:0}
.rg-3d .gviewport{position:absolute;inset:0;border:0;box-shadow:none;border-radius:0;aspect-ratio:auto}
.rg-nav{position:absolute;top:50%;translate:0 -50%;z-index:3;width:40px;height:40px;border-radius:50%;border:1px solid rgba(var(--hair-rgb),.18);
  background:var(--sheet-bg);color:var(--ink);display:grid;place-items:center;cursor:pointer;opacity:.9}
.rg-nav:hover{opacity:1}
.rg-nav svg{width:20px;height:20px}
.rg-prev{left:12px}.rg-next{right:12px}
.rg-stage.is-3d .rg-nav,.rg-stage.is-3d .rg-badge{display:none}
.rg-badge{position:absolute;left:12px;bottom:12px;z-index:3;max-width:calc(100% - 24px);font:500 12px/1.3 Inter,system-ui,sans-serif;color:var(--ink-soft);
  background:var(--sheet-bg);border:1px solid rgba(var(--hair-rgb),.12);padding:7px 10px;border-radius:999px}
.rg-thumbs{display:flex;gap:10px;margin-top:12px;overflow-x:auto;padding-bottom:2px}
.rg-thumb{flex:0 0 auto;width:74px;aspect-ratio:1/1;border-radius:10px;overflow:hidden;cursor:pointer;padding:0;
  border:1px solid rgba(var(--hair-rgb),.2);background:#f7f6f3;position:relative}
.rg-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.rg-thumb[aria-selected="true"]{outline:2px solid var(--brand);outline-offset:2px}
.rg-thumb:disabled{opacity:.35;cursor:not-allowed}
.rg-thumb .rg-3dlabel{position:absolute;inset:0;display:grid;place-items:center;font:600 13px/1 Inter,system-ui,sans-serif;color:var(--ink)}
.rg-note{min-height:1.4em;margin:10px 2px 0;font:400 13px/1.45 Inter,system-ui,sans-serif;color:var(--ink-soft)}
/* The closet pages: tall 4:5 photos, so a square stage (the 3D viewer's own
   shape, so the page doesn't move) and whole thumbnails on the same paper. */
.rg-tall .rg-stage.sweep{aspect-ratio:1/1;background:#f7f6f3}
.rg-tall .rg-thumb img{object-fit:contain;background:#f7f6f3}
@media(max-width:520px){.rg-thumb{width:60px}.rg-nav{width:34px;height:34px}.rg-badge{font-size:11px}}
.preview-hint{position:absolute;inset:0;display:grid;place-items:center;color:var(--ink-soft);font:500 14px system-ui,sans-serif;text-align:center;padding:20px;pointer-events:none;transition:opacity .3s}
.preview-hint b{color:var(--ink)}
/* Shown in place of the 3D stage AND the configurator when the page runtime
   itself failed to load — a blocked CDN, an offline tab. The page is a shell at
   that point, so this has to carry a way to still get the job done. */
.pdp-offline{display:grid;gap:8px;justify-items:center;text-align:center;padding:26px 20px;
  max-width:40ch;margin:0 auto;pointer-events:auto}
.pdp-offline b{font-family:var(--font-serif);font-weight:600;font-size:17px;color:var(--ink)}
.pdp-offline span{font-size:13.5px;line-height:1.6;color:var(--ink-soft)}
.pdp-offline a{color:var(--brand-deep);font-weight:600}
.preview-hint.hidden{opacity:0;visibility:hidden}

/* Buy box */
/* NO DROP SHADOW ON A CONTENT SURFACE. The homepage's vocabulary, now the
   site's: a 2px radius and a 1px rule in `--line`, and nothing floating. A
   shadow under a card this size is what made a cabinet shop read as a software
   product — and with the radius already squared, a card that is squared AND
   lifted reads as neither one thing nor the other. The border is the card. */
.buybox{min-width:0;background:var(--bg-card);border:1px solid var(--line);border-radius:var(--r-hard);padding:32px 32px 30px}
@media(max-width:520px){.buybox{padding:24px 20px}}
.buybox h1{font-size:clamp(27px,3.4vw,37px);font-weight:700;margin:0 0 13px}

/* `.pdp-range` — "Priced $455–$1,000 depending on size and finish", the one
   price that is in the SERVED HTML (2026-09-23). The generators write it
   hidden and empty straight under the <h1>; functions/_middleware.js fills
   the <b> and drops `hidden` only when this slug has a live price, so AI
   crawlers that never run JS still read what the thing costs.

   A quiet qualifier of the title rather than a headline: the exact figure
   lives on the Add to cart button, and two prices set at the same weight
   would read as two different prices. The negative top margin pulls it up
   into the h1's 13px so it reads as the title's second line. No `display`
   here, so the UA `[hidden]` rule still wins — the rule below says so
   explicitly in case anything later gives it one. */
.pdp-range{margin:-4px 0 16px;font-size:15px;line-height:1.45;color:var(--ink-soft)}
.pdp-range b{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}
.pdp-range[hidden]{display:none}

/* THE SAVE / OPEN DIALOG (assets/design-save.js), in the Closet Builder —
   the markup is the page's own `#sv-modal`. */
/* The dialog's own close button. */
.cb-sheet-close{background:var(--line-soft);border:0;border-radius:var(--r-hard);width:30px;height:30px;cursor:pointer;color:var(--ink-soft);display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}
.cb-sheet-close:hover{background:var(--line-strong);color:var(--ink)}
.cb-sheet-close svg{width:15px;height:15px}

/* ---- the dialog ---- */
.cb-modal{position:fixed;inset:0;z-index:200;display:flex;align-items:center;justify-content:center;
  padding:20px;opacity:0;pointer-events:none;transition:opacity .18s ease}
.cb-modal.open{opacity:1;pointer-events:auto}
.cb-modal-back{position:absolute;inset:0;background:rgba(var(--scrim-rgb),.5);backdrop-filter:blur(3px)}
/* SQUARE, like every other surface on the site, and it keeps its shadow -
   the rule is that a card sitting ON the page is a border and nothing else,
   while a thing genuinely floating OVER the page still casts. A dialog over a
   scrim is the clearest case of the second. Trimmed from a 70px drop to
   something the size of the box it belongs to. */
.cb-modal-card{position:relative;width:100%;max-width:412px;max-height:calc(100vh - 40px);
  display:flex;flex-direction:column;background:var(--bg-card);border:1px solid var(--line);
  border-radius:var(--r-hard);box-shadow:0 18px 44px -14px rgba(var(--shade-rgb),.42);overflow:hidden;
  transform:translateY(8px) scale(.985);transition:transform .18s ease}
.cb-modal.open .cb-modal-card{transform:none}
.cb-modal-head{flex:none;display:flex;align-items:center;gap:10px;
  padding:16px 18px 13px;border-bottom:1px solid var(--line)}
.cb-modal-head h2{flex:1;margin:0;font:600 16px var(--font-sans);color:var(--ink)}
.cb-modal-body{padding:16px 18px 18px;overflow-y:auto;-webkit-overflow-scrolling:touch}
.cb-modal-lede{margin:0 0 15px;font:500 12.5px/1.6 var(--font-sans);color:var(--ink-soft)}
.cb-modal-lede b{color:var(--ink);font-weight:700}
@media(max-width:520px){
  .cb-modal{padding:0;align-items:flex-end}
  .cb-modal-card{max-width:none;max-height:92vh;border-radius:var(--r-hard) var(--r-hard) 0 0;border-bottom:0}
}
body.cb-modal-open{overflow:hidden}

/* ---- form ---- */
.cb-form{display:flex;flex-direction:column;gap:11px}
.cb-field{display:flex;flex-direction:column;gap:5px}
.cb-field > span{display:flex;align-items:baseline;gap:6px;
  font:600 11px var(--font-sans);letter-spacing:.07em;text-transform:uppercase;color:var(--ink-soft)}
.cb-field > span i{font-style:normal;font-weight:500;letter-spacing:.02em;text-transform:none;opacity:.75}
.cb-field input{width:100%;font:500 14px var(--font-sans);color:var(--ink);background:var(--surface-input);
  border:1.5px solid var(--line);border-radius:var(--r-hard);padding:11px 12px;transition:border-color .15s,box-shadow .15s}
.cb-field input::placeholder{color:var(--ink-soft);opacity:.6}
.cb-field input:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
/* Codes are read a character at a time — set them wide and monospaced so an
   8 does not have to be told apart from a B at 14px Inter. */
.cb-code-input{font:700 19px/1 ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace!important;
  letter-spacing:.14em;text-align:center;text-transform:uppercase;padding:14px 12px!important}
.cb-check{display:flex;align-items:flex-start;gap:9px;margin-top:2px;cursor:pointer;
  font:500 12px/1.5 var(--font-sans);color:var(--ink-soft)}
.cb-check input{flex:none;margin:1px 0 0;width:16px;height:16px;accent-color:var(--brand-deep)}
.cb-check b{color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums}
.cb-form-err{margin:0;padding:10px 12px;border-radius:var(--r-hard);border:1px solid var(--danger-line);
  background:var(--danger-soft);font:500 12px/1.5 var(--font-sans);color:var(--danger-ink)}
/* Turnstile's slot on the three protected forms (assets/turnstile.js). Most
   visitors never see it - it is 'interaction-only' - so it takes no room until
   the challenge asks for a click. */
.cb-turnstile:empty{display:none}
.cb-turnstile{max-width:100%}
.cb-form-fine{margin:2px 0 0;font:500 11px/1.55 var(--font-sans);color:var(--ink-soft)}
.cb-form-fine b{color:var(--ink);font-weight:700}
/* THE NO-PRICE FALLBACK on the product pages. Same slot the buy button would
   occupy, so a pricing outage never leaves the box with nothing to act on —
   and the only phone number in the panel. Which is the point: it appears when
   it is the thing to do, and at no other time.

   A sunk panel rather than a `--bg` fill: the page ground is the same white as
   the card in three of the five palettes, and the panel has to read as an
   alternative to a button. `b` is optional. */
.cb-cta-blind{margin:12px 0 0;padding:12px 14px;border-radius:var(--r-hard);
  border:1px solid var(--line);background:var(--surface-sunk);
  text-align:left;font:500 12.5px/1.6 var(--font-sans);color:var(--ink-soft)}
.cb-cta-blind b{display:block;margin-bottom:2px;font:700 12.5px var(--font-sans);color:var(--ink)}
.cb-cta-blind span{display:block;margin-bottom:8px}
.cb-cta-blind a{display:inline-block;color:var(--brand-deep);font:700 14px var(--font-sans);
  white-space:nowrap;text-decoration:none;
  border-bottom:1.5px solid var(--brand-soft);transition:border-color .18s}
.cb-cta-blind a:hover{border-bottom-color:var(--brand)}
.cb-form .btn-block{margin-top:3px;padding:14px;font-size:15px}

/* ---- the code, after a save ---- */
.cb-code-box{display:flex;flex-direction:column;align-items:center;gap:7px;
  padding:20px 16px;border:1px solid var(--line);border-radius:var(--r-hard);background:var(--surface-sunk);margin-bottom:11px}
.cb-code-cap{font:600 10px var(--font-sans);letter-spacing:.18em;text-transform:uppercase;color:var(--ink-soft)}
.cb-code-box b{font:700 27px/1 ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace;
  letter-spacing:.1em;color:var(--brand-deep)}
.cb-modal-acts{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:14px}
.cb-mini{cursor:pointer;font:600 12px var(--font-sans);color:var(--ink);background:var(--surface-input);
  border:1.5px solid var(--line);border-radius:var(--r-hard);padding:10px 8px;transition:all .15s}
.cb-mini:hover{border-color:var(--brand-soft);background:var(--brand-soft);color:var(--brand-deep)}
.cb-mini.ok{border-color:var(--ok-line);background:var(--accent-soft);color:var(--accent)}

/* Confirmation views (saved / quote sent) are not <form>s, so they miss the
   spacing .cb-form gives its children, and the dismiss button falls back to
   .btn-block's stock 17px/16.5px — sized for the page's primary CTA, not for
   the foot of a dialog. Direct-child selectors, so a real form's own submit
   button and fine print are left exactly as they were. */
.cb-modal-body > .cb-code-box{margin-bottom:13px}
.cb-modal-body > .cb-modal-acts{margin-bottom:17px}
.cb-modal-body > .cb-form-fine{margin:0 0 18px}
.cb-modal-body > .btn-block{padding:13px;font-size:15px}

/* ---- codes this browser has seen ---- */
.cb-recent{margin-top:18px;padding-top:14px;border-top:1px solid var(--line)}
.cb-recent-cap{display:block;margin-bottom:8px;font:600 10px var(--font-sans);
  letter-spacing:.15em;text-transform:uppercase;color:var(--ink-soft)}
.cb-recent-row{display:flex;align-items:center;gap:10px;width:100%;text-align:left;cursor:pointer;
  background:var(--surface-sunk);border:1px solid var(--line);border-radius:var(--r-hard);padding:10px 12px;
  font-family:var(--font-sans);transition:border-color .15s,background .15s}
.cb-recent-row + .cb-recent-row{margin-top:6px}
.cb-recent-row:hover{border-color:var(--brand-soft);background:var(--bg-card)}
.cb-recent-row b{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font:600 13px var(--font-sans);color:var(--ink)}
.cb-recent-row span{flex:none;font:700 11.5px var(--font-sans);letter-spacing:.06em;
  color:var(--ink-soft);font-variant-numeric:tabular-nums}

/* Buy-box configurator (#bb-config on the product pages), rendered by the
   page's product script: steppers you can type into, segmented picks and a
   live message strip. */
.bb-config{display:flex;flex-direction:column;gap:6px;margin:0 0 24px}
.cfg-field{display:flex;flex-direction:column;gap:4px;min-width:0}
.cfg-key{font-size:9.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-soft)}

/* Number field: type any legal size, or step it along the ladder the shop
   actually builds to. */
.cfg-stepper{display:flex;align-items:stretch;border:1.5px solid var(--line);border-radius:var(--r-hard);background:var(--surface-input);overflow:hidden;transition:border-color .15s}
.cfg-stepper:hover{border-color:var(--line-strong)}
.cfg-stepper:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px rgba(var(--brand-rgb),.08)}
.cfg-stepper.bad{border-color:var(--danger-ink)}
.cfg-step{border:0;background:var(--surface-input);color:var(--ink-soft);cursor:pointer;font:600 15px var(--font-sans);padding:0 9px;line-height:1;transition:background .15s,color .15s}
.cfg-step:hover:not(:disabled){background:var(--line-soft);color:var(--brand-deep)}
/* A spent stepper has to LOOK spent: a shoe cavity ships full, so its + is
   disabled from the first render and needs to read that way. */
.cfg-step:disabled{opacity:.32;cursor:not-allowed}
.cfg-num{display:inline-flex;align-items:center;flex:1;min-width:0;justify-content:center}
.cfg-num input{border:0;background:transparent;width:100%;min-width:0;text-align:center;padding:9px 0 9px 4px;font:600 13.5px var(--font-sans);color:var(--ink);-moz-appearance:textfield;appearance:textfield}
.cfg-num input:focus{outline:0}
/* The native spinner. Hiding it takes BOTH halves and the second one was
   missing: WebKit draws it as two pseudo-elements on the input, Firefox
   draws its own and only stops for `appearance:textfield` on the input
   itself. So every one of these fields carried a stray up/down arrow box
   in Firefox, sitting inside a stepper that already has a − and a +.
   `appearance` is listed unprefixed as well — Chrome has honoured the
   standard property on these pseudo-elements since 84. */
.cfg-num input::-webkit-outer-spin-button,.cfg-num input::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;margin:0}
.cfg-unit{color:var(--ink-soft);font-weight:500;font-size:11.5px;padding-right:4px}
/* A COUNT rather than a measurement: the shelf steppers on a closet module
   card show a number you cannot type into, so the well holds a <b> where the
   others hold an <input>. Padded to the same height as one, or a row of
   counters sitting beside a row of steppers comes out shorter than them. */
.cfg-num b{padding:9px 4px;font:700 13.5px var(--font-sans);color:var(--ink);
  font-variant-numeric:tabular-nums;min-width:22px;text-align:center}

.cfg-seg{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:5px}
.cfg-seg button{cursor:pointer;min-width:0;border:1.5px solid var(--line);background:var(--surface-input);color:var(--ink-soft);border-radius:var(--r-hard);padding:9px 6px;font:600 12px var(--font-sans);transition:border-color .15s,background .15s,color .15s}
.cfg-seg button:hover:not(:disabled){border-color:var(--brand-soft);color:var(--brand-deep)}
.cfg-seg button.sel{border-color:var(--brand-deep);background:var(--brand-soft);color:var(--brand-deep)}
.cfg-seg button:disabled{opacity:.4;cursor:not-allowed}

.cfg-msgs{display:flex;flex-direction:column;gap:6px}
.cfg-msgs:empty{display:none}
/* Clear of whatever it lands under — the foot of the panel on a closet module
   page. Only when there is something to say:
   an empty strip that reserved space would put a gap under every panel that is
   working properly. */
.cfg-msgs:not(:empty){margin-top:11px}
.cfg-msg{border-radius:var(--r-hard);padding:9px 11px;font:500 12px/1.45 var(--font-sans)}
.cfg-msg.err{background:var(--danger-soft);color:var(--danger-ink)}
.cfg-msg.warn{background:var(--warn-soft);color:var(--warn-ink)}

/* Buy-box CTA: the ask, then the quieter links — so the eye lands on the
   action and finds the rest only when it goes looking. It seals the bottom of
   the card, so it runs the full width of it. */
.bb-cta{position:relative;margin:28px -32px -30px;padding:22px 32px 24px;
  border-top:1px solid var(--line);border-radius:0 0 var(--r-hard) var(--r-hard);
  background:linear-gradient(180deg,var(--surface-sunk) 0%,var(--bg-card) 55%,var(--bg-card) 100%)}
@media(max-width:520px){.bb-cta{margin:24px -20px -24px;padding:19px 20px 21px}}
/* A hairline of brand color along the top edge, fading out at both ends — the
   seam reads as intentional rather than as the card simply stopping. */
.bb-cta::before{content:"";position:absolute;top:-1px;left:22px;right:22px;height:1px;
  background:linear-gradient(90deg,transparent,rgba(var(--brand-rgb),.16),transparent)}
/* Square, like everything else in the buy box. `.btn` is a 999px pill
   site-wide, which is the one control that would still have read as an app on
   a squared product page — the home page overrides it the same way inside its
   own `main`. */
.pdp .btn{border-radius:var(--r-hard)}
.pdp .cb-cta-blind{border-radius:var(--r-hard)}
.bb-cta .btn-block{padding:15px 18px;font-size:15.5px;letter-spacing:.005em}
/* THE ACTION AND ITS PRICE, one line: what you are doing on the left, what it
   costs on the right, with the full width of the button between them. Centring
   them together would read as a single long label — "Add to cart $1,234" — and
   pushing them apart is what makes the figure a second piece of information
   rather than the end of the first.

   The figure is tabular so it does not jiggle as the steppers move, and a
   shade lighter than the label: on a filled brand button pure white on both
   halves gives the eye nothing to rank. */
.bb-cta .btn-block{justify-content:space-between;gap:14px}
.bb-cta-label{display:inline-flex;align-items:center;gap:8px;min-width:0}
.bb-cta-price{font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:-.01em;
  white-space:nowrap;opacity:.92}
/* Empty until there is a price. Without this the flex gap and `space-between`
   still reserve the slot, which pushes the label off-centre on a button that
   has nothing to put in it. */
.bb-cta-price:empty{display:none}
.bb-cta .btn-primary{box-shadow:0 1px 0 rgba(255,255,255,.22) inset,0 2px 5px rgba(var(--brand-rgb),.18),0 12px 26px -10px rgba(var(--brand-rgb),.5)}
.bb-cta .btn-primary:hover{box-shadow:0 1px 0 rgba(255,255,255,.22) inset,0 3px 7px rgba(var(--brand-rgb),.2),0 18px 34px -12px rgba(var(--brand-rgb),.55)}
.bb-cta-arrow{width:16px;height:16px;flex:none;transition:transform .2s ease}
.bb-cta .btn-primary:hover .bb-cta-arrow{transform:translateX(3px)}

/* ---------------------------------------------------------------------------
   THE SECOND DOOR. A quiet aside under the buy button, not a rival to it: a
   buy box has ONE primary action, and the way to keep that true is to stop the
   alternative looking like one.

   Two lines: the question in the reading voice, the action in brand ink under
   it, over a hairline that separates without boxing.
   ------------------------------------------------------------------------- */
.bb-cta-alt{display:flex;flex-direction:column;align-items:center;gap:3px;
  margin-top:16px;padding-top:15px;border-top:1px solid var(--line-soft);
  text-decoration:none;color:var(--ink-soft);transition:color .15s}
.bb-cta-alt span{font:500 12.5px var(--font-sans)}
.bb-cta-alt b{display:inline-flex;align-items:center;gap:6px;
  font:700 13px var(--font-sans);color:var(--brand-deep);letter-spacing:.005em;
  transition:color .15s}
.bb-cta-alt svg{width:14px;height:14px;flex:none;transition:transform .2s ease}
.bb-cta-alt:hover b{color:var(--brand)}
.bb-cta-alt:hover svg{transform:translateX(3px)}
.bb-cta-alt:focus-visible{outline:2px solid var(--focus-ring);outline-offset:3px}


.sec-eyebrow{display:inline-block;font-size:11.5px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--brand);background:var(--brand-soft);padding:6px 15px;border-radius:999px;margin-bottom:16px}
/* Sections are separated by a tint that fades out at its own edges, not by a
   rule, so one melts into the next. */
.band{border:0;background:linear-gradient(180deg,
  rgba(var(--band-rgb),0) 0%,rgb(var(--band-rgb)) 9%,
  rgb(var(--band-rgb)) 91%,rgba(var(--band-rgb),0) 100%)}
.band:has(+ .band){background:linear-gradient(180deg,
  rgba(var(--band-rgb),0) 0%,rgb(var(--band-rgb)) 9%,rgb(var(--band-rgb)) 100%)}
.band + .band{background:linear-gradient(180deg,
  rgb(var(--band-rgb)) 0%,rgb(var(--band-rgb)) 91%,rgba(var(--band-rgb),0) 100%)}
.section{padding:78px 0}

/* Footer — the shop's card beside three columns of links (2026-09-28).
   The markup, and why the number and email lead, are in SiteFooter in
   site-chrome.js. */
footer{position:relative;isolation:isolate;color:rgb(var(--foot-ink));margin-top:0;padding:52px 0 0;
  background:linear-gradient(180deg,var(--foot-1) 0%,var(--foot-2) 52%,var(--foot-3) 100%)}
/* A gold filament along the top edge, fading out at both ends — the same one
   the header draws beneath itself and the collection cards light on hover. */
footer::before{content:"";position:absolute;top:0;left:6%;right:6%;height:1px;z-index:1;
  background:linear-gradient(90deg,transparent,var(--foot-rule),transparent)}
/* A warm bloom off the top left, so the panel reads as lit rather than filled. */
footer::after{content:"";position:absolute;inset:0 0 auto;height:360px;z-index:0;pointer-events:none;
  background:radial-gradient(58% 100% at 16% 0%,var(--foot-glow),transparent 70%)}
footer .wrap{position:relative;z-index:2}
footer .foot-grid{display:grid;gap:36px;padding-bottom:40px;align-items:center}

/* The card: the contact letter's paper, with its brown top edge. */
footer .foot-card{background:var(--bg-card);border:1px solid var(--foot-line);border-top:3px solid var(--brand);border-radius:3px;
  padding:22px 18px 16px;box-shadow:0 1px 0 rgba(var(--hair-rgb),.03),0 26px 50px -30px rgba(var(--shade-rgb),.35)}
footer .foot-card-head{display:flex;flex-direction:column;gap:10px;padding-bottom:18px}
footer .foot-card a.foot-mark{padding:0;font-family:var(--font-serif);font-weight:600;font-size:19px;line-height:1.1;letter-spacing:-.015em;color:var(--foot-strong)}
footer .foot-card a.foot-mark:hover{transform:none}
footer .foot-mark small{display:block;margin-top:5px;font:600 10px var(--font-sans);letter-spacing:.2em;text-transform:uppercase;color:rgba(var(--foot-ink),.7)}
footer .foot-status{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;font-weight:500;color:rgba(var(--foot-ink),.85);white-space:nowrap}
footer .foot-status::before{content:"";flex:none;width:7px;height:7px;border-radius:50%;background:rgba(var(--foot-ink),.4)}
footer .foot-status.is-open::before{background:var(--accent);box-shadow:0 0 0 3px rgba(var(--accent-rgb),.18)}
footer .foot-row{display:grid;grid-template-columns:18px minmax(0,1fr) auto;align-items:center;gap:10px;min-height:50px;border-top:1px solid var(--foot-line)}
footer .foot-row > svg{width:15px;height:15px;fill:none;stroke:var(--brand);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
footer .foot-row small{display:none;font-size:12px;color:rgba(var(--foot-ink),.65)}
footer .foot-plain{font-size:13.5px;line-height:1.5;color:rgba(var(--foot-ink),.85);padding:8px 0}
/* The number and the email in the serif, and an underline that draws in. */
footer .foot-card a.foot-v{padding:0;justify-self:start;font-family:var(--font-serif);font-weight:500;font-size:14.5px;
  letter-spacing:-.01em;white-space:nowrap;color:var(--foot-strong);
  background:linear-gradient(currentColor,currentColor) 0 100%/0 1px no-repeat;transition:background-size .35s cubic-bezier(.16,1,.3,1)}
footer .foot-card a.foot-v:hover{background-size:100% 1px;transform:none}
footer .foot-card a.foot-tel{font-weight:600;font-size:22px;letter-spacing:-.018em}
footer .foot-copy{position:relative;display:grid;place-items:center;width:32px;height:32px;padding:0;border:1px solid var(--foot-line);border-radius:3px;
  background:transparent;color:rgba(var(--foot-ink),.75);cursor:pointer;transition:color .16s,border-color .16s}
footer .foot-copy:hover{color:var(--foot-strong);border-color:rgba(var(--foot-ink),.35)}
footer .foot-copy svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
footer .foot-copy::after{content:"Copied";position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);white-space:nowrap;
  font:600 11px var(--font-sans);padding:4px 7px;border-radius:3px;background:var(--toast-bg);color:var(--toast-fg);opacity:0;pointer-events:none;transition:opacity .16s}
footer .foot-copy.done::after{opacity:1}

/* The links: three short columns, two across on a phone. */
footer .foot-cols{display:grid;grid-template-columns:1fr 1fr;gap:26px 24px}
footer h4,footer h5{font-family:var(--font-sans);font-weight:700;font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:rgba(var(--foot-ink),.6);margin-bottom:8px}
footer a{display:block;width:fit-content;color:rgba(var(--foot-ink),.82);text-decoration:none;font-size:14px;padding:9px 0;overflow-wrap:anywhere;transition:color .16s ease,transform .16s ease}
footer a:hover{color:var(--foot-strong);transform:translateX(2px)}
footer a:focus-visible,footer button:focus-visible{outline:2px solid var(--brand);outline-offset:3px}
footer .foot-bottom{display:flex;flex-direction:column;gap:4px;
  background:linear-gradient(var(--foot-line),var(--foot-line)) 22px 0/calc(100% - 44px) 1px no-repeat;padding-top:18px;padding-bottom:24px;
  font-size:12.5px;letter-spacing:.012em;color:rgba(var(--foot-ink),.6)}
/* The email fills its row exactly at 390; a step down keeps it whole on a 360 phone. */
@media(max-width:374px){footer .foot-card a.foot-v{font-size:13.5px}}
@media(min-width:600px){
  footer .foot-card{padding:30px 34px 22px}
  footer .foot-card-head{flex-direction:row;justify-content:space-between;align-items:flex-start;gap:12px}
  footer .foot-row{gap:12px}
  footer .foot-row small{display:block}
  footer .foot-card a.foot-v{font-size:17px}
  footer .foot-card a.foot-tel{font-size:22px}
  footer .foot-cols{grid-template-columns:repeat(3,1fr)}
  footer a{padding:5px 0}
  footer .foot-bottom{flex-direction:row;flex-wrap:wrap;justify-content:space-between;gap:8px 20px;padding-top:20px;padding-bottom:26px}
}
@media(min-width:881px){
  footer{padding-top:72px}
  footer .foot-grid{grid-template-columns:minmax(0,500px) minmax(0,1fr);gap:80px;padding-bottom:56px}
}

/* ---------- cart button + badge ----------
   The count is written by assets/cart.js into anything marked
   data-cart-count, so the two headers on this site (the web component and the
   home page's hand-written one) stay in step without either knowing how the
   cart works. */
/* An icon button at every width since 2026-09-28: at the right end of the bar
   beside the number, where the text links it used to imitate no longer are. */
/* The account, beside the cart from 881px (the drawer has it below). A dot
   in the brand when this browser is signed in. */
.acct-link{position:relative;display:none;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:var(--r-hard);color:var(--ink);text-decoration:none;flex:none;transition:color .16s}
.acct-link:hover{color:var(--brand-deep)}
.acct-link svg{width:20px;height:20px}
.acct-dot{position:absolute;top:8px;right:8px;width:8px;height:8px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 2px var(--bg)}
@media(min-width:881px){.acct-link{display:inline-flex}}
.acct-link:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.cart-link{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:var(--r-hard);color:var(--ink);text-decoration:none;flex:none;transition:color .16s}
.cart-link:hover{color:var(--brand-deep)}
.cart-link svg{width:20px;height:20px}
.cart-badge{position:absolute;top:3px;right:1px;min-width:17px;height:17px;padding:0 5px;
  border-radius:999px;background:var(--brand-deep);color:var(--on-brand);
  font:700 10px/17px var(--font-sans);text-align:center;font-variant-numeric:tabular-nums;
  box-shadow:0 0 0 2px var(--bg)}
.cart-badge[hidden]{display:none}
.cart-link:focus-visible,.call-link:focus-visible,.tel-link:focus-visible,.nav-toggle:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* ---------- add-to-cart confirmation ----------
   Deliberately not a dialog. Adding to a cart is the thing the customer just
   asked for; interrupting them to confirm it worked would stop them mid-flow,
   which on a configurator is exactly where they least want stopping. */
.bb-toast{position:fixed;left:50%;bottom:24px;z-index:120;transform:translate(-50%,14px);
  max-width:min(460px,calc(100vw - 32px));background:var(--toast-bg);color:var(--toast-fg);
  border-radius:14px;padding:13px 18px;font:500 13.5px/1.5 var(--font-sans);
  box-shadow:0 18px 44px rgba(var(--shade-rgb),.34);opacity:0;pointer-events:none;
  transition:opacity .22s ease,transform .22s ease}
.bb-toast.show{opacity:1;transform:translate(-50%,0);pointer-events:auto}
.bb-toast b{font-weight:700}
.bb-toast a{color:var(--toast-link);font-weight:700;text-decoration:none;white-space:nowrap}
.bb-toast a:hover{text-decoration:underline}
.bb-toast.bad{background:var(--danger-deep)}
.bb-toast.bad a{display:none}
@media(prefers-reduced-motion:reduce){.bb-toast{transition:opacity .2s}}
@media(max-width:520px){.bb-toast{bottom:calc(16px + env(safe-area-inset-bottom,0))}}
/* Briefly green, so the button itself acknowledges the click even if the
   toast is scrolled past on a short screen. */
.btn.is-added{background:var(--accent-fill);color:var(--on-accent)}
/* Text for a screen reader only — used on icon-only buttons that already
   carry a visible label their icon merely repeats. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ═══ SITE CONTACT ══════════════════════════════════════════════════════
   <site-contact>, above the footer on every page; the markup is in
   site-chrome.js. Since 2026-09-27 it is one section, "Write to the shop.":
   the contact form written as a letter - the fields sit inside its sentence,
   and the deals opt-in is its P.S. - across the full width, then the small
   "just the deals" signup as its own row under it (2026-09-28; the call and
   email blocks that stood beside the letter are gone - both live in the
   footer).

   EVERY SELECTOR IS SCOPED TO .site-contact, and that is not tidiness. The
   block uses .form-note and .sub-form, and samples/index.html has elements by
   those names with its own styles. Unscoped, this would restyle that page.

   The component sits OUTSIDE main on every page, so it carries its own
   rhythm (--sc-*) and heading sizes rather than reading the homepage's. */
.site-contact{
  scroll-margin-top:80px;
  --sc-gap:48px;
  --sc-rule:28px;
  --sc-r:2px;
  display:block;
  margin-top:var(--sc-gap);
  padding-top:var(--sc-rule);
  padding-bottom:var(--sc-gap);
  border-top:1px solid var(--line)}
.site-contact h2{
  font-family:var(--font-serif);font-weight:600;margin-bottom:22px;
  font-size:32px;line-height:1.06;letter-spacing:-.022em;text-wrap:balance}

/* ── the newsletter: its own panel under the letter ──
   A tinted panel (--surface-sunk) the width of the letter: a serif title and
   one line, then the email field and a filled Subscribe button joined into one
   bar. Side by side from 881, stacked below. Still id="deals" and still
   /api/subscribe - only the words and the dress changed. */
.site-contact .sc-deals{display:grid;gap:16px;margin-top:22px;padding:24px 20px;
  background:var(--surface-sunk);border:1px solid var(--line);border-radius:3px}
.site-contact .sc-news-t{font-family:var(--font-serif);font-weight:600;font-size:22px;line-height:1.15;
  letter-spacing:-.018em;color:var(--ink);margin:0}
.site-contact .sc-deals-copy p{margin-top:6px;font-size:14.5px;line-height:1.55;color:var(--ink-soft);max-width:46ch}
/* Phone: the field and a full-width Subscribe stacked, one height, one
   corner; from 600 they join into a single bar. */
.site-contact .sc-sub-row{display:grid;grid-template-columns:minmax(0,1fr);gap:10px}
/* 16px, not smaller: iOS zooms the viewport on focus for anything under it. */
.site-contact .sc-sub-row input{min-width:0;min-height:52px;border:1px solid var(--line-strong);
  border-radius:var(--sc-r);padding:0 14px;font:inherit;font-size:16px;color:var(--ink);
  background:var(--bg-card);transition:border-color .18s}
.site-contact .sc-sub-row input:focus{outline:none;border-color:var(--brand)}
.site-contact .sc-sub-row .btn-primary{width:100%;border-radius:var(--sc-r);padding:0 22px;min-height:52px}
/* The honeypot. Off-screen rather than display:none - a bot that parses the
   CSS skips anything hidden, and this one is meant to be filled in. */
.site-contact .sub-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.site-contact .sub-form .form-note{margin-top:10px;text-align:center}
.site-contact .form-note.is-err{color:var(--danger-ink);font-weight:600}
/* What replaces the signup once it is sent. */
.site-contact .sub-done{
  display:flex;gap:10px;align-items:flex-start;margin:12px 0 0;
  background:var(--accent-soft);border:1px solid var(--accent);border-radius:var(--sc-r);
  padding:14px 16px;color:var(--ink);font-size:14.5px;line-height:1.5}
.site-contact .sub-done:focus{outline:none}
.site-contact .sub-done:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.site-contact .sub-done svg{flex:none;width:20px;height:20px;margin-top:1px;color:var(--accent)}

/* ── the letter ── */
.site-contact .sc-letter{position:relative;background:var(--bg-card);border:1px solid var(--line);
  border-top:3px solid var(--brand);border-radius:3px;padding:24px 20px 22px}
/* The letterhead: a small letter-spaced TO in the brand color, the shop's name
   in the serif italic, on one hairline rule. */
.site-contact .sc-to{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;margin:0 0 18px;padding-bottom:14px;
  border-bottom:1px solid var(--line);font-family:var(--font-sans);font-weight:400}
.site-contact .sc-to-k{font-size:10.5px;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:var(--brand)}
.site-contact .sc-to-n{font-family:var(--font-serif);font-style:italic;font-weight:500;font-size:20px;line-height:1.2;
  letter-spacing:-.01em;color:var(--ink)}
.site-contact .sc-line{font-family:var(--font-serif);font-size:clamp(20px,1.9vw,25px);line-height:2;
  letter-spacing:-.01em;color:var(--ink)}
/* The fields inside the sentence. On a phone each takes its own line inside
   the sentence; from 600px they sit inline at the width their words need. */
.site-contact .sc-f{display:inline-block;width:100%;white-space:nowrap}
.site-contact .sc-line input{display:inline-block;width:calc(100% - .45em);min-width:0;border:0;border-bottom:1.5px solid var(--line-strong);
  border-radius:0;background:none;padding:0 4px;font-family:var(--font-serif);font-size:inherit;line-height:1.5;
  color:var(--brand-deep);transition:border-color .18s}
.site-contact .sc-line input:focus{outline:none;border-bottom-color:var(--brand)}
.site-contact .sc-line input::placeholder,.site-contact .sc-letter textarea::placeholder{
  color:var(--ink-soft);opacity:.6;font-style:italic}
.site-contact .sc-letter textarea{display:block;width:100%;margin-top:14px;border:0;border-bottom:1.5px solid var(--line-strong);
  border-radius:0;background:none;resize:vertical;padding:10px 0;font-family:var(--font-serif);
  font-size:clamp(18px,1.6vw,21px);line-height:1.6;color:var(--ink);transition:border-color .18s}
.site-contact .sc-letter textarea:focus{outline:none;border-bottom-color:var(--brand)}
/* The P.S. - the contact form's marketing opt-in, unchecked by default. The
   text beside the box is its label. */
.site-contact .sc-ps{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;align-items:start;
  margin:16px 0 0;cursor:pointer;font-family:var(--font-serif);font-size:17px;line-height:1.45;color:var(--ink-soft)}
.site-contact .sc-ps b{color:var(--ink)}
/* 18px and brand-coloured: the browser default is 13px and grey and reads as
   disabled next to type this size. */
.site-contact .sc-ps input{width:18px;height:18px;margin:4px 0 0;accent-color:var(--brand);cursor:pointer}
.site-contact .sc-ps input:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* Phone: Send the letter across the full width, the privacy note centred
   under it; from 600 the two share a line. */
.site-contact .sc-send{display:grid;gap:12px;justify-items:center;margin-top:22px}
.site-contact .sc-send .btn-primary{width:100%}
.site-contact .form-note{font-size:12.5px;color:var(--ink-soft);line-height:1.5}
.site-contact .sc-send .form-note{margin:0}
.site-contact .form-note svg{width:12px;height:12px;display:inline-block;vertical-align:-1px;margin-right:4px;opacity:.75}
.site-contact .contact-err{color:var(--danger-ink);font-weight:600;margin-top:12px}

/* The receipt that stands in for the form once it is sent. */
.site-contact .contact-success{
  position:relative;overflow:hidden;
  background:var(--accent-soft);border:1px solid var(--accent);border-radius:var(--sc-r);padding:26px}
.site-contact .contact-success-check{width:40px;height:40px;color:var(--accent);margin-bottom:12px}
/* Dismiss. The receipt clears itself after a few seconds, but a customer who
   is done reading should not have to wait for a timer to get the form back. */
.site-contact .contact-success-x{
  position:absolute;top:10px;right:10px;display:grid;place-items:center;
  width:34px;height:34px;padding:0;border:0;border-radius:999px;
  background:transparent;color:var(--ink-soft);cursor:pointer;
  transition:color .18s,background .18s}
.site-contact .contact-success-x svg{width:17px;height:17px;margin:0}
.site-contact .contact-success-x:hover{color:var(--ink);background:rgba(var(--accent-rgb),.14)}
.site-contact .contact-success-x:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* How long the thanks has left, without a line of copy saying so. It stops if
   the receipt is held open. */
.site-contact .revert-bar{
  position:absolute;left:0;bottom:0;height:2px;width:100%;
  transform-origin:left;background:var(--accent);
  animation:sc-revert var(--revert-ms,7000ms) linear forwards}
.site-contact .contact-success.is-held .revert-bar{animation:none;transform:scaleX(0)}
@keyframes sc-revert{from{transform:scaleX(1)}to{transform:scaleX(0)}}
@media(prefers-reduced-motion:reduce){.site-contact .revert-bar{animation:none;transform:scaleX(0)}}
.site-contact .contact-success h3{font-size:21px;margin:0 0 6px;padding:0;border:0;
  font-family:var(--font-serif);font-weight:600;letter-spacing:-.02em;text-transform:none;color:var(--ink)}
.site-contact .contact-success p{color:var(--ink-soft);font-size:15.5px}
.site-contact .sent-copy{margin-top:10px;font-size:14px;color:var(--ink-soft)}
.site-contact .sent-copy summary{cursor:pointer;font-weight:600}

/* The homepage's square-cornered buttons, restated. site.css's own .btn is a
   999px pill, which is the shared chrome's shape - inside this section the
   page's own vocabulary wins. */
.site-contact .btn{border-radius:var(--sc-r);letter-spacing:.015em}
.site-contact .btn-primary{padding:16px 28px;font-size:16px;min-height:52px;
  box-shadow:0 1px 0 rgba(255,255,255,.14) inset}
.site-contact .btn-primary:hover{transform:none;background:var(--btn-bg-hover);
  box-shadow:0 1px 0 rgba(255,255,255,.14) inset}
.site-contact .btn-ghost{padding:0 18px;min-height:46px;font-size:15px;border-width:1px}

@media(min-width:600px){
  .site-contact{--sc-gap:64px;--sc-rule:36px}
  .site-contact h2{font-size:clamp(34px,3.4vw,46px);line-height:1.04;letter-spacing:-.024em}
  .site-contact .sc-f{display:inline;width:auto}
  .site-contact .sc-line input{width:auto}
  .site-contact .sc-line .w-m{width:8em}
  .site-contact .sc-line .w-l{width:11em}
  .site-contact .sc-line .w-s{width:9em}
  .site-contact .sc-letter{padding:32px 34px 30px}
  .site-contact h2{margin-bottom:26px}
  .site-contact .sc-deals{padding:28px 34px}
  .site-contact .sc-sub-row{grid-template-columns:minmax(0,1fr) auto;gap:0}
  .site-contact .sc-sub-row input{border-right:0;border-radius:var(--sc-r) 0 0 var(--sc-r)}
  .site-contact .sc-sub-row .btn-primary{width:auto;border-radius:0 var(--sc-r) var(--sc-r) 0}
  .site-contact .sc-send{display:flex;flex-wrap:wrap;align-items:center;justify-items:normal;gap:12px 18px}
  .site-contact .sc-send .btn-primary{width:auto}
  .site-contact .sub-form .form-note{margin-top:8px;text-align:left}
}
@media(min-width:881px){
  .site-contact{--sc-gap:clamp(72px,6.2vw,96px);--sc-rule:clamp(36px,3vw,48px)}
  .site-contact .sc-letter{padding:40px 44px 36px}
  .site-contact .sc-deals{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:48px;align-items:center;margin-top:24px;padding:34px 44px}
  .site-contact .sc-news-t{font-size:clamp(24px,2.2vw,30px)}
}

/* ═══ SITE CHAT ═════════════════════════════════════════════════════════
   The assistant, rendered by <site-chat> in site-chrome.js.

   EVERY RULE IS ROOTED AT #cb-widget (or body.cb-fs), and that is load-bearing
   rather than tidy: the chat's class names (.cb-panel, .cb-toggle, .cb-msg …)
   share the `cb-` prefix with the save dialog's and once shared it with the
   retired Closet Builder's, and a bare rule meant for one reaches the other.
   See the note on `.cb-msg` at the end of this section.

   THE BUTTON SHOWS AT EVERY WIDTH. It used to carry
       @media(max-width:980px){#cb-widget{display:none}}
   so on a phone there was no chat at all unless you opened the nav drawer.
   Below 980px the button opens the full-screen sheet; above it, the panel.

   Colors, radii and shadows are the page's own tokens throughout. The panel
   used to be a hardcoded 18px corner over a flat brand fill, which read as a
   third-party widget dropped onto the site rather than part of it. */
#cb-widget{position:fixed;bottom:24px;right:24px;z-index:100;
  display:flex;flex-direction:column;align-items:flex-end;gap:12px}
@media(max-width:600px){#cb-widget{bottom:18px;right:18px}}

/* The launcher wears the brand gradient, with a soft lift, so
   it reads as part of the same set as the rest of the chrome. */
#cb-widget .cb-toggle{width:56px;height:56px;border-radius:var(--radius-pill);border:none;
  background:linear-gradient(180deg,var(--brand-grad-a),var(--brand-grad-b));color:var(--on-brand);
  cursor:pointer;display:grid;place-items:center;
  box-shadow:0 1px 0 rgba(255,255,255,.18) inset,0 10px 26px -8px rgba(var(--brand-rgb),.62);
  transition:transform .18s cubic-bezier(.16,1,.3,1),box-shadow .18s;flex:none}
#cb-widget .cb-toggle:hover{transform:translateY(-2px);
  box-shadow:0 1px 0 rgba(255,255,255,.2) inset,0 16px 32px -8px rgba(var(--brand-rgb),.7)}
#cb-widget .cb-toggle svg{width:26px;height:26px}

#cb-widget .cb-panel{width:372px;max-width:calc(100vw - 28px);
  background:var(--bg-card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-lg);display:flex;flex-direction:column;overflow:hidden;
  transform-origin:bottom right}
#cb-widget .cb-panel[hidden]{display:none}
@media(prefers-reduced-motion:no-preference){
  #cb-widget .cb-panel:not([hidden]){animation:cb-in .26s cubic-bezier(.16,1,.3,1) both}
}
@keyframes cb-in{from{opacity:0;transform:translateY(10px) scale(.97)}to{opacity:1;transform:none}}

/* ---- header ---- */
#cb-widget .cb-head{display:flex;align-items:center;gap:11px;padding:13px 12px 13px 15px;
  background:linear-gradient(180deg,var(--brand-grad-a),var(--brand-grad-b));
  color:var(--on-brand);flex:none}
#cb-widget .cb-head-ic{width:34px;height:34px;border-radius:var(--radius-pill);
  background:var(--panel-chip);display:grid;place-items:center;flex:none}
#cb-widget .cb-head-ic svg{width:18px;height:18px}
#cb-widget .cb-head-info{flex:1;min-width:0}
/* Serif, like every other name on the site. */
#cb-widget .cb-head-info strong{display:block;font-family:var(--font-serif);font-weight:600;
  font-size:16px;letter-spacing:-.012em;line-height:1.2}
#cb-widget .cb-status{display:flex;align-items:center;gap:5px;font-size:11.5px;opacity:.82;margin-top:2px}
#cb-widget .cb-status i{width:6px;height:6px;border-radius:50%;background:var(--accent-soft);flex:none}
/* Calling is the highest-intent thing anyone can do in here, and it used to be
   a phone number spelled out in a sentence. It is now one tap, always. */
#cb-widget .cb-call,#cb-widget .cb-close{background:none;border:none;color:inherit;cursor:pointer;
  display:grid;place-items:center;width:32px;height:32px;flex:none;
  border-radius:var(--radius-sm);opacity:.82;text-decoration:none;transition:opacity .15s,background .15s}
/* currentColor is --on-brand here, so the wash follows the header rather
   than assuming the header is dark. */
#cb-widget .cb-call:hover,#cb-widget .cb-close:hover{opacity:1;background:color-mix(in srgb,currentColor 16%,transparent)}
#cb-widget .cb-call svg{width:17px;height:17px}
#cb-widget .cb-close svg{width:18px;height:18px}

/* ---- transcript ---- */
#cb-widget .cb-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;
  gap:9px;min-height:280px;max-height:min(52vh,440px);scroll-behavior:smooth;background:var(--bg)}
#cb-widget .cb-msg{max-width:88%;line-height:1.58;font-size:14.5px;padding:10px 13px;
  word-break:break-word;white-space:pre-wrap}
#cb-widget .cb-msg.bot{background:var(--bg-card);color:var(--ink);align-self:flex-start;
  border:1px solid var(--line);border-radius:var(--radius-sm) var(--radius-sm) var(--radius-sm) 3px}
#cb-widget .cb-msg.user{background:var(--brand-deep);color:var(--on-brand);align-self:flex-end;
  border-radius:var(--radius-sm) var(--radius-sm) 3px var(--radius-sm)}
/* The assistant routes people to pages; those arrive as real links now. */
#cb-widget .cb-msg.bot a{color:var(--brand-deep);font-weight:600;text-decoration:none;
  border-bottom:1px solid var(--brand-soft);transition:border-color .15s}
#cb-widget .cb-msg.bot a:hover{border-bottom-color:var(--brand)}
#cb-widget .cb-retry{display:inline-flex;margin-top:9px;padding:7px 13px;cursor:pointer;
  font:600 13px var(--font-sans);border-radius:var(--radius-sm);
  border:1px solid var(--line);background:var(--surface-input);color:var(--ink)}
#cb-widget .cb-retry:hover{border-color:var(--brand);color:var(--brand-deep)}

#cb-widget .cb-typing{display:flex;gap:4px;align-items:center;padding:12px 14px;align-self:flex-start;
  background:var(--bg-card);border:1px solid var(--line);
  border-radius:var(--radius-sm) var(--radius-sm) var(--radius-sm) 3px}
#cb-widget .cb-typing span{width:6px;height:6px;border-radius:50%;background:var(--ink-soft);
  animation:cb-bounce .9s infinite}
#cb-widget .cb-typing span:nth-child(2){animation-delay:.18s}
#cb-widget .cb-typing span:nth-child(3){animation-delay:.36s}
@keyframes cb-bounce{0%,60%,100%{transform:translateY(0);opacity:.45}30%{transform:translateY(-5px);opacity:1}}

/* ---- openers ----
   An empty text box is the highest-friction thing a chat can show. These stand
   in for it once, and go the moment anything is asked. */
#cb-widget .cb-chips{display:flex;flex-wrap:wrap;gap:7px;padding:0 14px 12px;background:var(--bg);flex:none}
#cb-widget .cb-chips[hidden]{display:none}
#cb-widget .cb-chip{cursor:pointer;font:600 12.5px var(--font-sans);color:var(--brand-deep);
  padding:8px 12px;border-radius:var(--radius-pill);border:1px solid var(--line);
  background:var(--bg-card);transition:border-color .15s,background .15s,transform .15s}
#cb-widget .cb-chip:hover{border-color:var(--brand);background:var(--brand-soft);transform:translateY(-1px)}

/* ---- composer ---- */
#cb-widget .cb-foot{padding:11px 12px;border-top:1px solid var(--line);display:flex;gap:8px;
  align-items:flex-end;flex:none;background:var(--bg-card)}
/* 16px, not smaller: iOS zooms the viewport on focus for anything under it. */
#cb-widget .cb-foot textarea{flex:1;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:10px 12px;font:inherit;font-size:16px;resize:none;outline:none;line-height:1.45;
  max-height:108px;overflow-y:auto;color:var(--ink);background:var(--surface-input);
  transition:border-color .15s,box-shadow .15s}
#cb-widget .cb-foot textarea:focus{border-color:var(--brand);
  box-shadow:0 0 0 3px rgba(var(--brand-rgb),.12)}
#cb-widget .cb-send{width:40px;height:40px;border-radius:var(--radius-sm);border:none;
  background:linear-gradient(180deg,var(--brand-grad-a),var(--brand-grad-b));color:var(--on-brand);
  cursor:pointer;display:grid;place-items:center;flex:none;align-self:flex-end;
  transition:filter .15s,opacity .15s}
#cb-widget .cb-send:hover{filter:brightness(1.08)}
#cb-widget .cb-send:disabled{opacity:.4;cursor:default;filter:none}
#cb-widget .cb-send svg{width:17px;height:17px}

/* ---- the full-screen sheet a phone gets ---- */
body.cb-fs{overflow:hidden}
body.cb-fs #cb-widget{display:flex !important;position:fixed;inset:0;
  flex-direction:column;align-items:stretch;z-index:200;gap:0;background:var(--bg)}
/* dvh, not vh: the on-screen keyboard shrinks the visual viewport, and vh does
   not follow it — the composer ended up underneath the keyboard. */
@supports (height:100dvh){body.cb-fs #cb-widget{height:100dvh}}
body.cb-fs #cb-widget .cb-toggle{display:none !important}
body.cb-fs #cb-widget .cb-panel{display:flex !important;flex:1;min-height:0;width:100% !important;
  max-width:100% !important;border-radius:0 !important;border:none !important;
  box-shadow:none !important;animation:none}
body.cb-fs #cb-widget .cb-messages{max-height:none !important;flex:1}
body.cb-fs #cb-widget .cb-msg{max-width:82%;font-size:15.5px}
body.cb-fs #cb-widget .cb-head{padding-top:calc(13px + env(safe-area-inset-top))}
body.cb-fs #cb-widget .cb-foot{padding-bottom:calc(11px + env(safe-area-inset-bottom))}

@media(prefers-reduced-motion:reduce){
  #cb-widget .cb-typing span{animation:none}
  #cb-widget .cb-toggle:hover,#cb-widget .cb-chip:hover{transform:none}
  #cb-widget .cb-messages{scroll-behavior:auto}
}
/* THE OLD CLOSET BUILDER'S MESSAGE STRIP, LEFT IN ON PURPOSE. It was a bare
   `.cb-msg`, and the chat's bubbles carry the same class — so under the
   #cb-widget rules above it still sets every bubble's display:flex, its gap
   and alignment, and the 500 weight and sans family of its type. Deleting it
   would restyle the chat; fold these into `#cb-widget .cb-msg` to retire it. */
.cb-msg{display:flex;gap:7px;align-items:flex-start;border-radius:var(--r-hard);padding:7px 9px;font:500 11.5px/1.45 var(--font-sans)}

/* ===========================================================================
   THE PAGE HERO
   ===========================================================================
   A label, a lede, an optional pair of actions, and a row of four facts over
   a hairline. Left-aligned, one column, no split.

   `.facts` is two-up on a phone with a rule between the pairs, four across
   from 881 with the rules turned off — a fact row that wraps to two lines
   should read as two rows, not as one row that ran out of space. */
.lede{font-size:18px;line-height:1.5;color:var(--ink-soft);max-width:58ch}
.cta-row{display:grid;gap:10px;justify-items:stretch}
.hero{padding-top:26px}
.hero .label{margin-bottom:14px}
.hero .lede{margin-top:18px}
.hero .cta-row{margin-top:26px}
.facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0;margin-top:32px;border-top:1px solid var(--line)}
.facts div{padding:16px 16px 4px 0}
.facts div:nth-child(even){padding-left:16px;border-left:1px solid var(--line)}
.facts div:nth-child(n+3){border-top:1px solid var(--line);margin-top:12px;padding-top:16px}
.facts b{display:block;font-family:var(--font-serif);font-weight:600;font-size:21px;
  line-height:1.1;letter-spacing:-.02em}
.facts span{display:block;margin-top:5px;font-size:12.5px;line-height:1.45;color:var(--ink-soft)}
@media(min-width:600px){
  .hero{padding-top:36px}
  .cta-row{display:flex;flex-wrap:wrap;gap:14px 26px;align-items:center}
  .cta-row .btn{width:auto}
}
@media(min-width:881px){
  .lede{font-size:clamp(18.5px,1.6vw,20px)}
  .hero{padding-top:clamp(40px,4.4vw,60px)}
  .hero .lede{margin-top:22px}
  .hero .cta-row{margin-top:30px}
  .facts{margin-top:clamp(32px,4vw,48px);grid-template-columns:repeat(4,minmax(0,1fr))}
  .facts div{padding:18px 22px 4px 0}
  .facts div:nth-child(even){padding-left:0;border-left:0}
  .facts div:nth-child(n+3){border-top:0;margin-top:0;padding-top:18px}
  .facts b{font-size:clamp(22px,2.2vw,27px)}
}
