:root{
  /* brand — First Light */
  --gold:#E9A94F; --ember:#C77E3F; --accent:#C77E3F; --mist:#C9B6C4; --fog:#8FA1B3;
  --charcoal:#2F3543; --charcoal-2:#3a4254; --charcoal-3:#454e60; --charcoal-4:#555e72;
  --cream:#F6F0E4; --oat:#F3E8D3; --tan:#EAD9BC; --tan-2:#E2CBA4;
  --sage:#9FAD97; --sage-dark:#7E8F7A; --paper:#FBF8F1; --slgray:#7d8390;
  --white:#ffffff; --ember-dark:#a5622c; --ember-darker:#8a5124; --ember-text:#976030; --gold-light:#f0b95e; --pine:#26301f;
  /* sun + sky */
  --sun-hi:#F5C471; --sun-mid:#FCE3AC; --sun-lo:#DD8B3F; --sun-deep:#E39A4C;
  --sky-1:#A8B8CB; --sky-2:#C4C3CE; --sky-3:#E4CDB4; --sky-4:#F2DDB4; --sky-5:#F6E9CC;
  /* text + lines */
  --ink-soft:#4a5061; --ink-mid:#5d6472; --ink-dim:#5a6070; --text-dim:#5f6673;
  --line:#eae4d6; --line-warm:#e7dcc4;
  --gray-soft:#9aa0ab; --gray-faint:#b7bdc7; --gray-light:#b9bfc9; --gray-lighter:#d7dce4; --gray-blue:#aeb5c2; --fog-ink:#8a90a0;
  /* type */
  --font-display:'Playfair Display',serif;
  --font-script:'Great Vibes',cursive;
  --font-body:'Montserrat',sans-serif;
  --font-mono:ui-monospace,monospace;
  --st-lime:#BFFF00;}

/* self-hosted fonts */
@font-face{font-family:'Great Vibes';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/great-vibes-400.woff2') format('woff2');}
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:600;font-display:swap;src:url('../fonts/playfair-display-600.woff2') format('woff2');}
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:700;font-display:swap;src:url('../fonts/playfair-display-700.woff2') format('woff2');}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/montserrat-400.woff2') format('woff2');}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:500;font-display:swap;src:url('../fonts/montserrat-500.woff2') format('woff2');}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:600;font-display:swap;src:url('../fonts/montserrat-600.woff2') format('woff2');}
@font-face{font-family:'Montserrat';font-style:normal;font-weight:700;font-display:swap;src:url('../fonts/montserrat-700.woff2') format('woff2');}

*{box-sizing:border-box; margin:0; padding:0;}
html{scroll-behavior:smooth;}
body{font-family:var(--font-body); color:var(--charcoal); background:var(--paper);}
h1,h2,h3{font-family:var(--font-display);}
img{max-width:100%; height:auto;}
.script{font-family:var(--font-script); font-weight:400;}
.wrap{max-width:73.75rem; margin:0 auto; padding:0 24px;}
.eyebrow{letter-spacing:5px; font-size:0.75rem; font-weight:600; color:var(--ember-text); text-transform:uppercase;}
.btn{display:inline-block; padding:14px 30px; border-radius:6px; font-weight:600; font-size:0.875rem; letter-spacing:1.5px; text-transform:uppercase; text-decoration:none; transition:all .25s;}
.btn-solid{background:var(--ember-dark); color:var(--white);}
.btn-solid:hover{background:var(--ember-darker); transform:translateY(-2px);}
.btn-ghost{border:2px solid var(--charcoal); color:var(--charcoal);}
.btn-ghost:hover{background:var(--charcoal); color:var(--cream);}
.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{position:absolute; left:-9999px; top:0; z-index:100; background:var(--charcoal); color:var(--cream); padding:10px 18px; border-radius:0 0 8px 0;}
.skip:focus{left:0;}

/* missing-data markers (Module 5) */
.nd-frame, .nd-inline{
  border:2px dashed var(--accent);
  background:repeating-linear-gradient(45deg,transparent,transparent 12px,rgba(255,255,255,.03) 12px,rgba(255,255,255,.03) 24px);
  color:var(--accent); font:600 .8rem/1.4 var(--font-mono);
  letter-spacing:.05em; text-transform:uppercase;
}
.nd-inline{display:inline-flex; align-items:center; padding:.15rem .5rem;}
.nd-frame{display:flex; align-items:center; justify-content:center; width:100%; padding:1rem; text-align:center;}

/* ============ NAV ============ */
nav{position:fixed; top:0; left:0; right:0; z-index:50; padding:16px 0; transition:all .3s;}
nav.solid{background:rgba(251,248,241,.96); box-shadow:0 2px 14px rgba(47,53,67,.08); padding:10px 0;}
.navrow{display:flex; align-items:center; justify-content:space-between;}
.navlogo{display:flex; align-items:center; gap:10px; text-decoration:none; color:var(--charcoal);}
.navlogo svg{width:44px; height:auto;}
.navlogo .nm{font-family:var(--font-script); font-size:1.625rem; line-height:1;}
.navlinks{display:flex; gap:28px; align-items:center;}
.navlinks a{text-decoration:none; color:var(--charcoal); font-size:0.8125rem; font-weight:600; letter-spacing:1.5px; text-transform:uppercase;}
.navlinks a:hover{color:var(--ember);}
.navlinks a.cta{background:var(--ember-dark); color:var(--white); padding:10px 18px; border-radius:6px;}
.navlinks a.cta:hover{color:var(--white); background:var(--ember-darker);}
/* (mobile nav rules moved to the MOBILE NAV + STICKY CTA block at the end of this
   file: the old rule display:none-d every non-CTA link, which is what left a phone
   with no way to reach About, Events or Custom Orders.) */

/* ============ HERO ============ */
.hero{position:relative; height:100vh; min-height:640px; overflow:hidden;
  background:linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 26%, var(--sky-3) 55%, var(--sky-4) 74%, var(--sky-5) 100%);}
/* The sun rises behind the back ridge: sized in vw so it tracks the hills
   (18.06vw tall, ridge at ~7.6vw at center) at every viewport width. Its
   center sits at the ridge line, so only a clean dome crests the hills. */
.sun{position:absolute; left:50%; bottom:2vw; width:12vw; height:12vw; margin-left:-6vw; z-index:2; border-radius:50%;
  background:radial-gradient(circle at 50% 30%, var(--sun-mid) 0%, var(--sun-hi) 45%, var(--sun-deep) 100%);
  box-shadow:0 0 80px 32px rgba(245,196,113,.5), 0 0 200px 90px rgba(245,196,113,.22);
  /* Reveal timing is a MOBILE PERFORMANCE budget, not just taste. The logo and
     text start at opacity:0, and Lighthouse cannot count the page's largest
     paint until their fade begins — reveals that started >1s after render put
     mobile LCP at 8.1s (PSI 2026-08-20) while desktop sat at 1.0s. So the
     content fades start almost immediately (.2s/.6s/.9s) and the sun keeps
     rising behind them; only the backdrop takes its time. */
  animation:sunrise 2.2s cubic-bezier(.33,.75,.4,1) forwards; opacity:.96;}
@keyframes sunrise{from{transform:translateY(24vh) scale(.92);} to{transform:translateY(0) scale(1);}}
.hills{position:absolute; left:0; right:0; bottom:-2px; z-index:3;}
.hills svg{display:block; width:100%; height:auto;}
@media(max-width:640px){
  .hills svg{height:120px;}
  .sun{width:112px; height:112px; margin-left:-56px; bottom:-6px;}
}
.heromist{position:absolute; inset:0; z-index:4; pointer-events:none;}
.fogbank{position:absolute; width:160%; left:-30%; height:46%; filter:blur(28px); opacity:.9;}
.fb1{top:34%; background:radial-gradient(ellipse 46% 60% at 30% 50%, rgba(250,247,240,.95), transparent 70%),
               radial-gradient(ellipse 40% 55% at 66% 45%, rgba(226,222,229,.9), transparent 70%),
               radial-gradient(ellipse 34% 50% at 88% 55%, rgba(250,247,240,.85), transparent 70%);
      animation:drift1 4.5s ease-in-out forwards;}
.fb2{top:18%; background:radial-gradient(ellipse 42% 60% at 55% 50%, rgba(243,238,243,.92), transparent 70%),
               radial-gradient(ellipse 36% 50% at 15% 55%, rgba(250,247,240,.88), transparent 70%);
      animation:drift2 5.5s ease-in-out forwards;}
.fb3{top:50%; background:radial-gradient(ellipse 50% 62% at 42% 50%, rgba(250,247,240,.98), transparent 72%),
               radial-gradient(ellipse 40% 55% at 78% 50%, rgba(236,231,236,.92), transparent 70%);
      animation:drift3 4s ease-in-out forwards;}
.fb4{top:8%; height:60%; opacity:.35;
      background:radial-gradient(ellipse 60% 70% at 50% 50%, rgba(250,247,240,.7), transparent 75%);
      animation:linger 14s ease-in-out infinite alternate;}
@keyframes drift1{0%{transform:translateX(0); opacity:.95;} 55%{opacity:.75;} 100%{transform:translateX(9%); opacity:0;}}
@keyframes drift2{0%{transform:translateX(0); opacity:.9;} 55%{opacity:.7;} 100%{transform:translateX(-11%); opacity:0;}}
@keyframes drift3{0%{transform:translateX(0); opacity:1;} 50%{opacity:.8;} 100%{transform:translateX(7%); opacity:0;}}
@keyframes linger{from{transform:translateX(-3%);} to{transform:translateX(3%);}}
.heroinner{position:relative; z-index:6; height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:0 20px;}
.herologo{width:min(520px, 86vw); opacity:0; animation:reveal .9s ease .2s forwards;}
.herologo svg{width:100%; height:auto; filter:drop-shadow(0 2px 18px rgba(251,248,241,.65));}
@keyframes reveal{from{opacity:0; transform:translateY(14px) scale(.985);} to{opacity:1; transform:none;}}
.herosub{margin-top:18px; font-size:1rem; letter-spacing:.4px; color:var(--ink-soft); max-width:32.5rem; opacity:0; animation:reveal .9s ease .6s forwards;}
.heroctas{margin-top:30px; display:flex; gap:16px; flex-wrap:wrap; justify-content:center; opacity:0; animation:reveal .9s ease .9s forwards;}
/* On phones the sticky shop bar covers the bottom of the viewport, and centering
   in the full 100vh parked the second CTA underneath it (and left a lot of empty
   sky up top). The extra bottom padding biases the centered block upward so both
   buttons clear the bar. 720px matches where the mobile bar exists at all. */
@media(max-width:720px){
  .heroinner{padding-bottom:22vh;}
}
@media (prefers-reduced-motion: reduce){
  .sun,.fb1,.fb2,.fb3,.fb4,.herologo,.herosub,.heroctas{animation:none !important; opacity:1; transform:none;}
  .fb1,.fb2,.fb3{opacity:0;}
}

/* ============ SECTIONS ============ */
section{padding:88px 0;}
.sechead{text-align:center; max-width:40rem; margin:0 auto 52px;}
.sechead h2{font-size:2.25rem; margin:10px 0 12px;}
.sechead p{color:var(--text-dim); line-height:1.6;}

/* categories */
.cats{display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:26px;}
.cat{background:var(--white); border:1px solid var(--line); border-radius:16px; overflow:hidden; text-decoration:none; color:var(--charcoal); transition:all .25s; box-shadow:0 2px 12px rgba(47,53,67,.05);}
.cat:hover{transform:translateY(-6px); box-shadow:0 14px 30px rgba(47,53,67,.12);}
.cat .ph{height:210px; display:flex; align-items:center; justify-content:center; position:relative;}
.ph-a{background:linear-gradient(150deg,var(--oat),var(--tan));}
.ph-b{background:linear-gradient(150deg,var(--charcoal),var(--charcoal-2));}
.ph-c{background:linear-gradient(150deg,var(--cream),var(--oat));}
.ph-d{background:linear-gradient(150deg,var(--tan),var(--tan-2));}
.cat .ph svg{width:130px; height:130px;}
/* A category showing a real piece instead of stand-in art. The photo fills the
   whole 210px band, so the gradient behind it never shows. */
.ph-photo{background:var(--oat); padding:0;}
.cat .ph img{width:100%; height:100%; object-fit:cover; display:block;}
.cat .ph .tag{position:absolute; bottom:10px; right:12px; font-size:0.5938rem; letter-spacing:2px; text-transform:uppercase; color:rgba(47,53,67,.45);}
.ph-b .tag{color:rgba(246,240,228,.4);}
.cat .info{padding:18px 20px 22px; text-align:center;}
.cat .info h3{font-size:1.25rem; margin-bottom:6px;}
.cat .info p{font-size:0.8125rem; color:var(--text-dim); line-height:1.5;}
.cat .info .go{display:inline-block; margin-top:12px; font-size:0.75rem; font-weight:600; letter-spacing:2px; text-transform:uppercase; color:var(--ember-text);}

/* fall banner */
.fallband{background:linear-gradient(120deg, var(--oat), var(--tan)); border-top:1px solid var(--line-warm); border-bottom:1px solid var(--line-warm);}
.fallrow{display:flex; align-items:center; gap:48px; flex-wrap:wrap;}
.fallrow .txt{flex:1 1 380px;}
.fallrow .txt h2{font-size:2.125rem; margin:10px 0 14px;}
.fallrow .txt p{color:var(--ink-mid); line-height:1.7; margin-bottom:24px;}
.fallart{flex:0 1 380px; display:flex; justify-content:center;}
.fallart img{width:100%; max-width:23.75rem; height:auto; border-radius:16px;
  box-shadow:0 14px 34px rgba(47,53,67,.16);}
.fallart svg{width:320px; height:auto;}

/* custom */
.custom{background:var(--charcoal); color:var(--cream);}
.custom .sechead h2{color:var(--cream);}
.custom .sechead p{color:var(--gray-light);}
.customrow{display:grid; grid-template-columns:1fr 1fr; gap:54px; align-items:start;}
@media(max-width:860px){.customrow{grid-template-columns:1fr;}}
.custom .list li{list-style:none; padding:14px 0 14px 40px; position:relative; color:var(--gray-lighter); line-height:1.55; font-size:0.9375rem; border-bottom:1px dashed var(--charcoal-3);}
.custom .list li::before{content:""; position:absolute; left:0; top:18px; width:22px; height:22px; border-radius:50%;
  background:radial-gradient(circle at 50% 35%, var(--sun-hi), var(--sun-lo));}
.custom .list li b{color:var(--cream);}
.cform{background:var(--charcoal-2); border-radius:16px; padding:30px;}
.cform label{display:block; font-size:0.6875rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray-blue); margin:16px 0 6px;}
.cform input,.cform textarea,.cform select{width:100%; padding:12px 14px; border-radius:8px; border:1px solid var(--charcoal-4); background:var(--charcoal); color:var(--cream); font-family:var(--font-body); font-size:0.875rem;}
.cform textarea{min-height:96px; resize:vertical;}
.cform input[type=file]{padding:10px 12px; font-size:0.8125rem; cursor:pointer;}
.cform input[type=file]::file-selector-button{margin-right:12px; padding:7px 14px; border:none; border-radius:6px;
  background:var(--charcoal-4); color:var(--cream); font-family:var(--font-body); font-size:0.75rem;
  font-weight:600; letter-spacing:.5px; cursor:pointer;}
.cform input[type=file]::file-selector-button:hover{background:var(--gold); color:var(--charcoal);}
.fieldhint{margin-top:7px; font-size:0.75rem; line-height:1.5; color:var(--gray-lighter);}
.cform .btn{margin-top:22px; width:100%; text-align:center; background:var(--gold); color:var(--charcoal); border:none; cursor:pointer;}
.cform .btn:hover{background:var(--gold-light);}

/* about */
.aboutrow{display:flex; gap:56px; align-items:center; flex-wrap:wrap;}
.aboutrow .pic{flex:0 0 300px; margin:0 auto;}
.aboutrow .pic .circle{width:300px; height:300px; border-radius:50%; background:linear-gradient(160deg, var(--oat), var(--tan)); border:6px solid var(--white); box-shadow:0 10px 34px rgba(47,53,67,.14); display:flex; align-items:center; justify-content:center; flex-direction:column; gap:8px; padding:30px;}
.aboutrow .txt{flex:1 1 420px;}
.aboutrow .txt h2{font-size:2.125rem; margin:8px 0 16px;}
.aboutrow .txt p{color:var(--ink-mid); line-height:1.75; margin-bottom:14px;}
.sig{font-family:var(--font-script); font-size:2.125rem; color:var(--ember); margin-top:6px;}

/* events */
.events{background:var(--cream);}
.evlist{max-width:47.5rem; margin:0 auto;}

/* reviews — warm cards in the site's own voice: Playfair for the customer's
   words, an ember attribution, a big soft quote mark. These carried no styling
   at all and rendered as bare browser <figure> defaults. */
.revs{max-width:47.5rem; margin:0 auto; display:grid; gap:22px;}
.rev{position:relative; background:var(--cream); border:1px solid var(--line-warm);
  border-radius:16px; padding:34px 36px 28px; margin:0;}
.rev::before{content:"\201C"; position:absolute; top:2px; left:22px;
  font-family:var(--font-display); font-size:4rem; line-height:1; color:var(--tan-2);}
.rev blockquote{margin:0; font-family:var(--font-display); font-size:1.25rem;
  line-height:1.6; color:var(--charcoal);}
.rev figcaption{margin-top:14px; font-size:0.75rem; font-weight:600; letter-spacing:2px;
  text-transform:uppercase; color:var(--ember-text);}
.rev figcaption::before{content:"\2014\00a0";}
@media(max-width:520px){.rev{padding:28px 22px 22px;} .rev blockquote{font-size:1.0625rem;}}

/* pickup strip */
.strip{background:var(--sage); color:var(--pine); padding:26px 24px; text-align:center; font-size:0.9375rem; line-height:1.7;}
.strip b{font-weight:700;}

/* footer */
footer{background:var(--charcoal); color:var(--gray-light); padding:60px 0 30px;}
.footgrid{display:grid; grid-template-columns:2fr 1fr 1fr; gap:40px; margin-bottom:40px;}
@media(max-width:760px){.footgrid{grid-template-columns:1fr;}}
footer .brand svg{width:60px; margin-bottom:12px;}
footer .brand .nm{font-family:var(--font-script); font-size:2rem; color:var(--cream);}
footer .brand p{font-size:0.8125rem; line-height:1.6; margin-top:10px; max-width:20rem;}
footer h3{color:var(--cream); font-size:0.8125rem; letter-spacing:2px; text-transform:uppercase; margin-bottom:14px; font-family:var(--font-body);}
footer a{display:block; color:var(--gray-light); text-decoration:none; font-size:0.875rem; margin-bottom:10px;}
footer a:hover{color:var(--gold);}
.footbar{border-top:1px solid var(--charcoal-3); padding-top:22px; display:flex; justify-content:space-between; flex-wrap:wrap; gap:10px; font-size:0.75rem; color:var(--gray-light);}

/* inner pages (privacy, 404) */
.pagehead{background:linear-gradient(160deg,var(--oat),var(--tan)); padding:140px 0 60px; text-align:center;}
.pagehead h1{font-size:2.5rem;}
.pagebody{max-width:47.5rem; margin:0 auto; padding:60px 24px 90px;}
.pagebody h2{font-size:1.5rem; margin:34px 0 12px;}
.pagebody p, .pagebody li{color:var(--ink-mid); line-height:1.75; margin-bottom:12px;}
.pagebody ul{margin-left:20px;}
.center{text-align:center;}

/* contact line on the slim inner-page footers */
.footcontact{font-size:0.875rem; line-height:1.7; margin-bottom:26px; color:var(--gray-light);}
.footcontact a{display:inline; color:var(--gold); text-decoration:none;}
.footcontact a:hover{text-decoration:underline;}
/* Turnstile widget: sits above the submit button on both forms.
   The flexible widget will not render under 300px. Give it less and it does not
   shrink, it inflates the track it sits in and bursts every field out of the
   card (SOP v7.9, "Turnstile on phones: the 300px floor"). Verified in
   production at a real phone width, because the widget is domain-bound and
   renders nothing on localhost. Arithmetic at the 360px floor, both cards:

     360  viewport (the verified floor, not 320)
    - 48  .wrap / .shopwrap padding  (24px each side)
    - 32  card padding               (1rem each side, normalized below)
    + 32  widget bleed               (-1rem each side)
    =312  clears the 300 floor with headroom

   min-width:0 on the card is the part that gets dropped. Without it the grid
   track keeps its auto minimum and the burst happens even when the widget
   itself would have fit. Measured before the fix: .cform rendered 360px wide
   inside a 312px track, 24px past .wrap's right edge. */
/* .ts-mount is the shop cart's widget, rendered explicitly by shop.js. It is
   deliberately not called .cf-turnstile: that class is what Turnstile's implicit
   scanner looks for, and this one must only ever be created in code. */
.cf-turnstile,.ts-mount{margin-top:18px; min-height:65px; min-width:0;}

/* How-you'll-pay picker (shared shop client). Renders only when the shop has
   payment methods configured in Products; styled to sit inside the dark cartbox
   like the fulfillment radios above it. */
.payrow{display:flex; flex-direction:column; gap:8px; margin-top:4px;}
.paychoice{display:flex; align-items:flex-start; gap:10px; border:1.5px solid var(--charcoal-4);
  border-radius:10px; padding:10px 12px; cursor:pointer; background:var(--charcoal-2);}
.paychoice input{accent-color:var(--gold); margin-top:3px;}
.paychoice.sel{border-color:var(--gold);}
.paychoice .pc-txt{display:flex; flex-direction:column; gap:2px; font-size:0.8438rem;}
.paychoice .pc-txt b{color:var(--cream);}
.paychoice .pc-detail{color:var(--gray-light); font-size:0.75rem; line-height:1.4;}
.paynote{color:var(--gray-light); font-size:0.75rem; line-height:1.5; margin-top:8px;}
/* The phone block for this lives at the very END of this file, on purpose.
   See the note there before moving it. */
/* honeypot: hidden from humans */
.hp{position:absolute; left:-5000px; opacity:0; height:0; overflow:hidden;}
/* form status note */
.form-note{margin-top:14px; font-size:0.875rem; line-height:1.5; min-height:1em;}
.form-note.success{color:var(--gold);}
.form-note.error{color:var(--mist);}
.eyebrow-gold{color:var(--gold);}
.navlogo .nm{display:flex; flex-direction:column; line-height:1;}
.navlogo .nm small{font-family:var(--font-body); font-size:0.5312rem; letter-spacing:3.5px; text-transform:uppercase; color:var(--ember-text); font-weight:600; margin-top:3px;}

/* scroll_theory footer credit (canonical) */
.st-credit{color:var(--gray-light); text-decoration:none; font-size:0.75rem;}
.st-credit:hover .st-wm{color:var(--cream);}
.st-wm{font-family:var(--font-mono); letter-spacing:0;}
.st-wm u{color:var(--st-lime); text-decoration:none;}

/* ============ SHOP / ORDER ============ */
.shopwrap{max-width:73.75rem; margin:0 auto; padding:40px 24px 90px; display:grid; grid-template-columns:1fr 360px; gap:34px; align-items:start;}
@media(max-width:900px){.shopwrap{grid-template-columns:1fr;}}
.stocking{max-width:40rem; margin:30px auto 0; text-align:center; background:var(--white); border:1px solid var(--line); border-radius:16px; padding:40px 34px;}
.stocking h2{font-size:1.625rem; margin-bottom:12px;}
.stocking p{color:var(--text-dim); line-height:1.7; margin-bottom:18px;}
.prodcat{grid-column:1; margin-bottom:8px;}
.prodcat h2{font-size:1.5rem; margin:26px 0 14px;}
.prodgrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:18px;}
.prod{background:var(--white); border:1px solid var(--line); border-radius:14px; padding:18px; cursor:pointer; text-align:left; font-family:var(--font-body); transition:all .2s;}
/* Product shots are square-letterboxed at build time, so one ratio holds for
   every card and the grid stays even no matter the slate's orientation. */
.prodshot{display:block; width:100%; height:auto; aspect-ratio:1/1; object-fit:contain;
  border-radius:10px; background:var(--white); margin-bottom:12px;}
/* The panel photo is a button now, so it can open the enlarged view. The float
   moves from the image to the button, or the text stops wrapping around it. */
.cfgshotbtn{display:flex; justify-content:center; float:right; width:170px; max-width:40%;
  margin:0 0 12px 16px; padding:0; border:0; background:none; cursor:zoom-in;}
.cfgshot{display:block; width:100%; height:auto; aspect-ratio:1/1; object-fit:contain;
  border-radius:10px;}
@media(max-width:560px){.cfgshotbtn{float:none; width:100%; max-width:100%; margin:0 0 14px; justify-content:center;}}
.prod:hover{transform:translateY(-3px); box-shadow:0 10px 24px rgba(47,53,67,.1);}
.prod h3{font-size:1rem; margin-bottom:6px; font-family:var(--font-display);}
.prod p{font-size:0.7812rem; color:var(--text-dim); line-height:1.5; margin-bottom:10px;}
.prod .pp{font-weight:700; color:var(--ember-text); font-size:0.875rem;}
.prod.active{outline:2px solid var(--ember);}

/* A card is a div holding two buttons -- pick the piece, or enlarge the photo.
   It cannot be one big button: a <button> inside a <button> is invalid and
   browsers silently un-nest it, which drops the zoom control on the floor.
   .prodpick therefore has to strip the button chrome back to card. */
.prodpick{display:block; width:100%; text-align:left; background:none; border:0;
  padding:0; font:inherit; color:inherit; cursor:pointer;}
.prodmedia{position:relative; margin-bottom:12px;}
.prodmedia .prodshot{margin-bottom:0;}
.prodzoom{position:absolute; right:8px; bottom:8px; width:32px; height:32px; border-radius:8px;
  border:1px solid var(--line); background:var(--white); color:var(--charcoal);
  font-size:0.875rem; line-height:1; cursor:zoom-in; opacity:0; transition:opacity .18s;}
.prod:hover .prodzoom, .prodzoom:focus-visible{opacity:1;}
/* Touch has no hover, so the control would otherwise never appear at all. */
@media(hover:none){.prodzoom{opacity:1;}}
.prodstock{display:block; margin-top:6px; font-size:0.75rem; font-weight:600; color:var(--ember-text);}
.prod.soldout{opacity:.62;}
.prod.soldout:hover{transform:none; box-shadow:none;}
.prod.soldout .prodshot{filter:grayscale(1);}
.prod.soldout .prodpick{cursor:not-allowed;}
.prod.soldout .prodstock{color:var(--text-dim);}

/* The configurator opens inside the grid, directly after its own card, spanning
   every column -- so it reads as that card unfolding. Appended to the column
   instead, it landed below every category and looked like the piece had been
   dumped at the bottom of the page. */
.prodgrid > .cfg{grid-column:1 / -1; margin-top:4px;}

/* Enlarged photo. */
.lightbox{position:fixed; inset:0; z-index:200; background:rgba(47,53,67,.88);
  display:flex; align-items:center; justify-content:center; padding:24px;}
.lb-img{max-width:min(92vw,900px); max-height:88vh; width:auto; height:auto;
  border-radius:12px; background:var(--white);}
.lb-close{position:absolute; top:18px; right:20px; width:44px; height:44px; border-radius:50%;
  border:none; background:var(--white); color:var(--charcoal); font-size:1.625rem; line-height:1;
  cursor:pointer;}
.cfg{background:var(--white); border:1px solid var(--line); border-radius:16px; padding:24px; margin-top:14px;}
.cfg h3{font-size:1.25rem; margin-bottom:4px;}
.cfg .desc{font-size:0.8438rem; color:var(--text-dim); line-height:1.55; margin-bottom:14px;}
.cfg label{display:block; font-size:0.6875rem; letter-spacing:2px; text-transform:uppercase; color:var(--text-dim); margin:14px 0 6px; font-weight:600;}
.cfg select{width:100%; padding:11px 12px; border-radius:8px; border:1px solid var(--line); background:var(--paper); font-family:var(--font-body); font-size:0.875rem; color:var(--charcoal);}
.sizegrid{display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:10px;}
.sizeopt{display:flex; justify-content:space-between; align-items:center; gap:8px; border:1.5px solid var(--line); border-radius:10px; padding:11px 13px; cursor:pointer; font-size:0.8438rem;}
.sizeopt input{accent-color:var(--ember);}
.sizeopt.sel{border-color:var(--ember); background:var(--oat);}
.tierrow{display:flex; gap:10px;}
.tierrow .sizeopt{flex:1; justify-content:center;}
.qtyrow{display:inline-flex; align-items:center; gap:14px; border:1.5px solid var(--line); border-radius:10px; padding:6px 10px;}
.qtyrow button{width:32px; height:32px; border-radius:8px; border:none; background:var(--oat); font-size:1.125rem; cursor:pointer; color:var(--charcoal); font-family:var(--font-body);}
.qtyrow span{min-width:1.5rem; text-align:center; font-weight:700;}
.addonrow{display:flex; align-items:center; gap:10px; padding:9px 0; border-bottom:1px dashed var(--line); font-size:0.8438rem;}
.addonrow:last-child{border-bottom:none;}
.addonrow input{accent-color:var(--ember);}
.addonrow .ap{margin-left:auto; color:var(--ember-text); font-weight:600;}
.cfgfoot{display:flex; justify-content:space-between; align-items:center; margin-top:18px; gap:14px; flex-wrap:wrap;}
.lineprice{font-size:1.1875rem; font-weight:700; font-family:var(--font-display);}
.cartbox{background:var(--charcoal); color:var(--cream); border-radius:16px; padding:24px; position:sticky; top:90px;
  /* The nav is fixed, so a jump to #shop-cart would tuck the heading under
     it. Clears the bar and leaves a little breathing room. */
  scroll-margin-top:96px;}
.cartbox h2{color:var(--cream); font-size:1.3125rem; margin-bottom:12px;}
.cartcount{font-size:0.75rem; font-weight:600; letter-spacing:1px; text-transform:uppercase;
  color:var(--gold); margin-left:10px; font-family:var(--font-body);}
.cartline{display:flex; gap:10px; align-items:flex-start; padding:11px 0; border-bottom:1px dashed var(--charcoal-3); font-size:0.8438rem;}
/* A piece landing in the order gets a moment of gold, so an add is visible even
   when the cart is off-screen on a phone and only the count changed. */
.cartline.justadded{animation:cartland 1.1s ease-out;}
@keyframes cartland{from{background:rgba(198,160,90,.32);} to{background:transparent;}}
/* Guarded locally rather than in the hero block above, which names its elements
   one by one -- a rule added here should not depend on someone remembering to go
   back and extend a list 190 lines away. */
@media (prefers-reduced-motion: reduce){
  .cartline.justadded{animation:none;}
  .prodzoom{transition:none;}
}
.cartline .cl-txt{flex:1;}
.cartline .cl-txt b{display:block; color:var(--cream);}
.cartline .cl-txt span{color:var(--gray-light); font-size:0.75rem; line-height:1.4;}
.cartline .cl-price{color:var(--gold); font-weight:700; white-space:nowrap;}
.cartline button{background:none; border:none; color:var(--gray-light); cursor:pointer; font-size:1rem; padding:0 2px; line-height:1;}
.cartline button:hover{color:var(--cream);}
.cartempty{color:var(--gray-light); font-size:0.8438rem; padding:8px 0 4px;}
.carttotal{display:flex; justify-content:space-between; align-items:baseline; padding:14px 0 4px; font-weight:700;}
.carttotal .amt{font-size:1.375rem; color:var(--gold); font-family:var(--font-display);}
/* Delivery, tax and the full total. The secondary rows read quieter than the
   subtotal above them; the grand total reads louder, because it is the number
   the shopper is actually deciding about. */
.carttotal.sub{padding:2px 0; font-weight:400; font-size:0.875rem; opacity:.8;}
.carttotal.sub .amt{font-size:0.9375rem; color:inherit; font-family:inherit;}
.carttotal.grand{border-top:1px solid rgba(0,0,0,.12); margin-top:6px; padding-top:10px;}
/* Delivery choices, and the payment choices beside them.
   These rows are <label> elements, and `.cartbox label` further up is a MORE
   specific selector than a bare class, so it was winning: display:block put the
   radio on its own line and the uppercase letter-spaced field styling turned a
   delivery option into a form heading. Scoping under .cartbox is what lets them
   look like the choices they are, and it fixes the payment picker too, which had
   been quietly losing the same fight. */
.cartbox .paychoice,
.cartbox .fulchoice{display:flex; align-items:flex-start; gap:10px; margin:0 0 8px;
  text-transform:none; letter-spacing:normal; font-size:0.8438rem; color:var(--cream);}
/* `.cartbox input` sets width:100% for the text fields, which stretched every
   radio to the full width of the row and shoved its label off to the right edge.
   Radios size themselves. */
.cartbox .paychoice input[type=radio],
.cartbox .fulchoice input[type=radio]{width:auto; flex:0 0 auto; accent-color:var(--gold); margin-top:3px;}
.shiplist{display:flex; flex-direction:column; margin-top:4px;}
.fulchoice{border:1.5px solid var(--charcoal-4); border-radius:10px; padding:10px 12px;
  cursor:pointer; background:var(--charcoal-2);}
.fulchoice.sel{border-color:var(--gold);}
.cartbox .fulchoice .fc-txt{display:flex; flex:1 1 auto; flex-direction:row;
  align-items:baseline; flex-wrap:wrap; gap:2px 10px; min-width:0;}
.fulchoice .fc-txt b{color:var(--cream);}
/* No nowrap: a price never contains a space, but a price_tbd phrase does,
   and a phrase that cannot wrap overruns this column instead of fitting. */
.fulchoice .fc-price{margin-left:auto; color:var(--gold); font-weight:700; text-align:right;}
.fulchoice .fc-note{flex-basis:100%; color:var(--gray-light); font-size:0.75rem; line-height:1.4;}
#zip-wrap input{max-width:12rem;}
.codewrap .coderow{display:flex; gap:8px; align-items:center;}
.cartbox .codewrap .coderow input{text-transform:uppercase; letter-spacing:1px; flex:1;}
.codewrap button{background:none; border:1.5px solid var(--gold); color:var(--gold);
  border-radius:8px; padding:9px 14px; cursor:pointer; font-family:inherit; font-size:0.8125rem;
  white-space:nowrap;}
.codewrap button:disabled{opacity:.6; cursor:default;}
.codenote{font-size:0.75rem; margin:6px 0 0; min-height:1em;}
.codenote.ok{color:var(--gold);}
.codenote.err{color:#e08a8a;}
.cartnote{font-size:0.7188rem; color:var(--gray-light); line-height:1.5; margin-top:6px;}
.cartbox label{display:block; font-size:0.6562rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray-blue); margin:13px 0 5px;}
/* `select` belongs in this rule, not just `input`. State became a dropdown on
   2026-08-30, and a select left unstyled does not fall back to something close
   to a text field -- it falls back to the OS control: white, black Arial, 19px
   tall, square, beside a 38px dark rounded input. The rule that covers this in
   embed/shop.v1.css never reaches these sites; each template carries its own. */
.cartbox input,.cartbox textarea,.cartbox select{width:100%; padding:10px 12px; border-radius:8px; border:1px solid var(--charcoal-4); background:var(--charcoal-2); color:var(--cream); font-family:var(--font-body); font-size:0.8438rem;}
/* City + State side by side: the address is structured fields now (street, apt,
   city, state, then ZIP), so shoppers include everything and the browser can
   autofill the whole set. The state column was 104px when a state was TYPED as
   two letters; a picker showing "Massachusetts" needs half the row. */
.addrrow{display:grid; grid-template-columns:1fr 1fr; gap:10px;}
.addrrow > div{min-width:0;}
.cartbox textarea{min-height:64px; resize:vertical;}
.fulrow{display:flex; gap:8px;}
.fulrow label{flex:1; display:flex; align-items:center; justify-content:center; gap:7px; border:1.5px solid var(--charcoal-4); border-radius:9px; padding:10px 6px; cursor:pointer; font-size:0.6875rem; letter-spacing:1px; margin:0; color:var(--gray-lighter);}
.fulrow label.sel{border-color:var(--gold); background:var(--charcoal-2); color:var(--cream);}
.fulrow input{accent-color:var(--gold);}
.cartbox .btn{width:100%; text-align:center; margin-top:18px; background:var(--gold); color:var(--charcoal); border:none; cursor:pointer;}
.cartbox .btn:hover{background:var(--gold-light);}

/* ============ TURNSTILE PHONE BLOCK (keep this LAST) ============
   This sits at the end of the file deliberately. The card rules it overrides
   (.cartbox padding:24px, .cform padding:30px) are defined further down than
   the widget's own block, and at equal specificity the later rule wins even
   from inside a media query. Placed mid-file, this silently under-corrected:
   the cart widget measured 296px, 4px under Cloudflare's 300px floor, because
   .cartbox kept its 24px padding. Move this up and that bug comes back.

   Arithmetic at the 360px floor, both cards:
     360  viewport (the verified floor, not 320)
    - 48  .wrap / .shopwrap padding  (24px each side)
    - 32  card padding               (1rem each side, normalized here)
    + 32  widget bleed               (-1rem each side)
    =312  clears the 300 floor with headroom                              */
@media(max-width:560px){
  .cform, .cartbox{padding:1.4rem 1rem;}
  .cform, .cartbox, .customrow > *, .shopwrap > *{min-width:0;}
  .cf-turnstile,.ts-mount{min-width:0; margin:12px -1rem;}
}


/* ============ MOBILE NAV + STICKY CTA (2026-08-16) ============ */
/* The nav previously hid every link except the CTA under 820px, so a phone got
   one button and no way to reach About, Events or Custom Orders. With her
   physical location coming, the menu needs somewhere to grow. */
.navtoggle{display:none; flex-direction:column; justify-content:center; gap:5px;
  width:44px; height:44px; padding:0 10px; background:none; border:0; cursor:pointer;}
.navtoggle span{display:block; height:2.5px; border-radius:2px; background:var(--charcoal); transition:transform .22s, opacity .22s;}
.navtoggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7.5px) rotate(45deg);}
.navtoggle[aria-expanded="true"] span:nth-child(2){opacity:0;}
.navtoggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7.5px) rotate(-45deg);}

@media(max-width:820px){
  .navtoggle{display:flex;}
  /* The drawer: full-width under the bar, closed by default. Replaces the old
     rule that simply display:none'd every non-CTA link. */
  .navlinks{position:absolute; left:0; right:0; top:100%; flex-direction:column;
    align-items:stretch; gap:0; background:var(--paper);
    border-top:1px solid rgba(47,53,67,.12); box-shadow:0 14px 26px rgba(47,53,67,.12);
    max-height:0; overflow:hidden; transition:max-height .26s ease;}
  .navlinks.open{max-height:60vh; overflow-y:auto;}
  .navlinks a{padding:15px 24px; border-bottom:1px solid rgba(47,53,67,.08); font-size:0.875rem;}
  /* Shop stays a button, and stays visible whether the drawer is open or not:
     it is the default action on this site, so it sits in the bar itself. */
  .navlinks a.cta{position:absolute; top:-56px; right:64px; padding:9px 18px; border-bottom:0;
    font-size:0.7812rem;}
  .navrow{position:relative;}
}
@media(max-width:420px){
  .navlinks a.cta{right:56px; padding:8px 14px; font-size:0.7188rem;}
  .navlogo .nm{font-size:1.3125rem;}
  .navlogo svg{width:36px;}
}

/* Sticky mobile CTA. Same shape as Oma Gourd: fixed bottom bar, full-width
   button, phone only. */
.mobile-bar{position:fixed; left:0; right:0; bottom:0; z-index:90; display:none;
  background:var(--paper); border-top:2px solid var(--ember);
  padding:.6rem 1rem calc(.6rem + env(safe-area-inset-bottom, 0px));
  box-shadow:0 -6px 18px rgba(47,53,67,.14);}
.mobile-bar .btn{width:100%; text-align:center; display:block;}
@media(max-width:720px){
  .mobile-bar{display:block;}
  /* Clear the bar so the footer and the cart's send button are never trapped
     underneath it. */
  body{padding-bottom:78px;}
}
@media (prefers-reduced-motion: reduce){
  .navlinks{transition:none;}
  .navtoggle span{transition:none;}
}

/* ---- Where to Find Me -------------------------------------------------------
   The booth card. These rules did not exist until 2026-08-18: the section
   shipped with an empty collection, so nothing ever rendered here and the first
   real row came out as three bare lines of text floating on the cream band.
   Matches the .cat treatment above (white, 1px --line, 16px radius, soft lift)
   so a place reads as the same kind of object as a product category. */
.ev{background:var(--white); border:1px solid var(--line); border-radius:16px;
  padding:26px 28px; box-shadow:0 2px 12px rgba(47,53,67,.05);
  display:flex; flex-direction:column; align-items:flex-start; gap:12px;
  text-align:left; transition:box-shadow .25s, transform .25s;}
.ev:hover{transform:translateY(-3px); box-shadow:0 14px 30px rgba(47,53,67,.12);}
.ev + .ev{margin-top:18px;}

/* When/hours as a pill, so a year-round booth does not read like a date. */
.ev-when{display:flex; flex-wrap:wrap; align-items:center; gap:8px;}
.ev-date{background:var(--oat); color:var(--ember-darker); font-size:0.6875rem; font-weight:700;
  letter-spacing:1.4px; text-transform:uppercase; padding:7px 14px; border-radius:999px;
  white-space:nowrap;}
/* Hours sit beside the date, quieter: the date is what somebody scans for, the hours are what
   they read once they have found it. Outlined rather than filled so two pills side by side do
   not fight. Absent on a row with no hours, which is why it is behind iffield. */
.ev-time{border:1px solid var(--line); color:var(--ink-mid); font-size:0.6875rem; font-weight:700;
  letter-spacing:1.4px; text-transform:uppercase; padding:6px 13px; border-radius:999px;
  white-space:nowrap;}
/* Set by js/main.js on a show whose last day has passed. Display:none rather than removing the
   node, so the page reads identically with JavaScript off -- everything shows, nothing breaks. */
.ev[hidden]{display:none;}

.ev-txt{display:flex; flex-direction:column; gap:6px;}
.ev-txt b{font-family:var(--font-display); font-size:1.3125rem; line-height:1.25; color:var(--charcoal);}

/* The address. Rendered twice in the template behind iffield:url / ifnotfield:url —
   a link when the row carries a map link, plain text when it does not. One marker
   with a bare href would put a dead link on the page for every row left blank.
   Tappable when the row has a map link — 44px min touch target,
   because the whole point of this card is somebody using it in a car. */
.ev-where{display:inline-flex; align-items:center; gap:7px; min-height:44px;
  color:var(--ember-text); font-size:0.9375rem; text-decoration:none;
  border-bottom:1px solid transparent; align-self:flex-start;}
a.ev-where:hover,a.ev-where:focus-visible{border-bottom-color:currentColor;}
.ev-where-flat{color:var(--ink-mid); min-height:0;}   /* no map link: not interactive, so no affordance */
.ev-pin{flex:none;}

.ev-note{color:var(--text-dim); font-size:0.9062rem; line-height:1.6; margin:0;}

@media(max-width:600px){
  .ev{padding:22px 20px;}
  .ev-txt b{font-size:1.1875rem;}
}

/* ---- shop toolbar: category tabs + search -----------------------------------
   Both are per-shop switches set in Products, so this whole block is inert until
   one is turned on. The tabs are built from the products themselves, which is why
   an empty category can never appear: a name only exists if something is in it. */
.shopbar{display:flex; flex-direction:column; gap:14px; margin:0 0 26px;}

.shopsearch{display:flex; flex-direction:column; gap:6px;}
.shopsearch-lab{font-size:0.6875rem; font-weight:700; letter-spacing:1.4px;
  text-transform:uppercase; color:var(--ink-mid);}
.shopq{width:100%; max-width:26.25rem; padding:12px 15px; border-radius:10px;
  border:1px solid var(--line); background:var(--white); color:var(--charcoal);
  font-family:var(--font-body); font-size:0.9375rem;}
.shopq:focus-visible{outline:2px solid var(--ember); outline-offset:2px; border-color:var(--ember);}
.shopq::placeholder{color:var(--gray-soft);}

.shoptabs{display:flex; flex-wrap:wrap; gap:9px;}
/* 44px tall: these are the primary way to move around a big shop on a phone. */
.shoptab{min-height:44px; padding:10px 18px; border-radius:999px; cursor:pointer;
  border:1px solid var(--line); background:var(--white); color:var(--ink-mid);
  font-family:var(--font-body); font-size:0.8438rem; font-weight:600;
  letter-spacing:.4px; transition:background .2s, color .2s, border-color .2s;}
.shoptab:hover{border-color:var(--tan-2); color:var(--charcoal);}
.shoptab:focus-visible{outline:2px solid var(--ember); outline-offset:2px;}
/* The chosen tab is filled, not just tinted — aria-pressed carries it for screen
   readers, and colour alone would not carry it for everyone else. */
.shoptab.on{background:var(--charcoal); border-color:var(--charcoal); color:var(--cream);}

.shopcount{margin:0; font-size:0.8438rem; color:var(--ink-mid); min-height:1.2em;}
.shopempty{margin:30px 0; padding:26px; text-align:center; border-radius:16px;
  border:1px dashed var(--line-warm); background:var(--paper);
  color:var(--ink-mid); font-size:0.9375rem;}

@media(max-width:600px){
  .shoptabs{gap:7px;}
  .shoptab{padding:10px 14px; font-size:0.8125rem;}
}

/* ---- add straight from the card --------------------------------------------
   A product with nothing to choose is added here rather than in the panel, so a
   $25 door hanger no longer drops a whole configurator into the grid to ask
   nothing. The control shows what is ALREADY in the order: empty reads "Add to
   order", one or more reads [- n +].
   The card keeps cursor:pointer for the panel path, so undo it for simple cards
   where only these controls do anything. */
.prod:has(.prodadd){cursor:default;}
.prodadd{display:flex; align-items:center; gap:8px; margin-top:12px;}

.prodaddbtn{flex:1; min-height:44px; padding:11px 14px; border-radius:9px; cursor:pointer;
  border:1px solid var(--ember-dark); background:var(--ember-dark); color:var(--white);
  font-family:var(--font-body); font-size:0.8125rem; font-weight:600; letter-spacing:.8px;
  text-transform:uppercase; transition:background .2s, border-color .2s;}
.prodaddbtn:hover{background:var(--ember-darker); border-color:var(--ember-darker);}
.prodaddbtn:focus-visible,.prodstep:focus-visible{outline:2px solid var(--ember); outline-offset:2px;}

/* 44px square: these get pressed with a thumb, repeatedly. */
.prodstep{width:44px; height:44px; flex:none; border-radius:9px; cursor:pointer;
  border:1px solid var(--line); background:var(--paper); color:var(--charcoal);
  font-family:var(--font-body); font-size:1.1875rem; line-height:1; transition:background .2s, border-color .2s;}
.prodstep:hover:not(:disabled){background:var(--oat); border-color:var(--tan-2);}
.prodstep:disabled,.prodaddbtn:disabled{opacity:.45; cursor:not-allowed;}

.prodcount{flex:1; text-align:center; font-weight:700; font-size:1rem; color:var(--charcoal);
  font-variant-numeric:tabular-nums;}
.prodsold{font-size:0.8125rem; font-weight:600; color:var(--slgray); letter-spacing:.4px;}

/* A card holding pieces should look like it, not just report it in the cart. */
.prod:has(.prodcount){border-color:var(--tan-2); box-shadow:0 2px 12px rgba(199,126,63,.12);}

/* ---- phones: the card becomes a row -------------------------------------
   Stacked, a card measured 491px tall on a 375px screen -- one piece filled
   the phone, so twenty-nine of them meant twenty-nine screens of scrolling
   (2026-08-24). Below 560px the slate becomes a square thumbnail on the left
   and its words sit beside it. Grid rather than flex, so an Add button lands
   under the text instead of beside the photo, and a card with no photo leaves
   an auto column that collapses to nothing. */
@media(max-width:560px){
  .prodgrid{grid-template-columns:1fr; gap:10px;}
  .prod{display:grid; grid-template-columns:auto 1fr; column-gap:12px; row-gap:6px;
    align-items:start; padding:10px;}
  .prodmedia{grid-column:1; grid-row:1 / -1; width:92px; margin-bottom:0;}
  .prod > :not(.prodmedia){grid-column:2; min-width:0;}
  .prodshot{margin-bottom:0; border-radius:8px;}
  .prodzoom{right:3px; bottom:3px; width:22px; height:22px; font-size:0.6875rem; border-radius:6px;}
  .prod h3{font-size:0.9375rem; margin-bottom:3px;}
  .prod p{font-size:0.75rem; margin-bottom:5px; display:-webkit-box; -webkit-line-clamp:2;
    -webkit-box-orient:vertical; overflow:hidden;}
  .prodadd, .qtyrow{justify-content:flex-start;}
  /* The count stretches (flex:1) so the stepper fills a full-width card. In a
     row card that leaves the minus and plus at opposite ends of the phone with
     a lake of white between them, so here it just hugs the number. */
  .prodcount{flex:0 0 auto; min-width:2.5ch;}
  .prodstep{width:40px; height:40px;}
}
