/* ============================================================
   North Carolina Engravings & Signs — BenSited build
   Brand palette, taken from the logo sheet: navy-slate, grey and cream.
   ============================================================ */

:root{
  /* Straight off the brand sheet she supplied: the dark, the grey and the
     cream sampled from her logo's own swatch row (#4E4F53 / #959B97 /
     #FFFCE9). The dark carries a little blue so it reads as the navy she
     asked for rather than flat charcoal. */
  --navy:#414A55;
  --navy-2:#4E5865;
  --navy-deep:#333A43;
  --slate:#4E4F53;        /* logo charcoal — dark surfaces */
  --grey:#959B97;         /* logo grey                     */
  --grey-2:#B6BBB6;
  --cream:#FFFCE9;        /* logo cream                    */
  --paper:#FAF7EA;        /* page background               */
  --paper-2:#F1EDDC;      /* alt section                   */
  --birch:#E4D8BC;        /* the wood she cuts             */
  --ink:#2E3238;
  --muted:#666D72;
  --text:#2E3238;
  --text-on-dark:#F6F3E4;
  --muted-on-dark:#BFC4BE;

  --font-display:'Zilla Slab', Georgia, 'Times New Roman', serif;
  --font-script:'Dancing Script', 'Brush Script MT', cursive;
  --font-body:'Nunito Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --wrap:1180px;
  --pad:clamp(20px,5vw,40px);
  --radius:14px;
  --shadow:0 16px 38px rgba(46,50,56,.18);
  --shadow-deep:0 24px 52px rgba(46,50,56,.28);
  --ease:cubic-bezier(.22,.61,.36,1);
  --settle:cubic-bezier(.16,.7,.3,1);   /* long ease-out for the intro hand-off */
}

*,*::before,*::after{box-sizing:border-box}
/* `scrollbar-gutter:stable` is load-bearing, not tidiness. The intro holds
   the page with `body{overflow:hidden}`, so on any platform with classic
   scrollbars (Windows, most Linux) the bar disappears for the duration and
   comes back at the hand-off — the viewport gains ~15px and everything
   centred, the engraved mark included, slides half of that sideways. The
   gutter is reserved from the first paint instead, so the width never
   changes and the mark does not move. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scrollbar-gutter:stable}
body{
  margin:0;
  background:var(--cream);
  color:var(--text);
  font-family:var(--font-body);
  font-size:17px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{max-width:100%;display:block}
a{color:var(--navy);text-underline-offset:3px}
a:hover{color:var(--grey)}

:focus-visible{outline:3px solid var(--navy);outline-offset:3px;border-radius:6px}

.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}
.skip-link{
  position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--navy);color:var(--cream);font-weight:800;
  padding:14px 20px;border-radius:0 0 10px 0;text-decoration:none;
}
.skip-link:focus{left:0}

.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--pad)}

/* ---------- Type ---------- */
.display{
  font-family:var(--font-display);
  font-weight:600;
  line-height:1.04;
  letter-spacing:-.01em;
  margin:0 0 18px;
  color:var(--navy);
  font-size:clamp(2rem,5vw,3.2rem);
}
.section-dark .display{color:var(--text-on-dark)}
/* "5 stars, over 200 buyers real reviews" broke after "real" because
   .section-head caps every head at 70ch. This one gets the whole wrap and
   a size tied to the viewport so it stays on one line down to a tablet;
   below that it wraps rather than shrink to unreadable. */
/* The head sits tight to the rows: the top row's own tall-card slack is
   the breathing room now, so the usual head margin would double it. And
   the long reviews all live in the lower row, so that row's slack hangs
   off the bottom — the section's own bottom padding comes off to match. */
#reviews .section-head{max-width:none;margin-bottom:8px}
#reviews{padding-bottom:clamp(16px,2vw,28px)}
#reviews-title{max-width:none;white-space:nowrap;font-size:min(3.2rem,4.15vw)}
@media (max-width:720px){
  #reviews-title{white-space:normal;font-size:clamp(2rem,7vw,2.6rem)}
}
h3{font-family:var(--font-display);font-weight:600;letter-spacing:0;margin:0}

.eyebrow{
  display:inline-flex;align-items:center;gap:10px;
  font-size:.74rem;font-weight:800;letter-spacing:.22em;text-transform:uppercase;
  color:var(--navy);margin:0 0 14px;
}
.eyebrow::before{content:"";width:26px;height:2px;background:var(--navy);flex:none}
.lead{font-size:clamp(1rem,2vw,1.14rem);color:var(--muted);max-width:64ch;margin:0 0 14px}
.section-dark .lead{color:var(--muted-on-dark)}

/* ---------- Buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-family:var(--font-body);font-weight:800;font-size:.92rem;
  letter-spacing:.04em;text-transform:uppercase;text-decoration:none;
  padding:15px 28px;border-radius:999px;border:2px solid transparent;
  cursor:pointer;min-height:50px;
  transition:transform .18s var(--ease), background-color .18s, border-color .18s, box-shadow .18s, color .18s;
}
.btn-primary{background:var(--navy);color:#FFF7EC;border-color:var(--navy)}
.btn-primary:hover{background:var(--grey);border-color:var(--grey);color:#FFF7EC;transform:translateY(-2px);box-shadow:0 12px 26px rgba(228,87,30,.35)}
.btn-ghost{background:transparent;color:var(--navy);border-color:rgba(59,42,29,.4)}
.btn-ghost:hover{border-color:var(--navy);color:var(--navy);transform:translateY(-2px);background:rgba(59,42,29,.06)}
.hero .btn-ghost{color:#F3EADC;border-color:rgba(243,234,220,.5)}
.hero .btn-ghost:hover{color:#FFF;border-color:#F3EADC;background:rgba(243,234,220,.1)}
.btn-block{width:100%}

.text-link{
  font-weight:800;text-decoration:none;color:var(--navy);
  border-bottom:2px solid rgba(228,87,30,.3);padding-bottom:2px;
  transition:border-color .18s;
}
.text-link:hover{border-color:var(--navy)}

/* ---------- Header ---------- */
.site-header{
  position:sticky;top:0;z-index:100;
  background:rgba(246,240,230,.95);
  border-bottom:1px solid transparent;
  transition:border-color .25s, box-shadow .25s;
}
.site-header.scrolled{border-color:rgba(59,42,29,.12);box-shadow:0 6px 24px rgba(42,29,19,.08)}
.nav{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:70px}
.brand{display:inline-flex;align-items:center;gap:12px;text-decoration:none}
.brand-mark{width:46px;height:auto;flex:none}
.brand-words{display:flex;flex-direction:column;line-height:1.15}
.brand-line1{font-family:var(--font-script);font-weight:700;font-size:1.12rem;color:var(--grey);letter-spacing:.01em}
.brand-line2{font-family:var(--font-display);font-weight:600;font-size:.98rem;letter-spacing:.06em;color:var(--navy);white-space:nowrap}
.nav-links{list-style:none;display:flex;align-items:center;gap:6px;margin:0;padding:0}
.nav-links a:not(.btn){
  display:inline-block;padding:10px 14px;text-decoration:none;
  color:var(--text);font-weight:700;font-size:.95rem;border-radius:9px;
  transition:background-color .15s,color .15s;
}
.nav-links a:not(.btn):hover{background:rgba(59,42,29,.07);color:var(--navy)}
.nav-cta{margin-left:8px;padding:11px 20px !important;min-height:42px}

.nav-toggle{
  display:none;width:46px;height:46px;border:0;border-radius:12px;
  background:var(--navy);cursor:pointer;position:relative;
}
.nav-toggle span,.nav-toggle span::before,.nav-toggle span::after{
  content:"";position:absolute;left:50%;top:50%;width:20px;height:2px;
  background:var(--cream);transform:translate(-50%,-50%);
  transition:transform .22s var(--ease), top .22s var(--ease), background-color .22s;
}
.nav-toggle span::before{top:-6px}
.nav-toggle span::after{top:8px}
.nav-toggle span::before,.nav-toggle span::after{left:0;transform:translate(0,0)}
.nav-toggle[aria-expanded="true"] span{background:transparent}
.nav-toggle[aria-expanded="true"] span::before{top:0;transform:rotate(45deg)}
.nav-toggle[aria-expanded="true"] span::after{top:0;transform:rotate(-45deg)}

@media (max-width:920px){
  .nav-toggle{display:inline-flex}
  .nav-links{
    position:absolute;left:0;right:0;top:100%;
    flex-direction:column;align-items:stretch;gap:4px;
    background:var(--cream);border-bottom:1px solid rgba(59,42,29,.14);
    padding:12px var(--pad) 20px;box-shadow:var(--shadow);
    display:none;
  }
  .nav-links.is-open{display:flex}
  .nav-links a:not(.btn){padding:14px 12px;font-size:1rem}
  .nav-cta{margin-left:0;margin-top:8px;text-align:center;justify-content:center}
}

/* ---------- Hero ----------
   The board is CSS on purpose: it is identical on the WebGL path (the
   canvas above it is transparent and draws only the engraving, the laser
   and the sparks) and on the no-GL / reduced-motion path, where the DOM
   title simply stays visible with an engraved text treatment. */
.hero{
  position:relative;overflow:hidden;isolation:isolate;
  /* Minus the sticky header, so the board fills exactly what you can see
     and no more. */
  min-height:min(calc(100svh - 71px), 900px);
  display:flex;align-items:stretch;
}
/* The board used to be one 94deg gradient of even stripes, which read as
   corrugated cardboard rather than wood: every stripe the same width, the
   whole thing repeating on one period the eye finds in a second. It is
   built the way a board actually looks now — cathedral figure first (the
   big soft arcs the grain sweeps around), then the fine lines over it,
   then knots, then the light. */
.hero-board{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    linear-gradient(180deg, rgba(255,244,222,.18) 0%, rgba(0,0,0,0) 28%, rgba(38,25,15,.34) 100%),
    /* Wide, uneven bands of tone across the width. Without these the board
       is one flat veneer: real stock is glued up from pieces that never
       quite match, and that mismatch is most of what says "wood". */
    linear-gradient(90deg,
      rgba(84,56,28,.16) 0%, rgba(84,56,28,.04) 9%, transparent 17%,
      rgba(224,186,136,.13) 24%, transparent 33%,
      rgba(84,56,28,.13) 41%, transparent 49%,
      rgba(224,186,136,.10) 58%, transparent 66%,
      rgba(84,56,28,.15) 74%, transparent 83%,
      rgba(224,186,136,.12) 91%, rgba(84,56,28,.10) 100%),
    radial-gradient(62% 130% at 24% 54%, rgba(126,88,48,.28), transparent 62%),
    radial-gradient(48% 120% at 71% 42%, rgba(126,88,48,.22), transparent 60%),
    radial-gradient(38% 90% at 92% 78%, rgba(206,166,116,.24), transparent 64%),
    linear-gradient(93deg,
      #A87F4F 0%, #B78C58 7%, #A57C4B 14%, #C09363 22%, #AD8252 30%,
      #BC9060 38%, #A67D4C 47%, #C29566 55%, #B08555 63%,
      #BE9262 71%, #A87F4F 80%, #B98D5D 89%, #AA804F 100%),
    #B08555;
}
/* Long grain. Four layers on deliberately coprime periods — 7, 13, 23 and
   31px — so the composite only repeats every few thousand pixels and there
   is no stripe rhythm to find. They are all within half a degree of each
   other on purpose: spread the angles and the layers cross instead of
   running together, and the board comes out woven like linen. */
.hero-grain{position:absolute;inset:0;opacity:.6;
  background:
    repeating-linear-gradient(90.8deg, rgba(58,38,20,.12) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(90.9deg, rgba(58,38,20,.09) 0 2px, transparent 2px 13px),
    repeating-linear-gradient(91.1deg, rgba(255,232,196,.08) 0 1px, transparent 1px 23px),
    repeating-linear-gradient(91.2deg, rgba(58,38,20,.07) 0 3px, transparent 3px 31px);
}
/* Three knots, kept out at the edges so they never crowd the mark. Each is
   a dark eye inside three rings that stop — `repeating-radial-gradient`
   was the obvious tool and the wrong one: it does not stop, so one knot
   ringed the entire hero like a felled trunk. */
.hero-knots{position:absolute;inset:0;opacity:.5;
  background:
    radial-gradient(ellipse 62px 34px at 12% 27%,
      rgba(52,32,13,.50) 0 16%, rgba(60,38,17,.16) 22% 30%, transparent 34% 44%,
      rgba(60,38,17,.20) 50% 58%, transparent 62% 76%, rgba(60,38,17,.12) 82% 90%, transparent 94%),
    radial-gradient(ellipse 46px 26px at 87% 64%,
      rgba(52,32,13,.42) 0 16%, rgba(60,38,17,.14) 22% 32%, transparent 36% 50%,
      rgba(60,38,17,.16) 56% 66%, transparent 70%),
    radial-gradient(ellipse 38px 22px at 63% 89%,
      rgba(52,32,13,.34) 0 18%, rgba(60,38,17,.12) 24% 34%, transparent 38% 54%,
      rgba(60,38,17,.13) 60% 70%, transparent 74%);
}
/* The light. A raking shaft from over the customer's left shoulder, which
   is what makes a flat plane read as a lit surface — and it breathes, very
   slowly, so the hero is never completely still even after the burn is
   over and the page is just sitting there. */
.hero-light{position:absolute;inset:-10% -20%;pointer-events:none;
  background:
    linear-gradient(104deg,
      rgba(255,241,214,0) 20%, rgba(255,241,214,.22) 33%,
      rgba(255,246,226,.40) 44%, rgba(255,241,214,.16) 56%, rgba(255,241,214,0) 70%),
    radial-gradient(78% 66% at 14% -8%, rgba(255,245,224,.46), transparent 64%),
    /* …and the far corner it never reaches. */
    radial-gradient(80% 70% at 104% 108%, rgba(40,26,14,.30), transparent 60%);
  animation:hero-breathe 19s ease-in-out infinite;
  will-change:opacity, transform;
}
@keyframes hero-breathe{
  0%,100%{opacity:.72;transform:translate3d(0,0,0) scale(1)}
  50%    {opacity:1;  transform:translate3d(2.5%,-1.5%,0) scale(1.05)}
}
.hero-vignette{position:absolute;inset:0;
  background:radial-gradient(110% 90% at 50% 34%, transparent 38%, rgba(42,29,19,.44) 100%);
}
.hero-gl{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none}

.hero-inner{
  position:relative;z-index:2;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding-top:24px;padding-bottom:60px;
}
/* Space the canvas engraves the name into. The DOM title occupies the same
   box, so when WebGL never arrives nothing moves — the words are simply
   painted by CSS instead of the laser. */
.hero-engrave-space{display:none}
.hero-title{margin:0 0 10px;display:flex;flex-direction:column;align-items:center;gap:0}
.hero-title-script{
  font-family:var(--font-script);font-weight:700;
  font-size:clamp(4.2rem,15vw,9.5rem);line-height:1.05;
  color:var(--ink);
  text-shadow:0 1px 0 rgba(255,235,200,.35), 0 -1px 2px rgba(30,18,10,.55);
}
.hero-title-caps{
  font-family:var(--font-display);font-weight:600;
  font-size:clamp(.78rem,2.3vw,1.32rem);letter-spacing:.34em;text-indent:.34em;
  text-transform:uppercase;color:var(--ink);
  text-shadow:0 1px 0 rgba(255,235,200,.3), 0 -1px 2px rgba(30,18,10,.5);
  margin-top:6px;
}
/* Once the canvas is engraving the same words, the DOM copy yields the
   pixels but keeps the accessibility tree. */
/* The mark, at the size and place the laser burns it. */
/* Layered shadows, because one blur reads as a sticker: a tight contact
   shadow for the edge sitting on the board, a mid shadow for the piece
   standing off it, and a wide soft one for the room. The pale top edge is
   the light catching the cut. */
.hero-logo{
  display:block;width:clamp(226px,28vw,364px);height:auto;margin:0 auto;
  filter:
    drop-shadow(0 -1px 0 rgba(255,240,214,.45))
    drop-shadow(0 3px 3px rgba(30,20,10,.34))
    drop-shadow(0 14px 22px rgba(30,20,10,.42))
    drop-shadow(0 42px 60px rgba(30,20,10,.32));
  transition:opacity .28s var(--ease);
}
/* Hidden from the very first paint, not from whenever hero.js has finished
   fetching three.js and decoding the mark. Hanging it off `.engraving` —
   a class the JS adds after all that — is what made the site open on the
   finished logo for a beat before it vanished and got burned in, which is
   the one thing this whole sequence exists to avoid. The <img> keeps its
   box the entire time: that box is what the engraving is measured against.

   `.shown` goes on at the end of the burn, and on every fallback path
   (reduced motion, no WebGL, lost context, both backstops), so the mark
   cannot be left invisible. With scripting off, `.no-js` never comes back
   off and the first rule never matches. */
html:not(.no-js) .hero-logo{opacity:0}
html:not(.no-js) .hero.shown .hero-logo{opacity:1}
.hero-gl{transition:opacity .5s var(--ease)}
.hero.engraved .hero-gl{opacity:0}
/* …and back, once the only thing left on it is the sawdust. */
.hero.ambient .hero-gl{opacity:1;transition:opacity 1.4s var(--ease)}

/* ---------- Intro hold ----------
   Everything except the board and the mark waits for the burn, then fades
   in around it. Scoped to :not(.no-js) so that with scripting off there is
   nothing to release the page and nothing is ever hidden. */
html.intro-on:not(.no-js) body{overflow:hidden}
/* The board covers the whole viewport for the duration, including the
   strip the header occupies — otherwise the page's cream shows above the
   wood and gives the intro away. Position only; the board is decorative
   and out of flow either way, so NOTHING moves, which is the one thing
   this sequence cannot afford. */
html.intro-on:not(.no-js) .hero-board{position:fixed;z-index:0}
html.intro-on:not(.no-js) .site-header,
html.intro-on:not(.no-js) .ticker,
html.intro-on:not(.no-js) .section,
html.intro-on:not(.no-js) .site-footer{opacity:0}
html.intro-on:not(.no-js) .site-header{transform:translateY(-8px)}

/* The page arrives in order, not all at once. Everything fading on the
   same clock read as a single pop; letting the header lead and the
   sections follow reads as the site settling in around the mark, which is
   the whole idea. Slow ease-out, no bounce — and slower and further apart
   since 3 Sep, because at just over a second with a third of a second
   between them they still arrived as one event. */
.site-header{transition:opacity 1.6s var(--settle) .12s, transform 1.6s var(--settle) .12s}
.ticker{transition:opacity 1.7s var(--settle) .5s}
.section{transition:opacity 1.9s var(--settle) .78s}
.site-footer{transition:opacity 1.9s var(--settle) 1s}

.hero-sub{
  margin:14px auto 18px;max-width:44ch;
  color:#F7EFDF;font-size:clamp(1.02rem,2.4vw,1.22rem);font-weight:600;
  text-shadow:0 1px 10px rgba(42,29,19,.5);
}
.hero-chips{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin:0 0 26px;padding:0}
.chip{
  display:inline-flex;align-items:center;gap:7px;
  background:rgba(35,22,16,.55);color:#F3EADC;
  border:1px solid rgba(243,234,220,.22);
  font-size:.8rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  padding:8px 14px;border-radius:999px;backdrop-filter:none;
}
.chip .star{color:#F3B34C}
.hero-actions{display:flex;flex-wrap:wrap;gap:14px;justify-content:center}

/* The sub-block starts hidden only while the engraving plays; app.js adds
   .is-on when the laser finishes (or right away on the fallback paths). */
.hero-under{opacity:0;transform:translateY(14px);transition:opacity 1.6s var(--settle) .26s, transform 1.6s var(--settle) .26s}
.hero-under.is-on{opacity:1;transform:none}
html.no-js .hero-under{opacity:1;transform:none}


@media (prefers-reduced-motion:reduce){
  .hero-light{animation:none}
}

/* ---------- Ticker ----------
   House rule: marquees NEVER pause on hover. */
.ticker{
  background:var(--navy);border-block:1px solid rgba(0,0,0,.3);
  overflow:hidden;padding:13px 0;
  contain:layout style paint;
}
.ticker-track{
  display:flex;width:max-content;
  animation:slide 30s linear infinite;
  will-change:transform;backface-visibility:hidden;transform:translate3d(0,0,0);
}
@keyframes slide{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
.ticker-item{
  display:inline-flex;align-items:center;gap:20px;padding-inline:20px;
  font-family:var(--font-display);font-weight:600;letter-spacing:.04em;
  font-size:1.02rem;color:var(--cream);white-space:nowrap;
}
.tick-dot{width:6px;height:6px;border-radius:1px;background:var(--navy);transform:rotate(45deg);flex:none}

/* ---------- Sections ---------- */
.section{padding:clamp(64px,9vw,110px) 0;position:relative}
.section-alt{background:var(--paper-2)}
.section-dark{background:var(--navy);color:var(--text-on-dark)}
.section-head{max-width:70ch;margin-bottom:clamp(30px,5vw,50px)}

/* Reveal states are set by GSAP; this is only the resting state so there is
   no flash before app.js runs. With no JS at all, html.no-js keeps content
   visible. */
html:not(.no-js) [data-reveal]{opacity:0;transform:translateY(26px)}
html:not(.no-js) .gl-off [data-reveal]{opacity:1;transform:none}

/* ---------- Maker ---------- */
.maker-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(28px,5vw,64px);align-items:center}
.maker-media{position:relative;min-height:420px}
.maker-photo{
  margin:0;border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-deep);
  border:6px solid #FFFDF4;
}
.maker-photo img{width:100%;height:100%;object-fit:cover}
.maker-photo--small{
  position:absolute;right:-6%;bottom:-8%;width:52%;
  transform:rotate(3deg);
  border-radius:12px;
}
@media (max-width:900px){
  .maker-grid{grid-template-columns:1fr}
  .maker-media{min-height:0;margin-top:8px}
  .maker-photo--small{right:0;bottom:-6%}
}

.stats{list-style:none;display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:26px 0 0;padding:0}
@media (max-width:640px){.stats{grid-template-columns:repeat(2,1fr)}}
.stat{
  background:#FFFDF4;border:1px solid rgba(59,42,29,.12);border-radius:12px;
  padding:16px 14px;text-align:center;
}
.stat strong{
  display:block;font-family:var(--font-display);font-weight:700;
  font-size:1.7rem;color:var(--navy);line-height:1.1;
}
.stat-star{color:var(--grey);font-size:1.1rem;vertical-align:6px;margin-left:2px}
.stat span{font-size:.8rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}

/* ---------- Gallery (React) ---------- */
.gallery-filters{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:26px}
.gallery-filter{
  border:2px solid rgba(59,42,29,.25);background:transparent;color:var(--navy);
  font-family:var(--font-body);font-weight:800;font-size:.84rem;
  letter-spacing:.05em;text-transform:uppercase;
  padding:10px 18px;border-radius:999px;cursor:pointer;
  transition:background-color .18s,border-color .18s,color .18s,transform .18s;
}
.gallery-filter:hover{border-color:var(--navy);transform:translateY(-1px)}
.gallery-filter.is-active{background:var(--navy);border-color:var(--navy);color:var(--cream)}

.gallery-grid{
  columns:3 300px;column-gap:16px;
}
.gallery-item{
  display:block;width:100%;margin:0 0 16px;break-inside:avoid;
  border:0;padding:0;background:none;cursor:zoom-in;
  border-radius:14px;overflow:hidden;box-shadow:var(--shadow);
  position:relative;
}
.gallery-item img{width:100%;transition:transform .5s var(--ease)}
.gallery-item:hover img{transform:scale(1.04)}
.gallery-item::after{
  content:attr(data-label);
  position:absolute;left:0;right:0;bottom:0;
  padding:26px 14px 12px;
  background:linear-gradient(180deg,transparent,rgba(35,22,16,.72));
  color:#F3EADC;font-size:.8rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  opacity:0;transition:opacity .3s var(--ease);
}
.gallery-item:hover::after,.gallery-item:focus-visible::after{opacity:1}
.gallery-empty{color:var(--muted);font-weight:700}

/* The work rows: two counter-scrolling marquees, same rules as the
   reviews — even copies, margins not gaps, duration set from measured
   width, and never a hover pause. */
.work-marquee{
  position:relative;overflow:hidden;
  margin-inline:calc(var(--pad) * -1);
  padding-block:4px;
  contain:layout style paint;
}
.work-marquee + .work-marquee{margin-top:16px}
.work-marquee::before,.work-marquee::after{
  content:"";position:absolute;top:0;bottom:0;width:clamp(20px,5vw,80px);
  z-index:2;pointer-events:none;
}
.work-marquee::before{left:0;background:linear-gradient(90deg,var(--paper-2),transparent)}
.work-marquee::after{right:0;background:linear-gradient(270deg,var(--paper-2),transparent)}
.work-track{
  display:flex;width:max-content;
  animation:reviewSlide var(--work-dur,70s) linear infinite;
  will-change:transform;backface-visibility:hidden;transform:translate3d(0,0,0);
}
.work-track--reverse{animation-direction:reverse}
.work-card{
  flex:none;height:240px;margin:0 16px 0 0;
  border:0;padding:0;background:none;cursor:zoom-in;
  border-radius:14px;overflow:hidden;box-shadow:var(--shadow);
  position:relative;
}
.work-card img{height:100%;width:100%;object-fit:cover}
.work-card::after{
  content:attr(data-label);
  position:absolute;left:0;right:0;bottom:0;
  padding:22px 12px 10px;
  background:linear-gradient(180deg,transparent,rgba(35,22,16,.72));
  color:#F3EADC;font-size:.76rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;
  opacity:0;transition:opacity .3s var(--ease);
}
.work-card:hover::after,.work-card:focus-visible::after{opacity:1}
@media (max-width:640px){.work-card{height:170px;margin-right:12px}}

/* Lightbox */
.lightbox{
  border:0;padding:0;background:transparent;max-width:min(92vw,980px);
}
.lightbox::backdrop{background:rgba(26,16,10,.88)}
.lightbox-frame{position:relative;background:var(--navy-deep);border-radius:16px;overflow:hidden;box-shadow:var(--shadow-deep)}
.lightbox-frame img{max-height:82vh;width:auto;max-width:100%;margin-inline:auto}
.lightbox-caption{color:var(--muted-on-dark);text-align:center;font-size:.9rem;font-weight:700;padding:12px}
.lightbox-btn{
  position:absolute;top:50%;transform:translateY(-50%);
  width:44px;height:44px;border-radius:50%;border:0;cursor:pointer;
  background:rgba(246,240,230,.92);color:var(--navy);font-size:1.3rem;font-weight:800;
  display:grid;place-items:center;
}
.lightbox-btn:hover{background:#FFF}
.lightbox-prev{left:10px}
.lightbox-next{right:10px}
.lightbox-close{position:absolute;top:10px;right:10px;transform:none}

/* ---------- Products ---------- */
.product-rows{display:flex;flex-direction:column;gap:clamp(40px,7vw,72px);margin-top:clamp(28px,4vw,48px)}
.product{
  display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(22px,4vw,60px);align-items:center;
}
.product:nth-child(even) .product-media{order:2}
.product-media{
  border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-deep);
  aspect-ratio:4/3.4;background:var(--birch);
}
.product-media img{width:100%;height:100%;object-fit:cover;object-position:center 30%}
/* One card breaks the 4:3.4 crop on purpose. The round-sign photo is a
   six-up of the whole catalogue — mountains, treeline, flowers, dinosaurs,
   a crown — and cover-cropping it to a portrait card guillotines the four
   outer signs, which is the entire reason the photo is there. It gets its
   own ratio and is contained, not cropped. */
.product-media--wide{aspect-ratio:1100/744}
.product-media--wide img{object-fit:contain;object-position:center}
.product-copy{position:relative}
.product-rank{
  position:absolute;right:0;top:-.48em;z-index:0;
  font-family:var(--font-display);font-weight:700;
  font-size:clamp(4rem,9vw,6.5rem);line-height:1;
  color:transparent;-webkit-text-stroke:1.5px rgba(228,87,30,.3);
  pointer-events:none;user-select:none;
}
.product-copy h3{
  font-size:clamp(1.5rem,3.4vw,2.2rem);color:var(--navy);
  margin:0 0 6px;position:relative;z-index:1;
}
.product-price{
  margin:0 0 10px;color:var(--navy-2);font-weight:800;
  font-size:.95rem;letter-spacing:.03em;text-transform:uppercase;
}
.product-price strong{color:var(--navy);font-size:1.15rem}
.product-copy > p:not(.product-price){margin:0 0 16px;color:var(--muted);max-width:52ch}
@media (max-width:860px){
  .product{grid-template-columns:1fr;gap:18px}
  .product:nth-child(even) .product-media{order:0}
  .product-media{aspect-ratio:16/11}
}

/* ---------- Estimator (React) ---------- */
.estimator{
  background:#FFFDF4;border:1px solid rgba(59,42,29,.12);
  border-radius:20px;box-shadow:var(--shadow);
  padding:clamp(20px,4vw,40px);
  display:grid;grid-template-columns:1fr 1fr;gap:clamp(22px,4vw,44px);
}
@media (max-width:860px){.estimator{grid-template-columns:1fr}}
.est-label{
  display:block;font-size:.78rem;font-weight:800;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);margin:0 0 10px;
}
.est-products{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:22px}
.est-chip{
  border:2px solid rgba(59,42,29,.22);background:transparent;color:var(--navy);
  font-family:var(--font-body);font-weight:800;font-size:.84rem;
  padding:9px 15px;border-radius:999px;cursor:pointer;
  transition:background-color .15s,border-color .15s,color .15s;
}
.est-chip.is-active{background:var(--navy-2);border-color:var(--navy-2);color:#FFFDF4}
.est-sizes{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:22px}
.est-size{
  border:2px solid rgba(59,42,29,.18);background:#FFF;color:var(--text);
  font-family:var(--font-body);font-weight:800;font-size:.9rem;
  padding:10px 13px;border-radius:10px;cursor:pointer;min-width:64px;
  transition:border-color .15s, background-color .15s, color .15s;
}
.est-size.is-active{border-color:var(--navy);background:var(--navy);color:#FFF7EC}
.est-size small{display:block;font-weight:700;font-size:.72rem;opacity:.75}
.est-name input{
  width:100%;padding:13px 15px;border-radius:10px;
  border:2px solid rgba(59,42,29,.18);background:#FFF;
  font-family:var(--font-body);font-size:1rem;font-weight:700;color:var(--text);
}
.est-name input:focus{outline:none;border-color:var(--navy)}
.est-note{font-size:.85rem;color:var(--muted);margin:16px 0 0}

.est-preview{
  display:flex;flex-direction:column;gap:14px;justify-content:center;
}
.est-stage{display:flex;flex-direction:column;gap:14px}
/* On a phone the two columns stack, which used to bury the wood board
   under the font list — you tapped a font and nothing you could see
   changed. The board is pulled above the controls and pinned under the
   header instead, so every tap in the picker lands in view. */
@media (max-width:860px){
  .estimator{display:grid}
  .est-preview{display:contents}
  .est-stage{
    order:-1;position:sticky;top:64px;z-index:3;
    background:#FFFDF4;padding:10px 0 12px;margin-bottom:4px;
    box-shadow:0 10px 12px -10px rgba(59,42,29,.28);
  }
  .est-board{min-height:150px;padding:18px}
  .est-board.est-board--round{width:min(190px,58%)}
  .est-preview-name{font-size:clamp(1.8rem,8vw,2.6rem)}
  .est-preview-name--second{font-size:clamp(1.2rem,5vw,1.7rem)}
  .est-price-row{order:1}
  .est-breakdown{order:1}
  .est-meta{order:2}
}
.est-board{
  position:relative;border-radius:16px;min-height:230px;
  display:grid;place-items:center;text-align:center;padding:26px;
  background:
    linear-gradient(180deg, rgba(255,244,222,.16), rgba(42,29,19,.18)),
    repeating-linear-gradient(92deg, rgba(66,45,26,.10) 0 2px, transparent 2px 10px),
    linear-gradient(94deg,#B78C58,#A57C4B 30%,#C09363 60%,#AA804F);
  box-shadow:inset 0 0 0 1px rgba(59,42,29,.25), var(--shadow);
}
.est-board.est-board--round{border-radius:50%;width:min(300px,100%);aspect-ratio:1;min-height:0;margin-inline:auto}
.est-preview-name{
  font-family:var(--font-script);font-weight:700;color:var(--ink);
  font-size:clamp(2.2rem,6vw,3.4rem);line-height:1.06;
  text-shadow:0 1px 0 rgba(255,235,200,.35), 0 -1px 2px rgba(30,18,10,.5);
  overflow-wrap:anywhere;
}
/* Round signs: first name big, middle name under it, both in the font. */
.est-preview-name--second{font-size:clamp(1.4rem,3.8vw,2.2rem);margin-top:.15em;line-height:1.1}
/* Monogram: the vine face is all flourish and needs the room. */
/* The vine face draws its letters small inside a tall em box (the
   flourishes need the headroom), so it is set roughly twice the size any
   other font gets before it reads as a monogram and not a doodle. */
.est-preview-name--mono{font-size:clamp(7rem,22vw,11rem);line-height:.9;letter-spacing:-.04em}
/* Closing key: "Sold" in the font, then the name — the sign's own layout. */
.est-preview-by{
  font-family:var(--font-body);font-weight:800;font-size:1.05rem;color:var(--ink);margin-top:6px;
}
/* The closing key is drawn as the sign is cut — a house with a chimney on
   the left, the key shaft running right, teeth along the bottom of the
   tip — by clipping a white panel to that silhouette and laying it on the
   wood board, the way hers sit on the table in the photos. The two text
   blocks are pinned to the house and the shaft, since "centre of the
   panel" is inside the roof. */
.est-board.est-board--key{display:block;padding:clamp(14px,3vw,24px);min-height:0}
.est-board.est-board--key > div{display:block;width:100%}
.est-key{
  position:relative;width:100%;aspect-ratio:2.7/1;
  background:
    repeating-linear-gradient(92deg, rgba(120,100,70,.07) 0 2px, transparent 2px 11px),
    linear-gradient(94deg,#FBF9F3,#F3EFE4 60%,#FAF7EF);
  clip-path:polygon(
    22% 0%, 44% 40%, 39% 40%, 39% 47%,
    100% 47%, 100% 66%,
    96% 66%, 96% 84%, 89% 84%, 89% 66%,
    83% 66%, 83% 84%, 76% 84%, 76% 66%,
    70% 66%, 70% 80%, 64% 80%, 64% 66%,
    39% 66%, 39% 100%, 5% 100%, 5% 40%, 0% 40%,
    9% 24%, 9% 8%, 13% 8%, 13% 16%
  );
}
.est-key-house{
  position:absolute;left:5%;top:40%;width:34%;height:60%;
  display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
}
.est-board--key .est-preview-name{
  color:#2E7F8A;text-shadow:none;font-size:clamp(1.4rem,4vw,2.3rem);line-height:1;
}
.est-board--key .est-preview-by{color:#25313D;font-size:clamp(.62rem,1.5vw,.86rem);margin-top:8px;white-space:nowrap;overflow:hidden;max-width:100%;text-overflow:ellipsis}
.est-key-tag{
  position:absolute;left:41%;top:47%;width:57%;height:19%;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-body);font-weight:800;color:#25313D;
  font-size:clamp(.6rem,1.5vw,.88rem);letter-spacing:.01em;white-space:nowrap;overflow:hidden;
}
.est-mono{margin:22px 0 0}
.est-mono .est-label{margin-bottom:10px}
.est-preview-sub{
  font-family:var(--font-display);font-weight:600;letter-spacing:.3em;text-indent:.3em;
  text-transform:uppercase;color:var(--ink);font-size:.9rem;margin-top:6px;
}
.est-price-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap}
.est-price{
  font-family:var(--font-display);font-weight:700;font-size:2.4rem;color:var(--navy);line-height:1;
}
.est-price small{font-size:1rem;color:var(--muted);font-family:var(--font-body);font-weight:700}
.est-meta{font-size:.85rem;font-weight:700;color:var(--navy-2)}

/* ---------- Font try-on (estimator) ----------
   A scrolling list of the customer's own word set in every font she
   offers. Fixed max-height on purpose: it has to read as a list you
   scroll through, not push the price off the screen. Each row keeps its
   height whether or not its webfont has arrived, so the list does not
   jump around as fonts land one at a time. */
.fontpick{margin:22px 0 0}
.fontpick-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap}
.fontpick-head .est-label{margin-bottom:10px}
.fontpick-tabs{display:flex;gap:6px;margin-bottom:10px}
.fontpick-tab{
  border:2px solid rgba(59,42,29,.18);background:#FFF;color:var(--navy);
  font-family:var(--font-body);font-weight:800;font-size:.76rem;
  padding:6px 12px;border-radius:999px;cursor:pointer;
  transition:background-color .15s,border-color .15s,color .15s;
}
.fontpick-tab.is-active{background:var(--navy-2);border-color:var(--navy-2);color:#FFFDF4}
.fontpick-list{
  max-height:270px;overflow-y:auto;overscroll-behavior:contain;
  border:1px solid rgba(59,42,29,.16);border-radius:12px;background:#FFF;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:1px;
  padding:1px;
}
.fontpick-item{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  min-height:76px;padding:10px 8px;cursor:pointer;
  background:#FFF;border:2px solid transparent;border-radius:10px;
  transition:background-color .15s,border-color .15s;
}
.fontpick-item:hover{background:#FFF7E8}
.fontpick-item.is-active{border-color:var(--navy);background:#FFF7E8}
.fontpick-word{
  font-size:1.45rem;line-height:1.25;color:var(--text);
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.fontpick-name{
  font-family:var(--font-body);font-weight:800;font-size:.66rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
}
.fontpick-note{font-size:.8rem;color:var(--muted);margin:10px 0 0;line-height:1.5}
.fontpick-note strong{color:var(--navy-2)}
.est-fontname{
  font-family:var(--font-body);font-size:.78rem;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;color:var(--muted);
  margin:0;text-align:center;
}
.est-fontname strong{color:var(--navy)}
@media (max-width:420px){
  .fontpick-list{grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}
  .fontpick-word{font-size:1.2rem}
}

/* ---------- Design / key-style pickers (order form) ----------
   The photo beside the select is a button: one tap opens that one design
   full size in the lightbox, which is what she asked for — "a single photo
   that's clickable so they can see just that one image". */
.field-hint{font-weight:600;color:var(--muted);text-transform:none;letter-spacing:0}
.design-preview{
  margin:12px 0 0;display:flex;align-items:center;gap:14px;width:100%;text-align:left;
  background:#FFFDF4;border:1px solid rgba(59,42,29,.14);border-radius:12px;padding:10px;
  cursor:pointer;font:inherit;color:inherit;transition:border-color .15s,box-shadow .15s;
}
.design-preview:hover,.design-preview:focus-visible{border-color:var(--navy);box-shadow:var(--shadow);outline:none}
.design-preview img{width:96px;height:96px;border-radius:12px;object-fit:cover;flex:none;background:var(--birch)}
#designPreview img{border-radius:50%}
.design-preview-cap{
  display:block;font-family:var(--font-body);font-weight:800;font-size:.9rem;color:var(--navy);
}
.design-preview-hint{display:block;font-size:.78rem;font-weight:700;color:var(--muted);margin-top:4px}
.design-preview[hidden]{display:none}

/* Round-sign block: a fieldset so the group reads as one question set. */
.field-group{
  margin:0 0 16px;padding:16px 16px 2px;border:1px solid rgba(59,42,29,.14);border-radius:14px;
  background:rgba(255,255,255,.45);
}
.field-group legend{
  padding:0 8px;font-family:var(--font-display);font-weight:700;font-size:1.05rem;color:var(--navy);
}
.field-group[hidden]{display:none}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:0 12px}
@media (max-width:520px){.field-row{grid-template-columns:1fr}}

/* ---------- Steps ---------- */
.steps{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(4,1fr);gap:18px;
  counter-reset:step;
}
@media (max-width:960px){.steps{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.steps{grid-template-columns:1fr}}
.step{
  background:#FFFDF4;border:1px solid rgba(59,42,29,.12);border-radius:16px;
  padding:24px 20px;position:relative;
}
.step-num{
  display:inline-grid;place-items:center;width:40px;height:40px;border-radius:50%;
  background:var(--navy);color:var(--cream);
  font-family:var(--font-display);font-weight:700;font-size:1.1rem;
  margin-bottom:14px;
}
.step h3{font-size:1.12rem;color:var(--navy);margin:0 0 8px}
.step p{margin:0;font-size:.94rem;color:var(--muted)}

/* ---------- Reviews ----------
   An infinite horizontal marquee. Same hardening as the ticker: the JS
   replicates the cards until half the track exceeds the viewport and the
   loop animates to exactly -50%, so the wrap lands on a copy boundary and
   there is no visible seam. Spacing is margin on each card, NOT a flex gap
   — a gap adds nothing after the last card, which is the classic glitch at
   the wrap point. House rule: no hover pause, ever. */
.review-marquee{
  position:relative;overflow:hidden;
  margin-inline:calc(var(--pad) * -1);   /* bleed to the section edges */
  padding-block:6px;
  contain:layout style paint;
}
/* Soft fade at both edges so cards enter and leave gracefully instead of
   being guillotined by the container. */
.review-marquee::before,.review-marquee::after{
  content:"";position:absolute;top:0;bottom:0;width:clamp(24px,6vw,90px);
  z-index:2;pointer-events:none;
}
.review-marquee::before{left:0;background:linear-gradient(90deg,var(--navy),transparent)}
.review-marquee::after{right:0;background:linear-gradient(270deg,var(--navy),transparent)}

.review-marquee + .review-marquee{margin-top:2px}

/* Top-aligned, NOT stretch: a row is as tall as its tallest card, and with
   65 reviews the longest runs eleven lines. Stretching made "Amazing job
   thank you" a 400px card of empty navy on a phone; left alone, the slack
   sits outside the card where the dark ground swallows it. */
.review-track{
  display:flex;align-items:flex-start;width:max-content;
  animation:reviewSlide var(--review-dur,60s) linear infinite;
  will-change:transform;backface-visibility:hidden;transform:translate3d(0,0,0);
}
/* The second row runs the other way at the same px/s, so the two rows read
   as one wall of reviews moving against itself. */
.review-track--reverse{animation-direction:reverse}
/* A row is as tall as its tallest card, so a short card leaves slack. The
   top row hangs its cards from the bottom edge and the lower row stands
   its cards on the top edge: the slack moves to the outside and the two
   rows sit directly on top of each other. */
.review-track--bottom{align-items:flex-end}
@keyframes reviewSlide{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}

.review{
  flex:none;width:min(360px,78vw);margin:0 16px 0 0;
  display:flex;flex-direction:column;
  background:var(--navy-2);border:1px solid rgba(243,234,220,.1);
  border-radius:16px;padding:22px 22px 18px;
}
.review blockquote{flex:none}

/* Reduced motion: the same cards, standing still in a wrapped row. app.js
   skips the cloning on this path, so nothing repeats. */
@media (prefers-reduced-motion:reduce){
  .review-track{animation:none;flex-wrap:wrap;width:auto;gap:16px}
  .review{margin:0}
  /* 65 standing still is a wall you scroll past, not a section. Show the
     first eight of each row; the rest are still in the HTML for search. */
  .review-track > .review:nth-child(n+9){display:none}
  .review-marquee{margin-inline:0}
  .review-marquee::before,.review-marquee::after{display:none}
}
.review-stars{color:#F3B34C;letter-spacing:3px;margin:0 0 10px;font-size:.95rem}
.review blockquote{margin:0 0 12px;font-size:.98rem;line-height:1.6;color:var(--text-on-dark)}
.review figcaption{font-size:.82rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-on-dark)}

/* ---------- Order ---------- */
.order-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,64px);align-items:start}
@media (max-width:900px){.order-grid{grid-template-columns:1fr}}
.order-points{list-style:none;margin:0 0 18px;padding:0;display:grid;gap:10px}
.order-points li{
  position:relative;padding-left:28px;font-weight:700;color:var(--text);
}
.order-points li::before{
  content:"";position:absolute;left:0;top:.42em;width:14px;height:14px;
  background:var(--grey);border-radius:3px;transform:rotate(45deg);
}
.order-fb{color:var(--muted);font-size:.95rem}
.order-fb-strong{font-weight:800;color:var(--navy)}

.order-form{
  background:#FFFDF4;border:1px solid rgba(59,42,29,.12);border-radius:20px;
  box-shadow:var(--shadow);padding:clamp(20px,4vw,36px);
}
.field{margin-bottom:16px}
.field label{display:block;font-size:.8rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:7px}
.field input,.field select,.field textarea{
  width:100%;padding:13px 15px;border-radius:10px;
  border:2px solid rgba(59,42,29,.18);background:#FFF;
  font-family:var(--font-body);font-size:1rem;color:var(--text);
}
.field textarea{resize:vertical;min-height:120px;line-height:1.55}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--navy)}
.field-error{display:block;min-height:0;color:#B3401B;font-size:.82rem;font-weight:700;margin-top:6px}
.hp-field{position:absolute;left:-9999px;top:-9999px}
/* "Best way to reach you" — two real radios wearing the estimator's pills.
   A fieldset needs its browser border, padding and min-width taken off before
   it will sit in the column like the other fields, and the legend has to be
   dressed as one of the uppercase labels by hand because `.field label` does
   not reach it.

   The inputs are moved off-screen rather than `display:none`, which would take
   them out of the tab order and off the a11y tree — the label pills are the
   visible control, but the radio is still the thing being focused, arrowed
   between and announced. */
.pref-field{border:0;padding:0;margin:0 0 16px;min-width:0}
.pref-field legend{
  padding:0;font-size:.8rem;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);margin-bottom:7px;
}
.pref-tabs{display:flex;gap:6px;flex-wrap:wrap}
.field .pref-tabs input{position:absolute;width:1px;height:1px;opacity:0;margin:0;padding:0;border:0}
.field .pref-tabs label{
  display:inline-block;margin:0;
  border:2px solid rgba(59,42,29,.18);background:#FFF;color:var(--navy);
  font-family:var(--font-body);font-weight:800;font-size:.82rem;
  letter-spacing:0;text-transform:none;
  padding:8px 16px;border-radius:999px;cursor:pointer;
  transition:background-color .15s,border-color .15s,color .15s;
}
.field .pref-tabs input:checked + label{background:var(--navy-2);border-color:var(--navy-2);color:#FFFDF4}
.field .pref-tabs input:focus-visible + label{outline:2px solid var(--navy);outline-offset:2px}
@media (prefers-reduced-motion:reduce){.field .pref-tabs label{transition:none}}

/* Turnstile. The widget draws itself at a fixed 300x65 in its own iframe, so
   the box only has to give it room and centre it on a phone. It stays empty
   and takes no space when no site key is on the form. */
.ts-box{margin:18px 0 0}
.ts-box:empty{margin:0}
.ts-box iframe{max-width:100%}
@media (max-width:520px){.ts-box{display:flex;justify-content:center}}
.form-status{margin:14px 0 0;font-weight:800;min-height:1.4em}
.form-status.ok{color:var(--navy-2)}
.form-status.err{color:#B3401B}

/* ---------- Footer ---------- */
.site-footer{background:var(--navy-deep);color:var(--muted-on-dark);padding:clamp(40px,6vw,64px) 0 34px}
.footer-inner{display:grid;grid-template-columns:1.2fr 1fr 1fr;gap:32px;align-items:start}
@media (max-width:820px){.footer-inner{grid-template-columns:1fr;gap:26px}}
/* The footer mark goes home too — house rule: every wordmark on the page
   returns you to the top of the hero. */
.footer-home{display:inline-block;line-height:0}
.footer-mark{width:190px;height:auto;margin-bottom:14px;filter:brightness(0) invert(1) opacity(.92);transition:opacity .2s ease}
.footer-home:hover .footer-mark{opacity:.75}

.footer-brand p{margin:12px 0 0;font-size:.92rem;line-height:1.6}
.footer-links{display:grid;gap:10px}
.footer-links a{color:var(--muted-on-dark);text-decoration:none;font-weight:700;font-size:.95rem}
.footer-links a:hover{color:var(--text-on-dark)}
.footer-meta p{margin:0 0 8px;font-size:.85rem}
/* The credit reads as one quiet line, so the link is underlined rather than
   recoloured — otherwise it either shouts or is invisible. `rel="noopener"`
   without `noreferrer`: the referrer is the point, it is what makes the click
   show up as referral traffic on bensited.com. */
.footer-credit{opacity:.7}
.footer-credit a{color:inherit;text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;text-decoration-color:rgba(255,255,255,.35);
  transition:color .18s ease,text-decoration-color .18s ease}
.footer-credit a:hover,.footer-credit a:focus-visible{
  color:var(--text-on-dark);text-decoration-color:currentColor}

/* ---------- Tilt cards (pointer interaction, fine pointers only) ---------- */
@media (hover:hover) and (pointer:fine){
  .tilt-card{transition:transform .3s var(--ease), box-shadow .3s var(--ease);will-change:transform}
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .ticker-track{animation:none}
  .hero-under{opacity:1;transform:none;transition:none}
  html:not(.no-js) [data-reveal]{opacity:1;transform:none}
  .gallery-item img,.gallery-item:hover img{transition:none;transform:none}
}

@media print{
  .site-header,.ticker,.hero-gl,.order-form,.site-footer{display:none}
}

/* ---------- Fonts & colors ---------- */
.options-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(16px,3vw,28px);align-items:start}
@media (max-width:760px){.options-grid{grid-template-columns:1fr}}
.option-card{
  margin:0;background:#FFFDF4;border:1px solid rgba(46,50,56,.12);
  border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);
}
.option-card img{width:100%;height:auto;display:block;background:#FFF}
.option-card figcaption{
  padding:14px 18px;font-size:.82rem;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border-top:1px solid rgba(46,50,56,.1);
}
/* The colour chart is square and busy — it earns the full width. */
.option-card--wide{grid-column:1 / -1}
@media (min-width:761px){
  .option-card--wide{display:grid;grid-template-columns:minmax(0,1fr) 280px;align-items:center}
  .option-card--wide figcaption{border-top:0;border-left:1px solid rgba(46,50,56,.1);font-size:.98rem;text-transform:none;letter-spacing:.01em;line-height:1.6;font-weight:600}
}

/* ---------- Turnaround ---------- */
.turnaround{
  margin-top:clamp(32px,5vw,52px);background:#FFFDF4;
  border:1px solid rgba(46,50,56,.12);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:clamp(22px,4vw,36px);
}
.turnaround-title{font-size:1.35rem;color:var(--navy);margin:0 0 18px}
.turnaround-list{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
@media (max-width:760px){.turnaround-list{grid-template-columns:1fr}}
.turnaround-list li{border-left:3px solid var(--grey);padding:4px 0 4px 16px}
.turnaround-list strong{display:block;font-family:var(--font-display);font-weight:700;font-size:1.5rem;color:var(--navy);line-height:1.1}
.turnaround-list span{font-size:.92rem;color:var(--muted)}
.turnaround-note{margin:20px 0 0;font-size:.95rem;color:var(--muted)}
.turnaround-note strong{color:var(--navy)}

/* ---------- Estimator: one line per name (Sep 2 2026) ----------
   Name signs and rounds take a first and a middle name, each with its own
   font; name signs also size and paint each name on its own, since every
   name is its own piece. The active line is the one the font chips dress. */
.est-lines{display:grid;gap:14px;margin:0 0 6px}
.est-line{
  border:2px solid rgba(59,42,29,.12);border-radius:14px;padding:14px 14px 12px;
  background:rgba(255,255,255,.55);transition:border-color .15s;
}
.est-line.is-active{border-color:var(--navy)}
.est-line .est-name-label{margin-bottom:8px}
.est-opt{font-weight:700;letter-spacing:0;text-transform:none;color:var(--muted)}
.est-sublabel{
  display:block;margin:12px 0 8px;font-family:var(--font-body);font-weight:800;
  font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);
}
.est-sizes--line{margin-bottom:12px;gap:6px}
.est-sizes--line .est-size{padding:7px 9px;min-width:52px;font-size:.82rem}
.est-sizes--line .est-size small{font-size:.66rem}
.est-check{
  display:flex;align-items:center;gap:10px;cursor:pointer;
  font-family:var(--font-body);font-weight:800;font-size:.9rem;color:var(--navy);
}
.est-check input{width:20px;height:20px;accent-color:var(--navy);cursor:pointer;margin:0;flex:none}
.est-check small{display:block;font-weight:700;font-size:.76rem;color:var(--muted)}
.est-backing{margin:22px 0 0}
.est-backing .est-label{margin-bottom:10px}
.fontpick-tabs--lines{flex-basis:100%;margin-bottom:8px}
.fontpick-tabs--lines .fontpick-tab{border-color:var(--navy);}
/* Per-name price lines under the total, name signs only. */
.est-breakdown{
  list-style:none;margin:-4px 0 0;padding:0;display:grid;gap:4px;
  font-family:var(--font-body);font-size:.85rem;font-weight:700;color:var(--muted);
}
.est-breakdown li{display:flex;justify-content:space-between;gap:12px;border-top:1px dashed rgba(59,42,29,.16);padding-top:4px}
.est-breakdown li span:last-child{color:var(--navy);white-space:nowrap}
/* Painted names read as her white paint on the wood; natural stays the
   dark cut-edge look. */
.est-preview-name.is-painted{color:#FFFDF4;text-shadow:0 1px 0 rgba(255,235,200,.2), 0 2px 4px rgba(30,18,10,.55)}
/* Stained, but which stain not yet said — a mid walnut, enough to read as
   "not paint, not bare wood" without claiming a choice. */
.est-preview-name.is-stained{color:#8A5A41;text-shadow:0 1px 0 rgba(255,235,200,.25), 0 2px 4px rgba(30,18,10,.4)}
/* A color off the chart carries itself; the heavy cut-edge shadow that
   makes bare birch read as carved would only muddy it. */
.est-preview-name.is-colored{text-shadow:0 1px 1px rgba(255,250,240,.3), 0 2px 5px rgba(30,18,10,.4)}

/* ---------- Estimator: her color chart, as swatches (3 Sep 2026) ----------
   "They can select that they want paint, but then they can't select a
   color" — her words, and she was right. Her own discs, small enough that
   all thirty fit without pushing the price off the screen, grouped the way
   her two chart photos are. The chart photos stay the reference; these are
   how you choose. */
.est-colors{margin:14px 0 0}
.est-colors .est-sublabel{margin-top:0}
.est-swatch-row{display:flex;align-items:flex-start;gap:10px;margin-bottom:8px}
.est-swatch-group{
  flex:none;width:58px;padding-top:7px;
  font-family:var(--font-body);font-weight:800;font-size:.62rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);
}
.est-swatches{display:flex;flex-wrap:wrap;gap:6px}
.est-swatch{
  width:26px;height:26px;flex:none;padding:0;border-radius:50%;cursor:pointer;
  background:var(--sw);border:2px solid rgba(59,42,29,.22);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.3);
  transition:transform .12s, border-color .12s, box-shadow .12s;
}
.est-swatch:hover{transform:scale(1.14)}
.est-swatch:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.est-swatch.is-active{border-color:var(--navy);box-shadow:0 0 0 2px var(--navy);transform:scale(1.14)}
/* Her two metallics are a sheen, not a flat color. */
.est-swatch.is-sheen{background-image:linear-gradient(135deg,rgba(255,255,255,.7),transparent 42%,rgba(0,0,0,.22))}
.est-swatch-name{margin:4px 0 0;font-family:var(--font-body);font-size:.82rem;font-weight:700;color:var(--muted)}
.est-swatch-name strong{color:var(--navy)}
@media (max-width:520px){
  .est-swatch{width:24px;height:24px}
  .est-swatch-row{gap:8px}
  .est-swatch-group{width:52px;font-size:.58rem;letter-spacing:.04em}
}

/* The chosen finish lands on the board: paint covers the birch, a stain
   sits in it, so the wood gradient stays underneath at .8. The slat
   grooves are cut into the board and show through either way, which is
   why they sit above the tint. */
.est-board::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:0;
  background:var(--tint,transparent);opacity:var(--tint-a,0);
}
.est-board--slat::before{z-index:1}
.est-key::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background:var(--tint,transparent);opacity:var(--tint-a,0);
}
.est-key-house,.est-key-tag{z-index:1}
/* Slat-wood backing: the round is built from boards, so the grooves show. */
.est-board > div{position:relative;z-index:1}
.est-board--slat::before{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:repeating-linear-gradient(180deg,
    transparent 0 46px, rgba(46,30,16,.42) 46px 48px, rgba(255,240,215,.22) 48px 49px, transparent 49px 52px);
}
/* Hidden rows inside the names block must actually hide — the grid on
   .field-row would otherwise beat the [hidden] default. */
.field-row[hidden],.field[hidden]{display:none}
