
/* Lora, Source Sans 3 and Caveat: SIL Open Font License 1.1 */
@font-face{font-family:'Lora';font-style:normal;font-weight:500 700;font-display:swap;src:url(/font-lora-600.woff2?v=80cadb0f) format('woff2')}
@font-face{font-family:'Source Sans 3';font-style:normal;font-weight:400 700;font-display:swap;src:url(/font-source-sans-3-400.woff2?v=330ad4f3) format('woff2')}
@font-face{font-family:'Caveat';font-style:normal;font-weight:600;font-display:swap;src:url(/font-caveat-600.woff2?v=f06f68eb) format('woff2')}

:root{
  --bg:#F2EADA;--surface:#FDFAF3;--card:#FDFAF3;
  --ink:#33261B;--ink-2:#6B5B48;
  --accent:#D0621B;--accent-dark:#90400C;
  --support:#5C7048;--rule:#DFD2BB;
  --header-bg:rgba(253,250,243,.95);--nav-hover:#F1E7D6;--ribbon-bg:#EDE3CF;
  
  --timer:#8E1F2F;--timer-ink:#FFF4E6;
  --wm-ink:#46503C;--wm-co:#C25A46;
  --display:'Lora',Georgia,'Times New Roman',serif;
  --sans:'Source Sans 3',system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
  --hand:'Caveat','Segoe Script',cursive;
  --max:1120px;--pad:clamp(20px,3.4vw,44px);--band:clamp(48px,6.4vw,84px);
  --gap:clamp(18px,2.3vw,30px);
  --radius:10px;--btn-radius:7px;
  --shadow:0 8px 20px rgba(51,38,27,.10);
  --photo-frame:1px solid #E4D8C0;--photo-mat:6px;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-padding-top:92px}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);
  font-size:clamp(15.5px,1.2vw,17px);line-height:1.62;overflow-x:hidden;-webkit-font-smoothing:antialiased;
  background-image:
    repeating-linear-gradient(0deg,rgba(51,38,27,.014) 0 1px,transparent 1px 5px),
    repeating-linear-gradient(90deg,rgba(51,38,27,.012) 0 1px,transparent 1px 6px)}
/* height:auto is load-bearing - see the note above the css literal in build.mjs */
img{display:block;max-width:100%;height:auto}
/* SPEED-PLAN Phase 1: the picture element below is layout-transparent for LAYOUT (margins, grid
   and flex sizing act as if the img sat directly in its old parent), so every descendant CSS rule
   written against the img keeps working unchanged. display:contents does not change which
   elements a selector matches, though - it only changes box generation - so the handful of
   direct-child X>img rules a wrapped photo could sit under had to move to X>img,X>picture>img
   by hand; grep this file for ">picture>img" to see them. The word "picture" never gets its own
   angle brackets in this comment on purpose - that literal tag text would otherwise sit inside
   every page's own inline style block and confuse any later regex scan (page-weight.mjs,
   verify-site.mjs) that finds a real picture element by matching that same text sitewide. */
picture{display:contents}
/* SPEED-PLAN Phase 7: only a picture carrying the inline one-kilobyte stand-in (class="ph",
   painted eager pictures only, never a lazy one) overrides the display:contents above - the
   class selector beats the bare-tag one, so every other picture keeps the Phase 1 behavior
   untouched. The stand-in has to actually paint somewhere, which display:contents would not
   let it do (no box of its own to carry a background-image), so .ph pictures get a real box. */
picture.ph{display:block;background-size:cover;background-position:center}
picture.ph>img{display:block}
a{color:var(--accent-dark);text-decoration:none}
a:hover{color:var(--accent)}
h1,h2,h3,h4{font-family:var(--display);color:var(--ink);margin:0;font-weight:700;text-wrap:balance}
h1{font-size:clamp(2rem,4.6vw,3.3rem);line-height:1.08;letter-spacing:-.015em}
h2{font-size:clamp(1.5rem,2.9vw,2.3rem);line-height:1.14;letter-spacing:-.01em}
h3{font-size:clamp(1.05rem,1.5vw,1.24rem);line-height:1.26}
p{margin:0}
.wrap{max-width:var(--max);margin:0 auto;padding-inline:var(--pad);width:100%}
.wrap.narrow{max-width:820px}
.hand{font-family:var(--hand);font-size:clamp(1.5rem,2.5vw,2.1rem);color:var(--accent);
  line-height:1.05;transform:rotate(-1.6deg);transform-origin:left}
.hand.light{color:#F0BE8C}
.sub{color:var(--ink-2);max-width:62ch;font-size:clamp(1rem,1.3vw,1.14rem)}
.stack{display:flex;flex-direction:column;gap:12px}
.sechead{display:flex;flex-direction:column;gap:12px;margin-bottom:clamp(26px,3.2vw,46px)}
/* A section head that follows the package grid needs its own air, or the heading crowds the
   bottom edge of the cards above it. */
.packs + .sechead{margin-top:clamp(46px,5.4vw,74px)}
.more{margin-top:clamp(22px,2.6vw,34px)}
/* buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;background:var(--accent);color:#fff;
  font-family:var(--sans);font-weight:700;border-radius:var(--btn-radius);text-decoration:none;
  padding:.78em 1.5em;font-size:.94rem;line-height:1.2;border:1px solid transparent;cursor:pointer;
  box-shadow:0 2px 0 rgba(51,38,27,.12);transition:background .18s ease}
.btn:hover{background:var(--accent-dark);color:#fff}
.btn:disabled{opacity:.6;cursor:default}
/* .btn sets its own display, so [hidden] alone does not hide one. The sitewide cart peek hides
   the order button the moment there is an order to go back to, and this is what makes that work. */
.btn[hidden]{display:none}
.btn-lg{padding:.95em 1.9em;font-size:1rem}
.btn-sm{padding:.62em 1.05em;font-size:.86rem}
.btn-line{display:inline-flex;align-items:center;gap:.5em;font-family:var(--sans);font-weight:700;
  color:var(--accent-dark);font-size:.94rem;border-bottom:2px solid var(--accent);padding-bottom:3px}
.btn-line::after{content:'\2192';transition:transform .18s ease}
.btn-line:hover::after{transform:translateX(3px)}
.btn-line.sm{font-size:.86rem}
a:focus-visible,summary:focus-visible,button:focus-visible,input:focus-visible,label:focus-visible{
  outline:2px solid var(--accent);outline-offset:3px}
/* header */
header.site{position:sticky;top:0;z-index:60;background:var(--header-bg);border-bottom:1px solid var(--rule);
  backdrop-filter:saturate(1.1) blur(6px)}
header.site .hdr-in{max-width:var(--max);margin:0 auto;padding:14px var(--pad);
  display:flex;align-items:center;gap:clamp(10px,1.4vw,26px);position:relative}
.brand{display:inline-flex;align-items:center;min-width:0;flex:0 1 auto;color:var(--wm-ink)}
/* The stacked wordmark. Both lines are the same size on purpose - that is what separates
   arrangement 8 from the tracked-small-top-line variants. The size interpolates through the
   three widths it was approved at: 16px on a 390 phone, 20px on an 834 iPad, 24px at 1280. */
.bt{font-family:var(--display);font-weight:500;font-size:clamp(1rem,.9vw + 12.5px,1.5rem);
  line-height:1.18;text-transform:uppercase;color:var(--wm-ink)}
.bt .wm-l{display:block;white-space:nowrap}
.bt b{font-weight:500;color:var(--wm-co);margin-left:.16em}
.navtoggle{position:absolute;opacity:0;pointer-events:none;width:0;height:0}
.hnav{display:flex;align-items:center;gap:2px;flex:1 1 auto;justify-content:center;min-width:0}
.hnav a{font-family:var(--sans);font-weight:600;font-size:.9rem;color:var(--ink);padding:8px 10px;
  border-radius:4px;white-space:nowrap}
.hnav a:hover{color:var(--accent-dark);background:var(--nav-hover)}
.hdr-right{display:flex;align-items:center;gap:10px;flex:none;margin-left:auto}
.navbtn{display:none;align-items:center;gap:8px;cursor:pointer;padding:9px 12px;border:1px solid var(--rule);
  border-radius:var(--btn-radius);background:var(--surface);flex:none}
.navbtn .bars{display:flex;flex-direction:column;gap:4px}
.navbtn .bars span{display:block;width:18px;height:2px;background:var(--ink);border-radius:2px}
.navbtn em{font-style:normal;font-family:var(--sans);font-weight:700;font-size:.76rem;
  text-transform:uppercase;letter-spacing:.09em}
/* THE CART CONTROL (flow lock-in Phase 10, 2026-08-28). Inside the two checkout flows the
   header's order button pointed at the page the customer was already standing on, so that slot
   carries what they are buying instead. Ships hidden and is revealed by the wizard's own boot:
   the count is painted by script, and a number baked into the HTML would be a stale one the
   moment scripting is off. */
.cartwrap{position:relative;flex:none}
.cartwrap[hidden]{display:none}
.cartbtn{display:inline-flex;align-items:center;gap:7px;cursor:pointer;padding:9px 13px;
  border:1px solid var(--rule);border-radius:var(--btn-radius);background:var(--surface);
  font-family:var(--sans);font-weight:700;font-size:.88rem;color:var(--ink);line-height:1}
.cartbtn:hover{border-color:var(--accent);color:var(--accent-dark)}
.cartico{flex:none;display:block}
/* The sitewide peek wears the flow cart's own look so the control reads as the same thing on
   every page. It is an anchor, so it needs the underline off; everything else it inherits. */
.cart-peek{text-decoration:none}
.cart-peek:hover{text-decoration:none}
/* Read by a screen reader, invisible to everyone else: on screen the button says "(2)". */
.cartsr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* THE PANEL OWNS A HEIGHT (FLOW-REVISION-PLAN Phase 25, 2026-09-09, k46). His words: "You can't
   scroll up inside of your cart, which I'm seeing now that I have it open with a bunch of things
   inside, to see everything that you've got all in there, if you've bought a bunch of upsells."
   It had no height and no overflow, so six lines grew it straight off the top of the screen - it
   opens UPWARD out of the sticky flowbar - with nothing anywhere to scroll. It scrolls inside
   itself now, and the heading and the checkout row are pinned to its two edges so neither the
   thing it is nor the way out of it can ever be scrolled away from. */
.cartpanel{position:absolute;top:calc(100% + 10px);right:0;z-index:70;
  width:min(360px,calc(100vw - 26px));background:var(--card);border:1px solid var(--rule);
  border-radius:var(--radius);box-shadow:0 14px 30px rgba(51,38,27,.18);padding:4px 16px 0;
  max-height:min(72vh,560px);overflow-y:auto;overscroll-behavior:contain}
.cartpanel[hidden]{display:none}
.cartpanel h4{position:sticky;top:0;z-index:2;background:var(--card);font-size:.98rem;padding-block:12px 2px}

@media(max-width:720px){
  .cartwrap{position:static}
  .cartpanel{left:12px;right:12px;width:auto;max-width:calc(100vw - 24px)}
}
.cartrow{display:flex;align-items:baseline;justify-content:space-between;gap:14px;
  font-size:.9rem;padding:9px 0;border-top:1px solid var(--rule)}
.cartrow span{flex:1 1 auto}
.cartfine{font-size:.82rem;line-height:1.45;color:var(--ink2);padding:6px 0 2px;border:0}

.cartrow b{font-family:var(--display);white-space:nowrap;font-size:1rem}
#review-summary > .cartrow.cartline:first-child > b{font-size:1.08rem;font-weight:700}
/* THE REMOVE CONTROL, ON EVERY LINE (Phase 25, k45, blocker). His words: "I'm actually not
   seeing a way for me to clear out my cart now, which is frustrating. The fact that when I just
   click into this cart, I can't delete something. Now I can only click on change, but there's no
   delete option. That's a big gap in UI." It used to be the add-on rows only: the package row and
   the delivery row carried a Change and nothing else. */
.cartx{flex:none;align-self:center;width:20px;height:20px;border-radius:50%;padding:0;
  border:1px solid var(--rule);background:none;color:var(--ink-2);cursor:pointer;
  font-size:.9rem;line-height:1;display:inline-flex;align-items:center;justify-content:center}
.cartx:hover{border-color:var(--accent);color:var(--accent-dark)}
.cartrow.cartsum{border-top:2px solid var(--ink);font-size:1rem;padding-top:11px}
/* The sales-tax note under the total. Quiet on purpose - it is a fact he needs before the Stripe
   page, not a thing to weigh the cart down with. No top border, so it reads as part of the total
   row above rather than as another line on the order. */
.cartrow.carttax{border:0;padding-top:4px;font-size:.78rem;color:var(--ink-2);line-height:1.35}
.cartrow.cartnone{color:var(--ink-2)}
/* THE CART BECOMES THE MULTI-BUY SURFACE (Phase 5b, mark 10, 2026-09-02): "to doug's feedback
   it's too hard to multi-buy from all these screens." Every line grows the same controls the
   review summary carries - a thumbnail, a stepper on a QTY_ADDONS sku, Remove, "Change" on the
   pumpkin and the delivery row, and a held discount stated as a plain fact - so a quantity, or
   which pumpkin this is, never costs a walk back to an earlier step. */
/* Phase 25: a line carries one more control than it used to (the X), so the row's own gap comes
   in to pay for it - a wider panel and a tighter row together leave the label MORE room than it
   had before, not less. */
.cartrow.cartline{align-items:center;gap:9px}
.cartrow.cartline .reviewimg{flex:none;border-radius:6px}
.cartlinemain{display:flex;flex-direction:column;gap:1px;flex:1 1 auto;min-width:0}
.cartheld{font-size:.78rem;font-weight:600;color:var(--accent-dark)}
.cartchange{flex:none;background:none;border:none;padding:0;font:inherit;font-family:var(--sans);
  font-weight:700;font-size:.82rem;color:var(--accent-dark);cursor:pointer;text-decoration:underline}
.cartchange:hover{color:var(--accent)}

.cartaddr{display:block;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
  font-family:var(--display);color:var(--ink)}
.cartchange-below{align-self:flex-start;margin-top:2px}
.cartrow.cartpkg{justify-content:center}
/* CLEAR CART (Phase 25, k45) and CONTINUE TO CHECKOUT (k47, his words: "There should be a way to
   just get to the straight checkout page quicker than having to scroll down to the bottom. It's a
   bit clunky."). Clear cart asks once before it wipes an order somebody spent ten minutes filling,
   and the checkout row is stuck to the bottom edge of the scrolling panel, so it is one press away
   from any step and from any scroll position inside the cart. */
.cartrow.cartclear{justify-content:center;gap:12px;flex-wrap:wrap;padding-bottom:4px}
.cartrow.cartclear span{flex:0 1 auto;font-size:.82rem;color:var(--ink-2);text-align:center}
.cartclearbtn{flex:none;background:none;border:none;padding:0;font:inherit;font-family:var(--sans);
  font-weight:700;font-size:.82rem;color:var(--ink-2);cursor:pointer;text-decoration:underline}
.cartclearbtn:hover{color:var(--accent-dark)}
.cartclearbtn.yes{color:var(--accent-dark)}
.cartrow.cartnone{padding-bottom:14px}
.cartgo{position:sticky;bottom:0;z-index:2;background:var(--card);
  border-top:1px solid var(--rule);padding-block:10px 14px}
.cartgo .btn{width:100%}
/* THE UNCONFIRMED-PUMPKIN WARNING (Phase 25, k13). His words: "If somebody continues without
   confirming something like this, I would kind of want it to flash a warning of, hey, before you
   continue, you haven't added that second engraved pumpkin to your cart, if you don't want to add
   that to your cart, press continue, otherwise if you do, press looks good." A dialog rather than
   a line of text under the button: Continue gets pressed at the bottom of a long step, and the
   panel it is about can be a whole screen away from it. The pair sits the way k12 lays out every
   other pair on this site, the quiet way out on the left and the filled yes on the right.
   REUSED, NOT COPIED, for the mini pumpkin (FLOW-REVISION-6 Phase 2, t05): warnUnconfirmed() in
   the wizard script swaps the line and both button labels for that case, same box, same place in
   front of Continue.
   THE X AND THE SECOND BOX (FLOW-REVISION-7 Phase 5, w10, his Change it note). The X, Escape and a
   scrim tap all close the box without answering it - a dismissal, not a "leave it out" - and the
   card's extra top padding below is permanent headroom for that button, not a per-mode tweak.
   cartwarn--ack is the second box's own mode, shown only at the checkout hand-off for a panel the
   customer dismissed and never resolved: one orange Continue, right aligned, no quiet skip beside
   it. */
.cartwarn{position:fixed;inset:0;z-index:90;display:flex;align-items:center;justify-content:center;
  padding:20px;background:rgba(28,19,11,.44)}
.cartwarn[hidden]{display:none}
.cartwarn-card{width:100%;max-width:420px;background:var(--card);border:1px solid var(--rule);
  border-radius:var(--radius);box-shadow:0 18px 40px rgba(51,38,27,.26);
  padding:48px clamp(18px,3vw,26px) clamp(18px,3vw,26px);text-align:center;position:relative}
.cartwarn-card p{margin:0 0 18px}
.cartwarn-x{position:absolute;top:8px;right:8px;width:34px;height:34px;border:0;cursor:pointer;
  border-radius:50%;background:rgba(0,0,0,.05);color:var(--ink-2);font-size:1.4rem;line-height:1}
.cartwarn-x:hover{color:var(--accent-dark);background:rgba(0,0,0,.09)}
.cartwarn-acts{display:flex;align-items:center;justify-content:space-between;gap:14px}
.cartwarn-skip{flex:none;background:none;border:none;padding:0;font:inherit;font-family:var(--sans);
  font-weight:600;font-size:.94rem;color:var(--ink-2);cursor:pointer;text-decoration:underline}
.cartwarn-skip:hover{color:var(--accent-dark)}
.cartwarn-card.cartwarn--ack .cartwarn-acts{justify-content:flex-end}
/* hero */
.hero{position:relative;isolation:isolate;background:var(--ink)}
.hero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
.hero::after{content:'';position:absolute;inset:0;z-index:1;
  background:linear-gradient(95deg,rgba(28,19,11,.86) 0%,rgba(28,19,11,.58) 52%,rgba(28,19,11,.2) 100%)}
.hero-body{position:relative;z-index:2;padding-block:clamp(58px,8.4vw,124px)}
.hero h1{color:#fff;max-width:16ch}
.hero .hand{margin-top:14px}
.lede{color:#F1E6D4;font-size:clamp(1rem,1.35vw,1.2rem);max-width:48ch;margin-top:18px}
.cta-row{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin-top:clamp(20px,2.4vw,32px)}
.hero .btn-line{color:#fff;border-bottom-color:rgba(255,255,255,.65)}
.hero .btn-line:hover{color:#fff}
/* interior page head (light, no dark band, no eyebrow) */
.pagehead{background:var(--surface);border-bottom:1px solid var(--rule)}
.pagehead .wrap{padding-block:clamp(34px,4.2vw,58px)}
.pagehead h1{max-width:20ch}
.pagehead.town h1{max-width:30ch}
/* j27+j28, FLOW-REVISION-PLAN Phase 12: the porch list only, tightening the gap above and below
   its lede now that the lede is two sentences instead of one; the shared .pagehead .wrap rule
   above (117 pages) does not move. */
.pagehead.porchlist .wrap{padding-block:clamp(20px,2.5vw,35px) clamp(17px,2.1vw,29px)}
.pagehead.porchlist + section.band.tight{padding-block-start:clamp(12px,1.3vw,17px)}
.pagehead .hand{margin-top:12px}
.pagehead .sub{margin-top:14px}
.pagehead.withimg .wrap{display:flex;align-items:center;gap:clamp(24px,3.4vw,52px)}
.pagehead.withimg .pagehead-txt{flex:1 1 auto;min-width:0}
.pagehead.withimg .pagehead-img{flex:0 0 clamp(200px,26vw,320px)}
.pagehead.withimg .pagehead-img img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:var(--radius);
  border:var(--photo-frame);padding:var(--photo-mat);background:var(--card);box-shadow:var(--shadow)}
/* Heading for the two order pages. It was a tan band with a strapline under the title until the
   UX review (item 16): the shade made it read as a hero and ate the top of the page, and the
   strapline was static across every step, so on Extras it still promised a preview of the
   pumpkin. Now it is the title alone, sitting directly on the page with the step bar right
   beneath it. Do not give this a background or a second line. */
.orderhead .wrap{padding-block:clamp(18px,2.2vw,26px) 0}
/* Smaller than a page h1 and unwrapped: at the old size "Design your engraved pumpkin" broke
   over two lines and the title alone was taller than the step bar it introduces. */
.orderhead h1{max-width:none;font-size:clamp(1.5rem,3vw,2.1rem);line-height:1.15}
.orderhead + section.band{padding-top:clamp(14px,1.6vw,20px)}
/* sections */
section.band{padding-block:var(--band)}
section.alt{background:var(--surface)}
/* how it works */
.steps{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap)}
.step{display:flex;flex-direction:column;gap:10px}
/* 2026-08-27: the photograph fills the width of its column and the words read beneath it. It
   was a 62px stamp beside the heading, which is the same postage-stamp complaint that killed
   the 82px add-on thumbnails one page earlier in the funnel. The "Step 1 / Step 2 / Step 3"
   captions went with it: three columns in a row already say it is three steps, and the numeral
   was the only thing the tiles needed a second element for. */
.stepicon{display:block;width:100%;border-radius:16px;overflow:hidden;
  border:var(--photo-frame);background:var(--card);box-shadow:var(--shadow)}
.stepicon img{width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;display:block}
.stepicon.drawn{display:flex;align-items:center;justify-content:flex-start;height:auto;aspect-ratio:auto;background:none;border:0;box-shadow:none;border-radius:0;color:var(--accent);padding:6px 0 4px}
.stepicon.drawn svg{display:block}
.step p{color:var(--ink-2);font-size:.98rem}
/* photo grids */
.shots{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap)}
.shots.g3{grid-template-columns:repeat(3,1fr)}
.shot{margin:0;display:flex;flex-direction:column;gap:12px}
.shot img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:var(--radius);
  border:var(--photo-frame);padding:var(--photo-mat);background:var(--card);box-shadow:var(--shadow)}
.shots.g3 .shot img{aspect-ratio:4/3}
.shots.g3.portrait .shot img{aspect-ratio:3/4}
/* square 3-up: the real photo set is a mix of portrait, square and landscape phone shots, and a
   square crop is the only one that keeps the engraving centered in all of them */
.shots.g3.square .shot img{aspect-ratio:1}
/* fr2 j04, 2026-09-08 walk: a hover lift on the home proof strip only, same precedent as
   .choice:hover .choice-img img. .shot img has no overflow parent, so the scale stays small.
   prefers-reduced-motion already zeroes transitions sitewide. */
.shots.lift .shot img{transition:transform .55s cubic-bezier(.2,.7,.3,1)}
.shots.lift .shot:hover img{transform:scale(1.04)}
/* ---- multi-step order flow ----
   Every step ships visible in the HTML so the form still works with JS off; the script is what
   hides all but the current one. [hidden] is forced because these panels are flex/grid parents
   and a bare attribute selector loses to their own display rule. */
[hidden]{display:none !important}
.wizbar{display:flex;list-style:none;margin:0 0 clamp(22px,2.6vw,34px);padding:0;gap:7px}
.wizbar li{flex:1;display:flex;flex-direction:column;gap:8px;font-family:var(--sans);
  font-size:.79rem;font-weight:600;color:var(--ink-2)}
.wizbar li::before{content:'';height:4px;border-radius:2px;background:var(--rule);transition:background .2s ease}
.wizbar li.done::before{background:var(--support)}
.wizbar li.current::before{background:var(--accent)}
.wizbar li.current{color:var(--accent-dark);font-weight:700}

@media(max-width:720px){
  .wizbar{gap:5px}
  .wizbar li{font-size:.68rem;gap:6px;text-align:center}
  
  .wizbar .wizlab{display:flex;flex-direction:column;align-items:center;gap:2px;line-height:1.15}
  .wizbar .wizdot{display:none}
  .wizbar .wizn{font-size:.78rem}
}

.wizbar li svg{vertical-align:-1px;margin-right:3px}
.wiznav{display:flex;align-items:center;gap:12px;margin-top:clamp(20px,2.4vw,28px)}
.wiznav .spacer{flex:1}

@media(min-width:721px){
  .wiznav{position:sticky;bottom:0;z-index:40;background:var(--card);
    margin-inline:calc(-1 * clamp(20px,2.4vw,32px));margin-bottom:calc(-1 * clamp(20px,2.4vw,32px));
    padding:11px clamp(20px,2.4vw,32px);border-radius:0 0 var(--radius) var(--radius)}
}


.stepsubmit{margin-top:24px}
#wiznav .stepsubmit{margin-top:0}
/* Quiet Back on the left, one obvious filled Next on the right. Back used to be a .btn-line,
   whose ::after arrow points RIGHT, so the button that walks you backwards read "Back ->".
   .btn-back mirrors .backlink instead: a left arrow, and no button weight competing with Next. */
.btn-back{display:inline-flex;align-items:center;gap:.45em;font-family:var(--sans);font-weight:600;
  font-size:.9rem;color:var(--ink-2);background:none;border:0;padding:6px 2px;cursor:pointer}
.btn-back::before{content:'\2190'}
.btn-back:hover{color:var(--accent-dark)}
/* THE STANDING RULE (UX review 2026-08-26, flagged four times by the owner): any control that
   re-opens or walks back something already done, without being plain wizard navigation (that
   is .btn-back), is this quiet bordered pill with a LEADING left arrow. Never .btn-line for
   one of these: its ::after arrow points right, which reads "forward" on a backward act. */
.btn-return{display:inline-flex;align-items:center;gap:.45em;font-family:var(--sans);font-weight:600;
  font-size:.88rem;color:var(--ink-2);background:none;border:1.5px solid var(--rule);border-radius:999px;
  padding:8px 16px 8px 14px;cursor:pointer;transition:color .18s ease,border-color .18s ease}
.btn-return::before{content:'\2190'}
.btn-return:hover{color:var(--accent-dark);border-color:var(--accent)}
/* .wizlinks, the cross-link between the two order flows, was deleted 2026-08-26 with its markup
   (UX review items 17 and 35). Its styles went with it. Do not add a link of any kind back into
   the wizard. */
/* The one-line-per-step note under the wizard died in the flow lock-in Phase 4 (2026-08-27)
   along with its map of five near-identical reassurances; none of them earned a read. The
   trust row keeps its no-JS fallback role; with JS the wizard simply hides it. */
.wizerr{color:var(--accent-dark);font-weight:600;font-size:.94rem;margin-top:12px;min-height:1px}
.review{border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden;
  background:var(--card);margin-bottom:18px}

.review>div:not(.cartrow){display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  padding:13px clamp(14px,1.6vw,20px);border-bottom:1px solid var(--rule);font-size:.96rem}
.review>div:last-child{border-bottom:0}
.review>div:not(.cartrow) span{color:var(--ink-2)}
.review>div:not(.cartrow) b{font-family:var(--display);color:var(--ink);text-align:right}
.review .cartrow{padding-inline:clamp(14px,1.6vw,20px)}
.reviewimg{width:52px;height:52px;border-radius:8px;object-fit:cover;flex:none;
  border:1px solid var(--rule);background:var(--card)}
/* The timed price ran out with the item still on the order. Its own row rather than a
   rewritten label, so the money row above it still itemizes the dollar it now costs. */
.review .noterow{color:var(--accent-dark);font-size:.9rem}
.review .noterow b{display:none}
/* THE SAVING, SAID OUT LOUD, flow lock-in Phase 16 (2026-08-28). His words: "when people get
   things for a discount, we should show both the percentage off per item, and the savings as it's
   own separate line item. Something like: your bundled purchase savings: $X". The percentages
   ride the extra rows themselves; this is the one row that totals them, and it is green because
   it is the only row on the summary that is money the customer is NOT paying. */
.review .saverow span,.review .saverow b{color:var(--support);font-weight:700}
/* FLOW-REVISION-PLAN Phase 11 (2026-09-08, j21): the total used to be the last row inside
   #review-summary's own bordered box, the same size and weight as a "Change" button beside it.
   It prints here instead, outside that box, centred, and the dollar figure takes the .formstep
   clamp so it reads with the same weight as a step heading rather than one more line item. */
.reviewtotal{display:flex;flex-wrap:wrap;justify-content:center;align-items:baseline;gap:10px;
  text-align:center;margin:6px 0 20px}
/* The sales-tax note, on its own line under the total in both layouts: flex-basis 100% breaks it
   off the desktop row, and the mobile rule below is already a column. Quiet, not a warning. */
.reviewtotal .revtax{flex-basis:100%;font-style:normal;font-size:.82rem;color:var(--ink-2);
  line-height:1.4;margin-top:2px}
.reviewtotal span{color:var(--ink-2);font-weight:600;font-size:.95rem}
.reviewtotal b{font-family:var(--display);font-weight:700;
  font-size:clamp(1.42rem,1.02rem + 1.1vw,1.9rem);line-height:1.18;letter-spacing:-.012em;
  color:var(--ink)}

@media(max-width:720px){
  
  .reviewtotal{flex-direction:column;align-items:center;gap:2px;margin:30px 0 34px}
  .reviewtotal span{font-size:.92rem}
  .reviewtotal b{font-size:clamp(2.2rem,1.3rem + 6vw,2.6rem)}
}
/* service-area coverage map beside its copy */
.areamap{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(26px,3.6vw,52px);align-items:center}
.areamap figure{margin:0;min-width:0;display:flex;flex-direction:column;gap:11px}
.areamap-txt{min-width:0}
.areamap img{width:100%;height:auto;border-radius:var(--radius);border:var(--photo-frame);
  padding:var(--photo-mat);background:var(--card);box-shadow:var(--shadow)}
.areamap figcaption{font-family:var(--hand);font-size:1.2rem;color:var(--accent-dark);
  transform:rotate(-.7deg);transform-origin:left}
.areamap-placeholder{display:flex;flex-direction:column;gap:10px;text-align:center;
  padding:clamp(30px,5vw,64px) clamp(20px,3vw,34px);border:1px dashed var(--rule);
  border-radius:var(--radius);background:var(--card)}
.areamap-placeholder b{font-family:var(--display);font-size:1.3rem;color:var(--ink)}
.areamap-placeholder span{color:var(--ink-2);font-size:.95rem;line-height:1.7}
.areamap-txt{display:flex;flex-direction:column;gap:14px;align-items:flex-start}
.areamap-txt p{color:var(--ink-2)}
/* interactive delivery-zone map. z-index:0 keeps Leaflet's own stacking context from painting
   its panes over the sticky header and the phone order bar. */
/* No min-height here. A grid item's automatic minimum size is its intrinsic size, so
   min-height:300px plus aspect-ratio:4/3 gave the map an intrinsic 400px width, which widened
   the single-column grid track past a 375px phone and put the whole page into horizontal
   scroll. The min-width:0 on the figure below is the other half of that guard. */
.zonemap{width:100%;aspect-ratio:4/3;position:relative;z-index:0;
  border-radius:var(--radius);border:var(--photo-frame);background:var(--card);
  box-shadow:var(--shadow);overflow:hidden}
.zonemap .leaflet-container{font-family:var(--sans);background:var(--card)}
/* THE BASEMAP IS BACKGROUND, THE ZONES ARE THE CONTENT. OSM standard is a general-purpose map:
   green parks, blue water, red and yellow highways, all at full strength, all competing with
   five coloured areas laid over it. Desaturating the tile pane turns it into the quiet
   street-and-place reference it needs to be here, which is what a paid basemap like CARTO
   Positron would have been bought for. Applied to the tile pane only, so the zone fills, the
   price chips, the legend and the zoom control keep their full colour. */
.zonemap .leaflet-tile-pane{filter:grayscale(.85) brightness(1.08) contrast(.92)}
/* THE BLACK BOX ON CLICK. Leaflet's keyboard handler makes the container focusable and focuses
   it on every mousedown, and .zonemap{overflow:hidden} clipped the browser's focus ring into a
   hard rectangle around the whole map. Suppressing :focus and keeping :focus-visible loses the
   mouse artefact and keeps the ring for anybody arriving by keyboard, which is the only reason
   the container is focusable in the first place. */
.zonemap .leaflet-container:focus{outline:none}
.zonemap .leaflet-container:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
/* AND THE SAME BOX AGAIN, ON THE ZONE ITSELF. Phase 6a killed the ring on the CONTAINER and the
   check it shipped with only ever looked at the container, so it passed while the real box was
   still there: clicking a tier focuses its SVG path, and the browser paints its default
   outline around that path's BOUNDING RECTANGLE, which is the square that gets drawn around a
   zone on the walk. Suppressed the same way, with the keyboard ring kept. */
.zonemap .leaflet-interactive:focus{outline:none}
.zonemap .leaflet-interactive:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.zone-tip{font-family:var(--sans);font-size:.86rem;line-height:1.45}
/* The colour key on the Leaflet map. Five colours with no legend is a puzzle, not a map. The
   swatch is a rounded square rather than a dot now that the map draws filled areas: a circle
   keyed to a region reads as a pin. */
.zonelegend{background:var(--surface);border:1px solid var(--rule);border-radius:8px;
  padding:6px 9px;font-family:var(--sans);font-size:.78rem;line-height:1.5;color:var(--ink);
  box-shadow:0 1px 4px rgba(51,38,27,.18);display:flex;flex-wrap:wrap;gap:4px 11px}
.zonelegend i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:5px}
/* The price written on each zone. It has to stay legible over a busy OSM basemap AND over its
   own fill, so it is a solid chip in the zone's colour with white type, not text with a halo.
   pointer-events stay off in the icon class so the chip never eats a hover meant for the
   polygon under it. Leaflet anchors a divIcon by its top left corner, so the transform is what
   actually centres the chip on the point rather than hanging it down and to the right. */
.zonelabel{pointer-events:none}
.zonelabel span{display:inline-block;transform:translate(-50%,-50%);white-space:nowrap;
  font-family:var(--sans);font-size:.74rem;font-weight:700;letter-spacing:.02em;
  padding:3px 9px;border-radius:999px;box-shadow:0 1px 3px rgba(51,38,27,.35)}
@media (max-width:560px){.zonelabel span{font-size:.66rem;padding:2px 7px}}
/* On a phone the 4/3 box is taller than it needs to be for a shape this wide, and the height it
   buys is all sky. aspect-ratio has to be turned OFF in the same rule: aspect-ratio plus a
   min-height is what gave this box an intrinsic 400px width and put the page into sideways
   scroll (see the note above the .zonemap rule). */
@media (max-width:560px){.zonemap{aspect-ratio:auto;height:62vw;min-height:260px}}
/* town pages */
.townpass{margin-bottom:clamp(15px,1.9vw,20px)}
.townpass p{margin:0}

.townfee{background:var(--card);border:1px solid var(--rule);border-left:4px solid var(--accent);
  border-radius:var(--radius);padding:clamp(15px,1.9vw,20px) clamp(16px,2vw,22px);
  color:var(--ink);font-weight:600;margin-top:clamp(20px,2.4vw,30px);margin-bottom:clamp(24px,3vw,34px)}

.townfee-ctas{display:flex;flex-wrap:wrap;align-items:center;gap:14px 28px}
.alsoserving{color:var(--ink-2);font-size:.94rem;margin:0 0 8px}

.nearby-head{margin-top:clamp(26px,2.9vw,34px)}
/* /areas/ and /service-area/ directories, grouped by county (Phase T3/T4) */
div.countygroup+div.countygroup{margin-top:clamp(26px,3vw,38px)}
details.countygroup+details.countygroup{margin-top:clamp(14px,1.6vw,22px)}
details.countygroup .dirlist{margin:6px 0 20px}
.dirlist{list-style:none;margin:12px 0 0;padding:0}
.dirlist li{padding:11px 0;border-bottom:1px solid var(--rule);font-family:var(--sans);font-size:.98rem}
.dirlist a{font-weight:700;text-decoration:none;color:var(--ink)}
.dirlist a:hover{color:var(--accent-dark)}
.dirfee{color:var(--ink-2)}
.dirlist em{font-style:normal;color:var(--ink-2);font-size:.9rem;display:block;margin-top:2px}

@media (max-width:720px){
  .dirlist li{display:grid;grid-template-columns:1fr auto;grid-template-rows:auto auto;
    column-gap:10px;row-gap:2px}
  .dirlist li a{grid-column:1;grid-row:1}
  .dirlist li em{grid-column:1;grid-row:2;margin-top:0}
  .dirfee{grid-column:2;grid-row:1/3;align-self:center;text-align:right}
  .dirfeecomma{display:none}
}
/* THE TOWN SEARCH on /service-area/ (2026-09-03). The list under it is a hundred rows across
   eight counties, and the fastest path to one town is typing three letters of its name. */
.townfind{margin:14px 0 clamp(20px,2.4vw,28px)}
.townfind-lab{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.townfind-in{width:100%;max-width:520px;font-family:var(--sans);font-size:1rem;
  padding:13px 15px;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);color:var(--ink)}
.townfind-in:focus{outline:2px solid var(--accent);outline-offset:1px}
.townfind-msg{font-family:var(--sans);font-size:.95rem;color:var(--ink-2);margin:10px 0 0}
.townfind-hits{max-width:520px}
/* The county groups collapse on /service-area/ only, so every rule here is scoped to the summary
   a <details> has and none of them can reach the open list on /areas/. */
.countygroup summary{cursor:pointer;list-style:none;display:flex;align-items:baseline;gap:10px;
  padding:9px 0;border-bottom:1px solid var(--rule)}
.countygroup summary::-webkit-details-marker{display:none}
.countygroup summary::after{content:'+';margin-left:auto;font-family:var(--sans);
  font-weight:700;font-size:1.15rem;color:var(--accent-dark)}
.countygroup[open] summary::after{content:'-'}
.countygroup summary h2{display:inline;margin:0;font-size:clamp(1.15rem,1.7vw,1.4rem)}
.dirn{font-family:var(--sans);font-size:.9rem;color:var(--ink-2)}
.arealist{display:flex;flex-wrap:wrap;gap:9px;margin-top:14px}
.arealist a{font-family:var(--sans);font-size:.9rem;font-weight:600;text-decoration:none;
  color:var(--accent-dark);background:var(--card);border:1px dashed var(--ribbon-bg);
  border-radius:999px;padding:7px 15px;transition:border-color .2s ease,color .2s ease}
.arealist a:hover{border-color:var(--accent);border-style:solid;color:var(--ink)}
.pack h3 a{color:inherit;text-decoration:none}
.pack h3 a:hover{color:var(--accent-dark)}
/* photo row sitting under the narrow text block in the same About band */
.about-shots{margin-top:clamp(26px,3vw,40px)}
/* ---- two-way chooser: one engraved pumpkin, or the whole porch. Image led, no pricing. ---- */
.choices{display:grid;grid-template-columns:1fr 1fr;gap:clamp(18px,2.4vw,32px)}
.choice{position:relative;display:flex;flex-direction:column;border-radius:var(--radius);
  overflow:hidden;background:var(--card);border:1px solid var(--rule);box-shadow:var(--shadow);
  text-decoration:none;color:inherit;
  transition:transform .28s cubic-bezier(.2,.7,.3,1),box-shadow .28s ease,border-color .28s ease}
/* 1:1, not 4/3. The engraved card's photo is an upright pumpkin, and a landscape box cropped it
   to a zoomed middle band with the stem and the porch deck cut off. No 4/3 crop of that source
   could fix it: the pumpkin is 1164px tall inside a 1024px-wide frame, so a landscape crop that
   held all of it would need width the photograph does not have. Both cards were re-checked at
   1:1; the styled-porch shot survives the square crop with its steps and mums intact. */
.choice-img{position:relative;overflow:hidden;aspect-ratio:1;background:var(--bg)}
.choice-img img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .55s cubic-bezier(.2,.7,.3,1)}
/* warm scrim so the card reads as one object and the photo edge is never harsh */
.choice-img::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgba(51,38,27,.42),rgba(51,38,27,0) 58%);
  transition:opacity .28s ease}
.choice-body{display:flex;flex-direction:column;gap:9px;padding:clamp(18px,2vw,26px);flex:1}
.choice-body b{font-family:var(--display);font-weight:700;line-height:1.18;color:var(--ink);
  font-size:clamp(1.18rem,1.8vw,1.55rem)}
.choice-body em{font-style:normal;color:var(--ink-2);font-size:.97rem;flex:1}
.choice-go{font-family:var(--sans);font-weight:700;font-size:.92rem;color:var(--accent-dark);
  display:inline-flex;align-items:center;gap:9px;margin-top:2px}
.choice-go svg{transition:transform .28s ease;flex:none}
.choice:hover{transform:translateY(-5px);border-color:var(--accent);
  box-shadow:0 16px 36px rgba(51,38,27,.17)}
.choice:hover .choice-img img{transform:scale(1.07)}
.choice:hover .choice-img::after{opacity:.6}
.choice:hover .choice-go svg{transform:translateX(6px)}
.choice:focus-visible{outline:3px solid var(--accent);outline-offset:3px}
.split-img.tall img{aspect-ratio:4/5}
.shot figcaption{font-family:var(--hand);font-size:1.24rem;line-height:1.2;color:var(--accent-dark);
  transform:rotate(-.8deg);transform-origin:left}
/* package cards */
.packs{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap)}
.packs.g3{grid-template-columns:repeat(3,1fr)}
/* The engraved landing's offer panel, 2026-08-26 night. It reuses the /preorder/ .prehero grid
   wholesale (photo left, headline top right, the priced card bottom right, stacking to one
   column below 1100px) rather than inventing a second above-the-fold layout, because that grid
   was already measured against the fold on a phone. This replaced the item-12 layout, which put
   a pagehead, a four-up strip and a full-width photo above the price. */
.landing-offer .prehero-img img{width:100%;height:100%;object-fit:cover;display:block}
.landing-offer .prehero-head .sub{max-width:46ch}
/* Only on a band that has to clear the fold with a priced card in it. A 1280x800 laptop is the
   tightest real case: at the normal band pad the $65 CTA landed 47px under the fold. */
.band.tight{padding-block-start:clamp(24px,2.6vw,34px)}
.pack{position:relative;background:var(--card);border:1px solid var(--rule);border-radius:var(--radius);
  padding:clamp(20px,2vw,28px);display:flex;flex-direction:column;gap:14px;box-shadow:var(--shadow);
  transition:border-color .18s ease,transform .18s ease}
/* gap 9px -> 14px on 2026-09-04, his words off the packages page: "I want slightly more spacing
   between the Headline, the price, the start of the bullets, and the CTA. It's all very tight
   right now." One token moves all four, because the card is a flex column and nothing else
   separates those rows (the h3 is bare and carries margin:0 from the global heading rule). A
   margin on the h3 would rebuild a smaller version of the reserved heading block that was ripped
   out on 2026-09-03, so it is the gap and only the gap. The photo keeps its old distance from
   the name: .pack .packshot drops its 4px bottom margin to hold the pair together. */
.pack:hover{border-color:var(--accent);transform:translateY(-2px)}
/* NO heading reservation. It existed so a row of four cards lined their prices up, and with the
   subtitle slot gone on 2026-09-03 it was the last thing holding a visible gap between a package
   name and its price. A name that wraps at some middle width drops its own price a line; dead air
   on every card at every width was the worse trade. */
/* 2026-09-03: the subtitle slot AND the tier eyebrow came off every package card. The reserved
   two lines were the weird spacing on the row - two of the three cards printed an empty subtitle
   and held 2.7em of nothing between the name and the price. The heading reservation went with
   them, so a name now sits directly on top of its price. */
.pack .price{font-family:var(--display);font-weight:700;font-size:clamp(1.5rem,2.1vw,1.95rem);
  color:var(--accent-dark);line-height:1;font-variant-numeric:tabular-nums}
.pack .price span{font-size:.62em;vertical-align:.42em;margin-right:.04em}
.pack .blurb{color:var(--ink-2);font-size:.96rem;flex:1}
.pack ul{margin:0 0 5px;padding-left:19px;color:var(--ink-2);font-size:.96rem;flex:1;
  display:flex;flex-direction:column;gap:7px}
.pack .tag{position:absolute;top:0;left:clamp(20px,2vw,28px);transform:translateY(-50%);
  background:var(--accent);color:#fff;font-family:var(--sans);font-weight:700;font-size:.68rem;
  text-transform:uppercase;letter-spacing:.1em;padding:5px 11px;border-radius:var(--btn-radius)}
.pack.flag{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent);background:#fff}
.pack .btn-line{align-self:flex-start}
/* The order CTA on a package card: full card width, bigger type, real button weight. */
.pack .btn-order{align-self:stretch;margin-top:auto;padding:.92em 1.4em;font-size:1.04rem;
  font-weight:700;letter-spacing:.005em;box-shadow:var(--shadow)}
.pack .btn-order:hover{transform:translateY(-1px)}
/* split band (about, notes) */
.split{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(26px,3.6vw,56px);align-items:center}
.split.flip .split-img{order:2}
.split-img img{width:100%;aspect-ratio:3/2;object-fit:cover;border-radius:var(--radius);
  border:var(--photo-frame);padding:var(--photo-mat);background:var(--card);box-shadow:var(--shadow)}
.split-txt{display:flex;flex-direction:column;gap:14px;align-items:flex-start}
.split-txt p{color:var(--ink-2)}
/* What his "why isn't that in the orange color font" walk was really about (settled 2026-08-31
   after a wrong first fix put the script on the heading): the hand sub under the Mike-and-Doug
   heading NEVER rendered orange, because .split-txt p above outranks .hand on specificity and
   painted it ink. The heading stays the display serif; the hand line gets its accent back. */
.split-txt p.hand{color:var(--accent)}
.split-txt .strong{font-weight:700;color:var(--ink)}
/* FAQ */
.faq{border-top:1px solid var(--rule)}
.faq details{border-bottom:1px solid var(--rule)}
.faq summary{list-style:none;cursor:pointer;padding:18px 44px 18px 0;position:relative;
  font-family:var(--display);font-weight:600;font-size:1.06rem;color:var(--ink)}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:'';position:absolute;right:8px;top:50%;width:11px;height:11px;
  border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);
  transform:translateY(-70%) rotate(45deg);transition:transform .2s ease}
.faq details[open] summary::after{transform:translateY(-25%) rotate(225deg)}
.faq p{padding:0 44px 20px 0;color:var(--ink-2);max-width:70ch}
.faq p+ul.faqlist,.faq ul.faqlist+p{margin-top:0}
.faq p:has(+ul.faqlist){padding-bottom:8px}
.faq ul.faqlist{margin:0 0 14px;padding:0 44px 0 20px;color:var(--ink-2);max-width:70ch}
.faq ul.faqlist li{margin-bottom:7px}
.faq ul.faqlist b{color:var(--ink)}
/* Click-to-copy email (ruling 15). Hidden until a click copies the address, then it fades. */
.copymail{cursor:pointer}
.copynote{display:block;font-size:.88rem;line-height:1.4;margin-top:6px;color:inherit;opacity:0;
  transition:opacity .4s;max-width:36ch}
.copynote.show{opacity:.92}
/* order form */
.orderform{background:var(--card);border:1px solid var(--rule);border-radius:var(--radius);
  padding:clamp(20px,2.4vw,32px);box-shadow:var(--shadow)}
/* No leading numeral: the wizbar above already says which step this is, and the two flows now
   number differently (the engraved one has no package step), so a baked-in "3" was going to be
   wrong on one of them. */
/* Flow lock-in Phase 10 (2026-08-28), off the third full walk: the text at the start of each of
   these sections is "too subtle and small. It needs to grab more attention." One rule, so every
   step of both flows moves together. The floor is set for a 375 phone, where the form column is
   291px wide and the longest heading takes two lines. */
.formstep{font-family:var(--display);font-weight:700;
  font-size:clamp(1.42rem,1.02rem + 1.1vw,1.9rem);line-height:1.18;letter-spacing:-.012em;
  color:var(--ink);display:flex;align-items:center;gap:11px;margin-bottom:clamp(16px,1.6vw,20px)}
/* A SECTION START INSIDE A STEP (R12 and R27, 2026-09-04). His words: "all of these sections on
   the site where it says 'Bundle and Save' (it's the start of a new section), there needs to be
   more white space. It needs to be the same kind of size and prominence as the review and
   checkout font." So it is literally .formstep, the step heading itself, plus air above it. A
   step's OPENING heading is a bare .formstep and gets no top margin, which is why this is a
   modifier and not a change to the base rule. .addonrowhead is retired for section starts; it
   survives only where a line genuinely is a caption on one row. */
.formstep.sectionhead{margin-top:clamp(28px,3.4vw,40px)}
/* A heading row for the design step's own caption. It used to also carry a way-back control in the
   top right (Phase 7); FLOW-REVISION-PLAN Phase 29 (k15) removed that control, since the wizard's
   own Back button at the bottom already closes an open fork door and returns to the two doors, so
   this row is just the heading now. */
.stephead{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:6px 14px;margin-bottom:14px}
.stephead .formstep{margin-bottom:0}
.formnote{color:var(--ink-2);font-size:.95rem}
/* 2026-08-28, off the walk of the Phase 10 headings: the sub beneath a step heading takes the
   orange hand accent the rest of the site already uses. ONLY the note directly under a .formstep
   gets it - card blurbs, the ZIP line and the week note stay plain. .hand is declared hundreds of
   lines above .formnote, so a bare pair of classes loses the color and the size to the later
   rule; this puts both back rather than reordering a class every page depends on. The face is a
   display face on a rotation, which is why every one of these subs was SHORTENED to a phrase it
   can wear on one line in a 291px form column. */
.formnote.hand{color:var(--accent);font-size:clamp(1.5rem,2.5vw,2.1rem);line-height:1.05;
  margin-top:4px}
.hairline{height:1px;background:var(--rule);margin-block:clamp(22px,2.4vw,30px)}
.pickcard{cursor:pointer;position:relative}
.pickcard input{position:absolute;top:16px;right:16px;accent-color:var(--accent);width:18px;height:18px}
.pickcard:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
.opts{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.opt{display:flex;align-items:flex-start;gap:11px;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);padding:16px 18px;cursor:pointer}
.opt:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
.opt input{margin-top:4px;accent-color:var(--accent);width:18px;height:18px;flex:none}
.opt b{display:block;font-family:var(--display);font-size:1.02rem}
/* Its own line-height and its own block since 2026-08-28. Without one the description inside a
   radio card inherited the body's 1.6 and a sentence that wrapped read double-spaced. */
.opt em{font-style:normal;color:var(--ink-2);font-size:.92rem;display:block;margin-top:4px;line-height:1.4}
.field label{display:block;font-weight:700;font-size:.94rem;margin-bottom:7px}
.field input[type=text],.field input[type=email],.field input[type=tel]{width:100%;max-width:260px;padding:12px 13px;border:1px solid var(--rule);
  border-radius:var(--btn-radius);font-size:1rem;font-family:var(--sans);color:var(--ink);background:#fff}
.field input[type=text]:focus,.field input[type=email]:focus,.field input[type=tel]:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 2px rgba(208,98,27,.25)}
.field textarea{width:100%;max-width:420px;padding:12px 13px;border:1px solid var(--rule);
  border-radius:var(--btn-radius);font-size:1rem;font-family:var(--sans);color:var(--ink);background:#fff;
  resize:vertical;min-height:74px}
.field textarea:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 2px rgba(208,98,27,.25)}
/* The events form's own missing-fields note. Hidden by default; the browser's own required-field
   refusal is what stops a bad no-JS submit, and #missing exists as the redirect target for a
   server-side reject, revealed only when the URL fragment targets it. */
.sentnote{font-weight:600;color:var(--accent-dark)}
.sentnote:target{display:block}
#zip-msg,#checkout-error{min-height:1.3em;font-size:.9rem;margin-top:7px}
/* How can we reach you about your order: name/email/phone on the delivery step. */
.contacthead{font-family:var(--display);font-weight:600;font-size:1.02rem;color:var(--ink);margin-bottom:12px}
/* The wizard's consent box, restyled 2026-08-27 when the contact block moved onto the week step:
   it sits directly under the phone field it depends on, and it is drawn as a card so it reads as
   something being offered rather than as fine print at the bottom of a form. Scoped to the step,
   because the pre-order panel and the thank-you page use the same class in a flatter treatment.
   The LABEL TEXT is quoted in the Twilio A2P campaign and is not styling anyone may reword. */
#step-week .contactgrid{margin-bottom:14px}
#step-week .smsopt{margin-top:0;padding:13px 15px;border:2px solid var(--rule);border-radius:var(--radius);
  background:var(--ribbon-bg);color:var(--ink);font-weight:600}
#step-week .smsopt:has(input:checked){border-color:var(--accent)}
/* The second half of the 2026-08-28 split. The transactional box keeps the card treatment beside
   the phone field because it is the one that gates the step. The marketing box gates nothing, so
   it is drawn plainer and sits under it, and the two never read as one ask. */
#step-week .smsopt.smsopt-extra{margin-top:10px;border-width:1px;background:transparent;font-weight:400}
.smsdisc{color:var(--ink-2);font-size:.86rem;margin:8px 0 0;line-height:1.45;max-width:52ch}
/* No link here on purpose: nothing inside the wizard may send a paying customer somewhere
   else (the standing UX ruling, asserted by test-order-flow). The truncated flow footer carries
   privacy and terms, which is exactly why Phase 12 kept them in it. */
.smsfine{color:var(--ink-2);font-size:.84rem;margin:10px 0 0;line-height:1.45}
.contactgrid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px;margin-bottom:22px}
.contactgrid .field input{max-width:none}
@media(min-width:721px){
  /* FLOW-REVISION-PLAN Phase 11 (2026-09-08, j14+j16): scoped to #zipblock's own grid so the
     shared .contactgrid rule above (three other pages use its plain three-column default) never
     moves, and gated to the desktop width so the 720px collapse below still turns every field
     into one column on a phone without a specificity fight. Two columns, not three: street is
     the only field that stands alone, so it takes the full row and the other four pair up under
     it two at a time (apt with city, state with the ZIP box), the way /delivery-quote/'s own
     address block already wraps a street-through-ZIP run into one grid. */
  #zipblock .contactgrid{grid-template-columns:1fr 1fr}
  #zipblock .contactgrid>.field:first-child{grid-column:1/-1}
}
.smsopt{display:flex;align-items:flex-start;gap:10px;margin-top:-10px;cursor:pointer;
  color:var(--ink-2);font-size:.94rem;line-height:1.45;border:1px solid transparent;border-radius:var(--radius)}
.smsopt input{margin-top:2px;accent-color:var(--accent);width:18px;height:18px;flex:none}

.smsopt.nudge{border-color:var(--accent);background:var(--ribbon-bg);color:var(--accent-dark)}
/* Shown only on the Stripe cancel_url bounce, so an abandoned card page never reads as lost work. */
.resumenote{display:flex;align-items:center;gap:9px;font-family:var(--sans);color:var(--ink);
  font-size:.94rem;background:var(--ribbon-bg);border:1px solid var(--rule);border-radius:var(--radius);
  padding:13px 16px;margin-bottom:20px}
.resumenote::before{content:'';width:7px;height:7px;border-radius:50%;background:var(--support);flex:none}
/* THE WEEK PICKER. Chips (2026-08-27) became a select, and the select became CARDS in Phase 4a
   (2026-09-02) on his mockup mark 4. A select shows one week at a time, so the number saying how
   full a week is only ever arrived for the week already chosen; on a card every week states its
   own. The count tile sits on the LINE BELOW the date, never inline beside it - his mark, and the
   reason is that a tile inline reads incongruous against "Week of Nov 2" and wraps against
   "Week of Sept 28". .weekmain is what puts it there, and it is what lets a per-week note
   sit between the two without an empty grid row on every other card. The only note the grid
   ever carried was Thanksgiving, and that week left the season with R14 (2026-09-04), so
   WEEK_NOTES bakes empty today; the rule stays because the shape is what holds the layout. */
/* MONTH FIRST (R15, 2026-09-04). #week-grid stops being the card grid and becomes a stack of
   month disclosures; .weekgrid moves INSIDE each one, so every .weekcard rule below is untouched
   and the two-column rule still applies to cards rather than to months. */
.monthstack{display:flex;flex-direction:column;gap:10px}

.monthstack[hidden]{display:none}
#clear-week-head[hidden]{display:none}
.monthbox{border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);
  overflow:hidden}
.monthbox[open]{border-color:var(--accent)}
.monthsum{display:flex;align-items:center;gap:10px;padding:14px 16px;cursor:pointer;
  list-style:none;font-family:var(--display);font-weight:600;font-size:1.08rem;color:var(--ink)}
.monthsum::-webkit-details-marker{display:none}
/* The chevron is drawn, not a glyph, so it turns with the disclosure and needs no icon font. */
.monthsum::after{content:'';margin-left:auto;width:9px;height:9px;flex:none;
  border-right:2px solid var(--ink-2);border-bottom:2px solid var(--ink-2);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .16s ease}
.monthbox[open] .monthsum::after{transform:rotate(-135deg) translate(-2px,-2px)}
.monthsum b{font-weight:600}
.monthn{font-family:var(--sans);font-weight:600;font-size:.82rem;color:var(--ink-2)}
.monthbox[open] .monthsum{border-bottom:1px solid var(--rule)}
.monthbox .weekgrid{padding:13px 14px 15px}
@media (prefers-reduced-motion:reduce){.monthsum::after{transition:none}}
.weekgrid{display:grid;gap:9px}
/* Two columns once there is room for them. The season is eleven weeks long, and eleven
   full-width cards on a desktop is a column of mostly empty card. The phone stays one per row,
   which is the width the picker was drawn at. */
@media(min-width:700px){.weekgrid{grid-template-columns:1fr 1fr}}

.weekcard{display:grid;grid-template-columns:auto 1fr;align-items:start;gap:0 11px;cursor:pointer;
  border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);padding:13px 15px}

.weekcard[hidden]{display:none}
.weekcard input{accent-color:var(--accent);width:17px;height:17px;margin:2px 0 0}
.weekmain{display:flex;flex-direction:column;align-items:flex-start;gap:5px;min-width:0}
.weekcard b{font-family:var(--display);font-weight:600;font-size:1rem;color:var(--ink)}
.weekcard small{color:var(--ink-2);font-size:.86rem}
.weekcard:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
/* The count tile. Green while a week is a long way off, amber inside two weeks, cranberry inside
   ten days, and the flat tan once it is off the board. The colours carry no information the words
   do not: a reader who sees none of them still reads the number and the state. */
.weekcount{font-size:.82rem;font-weight:700;padding:3px 9px;border-radius:999px;
  background:#E7EBDE;color:#41522F}
/* j65, FLOW-REVISION-PLAN Phase 10: the clear-away grid's own weeks carry no scarcity (weekBand's
   noScarcity flag), so band.text is always the empty string there and renderWeeksInto still emits
   the span - CSS is what hides it, never the markup, so the delivery grid's own weekcount (its
   band.text is never empty) is untouched and test-order-flow's own reads of it still find text. */
.weekcount:empty{display:none}
.weekcard.mid .weekcount{background:#F6E7C9;color:#8A5A12}
.weekcard.low .weekcount{background:#F6DCD9;color:var(--timer)}
.weekcard.closed{opacity:.62;cursor:default}
.weekcard.closed .weekcount{background:#E4DAC7;color:var(--ink-2)}
/* .weekblock (the hairline wrapper the clear-away and the contact fields both used to sit under)
   left with both of them: the clear-away is its own screen since Phase Q11 and the contact fields
   became their own step in D1. Nothing on the site uses the class any more. */
/* THE HAUL SCREEN'S OWN PRICE LINE DIED HERE (j40, FLOW-REVISION-PLAN Phase 13a): the card just
   below already carries the same $75 (data-price-for="removal-75"), so this was the number
   printed twice on one screen. The card's own price stays; only this standalone line is gone. */
/* #haul-note and the spacing rule that scoped to it both left in FLOW-REVISION-PLAN Phase 31
   (k32): the note is cut (see haulScreen()'s own comment), and .formstep's ordinary margin-bottom
   is the right amount of air above the wipe slider that now follows it directly - .typair carries
   no top margin of its own, so there is nothing here left to double up against. */

/* ---------------- Frame A: the product page carries the engraving (ADS-PLAN.md Phase 4, then
   Phase 4b) ----------------------------------------------------------------------------------
   Ported off /home/pumpkin/workspace/data/frame-a-spec.html's .pdp block, first to his marks at
   F1b and now to his further marks at F1c plus the GATE F1c answer riding on top of them - the
   spec already carries both rounds, so the numbers below are ported whole rather than split into
   two passes. Classes prefixed pdp- where the mockup's own name (.choice, .fieldlab,
   .linefields...) already means something else sitewide; the container-level names (.pdp,
   .pdpgallery, .pdpbuy, .pdppresets, .pdpmore) were free and keep the mockup's own names, per its
   own comment 2. Container queries in the spec are ordinary media queries here, at the spec's own
   720/1100 numbers. */
/* GATE F1c mark 1: "we can introduce more white space ... everything is so tight and so
   compressed that I just think it's tough for a cold new user coming in to ingest everything." */
.pdp{display:grid;grid-template-columns:1fr;gap:44px}
@media (min-width:720px){.pdp{gap:38px}}
@media (min-width:1100px){.pdp{gap:52px}}
/* #design-form wraps every grid child (the gallery, the buy column, the presets, the questions)
   so ALL of it - not just the buy column - answers to one form element and one JS scope, which is
   what lets a ready-made design picked in .pdppresets and a style picked in .pdpbuy read off the
   same radio groups. display:contents takes the form itself out of the grid so its children still
   place by .pdpgallery/.pdpbuy/.pdppresets/.pdpmore below, exactly as if it were not there. */
#design-form{display:contents}
/* HIS MARK AT F1b, the whole reason this grid exists: "on the desktop version there is a huge
   blank bottom-left quadrant... On the tablet view there is just this big gap where the images
   go." GATE F1c mark 9 puts the tablet placement BACK after Phase 2c had swapped it to fill the
   column by height, which quietly reversed this same F1b instruction: "the common questions we
   get should not go down there, down below the image previews ... having the common questions
   we get above [the presets heading] doesn't feel right." The designs sit under the gallery, two
   across, and the questions run full width at the foot. */
@media (min-width:720px){
  .pdp{grid-template-columns:minmax(0,1.04fr) minmax(0,1fr);align-items:start}
  .pdpgallery{grid-column:1;grid-row:1}
  .pdpbuy{grid-column:2;grid-row:1/span 2}
  .pdppresets{grid-column:1;grid-row:2}
  .pdppresets .presetgrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
  .pdpmore{grid-column:1/-1;grid-row:3;margin-top:6px}
}
/* At desktop the questions move UNDER THE BUY COLUMN and the gallery spans both rows, so column 1
   is tall by design and column 2 has no hole under its button. The ready-made designs then run
   the full width at the foot. */
@media (min-width:1100px){
  .pdp{grid-template-columns:minmax(0,1.3fr) minmax(0,1fr)}
  .pdpgallery{grid-column:1;grid-row:1}
  .pdpbuy{grid-column:2;grid-row:1}
  .pdppresets{grid-column:1/-1;grid-row:2;margin-top:10px}
  .pdppresets .presetgrid{grid-template-columns:repeat(4,minmax(0,1fr));gap:18px}
  .pdpmore{grid-column:1/-1;grid-row:3;margin-top:10px}
}
.pdpmore{min-width:0}
/* GATE F1c mark 2 and its own GATE answer: the section labels ("Engraving font style", "Pumpkin
   color") and these two block headings read at the same size now, his own H1's, so scrolling
   between sections is obvious. CLAUDE'S CALL, recorded as Claude's: .pdp-h2 already served both
   the presets heading and the questions heading, so it carries the spec's .acchead/.presethead
   numbers directly rather than splitting into two classes with one rule between them. */
.pdp-h2{margin:0 0 20px;font-family:var(--display);font-weight:700;font-size:1.6rem;
  line-height:1.2;letter-spacing:-.01em}
@media (min-width:720px){.pdp-h2{margin-bottom:18px;font-size:1.32rem}}
@media (min-width:1100px){.pdp-h2{font-size:1.46rem}}

/* HIS MARK AT F1b: "in both tablet and desktop the different images should not appear on the
   left-hand side, they should appear below the primary image that is selected." */
.pdp-gwrap{display:grid;grid-template-columns:1fr;gap:14px}
.pdp-gmain{position:relative;border-radius:var(--radius);overflow:hidden;border:1px solid var(--rule);
  background:var(--card)}

.pdp-gmain img{display:block;width:100%;aspect-ratio:4/5;object-fit:contain}
.pdp-gdots{position:absolute;left:0;right:0;bottom:10px;display:flex;justify-content:center;gap:6px}
.pdp-gdots i{display:block;width:6px;height:6px;border-radius:50%;background:rgba(253,250,243,.55)}
.pdp-gdots i.on{background:#FDFAF3}


.pdp-gtag{position:absolute;top:10px;right:10px;max-width:40%;text-align:right;
  background:rgba(253,250,243,.92);border:1px solid var(--rule);border-radius:8px;padding:6px 10px;
  box-shadow:0 1px 5px rgba(27,18,10,.16)}
.pdp-gtag b{display:block;font-family:var(--display);font-weight:700;font-size:.86rem;line-height:1.25}
.pdp-gtag em{display:block;font-style:normal;font-size:.7rem;color:var(--ink-2);line-height:1.25;margin-top:2px}

@media (max-width:720px){
  .pdp-gtag{max-width:30%;padding:4px 7px;border-radius:6px;top:7px;right:7px}
  .pdp-gtag b{font-size:.68rem;line-height:1.2}
  .pdp-gtag em{font-size:.56rem;line-height:1.2;margin-top:1px}
}
/* The arrows are tablet and desktop only, and the back arrow stays hidden until the gallery has
   moved once - his mark at F1b, "the left or backwards-facing arrow only appears once they
   scroll to the right one time." .pdp-gwrap.moved is set by the first tap on next. */
.pdp-garrow{display:none}
@media (min-width:720px){
  .pdp-garrow{display:flex;align-items:center;justify-content:center;position:absolute;top:50%;
    transform:translateY(-50%);width:38px;height:38px;padding:0;border-radius:50%;
    background:rgba(253,250,243,.92);border:1px solid var(--rule);color:var(--ink);cursor:pointer;
    box-shadow:0 1px 5px rgba(27,18,10,.16)}
  .pdp-gprev{left:10px}
  .pdp-gnext{right:10px}
  .pdp-gwrap:not(.moved) .pdp-gprev{display:none}
}

.pdp-gthumbs{display:flex;gap:11px;overflow-x:auto;padding-bottom:3px;
  background:
    linear-gradient(to left,var(--bg) 0,var(--bg) 60%,rgba(0,0,0,0) 100%) 100% 0/28px 100% no-repeat local,
    linear-gradient(to left,rgba(51,38,27,.16),rgba(51,38,27,0)) 100% 0/20px 100% no-repeat scroll}
.pdp-gthumb{flex:none;width:64px;padding:0;background:none;border-radius:7px;overflow:hidden;
  border:1px solid var(--rule);cursor:pointer}
@media (min-width:720px){.pdp-gthumb{width:76px}}
.pdp-gthumb.on{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
.pdp-gthumb img{display:block;width:100%;aspect-ratio:1/1;object-fit:cover}

.pdpbuy{display:flex;flex-direction:column;gap:40px;min-width:0}
@media (min-width:720px){.pdpbuy{gap:26px}.pdpbuy .hairline{margin-block:-13px}}
.pdpbuy form{display:flex;flex-direction:column;gap:40px}
@media (min-width:720px){.pdpbuy form{gap:26px}}
.pdpbuy h1{margin:0;font-family:var(--display);font-weight:700;font-size:1.6rem;line-height:1.12;
  letter-spacing:-.015em}
@media (min-width:1100px){.pdpbuy h1{font-size:1.95rem}}
.pdp-pricerow{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;margin-top:-4px}
.pdp-price{font-family:var(--display);font-weight:700;font-size:1.8rem;color:var(--accent-dark);
  line-height:1}
.pdp-pricenote{font-size:.9rem;color:var(--ink-2)}
.pdp-delivery-note{font-size:.9rem;color:var(--ink-2);margin:0}

.pdp-delivery-note:empty{display:none}


.pdpbuy .hairline{margin-block:-20px}

/* HIS MARK AT F1b: "the little text above that, the size of the font for style and colour...
   can be made bigger and more prominent." GATE F1c answer mark 10, mobile only: ".choice 14 to
   18." */
.pdp-choice{display:flex;flex-direction:column;gap:18px}
@media (min-width:720px){.pdp-choice{gap:14px}}
/* GATE F1c mark 2 and its own GATE answer, mark 11: "the size of the font for engraving font
   style, pumpkin color and what it says is too small ... it should be the same size as the font
   that says See your pumpkin engraved before you order." That is the H1, 1.6rem on a phone, so
   that is the number here too. Tablet and desktop keep what they already had. */
.pdp-fieldlab{display:block;font-family:var(--display);font-weight:700;font-size:1.6rem;
  line-height:1.2;letter-spacing:-.008em}
@media (min-width:720px){.pdp-fieldlab{font-size:1.24rem}}
@media (min-width:1100px){.pdp-fieldlab{font-size:1.34rem}}

/* GATE F1c mark 3: "I don't like how the Kreiders is now emblazoned onto this website ... I kind
   of preferred what we had before: at least some descriptive element to the engraving font
   style." One option per row instead of three cramped columns: the site's own photograph of that
   style, cropped to the lettering, the name in the display face, and one sentence saying what it
   is for. No invented surname is set as type. */
.pdp-styrow{display:grid;gap:11px}
.pdp-sty{position:relative;display:flex;align-items:center;gap:14px;border:1px solid var(--rule);
  border-radius:10px;background:var(--card);padding:10px 14px 10px 10px;cursor:pointer;min-width:0}
.pdp-sty input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.pdp-styimg{width:70px;height:76px;flex:none;border-radius:8px;overflow:hidden;
  border:1px solid var(--rule)}
.pdp-styimg img{display:block;width:100%;height:100%;object-fit:cover}
.pdp-stytext{display:flex;flex-direction:column;gap:3px;min-width:0}
.pdp-stytext b{font-family:var(--display);font-size:1.04rem;line-height:1.2;font-weight:700}
.pdp-stytext em{font-style:normal;font-size:.88rem;line-height:1.35;color:var(--ink-2)}
.pdp-sty:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent);background:#fff}
.pdp-sty:has(input:checked) .pdp-stytext b{color:var(--accent-dark)}

/* GATE F1c answer mark 10, mobile only: ".dotrow to 20px 26px." Mark 12: "the pumpkin color
   portion needs to be more distinctive, maybe by expanding the size of those little swatches of
   color" - 32px to 56px at every width, the name under each up to .88rem. */
.pdp-dotrow{display:flex;flex-wrap:wrap;align-items:flex-start;gap:20px 26px}
@media (min-width:720px){.pdp-dotrow{gap:14px 30px}}
.pdp-dotpick{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;
  max-width:96px;cursor:pointer}
.pdp-dotpick input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.pdp-dotpick em{font-style:normal;font-size:.88rem;font-weight:600;color:var(--ink-2);line-height:1.2;
  text-align:center}
.pdp-dotpick:has(input:checked) em{color:var(--accent-dark)}
.pdp-dot{display:block;width:56px;height:56px;border-radius:50%;overflow:hidden;border:1px solid var(--rule)}
.pdp-dot img{display:block;width:100%;height:100%;object-fit:cover}
.pdp-dotpick:has(input:checked) .pdp-dot{box-shadow:0 0 0 2px var(--accent);border-color:var(--accent)}

/* One line per row at every width, the wrong rhythm being three across and short (round two).
   GATE F1c answer mark 10, mobile only: ".linefields 16 to 18." */
.pdp-linefields{display:grid;gap:18px}
@media (min-width:720px){.pdp-linefields{gap:16px}}

.pdp-linefields .field label{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;font-weight:700;
  font-size:.94rem;margin-bottom:8px}
.pdp-linefields .field input{width:100%;max-width:none}
.pdp-optnote{flex-basis:100%;font-weight:400;color:var(--ink-2)}
.pdp-optnote:empty{display:none}
.pdp-cc{margin-left:auto;font-weight:600;font-size:.78rem;color:var(--ink-2);
  font-variant-numeric:tabular-nums}

/* GATE F1c mark 6 DELETES the alternate CTA outright: "we should not have an alternate CTA of
   See it on a pumpkin first. It was offering exactly what the button now does." .pdp-ctanote and
   .pdp-seeit go with it. */

.presetgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
@media (min-width:560px){.presetgrid{grid-template-columns:repeat(4,minmax(0,1fr))}}

.preset{display:flex;flex-direction:column;gap:12px;cursor:pointer}

.presetgo{display:flex;flex-direction:column;gap:12px;color:inherit;text-decoration:none}
.presetbuy{width:100%;margin-top:2px}
.preset input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.pr{aspect-ratio:1/1;border-radius:8px;border:1px solid var(--rule);display:block;width:100%;
  object-fit:cover}
.preset b{font-family:var(--display);font-size:.82rem;line-height:1.2;font-weight:600}

.preset:has(input:checked) .pr{box-shadow:0 0 0 2px var(--accent) inset}

.preset .pr{transition:transform .18s cubic-bezier(.2,.7,.3,1)}
.preset:hover .pr{transform:scale(1.03)}

.proofband{border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:14px 0;
  display:grid;gap:8px}
.proofband .proofcard{background:var(--card);border:1px solid var(--rule);border-radius:8px;
  padding:11px 12px}
.proofband .proofcard em{display:block;font-style:normal;color:var(--ink-2)}
.proofband .proofcard b{display:block;font-size:.82rem;color:var(--ink-2);margin-top:8px}
.proofband .proofphoto{display:block;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:6px;margin-bottom:8px}

/* engraving design step */

/* UX review item 20 (2026-08-26): these three fields were laid out as a 340px column because a
   live preview panel used to sit to their right. That panel is gone and nothing was re-laid out
   after it, so a narrow stack sat orphaned under a full-width row of style tiles and, in his
   words, looked very cheap. One field per lettering line, spanning the form, the same rhythm as
   the tiles above them. */
/* The pumpkin colour swatches (Phase 19). Amazon's shape: a row of circles, the chosen one wearing
   a ring, its name under it. The circle is a photograph of that colour's own base pumpkin, so the
   ring has to sit OUTSIDE the image rather than as a border on it - a border would eat two pixels
   of skin and make the selected swatch a different size from its neighbours. Two stacked shadows
   do it: one in the card colour to hold the gap, one in the accent to draw the ring. */
.swatchrow{margin-top:clamp(18px,2.2vw,24px)}
/* Weight 700 and a bigger disc since 2026-09-03. Doug, on the 2026-09-02 call: "it's not big
   enough, right? It's not prominent enough. I'm not even going to see that." */
.swatchlab{display:block;font-family:var(--sans);font-weight:700;font-size:1.06rem;color:var(--ink);
  margin-bottom:10px}
/* Three across, ALWAYS one row, and that is why the cards flex instead of carrying a fixed width.
   A fixed 88px was fine on the design step and wrapped inside pumpkin two's panel, which is the
   same control in a card with its own padding: three 88px cards plus gaps want 292px and that
   panel offers about 259px at 375. Sharing whatever width there is costs nothing and cannot wrap. */
.swatches{display:flex;gap:clamp(10px,2vw,22px)}
.swatch{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;
  flex:1 1 0;min-width:0;max-width:140px;cursor:pointer;text-align:center}
.swatch input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.swatch img{display:block;width:96px;height:96px;border-radius:50%;object-fit:cover;
  box-shadow:0 0 0 1px var(--rule)}
/* The ring is accent-DARK where every other selected state on this flow is accent. The reason is
   the one swatch it has to work on: an accent-orange ring around a photograph of orange pumpkin
   skin is a ring the same colour as the thing it is circling. The deeper rust separates from all
   three skins, and the label under the circle changes weight and colour on the same beat, so the
   selected state is never carried by one signal alone. */
.swatch:has(input:checked) img{box-shadow:0 0 0 3px var(--card),0 0 0 5px var(--accent-dark)}
.swatch:has(input:focus-visible) img{box-shadow:0 0 0 3px var(--card),0 0 0 5px var(--ink)}
.swatch b{font-family:var(--sans);font-weight:600;font-size:.82rem;line-height:1.25;color:var(--ink-2)}
.swatch:has(input:checked) b{color:var(--ink)}
/* The display flow's colour MIX, which is a different question with a different answer shape: not
   one skin but a spread across a package's worth of pumpkins, so each option pictures three
   circles instead of one and reads as a card rather than as a chip. */
.mixrow{margin-top:clamp(24px,3vw,34px)}
.mixes{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.mix{position:relative;display:block;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);padding:16px 16px 18px;cursor:pointer}
.mix:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
.mix input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
/* THE PALETTE TILE'S PICTURE (2026-09-03). It bleeds to the card edge, so the card reads as the
   porch it is selling rather than as a form control with a thumbnail on it. */
.mixpic{display:block;margin:-16px -16px 12px;border-radius:calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  overflow:hidden;aspect-ratio:3/2;background:var(--ribbon-bg)}
.mixpic img{display:block;width:100%;height:100%;object-fit:cover}
.mixplus{display:inline-block;margin-left:7px;font-family:var(--sans);font-size:.8rem;font-weight:700;
  color:var(--accent-dark);vertical-align:1px}
/* The mum row was built for an add-on card, where it stays hidden until the card is ticked. Under
   the palette there is no card to tick: the package already contains its mums, so the question is
   always live and the row is always shown. */
.mixrow .mumpref{display:block;margin-top:clamp(18px,2vw,24px)}
/* The contents, under the palette's name. Overlapped so two discs read as one pair rather than
   as two more things to choose. */
.mixdots{display:flex;margin:8px 0 0}
.mixdots img{display:block;width:34px;height:34px;border-radius:50%;object-fit:cover;
  box-shadow:0 0 0 2px var(--card)}
.mixdots img + img{margin-left:-10px}
.mix b{display:block;font-family:var(--display);font-size:1.02rem;line-height:1.2;color:var(--ink)}
.mix em{display:block;font-style:normal;font-size:.88rem;line-height:1.4;color:var(--ink-2);margin-top:5px}
.linefields{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;grid-template-rows:auto auto}
.linefields .field input[type=text]{max-width:100%}

.linefields .field{display:grid;grid-template-rows:subgrid;grid-row:span 2;gap:0}
.linefields .cap{display:flex;justify-content:space-between;align-items:flex-end;gap:10px;
  min-height:2.6em}
@supports not (grid-template-rows:subgrid){.linefields .cap label{min-height:2lh}}

.linefields .capname,.linefields .capopt{display:block}
.linefields .capopt{font-weight:400;color:var(--ink-2)}

.linefields .capopt:empty::before{content:"\00a0"}
.linefields .count{font-size:.82rem;color:var(--ink-2);font-variant-numeric:tabular-nums}
.linefields .count.over{color:var(--accent-dark);font-weight:700}
.stage{border:1px solid var(--rule);border-radius:var(--radius);background:var(--ribbon-bg);
  aspect-ratio:2/3;max-width:380px;width:100%;margin-inline:auto;position:relative;overflow:hidden;
  display:flex;align-items:center;justify-content:center;text-align:center}
.stage img{width:100%;height:100%;object-fit:cover;display:block}
/* The loading beat. The bar is honest about being an estimate: it eases toward the end and
   waits there rather than pretending to finish on a schedule it does not know. */
.stage .load{position:absolute;inset:0;background:var(--ribbon-bg);display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:14px;padding:0 24px}
.stage .load p{font-family:var(--display);color:var(--ink);font-size:1.02rem;min-height:2.6em}
.stage .load small{color:var(--ink-2);font-size:.85rem}

.stage .bar{width:min(220px,80%);height:11px;border-radius:6px;background:var(--rule);overflow:hidden}
.stage .bar i{display:block;height:100%;width:0;background:var(--accent);border-radius:6px;
  transition:width .9s linear}




@media(min-width:980px){
  .previewsplit{display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,1fr);gap:38px;align-items:start}
  .previewsplit:not(:has(.stage.selling)) .stage{max-width:none;margin-inline:0}
}

@media(min-width:1100px){
  .pdp.previewsplit{grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);gap:52px}
}

@media(max-width:979px){
  #design-preview .previewsplit-info{margin-top:clamp(18px,2.4vw,24px)}
}
.previewsplit:has(.stage.selling){display:block}

.stage.selling{aspect-ratio:auto;height:auto;max-width:520px}



@media(min-width:980px){
  #step-preview .previewsplit-media .stage,
  #design-preview .previewsplit-media .stage,
  .extradesign .previewsplit-media .stage{height:clamp(300px,calc(100vh - 300px),640px);
    width:auto;max-width:100%;margin-inline:auto}
  
  #step-preview .previewsplit-media .stage.selling,
  #design-preview .previewsplit-media .stage.selling,
  .extradesign .previewsplit-media .stage.selling{height:auto;width:100%;max-width:520px;
    margin-inline:auto}
}


#step-preview .previewdetails-lead, #design-preview .previewdetails-lead,
.extradesign .previewdetails-lead{color:var(--ink);
  font-weight:600;font-size:clamp(1.05rem,0.98rem + 0.35vw,1.2rem);margin:18px 0 10px}
#step-preview .previewdetails ul, #design-preview .previewdetails ul,
.extradesign .previewdetails ul{margin:0 0 22px;padding-left:1.2em;
  color:var(--ink-2);font-size:1.05rem;line-height:1.5}
#step-preview .previewdetails li, #design-preview .previewdetails li,
.extradesign .previewdetails li{margin-bottom:10px}

#step-preview .engrave-actions, #design-preview .engrave-actions{justify-content:space-between}

.reviewcar{margin-top:22px;text-align:center}

#design-preview .reviewcar,.extradesign .reviewcar{margin-top:44px}
.reviewcar-head{font-family:var(--sans);font-weight:700;font-size:.82rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-2);margin:0 0 12px}
.reviewcar-view{display:flex;align-items:center;gap:6px}
.reviewcar-track{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;flex:1;min-width:0;
  scrollbar-width:none;-ms-overflow-style:none;scroll-behavior:smooth}
.reviewcar-track::-webkit-scrollbar{display:none}
.revquote{flex:0 0 100%;min-width:100%;scroll-snap-align:center;scroll-snap-stop:always;
  padding:0 2px}

.reviewcar-arrow{display:none}
@media(min-width:900px){
  .reviewcar-arrow{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
    width:30px;height:30px;border-radius:50%;border:1px solid var(--rule);background:var(--card);
    color:var(--ink-2);padding:0;cursor:pointer}
  .reviewcar-arrow:hover{color:var(--accent-dark);border-color:var(--accent)}
}
.reviewstars{display:flex;gap:2px;color:var(--accent);margin-bottom:8px;justify-content:center}
.reviewtext{color:var(--ink);font-size:.98rem;line-height:1.5;margin:0 0 8px}
.reviewwho{color:var(--ink-2);font-size:.86rem;font-weight:600;margin:0}
.reviewdots{display:flex;justify-content:center;gap:7px;margin-top:14px}
.reviewdots button{width:7px;height:7px;border-radius:50%;background:var(--rule);border:0;
  padding:0;cursor:pointer}
.reviewdots button.on{background:var(--accent);transform:scale(1.35)}
/* THE FAILURE BLOCK (2026-09-03). It sits where the picture would have been, so a preview that
   did not arrive is exactly as visible as one that did. What shipped before dropped the customer
   back on the form with a sentence in #render-error that the next keystroke erased, which is why
   a failed preview looked like nothing at all had happened. This block stays put. */
.wizfail{border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);
  padding:clamp(18px,2.4vw,26px);text-align:center;max-width:380px;margin:0 auto}
.wizfail-head{font-family:var(--display);font-weight:700;font-size:1.06rem;color:var(--ink);margin:0}
.wizfail-body{font-family:var(--sans);font-size:.94rem;line-height:1.5;color:var(--ink-2);
  margin:10px 0 0}
.wizfail-acts{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;align-items:center;
  margin-top:18px}
.wizfail-skip{font-family:var(--sans);font-weight:600;font-size:.92rem;color:var(--ink-2);
  background:none;border:0;padding:6px 2px;cursor:pointer;text-decoration:underline;
  text-underline-offset:3px}
.wizfail-skip:hover{color:var(--ink)}

.stage .loadshow{position:static;width:100%;justify-content:flex-start;gap:0;padding:0 0 16px}
/* Phase 7: the status block leads, so it needs its own padding back (.loadshow strips the side
   padding for the full-bleed slideshow underneath it). */
.stage .loadtop{display:flex;flex-direction:column;align-items:center;gap:11px;
  width:100%;padding:20px 20px 16px}
.stage .load p.loadhead{font-family:var(--display);font-size:1.06rem;min-height:0;margin:0}

.stage .load p.load-stage{font-family:var(--sans);font-size:.86rem;color:var(--ink-2);
  min-height:0;margin:0}

.stage .load .waitcopy{width:100%;display:flex;flex-direction:column;align-items:center;
  justify-content:center}
.stage .load .waitcopy p{font-family:var(--sans);font-size:.94rem;line-height:1.5;color:var(--ink-2);
  margin:0;max-width:42ch;text-align:center}
.stage .load .waitcopy p[hidden]{display:none}

.showpix{position:relative;flex:none;width:100%;aspect-ratio:1;overflow:hidden}
.showpix .showslide{position:absolute;inset:0;opacity:0;transition:opacity .8s ease}
.showpix .showslide.on{opacity:1}
.showpix .showslide img{width:100%;height:100%;object-fit:cover;display:block}

.showpix .showslide .showreview{width:100%;height:100%;box-sizing:border-box;display:flex;
  flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:20px 22px;
  background:var(--card);text-align:center;overflow-y:auto}

.showreview .reviewstars{margin-bottom:0}
.showreview .reviewtext{font-size:clamp(.82rem,.62rem + .9vw,1.06rem);line-height:1.5;margin:0;max-width:36ch}
.showreview .reviewwho{font-size:clamp(.78rem,.68rem + .35vw,.92rem)}
.showtag{position:absolute;left:12px;bottom:12px;z-index:2;background:rgba(28,19,11,.72);color:#F1E6D4;
  font-family:var(--sans);font-size:.8rem;font-weight:600;padding:5px 11px;border-radius:4px}
.stage .load p.showhead{font-size:1.14rem;line-height:1.3;min-height:0;max-width:26ch;padding:0 18px;
  margin:14px 0 0}
.previewnote{color:var(--ink-2);font-size:.88rem;margin-top:10px;max-width:38ch;margin-inline:auto;
  text-align:center}
/* Pumpkin two's engraving (Phase 13, 2026-08-28), on the extras step directly under the card that
   sells it. A bordered, tinted panel rather than a bare run of fields, because it opens INSIDE a
   menu of add-on cards and has to read as one thing that belongs to the card above it. It reuses
   .pdp-styrow, .linefields and .stage wholesale (FLOW-REVISION-6 Phase 2 put the style row on this
   shared form too), so pumpkin two is asked the same question in the same shape as pumpkin one and
   neither can quietly drift from the other.
   The [hidden] rule is declared: nothing here sets display, but the panel is revealed and hidden
   by script and an attribute that loses to a later rule is the trap .addonflag was written for. */
.secondesign{margin-top:clamp(18px,2.2vw,26px);border:2px solid var(--rule);
  border-radius:var(--radius);background:var(--ribbon-bg);padding:clamp(16px,2vw,22px)}
.secondesign[hidden]{display:none}
.secondesign .formstep{margin-bottom:4px}
.secondesign .pdp-styrow{margin-top:14px}
.secondesign .stage{margin-top:16px}
/* Pumpkin three through nine (FLOW-REVISION-PLAN Phase 15, 2026-09-08) reuse .secondesign's own
   box wholesale - it is a panel for one pumpkin's engraving either way - and carry .extradesign
   alongside it as the hook for the two states below, which apply to every panel including pumpkin
   two's own .secondesign box. */

.extradesign.rendering .pdp-styrow,.extradesign.rendering .swatchrow,.extradesign.rendering .linefields,
.extradesign.rendering .renderrow,.extradesign.rendered .pdp-styrow,.extradesign.rendered .swatchrow,
.extradesign.rendered .linefields,.extradesign.rendered .renderrow,.extradesign.approved .pdp-styrow,
.extradesign.approved .swatchrow,
.extradesign.approved .linefields,.extradesign.approved .renderrow{display:none}

.extradesign.approved .formnote, .extradesign.rendering .formnote,
.extradesign.rendered .formnote, .extradesign.rendered .previewnote,
.extradesign.approved .previewnote, .extradesign.rendering .previewnote{display:none}

.extradesign.approved .stylab, .extradesign.rendering .stylab,
.extradesign.rendered .stylab{display:none}

.extradesign .stage{max-width:none;margin-inline:0}


.extradesign.rendered .engrave-actions:not(.renderrow),
.extradesign.approved .engrave-actions:not(.renderrow){justify-content:space-between;
  align-items:center}

.approvedchip{display:inline-flex;align-items:center;gap:6px;color:var(--support);
  font-family:var(--sans);font-weight:700;font-size:.86rem}
.approvedchip[hidden]{display:none}

#included-panels .secondesign{margin-top:0;border:0;background:none;padding:0;border-radius:0}


#included-panels .secondesign > .formstep{margin-bottom:clamp(20px,2.4vw,32px)}
#included-panels .secondesign .pdp-styrow{margin-top:10px}

.extradesign .stylepic{position:relative;border-radius:var(--radius);overflow:hidden;
  background:var(--ribbon-bg)}
.extradesign .stylepic img{display:block;width:100%;height:auto}

.extradesign .stylab{display:block;margin-bottom:2px}

@media(max-width:979px){
  .extradesign .previewsplit-info{margin-top:clamp(18px,2.4vw,24px)}
}

.extradesign.rendered .stylepic,.extradesign.approved .stylepic,
.extradesign.rendering .stylepic{display:none}
.extradesign .previewsplit:has(.wizfail:not([hidden])){display:block}
.extradesign .previewsplit:has(.wizfail:not([hidden])) .stylepic{display:none}


#included-wait[hidden]{display:none}
#included-wait .stage{max-width:520px;margin-inline:auto}


#design-preview{margin-top:clamp(20px,2.4vw,28px)}
#included-panels > #design-preview{margin-top:0}
#included-wait{margin-top:0}
/* "How many more?" (j33). Reads and sits like the price row it follows: shown only once the hero
   card is ticked (the same :has() gate the generic .qty stepper already reads), and the label
   travels with its own control as one unit so the two can never show apart. */
.addoncard.herocard .extracount-l-wrap{display:none;align-items:center;gap:10px;margin-top:10px}
.addoncard.herocard:has(input[type="checkbox"]:checked) .extracount-l-wrap{display:flex}
.extracount-l-wrap[hidden]{display:none}
.extracount-l{font-family:var(--sans);font-weight:700;font-size:.92rem;color:var(--ink)}
.extranote{margin-top:10px;color:var(--ink-2);font-size:.86rem;max-width:36ch}
.extranote[hidden]{display:none}
.bulkorder{color:var(--accent-dark);font-weight:700;text-decoration:underline}

.engrave-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:14px}

.engrave-actions[hidden]{display:none}
/* The accepted-preview row (R2, 2026-09-04): one column, the accept stretched across it, the way
   back centered underneath as quiet text. Not a modifier on .btn-back itself, because that class
   is also the wizard's own Back control and that one stays left-aligned in its nav row.
   RETIRED FROM MARKUP (FLOW-REVISION-PLAN Phase 11, 2026-09-08, j16+j17): both copies that wore
   this class now sit side by side, Back-left/Next-right, per the scoped .engrave-actions rule
   above. The rule stays defined rather than deleted - nothing pins its absence, and a future
   accepted-preview row that wants a stacked, full-width accept again has it ready. */
.engrave-actions.stacked{flex-direction:column;align-items:stretch;gap:4px}
.engrave-actions.stacked .btn-back{align-self:center}
/* The fork (LOCKIN-PLAN Phase 6, 2026-08-27). Two doors into the design step. Both cards carry a
   photograph of a real engraved pumpkin and exactly one line of copy, which is what keeps them the
   same height: the owner's note off the preview was that unequal cards read as a mistake. The
   image box is SQUARE, and that is not a free choice: the ready-made card shows a preset tile, and
   those are square because Mike's standalone art is a wide band no 4:5 window can hold (see
   bake-preset-tiles.py). A 4:5 fork box cut "Welcome to our patch" off at both edges. The typed
   card is baked square to match. */
.fork{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(12px,1.6vw,18px)}
.forkcard{display:flex;flex-direction:column;text-align:left;padding:0;cursor:pointer;
  font:inherit;color:inherit;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);overflow:hidden;box-shadow:var(--shadow)}
.forkcard img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover}
.forksay{display:flex;flex-direction:column;gap:6px;padding:14px 16px 16px}
.forkcard b{font-family:var(--display);font-size:1.08rem;line-height:1.25}
.forkline{color:var(--ink-2);font-size:.95rem}
.forkgo{color:var(--accent-dark);font-weight:700;font-size:.94rem}
.forkcard:hover{border-color:var(--accent)}
.forkcard:hover .forkgo{text-decoration:underline}
/* The ready-made designs (DESIGNS-PLAN Phase 2). Mike's standalone artwork, sold as pictured,
   so these tiles are not samples of a style: each one IS the thing being bought and there is no
   render at customer time behind it. Square rather than the 4:5 above because the artwork runs
   as a wide band across the pumpkin and a 4:5 window cannot hold it. */
.presets{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
.presetcard{border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);
  overflow:hidden;cursor:pointer;display:block;position:relative}
.presetcard:has(input:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
.presetcard input{position:absolute;top:10px;right:10px;accent-color:var(--accent);width:18px;height:18px}
.presetcard img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover}
.presetcard b{display:block;font-family:var(--display);font-size:.98rem;padding:10px 12px 12px}
/* The way back to typing your own words, and the default every no-JS order starts on. */
.presetnone{display:flex;align-items:center;gap:10px;margin-top:14px;cursor:pointer;
  font-size:.95rem;color:var(--ink)}
.presetnone input{accent-color:var(--accent);width:18px;height:18px;flex:none}
/* The same four ready-made designs on the engraved landing page (DESIGNS-PLAN Phase 4). Outside
   the wizard there is nothing to choose on the page itself, so a tile is a plain link that lands
   on the order page with that design already picked. Same square tile as the picker, framed like
   the photo grids around it rather than like a form control. */
.readymade{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap)}
.rmcard{display:block;border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);
  overflow:hidden;text-decoration:none;color:var(--ink);box-shadow:var(--shadow)}
.rmcard img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover}
.rmcard b{display:block;font-family:var(--display);font-size:1rem;padding:12px 14px 14px}
.rmcard:hover{border-color:var(--accent)}
.rmcard:hover b{text-decoration:underline}
/* Phase 7 (2026-08-27) re-cut the design step's breakpoints on the owner's two notes. Tablet: "i
   think on tablet it should be doable to have everything side by side. but test." Probed at
   768/820/1024 and the three lettering tiles hold three-up down to the phone, so the 820 rule that
   pulled them to two-up is gone. The four preset squares do not: at 768 four-up is a 170px tile,
   so they break to two-up at 720. */
@media(max-width:820px){
  .readymade{grid-template-columns:repeat(2,1fr)}
  .linefields{grid-template-columns:1fr}
}
@media(max-width:720px){
  .presets{grid-template-columns:repeat(2,1fr)}
}

#design-ready .readypdp{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(220px,1fr);
  gap:clamp(18px,2.4vw,30px);align-items:start;margin-top:14px}
#design-ready .readypdp-info{display:flex;flex-direction:column;gap:10px}
#design-ready .readypdp-info h2{font-size:clamp(1.3rem,1.8vw,1.55rem);line-height:1.16}
#design-ready .readypdp-info p{margin:0;color:var(--ink-2)}

#ready-pdp-blurb{min-height:calc(1lh * 2)}


#design-ready .readypicks{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:1fr;gap:8px}
#design-ready .readypick{border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);
  cursor:pointer;font-family:var(--sans);font-weight:600;font-size:.86rem;padding:10px 6px;
  color:var(--ink);text-align:center}
#design-ready .readypick[aria-pressed="true"]{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}

#design-ready .pgal-slide>img,#design-ready .pgal-slide>picture>img,#design-ready .pgal-thumb img{aspect-ratio:1/1}
@media(max-width:720px){
  #design-ready .readypdp{grid-template-columns:1fr}
  #design-ready .readypicks{grid-template-columns:repeat(2,1fr)}
  
  #design-ready .pgal-thumbs{gap:6px}
  #design-ready .pgal-thumb{flex:1 1 0;min-width:0}
}

#design-ready .swatchrow{margin-bottom:14px}

#design-own .ownpdp{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(220px,1fr);
  gap:clamp(18px,2.4vw,30px);align-items:start;margin-top:14px}
#design-own .ownpdp-info{display:flex;flex-direction:column;gap:clamp(18px,2.2vw,24px)}
#design-own .ownpdp-info .swatchrow{margin-top:0}
#design-own .ownpdp-info .engrave-actions{margin-top:0}


#design-own .pgal-slide>img,#design-own .pgal-slide>picture>img{aspect-ratio:4/5;object-fit:contain}
#design-own .pgal-thumb img{aspect-ratio:1/1}
@media(max-width:720px){
  
  #design-own .ownpdp{grid-template-columns:minmax(0,1fr)}
  #design-own .ownpdp-media{min-width:0}
  #design-own .pgal{min-width:0}
}
/* Extras step (Phase 7). The add-ons used to be four lines of text on the review step pointing
   at Stripe's own page; they are a real on-page choice now, so each one gets a picture where we
   have one, its price, and one sentence about what actually turns up. */
.addonpicker{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
/* Flow lock-in Phase 3 (2026-08-27): the step is a menu in three tiers. The hero card spans the
   form; the singles run three across until the phone column takes over. */
.addonpicker.picker-hero{grid-template-columns:1fr}
/* The haul-away step (Phase 15). One card per row at every width: it is a two-card screen where
   the second card only exists because the first was ticked, so side by side would show an empty
   half column for everyone who says no. */
.addonpicker.picker-haul{grid-template-columns:1fr}
/* UX review items 26, 27 and 42 (2026-08-26). The 82px thumbnail beside four lines of copy was
   17 percent of the card width, and he hit it twice in one recording: once as the problem ("tiny
   imagery of all these upsells that we provide", "so small on my huge screen here"), and once as
   the ONE thing in twenty minutes he liked, the phone card, which he named as the model for the
   desktop one. So the phone treatment shipped in Phase 9 is simply the treatment now, at every
   width: the photograph goes full bleed across the top and the choice sits under it. The picture
   IS the offer on this step.
   The [hidden] rule is repeated on the picker because display:grid beats the attribute. */
.addoncard{display:grid;grid-template-columns:auto 1fr;column-gap:12px;row-gap:12px;
  grid-template-areas:"img img" "check text";align-items:start;border:1px solid var(--rule);
  border-radius:var(--radius);background:var(--card);padding:14px 16px;cursor:pointer;overflow:hidden;
  position:relative}
.addonpicker:not(.picker-hero) .addoncard{align-content:start}
.addonpicker .addoncard[hidden]{display:none}
.addoncard:has(input[type="checkbox"]:checked){border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}

.addoncard > input[type="checkbox"]{grid-area:check;margin-top:4px;accent-color:var(--accent);width:18px;height:18px;flex:none}
.addonimg{grid-area:img;display:block;width:calc(100% + 32px);max-width:none;height:auto;
  aspect-ratio:4/3;object-fit:cover;margin:-14px -16px 0}
.addontext{grid-area:text;display:block;min-width:0}
.addoncard b{display:block;font-family:var(--display);font-size:1.02rem}
.addonprice{display:block;font-family:var(--display);color:var(--accent-dark);font-size:1.02rem;margin-top:2px}
.addoncard em{display:block;font-style:normal;color:var(--ink-2);font-size:.92rem;margin-top:5px}
.addonwhen{font-size:.84rem}
/* THE QTY STEPPER (Phase 5b, mark 10, 2026-09-02). Shared shape for the extras card and the cart
   row, which wear the same control. On the card it is hidden until the card is ticked - a count
   is meaningless on something not on the order - through the same :has() the border rule above
   reads; on the cart row (not inside .addoncard) it simply always shows, because everything
   listed there is already on the order. */
/* THE PER-CARD COUNTDOWN (R3, 2026-09-04). Quiet everywhere it first shipped: beside a struck
   price that is already doing the persuading, a big red clock on six cards at once reads as a
   casino. Tabular numerals so the seconds do not shuffle the layout every tick.
   FLOW-REVISION-PLAN Phase 10 (2026-09-08, j22) turns it back up, scoped to the Review shop
   (#shop-picker) alone: bigger and in --timer red, because this is the LAST bundle screen before
   checkout and the clock doing less work everywhere else is exactly why it can do more here. */
.bumpclock{display:block;margin-top:4px;font-family:var(--sans);font-weight:700;font-size:.82rem;
  color:var(--accent-dark);font-variant-numeric:tabular-nums}
.bumpclock[hidden]{display:none}
#shop-picker .bumpclock{font-size:1.06rem;margin-top:8px;color:var(--timer)}
.qty{display:flex;align-items:center;gap:8px}
.addoncard .qty{display:none;margin-top:8px}
.addoncard:has(input[type="checkbox"]:checked) .qty{display:flex}
.qtybtn{flex:none;width:28px;height:28px;border-radius:50%;border:1px solid var(--rule);
  background:var(--card);font:inherit;font-size:1.05rem;line-height:1;cursor:pointer;color:var(--ink);
  display:flex;align-items:center;justify-content:center;padding:0}
.qtybtn:hover{border-color:var(--accent);color:var(--accent-dark)}
.qtybtn[hidden]{display:none}
.qtyinput{width:46px;text-align:center;font:inherit;font-size:.92rem;padding:5px 2px;
  border:1px solid var(--rule);border-radius:6px;background:var(--card);color:var(--ink)}
.qtynum{min-width:1.3em;text-align:center;font-family:var(--display)}
/* The Front Step's value sentence. He read the card exactly right - "the front step isn't
   discounted - it costs more than buying the items separately, it looks like?" - because the
   three components sitting on the same screen add up to less than the ask and the five extra
   pumpkins that carry the whole difference had no price anywhere. This line puts the difference
   on screen in the same numbers as the cards beside it. Injected, never baked. */
.addonvalue{color:var(--accent-dark) !important;font-weight:600}
.addonvalue[hidden]{display:none}
/* The second-pumpkin card. NOT class "hero": the home page's .hero rules ship in this same
   stylesheet and its ::after paints a dark overlay over anything carrying that class (found the
   hard way). It kept the class name through Phase 9, where it stopped leading the step and
   became the last card on it, because the two things the class does are still exactly right:
   position:relative anchors the discount flag, and the image box matches the hand-cut 3:2 crop
   of the pair photo, so the CSS never blind-crops what the crop already decided. */
.addoncard.herocard{position:relative}
.addoncard.herocard .addonimg{aspect-ratio:3/2}

@media(min-width:721px){
  .picker-hero .addoncard.herocard{grid-template-columns:minmax(0,42%) auto 1fr;
    grid-template-areas:"img check text";column-gap:20px;align-items:center;padding:0}
  .picker-hero .addoncard.herocard .addonimg{width:100%;margin:0;height:100%;aspect-ratio:auto;
    min-height:220px}
  .picker-hero .addoncard.herocard > input[type="checkbox"]{margin:0 0 0 20px}
  .picker-hero .addoncard.herocard .addontext{padding:22px 22px 22px 0}
  .picker-hero .addoncard.herocard .addonflag{top:14px;left:14px}
}
.addonflag{position:absolute;top:14px;left:14px;z-index:1;background:var(--accent);color:#fff;
  font-family:var(--sans);font-size:.78rem;font-weight:700;letter-spacing:.02em;line-height:1.25;
  padding:5px 10px;border-radius:4px;pointer-events:none;max-width:calc(100% - 28px)}
#shop-picker .addonflag{font-size:1rem}
.addonsub{display:block;font-size:.86rem;font-weight:600;color:var(--accent-dark);margin-top:2px}
.addonprice s{color:var(--ink-2);text-decoration:line-through;font-weight:400;
  margin-right:5px}
.bundlelab{display:block;font-family:var(--sans);font-size:.78rem;font-weight:600;
  color:var(--ink-2);letter-spacing:.01em;margin-top:1px}
.addonflag[hidden]{display:none}
/* OUT OF STOCK (flow lock-in Phase 20, 2026-08-28). The same badge shape as the discount flag
   above, on the opposite corner of the same card, because they are the same kind of thing: one
   fact about this product, stated where the eye already is. Ink rather than red - a pumpkin we
   ran out of is not an error, it is just gone. */
.soldout{display:inline-block;background:var(--ink);color:#fff;font-family:var(--sans);
  font-size:.78rem;font-weight:700;letter-spacing:.02em;line-height:1.25;padding:5px 10px;
  border-radius:4px;pointer-events:none}
.soldout[hidden]{display:none}
.addoncard .soldout{position:absolute;top:14px;right:14px;z-index:2}
.pack .soldout{margin:8px 0 2px}
/* The card stays on the menu. It is NOT display:none, deliberately: a row that silently vanishes
   makes the customer wonder what they misremembered, and the honest answer is to leave it there
   saying why. The photograph keeps most of its colour so the card still reads as a picture of a
   real thing rather than as a greyed-out form control. */
.addoncard.gone,.pack.gone{opacity:.55;cursor:default}
.addoncard.gone .addonimg{filter:grayscale(.7)}
.addoncard.gone input,.pack.gone input{cursor:default}
/* Said once, on the last screen, when the PACKAGE is what sold out. */
.stocknote{color:var(--accent-dark);font-weight:600;margin-top:20px}
.stocknote[hidden]{display:none}

.familycard{position:relative;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);padding:15px 15px 17px;margin-top:clamp(18px,2.2vw,24px)}
.familycard[hidden]{display:none}
.familycard .soldout{position:absolute;top:14px;right:14px;z-index:2}

.familycard .addonflag{position:static;display:inline-block;margin:0 0 8px}
.famtick{display:flex;align-items:center;gap:8px;cursor:pointer;font-family:var(--sans);
  font-weight:600;font-size:.92rem;margin-bottom:8px}
.famtick input[type=checkbox]{accent-color:var(--accent);width:18px;height:18px;flex:none}
.familycard.gone{opacity:.55}
.familycard.gone .famimg img{filter:grayscale(.7)}
.familycard h3{margin:0;font-family:var(--display);font-weight:700;font-size:1.1rem;line-height:1.2}
.famblurb{margin:6px 0 0;font-size:.9rem;color:var(--ink-2);line-height:1.45}
.familycard .addonprice{margin-top:8px}

.familycard .perpumpkin{font-weight:600}

.famtiles{display:grid;grid-template-columns:repeat(3,1fr);gap:9px;margin-top:12px}
.famtile{display:flex;flex-direction:column;gap:7px;min-width:0;cursor:pointer}
.famimg{position:relative;display:block;border-radius:8px;overflow:hidden;border:1px solid var(--rule)}
.famimg img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover}
.famtile input[type=text]{width:100%;min-width:0;padding:8px 9px;border:1px solid var(--rule);
  border-radius:8px;font:inherit;font-size:.9rem;background:var(--card);color:var(--ink)}
.famtile input[type=text]:focus{border-color:var(--accent);outline:none;
  box-shadow:0 0 0 2px rgba(208,98,27,.25)}

.famtile input[type=text]:disabled{background:var(--bg);color:var(--ink-2);cursor:not-allowed}

@media(max-width:720px){
  .famtile input[type=text]{font-size:.78rem;padding:8px 5px}
}
.famnote{margin:11px 0 0;font-size:.86rem;color:var(--ink-2);line-height:1.45}

.famchips{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}
.famchip{display:inline-flex;align-items:center;gap:6px;background:var(--bg);border:1px solid var(--rule);
  border-radius:999px;padding:4px 6px 4px 12px;font-size:.86rem}
.famchip button{flex:none;width:20px;height:20px;border-radius:50%;border:none;background:transparent;
  font:inherit;font-size:.95rem;line-height:1;cursor:pointer;color:var(--ink-2);padding:0;
  display:flex;align-items:center;justify-content:center}
.famchip button:hover{color:var(--accent-dark)}
/* The post-checkout upgrade tease. One button-styled line since 2026-08-31 (the old face carried
   a before-and-after pair, a claim, a price and a blurb, and the verdict on it was too busy);
   the photograph of the finished porch, the includes list and the price all live in the pop-out
   the click opens. Still a <details>, so it works with JavaScript off. */
.upgradetease{margin-top:12px}


.upgradetease img{max-height:min(420px,44vh);object-position:center}

#upgrade-tease-add.on{background:transparent;border:1px solid var(--rule);color:var(--accent-dark)}
/* THE OFFER CARD (B4, 2026-09-04). No summary, no disclosure triangle, nothing to open. The wipe
   carries its own frame, so the card only has to hold the two of them together. */
.teasecard{margin-top:14px;padding:16px;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);box-shadow:var(--shadow);color:var(--ink-2);font-size:.95rem}
.teasecard ul{margin:0 0 10px;padding-left:1.2em}
.teasecard li{margin-bottom:4px}
.teasehead{font-family:var(--display);font-weight:700;
  font-size:clamp(1.42rem,1.02rem + 1.1vw,1.9rem);line-height:1.18;letter-spacing:-.012em;
  color:var(--accent-dark);margin:12px 0 10px}

.teasebtn{display:flex;align-items:center;justify-content:center;gap:8px;text-align:center;
  padding:14px 18px;border:2px solid var(--accent);border-radius:var(--btn-radius);
  background:var(--ribbon-bg);color:var(--accent-dark);font-family:var(--display);
  font-weight:700;font-size:1.04rem;line-height:1.3}
.teasebtn::after{content:'\2193';font-family:var(--sans);font-weight:800}
.upgradetease[open] .teasebtn::after{content:'\2191'}
.upgradetease summary:hover .teasebtn{background:var(--card)}
.teasemore{margin-top:12px;padding:16px;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--card);box-shadow:var(--shadow);color:var(--ink-2);font-size:.95rem}
.teasebig{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  border-radius:var(--radius);margin-bottom:12px}
.teasemore ul{margin:0 0 10px;padding-left:1.2em}
.teasemore li{margin-bottom:4px}
/* The tier headers of the extras menu and the Review shop: the which-of-two, then piece by
   piece. */
.addonrowhead{font-family:var(--display);font-size:1.02rem;color:var(--ink-2);margin:22px 0 12px}


.shopwrap{margin-top:clamp(28px,4.2vw,46px)}

.addoncard .inorder{display:none;color:var(--support);font-weight:700;font-size:.86rem;margin-top:2px}
.addoncard:has(input[type="checkbox"]:checked) .inorder{display:block}

.mumpref{display:none;margin-top:12px}
.mumpreflab{display:block;font-family:var(--sans);font-weight:700;font-size:.92rem;
  color:var(--ink);margin-bottom:8px}

.mumpref .opts{grid-template-columns:1fr}
/* Plain-language delivery pricing above the ZIP box, so the number is never a surprise the
   customer has to type a ZIP to discover. */
.trustrow{display:flex;gap:22px;flex-wrap:wrap;color:var(--ink-2);font-size:.92rem;margin-top:22px}
.trustrow span{display:inline-flex;align-items:center;gap:8px}
.trustrow span::before{content:'';width:7px;height:7px;border-radius:50%;background:var(--support);flex:none}
/* closer */
.closer{background:linear-gradient(150deg,#33261B 0%,#5C4128 100%);color:#fff;
  padding-block:var(--band);text-align:center}
.closer p{color:#F1E6D4;max-width:54ch;margin:14px auto clamp(22px,2.6vw,30px)}
.closer .choices{text-align:left}


.closer .choices-events{color:#F1E6D4;max-width:54ch;margin:clamp(44px,5.2vw,60px) auto 0;
  font-size:clamp(1.12rem,1.5vw,1.32rem);font-weight:600;line-height:1.4}

.closer .choices-events-cta{margin:clamp(14px,1.6vw,18px) auto 0;text-align:center}
.closer .choices-events-cta .btn{display:inline-block}
/* THE POP-UP (EMAIL-PLAN Phase 3). The photograph is the offer, so it leads the card and it is
   the one thing given real height. Read at 390px first: the card scrolls inside itself rather
   than pushing the page, because on a short phone the three fields plus the picture do not fit. */
.pop{position:fixed;inset:0;z-index:900;display:flex;align-items:center;justify-content:center;padding:16px}
.pop-back{position:absolute;inset:0;background:rgba(28,20,14,.62)}
.pop-card{position:relative;width:min(430px,100%);max-height:calc(100dvh - 32px);overflow:auto;
  background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);padding:0 22px 22px;
  text-align:center}

.pop-shot{display:block;width:calc(100% + 44px);max-width:none;margin:0 -22px 16px;height:auto;
  aspect-ratio:4/3;object-fit:cover;object-position:50% 58%;
  border-radius:var(--radius) var(--radius) 0 0}
@media (max-width:480px){
  .pop-card{padding:0 16px 16px}
  .pop-shot{width:calc(100% + 32px);margin:0 -16px 14px}
}
.pop-x{position:absolute;top:8px;right:10px;z-index:2;width:34px;height:34px;border:0;cursor:pointer;
  border-radius:50%;background:rgba(253,250,243,.86);color:var(--ink);font-size:1.5rem;line-height:1}
.pop-h{font-size:clamp(1.3rem,4vw,1.6rem);margin:0 0 6px}
.pop-p{color:var(--ink2);margin:0 0 16px;font-size:1rem}
.pop-form{text-align:left}
.pop-l{display:block;font-size:.9rem;color:var(--ink2);margin:0 0 4px}
.pop-i{width:100%;box-sizing:border-box;padding:11px 12px;margin:0 0 12px;font:inherit;
  border:1px solid var(--rule);border-radius:8px;background:var(--bg);color:var(--ink)}
.pop-err{color:#a3341f;font-size:.92rem;margin:0 0 10px}
.pop-go{width:100%;margin-top:4px}
/* THE FOOTER LINE. One field, no offer, on the full sitewide footer only (flow pages ship
   flowFooter() and get neither door). */
.fline{border-top:1px solid var(--rule);margin-top:clamp(20px,3vw,30px);padding-top:clamp(18px,2.4vw,24px)}

.fline-form{display:flex;flex-wrap:wrap;align-items:center;column-gap:24px;row-gap:10px}
.fline-l{flex:2 1 280px;font-size:.98rem;margin:0;max-width:56ch}
.fline-row{display:flex;flex:1 1 300px;gap:8px;flex-wrap:wrap;max-width:440px;margin:0}
.fline-i{flex:1 1 200px;min-width:0;padding:10px 12px;font:inherit;border:1px solid var(--rule);
  border-radius:8px;background:var(--card);color:var(--ink)}
.fline-go{padding:10px 18px;font:inherit;cursor:pointer;border:1px solid var(--ink);border-radius:8px;
  background:var(--ink);color:var(--card)}
.fline-msg{flex:0 0 100%;margin:10px 0 0;font-size:.95rem;color:var(--ink2)}
/* THE PREVIEW PAGE (EMAIL-PLAN Phase 3). Unlisted, single-purpose, read on a phone first: one
   column, the picture as large as the viewport allows, and one question under it. The wait state
   and the answer state are the same width so the page does not jump when the render lands. */
.pv{max-width:660px;text-align:center;padding-block:clamp(40px,5vw,72px)}
.pv-h1{font-size:clamp(1.7rem,4.4vw,2.4rem);margin:0 0 10px}
.pv-sub{color:var(--ink2);margin:0 auto 26px;max-width:46ch}
#pv-words{font-family:var(--display);font-size:clamp(1.3rem,3.4vw,1.7rem);color:var(--ink)}
.pv-bar{height:8px;border-radius:99px;background:var(--rule);overflow:hidden;margin:0 0 22px}
.pv-bar i{display:block;height:100%;width:0;border-radius:99px;background:var(--accent);
  transition:width .3s linear}
/* The pitch line. It swaps every six seconds, so it is given a fixed minimum height: without one
   a two-line point pushes the whole page down and a one-line point pulls it back up. */
.pv-sell{min-height:3.2em;display:flex;align-items:center;justify-content:center;
  font-size:1.06rem;color:var(--ink);max-width:44ch;margin:0 auto}
.pv-shot{margin:0 0 24px}
.pv-shot img{display:block;width:100%;height:auto;border-radius:var(--radius);box-shadow:var(--shadow)}
.pv-ask{font-family:var(--display);font-size:clamp(1.35rem,3.6vw,1.75rem);margin:0 0 18px}
.pv-btns{display:flex;flex-direction:column;align-items:center;gap:12px;margin:0 0 20px}
.pv-btns .btn{display:inline-block}
.pv-no{background:none;border:0;padding:6px 2px;font:inherit;font-size:.98rem;color:var(--ink2);
  text-decoration:underline;cursor:pointer}
.pv-fine{font-size:.95rem;color:var(--ink2);margin:0;max-width:44ch;margin-inline:auto}
/* thank you */
.ty{text-align:center;padding-block:clamp(48px,6vw,84px)}
.ty .check{width:80px;height:80px;margin:0 auto 22px;display:block}
/* j51, FLOW-REVISION-PLAN Phase 15: .ty is shared with the 404 page, so the shorter, tighter
   confirmation j52 leaves behind (no #ty-details card under it any more) is scoped to .ty-done,
   the /thanks/ wrapper's own class, rather than to bare .ty. */
.ty.ty-done{padding-block:clamp(20px,2.4vw,32px)}
/* fr2 j40: tighter now that the tease line below carries the size the heading gave up. */
.ty.ty-done .check{width:44px;height:44px;margin-bottom:10px}
/* fr2 j39, his note: "the upsell font should be the size of the thank you name we currently have,
   and the thank you name should be shrunk down." This is the shrink; .tybefore below takes on the
   size this rule gave up. */
.ty.ty-done h1{font-size:clamp(1.35rem,2.6vw,1.9rem)}
.tycard{max-width:540px;margin:0 auto;background:var(--card);border:1px solid var(--rule);
  border-radius:var(--radius);padding:26px;text-align:left;box-shadow:var(--shadow)}
/* j53: the line that opens the offer, hidden until showOffer reveals both together.
   fr2 j38 j39: centred now (text-align:left dropped, so it inherits .ty's own center), and set at
   the size #ty-heading had before j39 shrank it a step. */
.tybefore{max-width:min(760px,100%);margin:0 auto 10px;font-family:var(--display);
  font-size:clamp(1.6rem,3vw,2.2rem)}
/* fr2 j38: the drag hint under the tease line, smaller type, same precedent as .pv-fine. */
.tydrag{max-width:min(760px,100%);margin:0 auto 18px;font-size:.95rem;color:var(--ink2)}
/* The post-purchase offer (Phase 9). Four short lines under the picture of the thing being
   offered, then the button. Everything about it is downstream of the confirmation above it:
   nothing here renders until the customer has been told plainly that their money landed.
   Flow lock-in Phase 8 (2026-08-27): the card runs WIDER than the confirmation card above it,
   because the photograph is the pitch and 540px was selling it in a thumbnail. The prose keeps
   a readable measure inside the wider card. The 3/2 crop is gone with the fixed height: the
   photograph is natively 3:2 so it clamped nothing today, but it would have cropped any taller
   replacement, and the before/after pair that lands in Phase 12 is exactly that. */
.tyoffer{max-width:min(760px,100%)}
.tyoffer img{display:block;width:100%;height:auto;border-radius:var(--radius);margin-bottom:18px}
.tyoffer p{max-width:62ch}
.tyoffer h2{font-size:1.34rem;margin-bottom:10px}
.tyoffer p{margin-bottom:12px}
.tyoffer .tyterms{font-family:var(--display);font-size:1.06rem;color:var(--ink)}
/* The clock. Driven from the order's own expiry, never from page load, so a refresh cannot
   restart it and a screenshot of it cannot be wrong about how long is left.
   BIG, BOLD, COLOURED AND ABOVE THE PAIR since the flow lock-in Phase 17 (2026-08-28). It used
   to be a small tinted pill under the terms line, which is where a countdown goes when nobody
   wants it seen. It is now a full-width bar across the head of the card, in accent-dark against
   cream, with the numerals at display size - and it is square along the bottom so it reads as one
   block with the before-and-after underneath rather than as a badge floating over it. */
/* Scoped .tyoffer .tybar and not bare .tybar: the card's own margin rule on .tyoffer p is
   class-plus-element and would otherwise win the margin, which puts a 12px gap between the bar
   and the pair and breaks the one-block look the square corners are there to make.
   NO BACKTICKS IN THIS COMMENT, and it cost a build to learn: every line of this stylesheet is
   TEXT inside a template literal in build.mjs, so quoting a CSS rule in backticks here ends the
   literal and silently eats the rules on either side of it. */
.tyoffer .tybar{display:flex;align-items:center;justify-content:center;gap:10px;
  background:var(--accent-dark);color:#FDF6E8;margin:0 0 2px;padding:9px 14px;max-width:none;
  border-radius:var(--radius) var(--radius) 0 0;font-family:var(--sans);font-weight:800;
  font-size:.76rem;text-transform:uppercase;letter-spacing:.09em;line-height:1}
.tybar[hidden]{display:none}
.tybar #ty-clock-time{font-family:var(--display);font-size:clamp(1.45rem,5.4vw,1.95rem);
  font-weight:700;letter-spacing:0;text-transform:none;font-variant-numeric:tabular-nums;
  line-height:1}
/* Square the pair's top corners only while the bar is actually showing, so an expired card is
   still a properly rounded photograph rather than a block with a flat edge and nothing above it.
   The single-photograph fallback this used to also cover left with it in Phase Q13: every rung
   shows the pair now. */
.tybar:not([hidden]) + .typair{border-radius:0 0 var(--radius) var(--radius)}
/* HIS BULLETS, under the heading and above the rows (Phase Q13). Reused for the Front Step
   downsell's own "You get:" list. */
.tylead{font-weight:700;margin-bottom:6px}
.tyoffer ul{margin:0 0 14px;padding-left:19px;color:var(--ink-2);font-size:.95rem;
  display:flex;flex-direction:column;gap:6px}
/* THE PRICE ROWS (Phase Q13). A labelled row block replaces the old struck-price-plus-pill: the
   package name with its shelf price, then whatever brought it down, then the total, so every
   number on the card explains itself. Reused verbatim for the Front Step downsell's own rows. */
.typrice{display:flex;flex-direction:column;gap:8px;margin-bottom:14px}

.tyrow{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:8px 12px;
  font-family:var(--display);font-size:1.04rem}
.tyrow[hidden]{display:none}
.tyrow-label{color:var(--ink-2);flex:1 1 0;min-width:0}
.tyrow>b,.tyrow>s{flex:0 0 auto;margin-left:auto;text-align:right}
.tyoffer .tyrow s{text-decoration:none;color:var(--ink-2);font-size:1.04rem}
.tyoffer .tyrow s.struck{text-decoration:line-through}
.tyrow-save b,.tyrow-credit b{color:var(--accent-dark)}
.tyrow-total{padding-top:8px;border-top:1px solid var(--rule)}
.tyrow-total .tyrow-label{color:var(--ink);font-weight:700}
.tyoffer .tyrow-total b{font-weight:700;line-height:1;color:var(--ink);
  font-size:clamp(1.7rem,5.5vw,2.15rem)}
/* The before and after on the thank-you offer (2026-08-28, PORCH-COMBOS-PLAN wiring; every rung
   since Phase Q13). Left is the parent's own package photo, right is the target's; both are a
   flat lookup by sku now rather than a picker built off owned add-ons. It ships baked as the
   engraved pumpkin beside the Signature Porch, so the card is a true before and after even if the
   order lookup tells it nothing. .tyshot unsets the margin and the radius the plain .tyoffer img
   rule sets, since it sits inside a frame rather than being one directly.
   Stacked below 561 for the reason the extras tease stacks: two across at 375 is 145px a frame,
   which is too small to see the thing the pair exists to show. */
.typair{position:relative;border-radius:var(--radius);overflow:hidden;margin-bottom:18px;
  --split:50%}

#ty-story{margin-top:22px}

#ty-story img{display:block;width:100%;height:auto;max-height:38vh;object-fit:cover;
  border-radius:var(--radius);margin-bottom:16px}

.ty-longsub #ty-story{margin-top:8px}
.ty-longsub #ty-story img{max-height:32vh}
.typair[hidden]{display:none}
.tyframe{position:relative;display:block;overflow:hidden}

.typair .tyshot{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  border-radius:0;margin-bottom:0}

@media (min-width:900px) and (max-height:760px) {
  #haul-pair .tyframe-have .tyshot{aspect-ratio:auto;height:min(44vh,360px)}
  
  #haul-pair .tyframe-have .tyshot{height:min(24vh,220px)}
  #haul-screen .formstep{margin-bottom:8px}
  #haul-pair{margin-bottom:8px}
  #haul-picker .addoncard{padding:10px 16px}
  #haul-picker .addoncard em{display:none}
}
/* The Signature layer sits over the order's own porch and is wiped in from the right by the
   clip; the divider and its handle ride the same variable so the three can never disagree. */
.tyframe-sig{position:absolute;inset:0;clip-path:inset(0 0 0 var(--split))}
.tyframe-sig .tyshot{position:absolute;inset:0;height:100%}
.tydivider{position:absolute;top:0;bottom:0;left:var(--split);width:3px;margin-left:-1.5px;
  background:#FDF6E8;box-shadow:0 0 6px rgba(28,19,11,.5);pointer-events:none;z-index:2}
.tydivider::after{content:'\2194';position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);width:34px;height:34px;border-radius:50%;background:#FDF6E8;
  color:var(--ink);display:flex;align-items:center;justify-content:center;font-size:1.05rem;
  box-shadow:0 2px 8px rgba(28,19,11,.4)}
.tyrange{position:absolute;inset:0;z-index:3;width:100%;height:100%;margin:0;opacity:0;
  cursor:ew-resize;-webkit-appearance:none;appearance:none}

.tylabs{position:absolute;inset:0;z-index:1;pointer-events:none}
.tylab{position:absolute;left:8px;top:8px;background:rgba(28,19,11,.72);color:#F1E6D4;
  font-family:var(--sans);font-size:.72rem;font-weight:600;letter-spacing:.02em;
  padding:3px 8px;border-radius:3px;transition:opacity .25s}
.tylab-r{left:auto;right:8px}


#haul-pair .tylab{font-size:clamp(.68rem,.175rem + 2.42vw,.95rem);
  padding:clamp(4px,1.2px + 1.33vw,8px) clamp(8px,2.5px + 2.25vw,14px);border-radius:6px;
  left:12px;top:12px}
#haul-pair .tylab-r{left:auto;right:12px}

@media (max-width:390px){
  .tylab{font-size:.64rem;padding:2px 6px}
}

.tyterms[hidden]{display:none}
.tydecline{margin-top:16px}

.pgal{display:flex;flex-direction:column;gap:11px}
.pgal-stage{position:relative;border-radius:var(--radius);overflow:hidden;background:var(--card);
  box-shadow:var(--shadow)}
.pgal-track{display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;
  scroll-behavior:smooth;scrollbar-width:none;-ms-overflow-style:none;cursor:grab}
.pgal-track.grabbing{cursor:grabbing;scroll-behavior:auto;user-select:none}
.pgal-track::-webkit-scrollbar{display:none}
.pgal-slide{flex:0 0 100%;scroll-snap-align:center;position:relative;min-width:0}

.pgal-slide>img,.pgal-slide>picture>img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  -webkit-user-drag:none;user-select:none}
.pgal-slide .typair{margin-bottom:0;border-radius:0;box-shadow:none}
.pgal-cap{position:absolute;left:10px;bottom:10px;z-index:2;background:rgba(28,19,11,.72);
  color:#F1E6D4;font-family:var(--sans);font-size:.78rem;font-weight:600;padding:5px 11px;
  border-radius:4px;pointer-events:none}

.pgal-btn{position:absolute;top:50%;transform:translateY(-50%);z-index:4;width:38px;height:38px;
  border-radius:50%;border:0;background:rgba(253,250,243,.86);color:var(--ink);cursor:pointer;
  display:none;align-items:center;justify-content:center;font-size:1.1rem;line-height:1;
  box-shadow:0 2px 10px rgba(51,38,27,.25)}
.pgal-btn:hover{background:#fff}
.pgal-btn.prev{left:12px}
.pgal-btn.next{right:12px}
.pgal-thumbs{display:flex;gap:9px;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
.pgal-thumbs::-webkit-scrollbar{display:none}
.pgal-thumb{flex:0 0 86px;padding:0;border:2px solid var(--rule);border-radius:6px;overflow:hidden;
  background:none;cursor:pointer;line-height:0;transition:border-color .18s ease}
.pgal-thumb img{width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;display:block}
.pgal-thumb.on{border-color:var(--accent)}
.pgal-dots{display:flex;justify-content:center;gap:7px}
.pgal-dots i{width:7px;height:7px;border-radius:50%;background:var(--rule);display:block;
  transition:background .2s ease,transform .2s ease}
.pgal-dots i.on{background:var(--accent);transform:scale(1.35)}

.pdp-grid{display:grid;grid-template-columns:minmax(0,1.9fr) minmax(300px,1fr);
  gap:clamp(24px,3.2vw,44px);align-items:start}
.pdp-buy{position:sticky;top:92px;display:flex;flex-direction:column;gap:12px;
  background:var(--card);border:1px solid var(--rule);border-radius:var(--radius);
  padding:clamp(20px,2vw,28px);box-shadow:var(--shadow)}
.pdp-buy h1{font-size:clamp(1.85rem,2.8vw,2.4rem);line-height:1.1}
.pdp-buy .price{font-family:var(--display);font-weight:700;font-size:clamp(2rem,3.2vw,2.7rem);
  color:var(--accent-dark);line-height:1;font-variant-numeric:tabular-nums}
.pdp-buy .price span{font-size:.62em;vertical-align:.42em;margin-right:.04em}

.pdp-desc{display:flex;flex-direction:column;gap:12px}
.pdp-desc ul{margin:0;padding-left:19px;color:var(--ink-2);font-size:.97rem;
  display:flex;flex-direction:column;gap:7px}

.pdp-desc-head{font-size:clamp(1.15rem,1.6vw,1.4rem);margin-bottom:10px}
.pdp-desc-lead{margin:0 0 8px;color:var(--ink-2)}
.pdp-desc-wide{display:block;margin-top:clamp(14px,2vw,26px);max-width:70ch;font-size:1.06rem}
.pdp-desc-wide ul{font-size:1.02rem}
.pdp-desc-phone{display:none}
/* The palette picker inside the buy box. Three tiles side by side would be a hundred pixels
   wide each in a 300px column, so here it stacks: picture left, words right, all the same copy
   the wizard shows. The control must read the same in both places it is asked. */
.pdp-buy .mixrow{margin-top:6px}
.pdp-buy .mixrow .formstep{font-size:1.12rem;margin-bottom:12px}
/* The handwriting is a display face sized for a full-width step. In a 300px column it wraps to
   three lines and swamps the control it is reassuring about, so here it is plain and small. This
   already answers GO-LIVE-PLAN Phase 2's "#palette-sameprice loses the hand class (upright)" at
   390 - the buy box is a narrow column at every width, so this override was already in force there
   - what his mobile mark 6:11 ("slanted and cramped") still had a real complaint about was the
   CRAMPED half: no room between the sentence and the first palette card below it. */
.pdp-buy .mixrow .formnote.hand{font-family:var(--sans);font-style:normal;font-size:.88rem;
  color:var(--ink-2);margin-bottom:10px}
@media(max-width:720px){
  .pdp-buy .mixrow .formnote.hand{margin-bottom:18px}
}
/* THE COMPACT PALETTE TILE (R9, 2026-09-04). It was scoped to .pdp-buy and he asked for it in the
   wizard too, by describing it: a small picture on the left, the name, the colour discs overlapped
   in a row under it, the note. Now it hangs off .mixrow, which palettePicker is the only thing
   that emits, so the package page and the colour step cannot drift apart again. The discs overlap
   by design and a one-colour palette shows exactly one disc, which is what he liked about it. */
.mixrow .mixes{grid-template-columns:1fr;gap:10px}
#colors-mums .mumpref{margin-top:0}
#colors-mums .mumpreflab{display:none}
.mixrow .mix{display:grid;grid-template-columns:78px minmax(0,1fr);
  grid-template-rows:auto auto auto;column-gap:12px;align-content:center;padding:10px}
.mixrow .mixpic{grid-column:1;grid-row:1/4;align-self:center;margin:0;aspect-ratio:1/1;
  border-radius:calc(var(--radius) - 5px)}
.mixrow .mix b{grid-column:2;grid-row:1;align-self:end;font-size:.98rem}
.mixrow .mixdots{grid-column:2;grid-row:2;margin:5px 0 2px}
.mixrow .mixdots img{width:26px;height:26px}
.mixrow .mixdots img + img{margin-left:-8px}
.mixrow .mix em{grid-column:2;grid-row:3;align-self:start;font-size:.84rem;margin-top:3px}
.pdp-buy .mumpref{margin-top:14px}

.pdp-buy .arrangementnote{margin-top:14px}
.pdp-buy .arrangementnote textarea{width:100%}
.pdp-buy .btn-order{padding:.92em 1.4em;font-size:1.06rem;font-weight:700;box-shadow:var(--shadow)}
.pdp-buy .btn-order:hover{transform:translateY(-1px)}

.pdp-detail{display:grid;grid-template-columns:1fr;gap:clamp(24px,3.2vw,44px);align-items:start}
.pdp-detail h2{font-size:clamp(1.3rem,2vw,1.65rem);margin-bottom:12px}
.pdp-detail ul{margin:0;padding-left:19px;color:var(--ink-2);display:flex;flex-direction:column;gap:9px}

.pack .packshot{display:block;margin:calc(clamp(20px,2vw,28px) * -1) calc(clamp(20px,2vw,28px) * -1) 0;
  border-radius:var(--radius) var(--radius) 0 0;overflow:hidden;line-height:0}
.pack .packshot img{width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;display:block}
@media(max-width:900px){
  
  
  .pdp-grid{grid-template-columns:minmax(0,1fr)}
  .pdp-buy{position:static}
  .pgal-btn{display:none !important}
  
  .pdp-desc-phone{display:block}
  .pdp-desc-wide{display:none}
}
.tydecline button{background:none;border:0;padding:0;color:var(--ink-2);font-size:.92rem;
  text-decoration:underline;cursor:pointer;font-family:inherit}
.tydecline button:hover{color:var(--ink)}

.offercard .tyrow-label{color:var(--ink)}

.offercard ul{color:var(--ink);margin-bottom:32px}
.offercard .typrice{gap:12px;margin-bottom:28px}
.offercard .tyrow+.tyrow{padding-top:8px;border-top:1px solid var(--rule)}

.offercard .offeract{display:flex;flex-direction:column;align-items:center;gap:12px 16px;text-align:center}
.offercard .offeract .tydecline{margin:0}
.offercard .tydecline[hidden]{display:none}

#ty-clearweek{margin-bottom:24px}
/* prose */
.prose{max-width:74ch;display:flex;flex-direction:column;gap:15px}
.prose h2{margin-top:22px;font-size:clamp(1.3rem,2vw,1.6rem)}
.prose ul{padding-left:20px;margin:0;display:flex;flex-direction:column;gap:8px;color:var(--ink-2)}
.prose p{color:var(--ink-2)}
/* footer */
footer.site{background:var(--ink);color:#CDBCA3;font-size:.93rem}
footer.site a{color:#fff}
footer.site a:hover{color:var(--accent)}
/* Flex, not a 1.6fr/1fr/1fr grid: the old ratio sized the link columns off the container instead
   of their contents, which left a dead gap on the right and made the whole block look lopsided.
   Brand takes the slack, the two link columns sit at their natural width against the right. */
footer.site .fgrid{display:flex;flex-wrap:wrap;justify-content:space-between;
  gap:clamp(30px,4vw,72px);padding-block:clamp(42px,4.6vw,62px) clamp(30px,3.4vw,44px)}
footer.site .fbrand{flex:1 1 300px;max-width:42ch}
footer.site .fcol{flex:0 0 auto;min-width:148px}
/* Same lockup on the dark ground: cream name, coral suffix. Set as tokens on .brand so the
   .bt rules above keep working untouched. */
footer.site .brand{--wm-ink:#F6F2E6;--wm-co:#E0725E;color:var(--wm-ink);margin-bottom:14px}
footer.site .fbrand p{color:#CDBCA3;margin:0;line-height:1.62}
footer.site h4{color:#fff;font-family:var(--sans);font-size:.8rem;text-transform:uppercase;
  letter-spacing:.13em;margin-bottom:15px}
footer.site ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:11px}
/* info@signaturepumpkinco.com is 27 unbreakable characters. Without this it blows out of a
   narrow footer column and takes the whole page into horizontal scroll on a phone. */
footer.site ul a{text-decoration:none;overflow-wrap:anywhere}
footer.site ul a:hover{text-decoration:underline;text-underline-offset:3px}
/* fr2 j01, 2026-09-08 walk: Towns no longer forces its own wide row. It inherits
   footer.site .fcol{flex:0 0 auto;min-width:148px} and stacks like every other column
   (Explore, Get in touch), which is what he asked for once Lebanon dropped the list to four
   towns plus All towns. */
/* The copyright used to be its own darker strip below the footer, which read as a second,
   detached bar. It now sits inside the same wrap, separated by a hairline instead of a slab. */
footer.site .fbase{display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;
  font-size:.79rem;color:#A8977D;border-top:1px solid rgba(255,255,255,.13);
  padding-block:20px clamp(26px,3vw,34px)}

footer.site .fbase span:last-child{margin-left:auto}
footer.site .fbase a{color:#CDBCA3}
footer.site .fbase a:hover{color:var(--accent)}

footer.site .fbase:has(.fmail){justify-content:flex-start}
footer.site .fbase .fmail{margin-left:auto}

footer.site .fbase .fdot{margin:0 -6px}

footer.site .fbase span.flegal{margin-left:0}
/* ---------- v4: bottom row reads  (c) left ......... Privacy - Terms   [fb] [ig]  ---------- */
footer.site .fbase{align-items:center}
/* The sitewide footer's legal span used to be the last child, so the base rule
   .fbase span:last-child{margin-left:auto} right-aligned it. The glyph row is the last child
   now, so that rule no longer matches and this class takes the job instead. Its own name, not
   .flegal, because .flegal already belongs to flowFooter's three-span row and carries its own
   narrow-width rules. */
footer.site .fbase .fbottomlegal{margin-left:auto}
/* -6px right pulls the LAST GLYPH's own edge onto the divider's right edge. The 30px box keeps
   its tap target; without this the box's internal padding leaves the glyph 6px shy of the line,
   and FLOW-REVISION-6 Phase 7 already ruled this row aligns to the divider. */
footer.site .fsocial{display:flex;gap:2px;margin:0 -6px 0 16px}
footer.site .fsocial a{
  position:relative;display:grid;place-items:center;
  width:30px;height:30px;border-radius:6px;
  color:#A8977D;text-decoration:none;
  transition:color .28s cubic-bezier(.22,.61,.36,1),
             transform .28s cubic-bezier(.22,.61,.36,1);
}
footer.site .fsocial a svg{width:17px;height:17px;display:block;overflow:visible}
footer.site .fsocial a .mono{fill:currentColor;transition:opacity .28s cubic-bezier(.22,.61,.36,1)}
footer.site .fsocial a .grad{opacity:0;transition:opacity .28s cubic-bezier(.22,.61,.36,1)}
footer.site .fsocial a:hover,footer.site .fsocial a:focus-visible{transform:translateY(-1px)}
footer.site .fsocial a.fb:hover,footer.site .fsocial a.fb:focus-visible{color:#1877F2}
footer.site .fsocial a.ig:hover .mono,footer.site .fsocial a.ig:focus-visible .mono{opacity:0}
footer.site .fsocial a.ig:hover .grad,footer.site .fsocial a.ig:focus-visible .grad{opacity:1}
footer.site .fsocial a:focus-visible{outline:2px solid #A8977D;outline-offset:2px}
@media (prefers-reduced-motion:reduce){
  footer.site .fsocial a,footer.site .fsocial a .mono,footer.site .fsocial a .grad{transition-duration:.01ms}
  footer.site .fsocial a:hover,footer.site .fsocial a:focus-visible{transform:none}
}
@media (max-width:560px){
  /* the row is already justify-content:flex-start down here, so the auto margin comes off and the
     three pieces read left to right instead of splitting to opposite corners of a wrapped row */
  footer.site .fbase .fbottomlegal{margin-left:0}
  footer.site .fsocial{margin-left:auto}
}
/* the sign-up field and button ride the right edge, on the divider */
footer.site .fline-row{margin-left:auto;justify-content:flex-end}
/* equal air above and below the sign-up row so it sits centred between the two hairlines. The
   base rule sets padding-top only, which is what made it ride high and sit tight on the divider
   underneath. */
footer.site .fline{padding-block:clamp(24px,3vw,32px)}
/* sticky phone order bar */

.mobilebar{display:none;position:sticky;bottom:0;z-index:55;align-items:center;justify-content:space-between;
  gap:12px;padding:11px var(--pad);background:var(--card);border-top:1px solid var(--rule);
  box-shadow:0 -6px 18px rgba(51,38,27,.10)}
.mb-txt{display:flex;flex-direction:column;line-height:1.2}
.mb-txt b{font-family:var(--display);font-size:.96rem}
.mb-txt span{font-size:.82rem;color:var(--ink-2)}
/* The flow's own sticky phone bar (flow lock-in Phase 10, 2026-08-28). Its own class, the way
   .preorderbar has its own: the sitewide bar sells the order page, and inside the wizard that
   slot has to carry what the order costs so far rather than a link back to the page the customer
   is already standing on. The ruling behind it: "that only serves to confuse people." */
.flowbar{display:none;position:sticky;bottom:0;z-index:55;align-items:center;justify-content:space-between;
  gap:12px;padding:11px var(--pad);background:var(--card);border-top:1px solid var(--rule);
  box-shadow:0 -6px 18px rgba(51,38,27,.10)}
.flowbar[hidden]{display:none}
.fb-txt{display:flex;flex-direction:column;line-height:1.2;min-width:0}
.fb-txt b{font-family:var(--display);font-size:.96rem}
.fb-txt span{font-size:.82rem;color:var(--ink-2)}
/* Opens upward: there is nothing below the bar for it to open into. */
.flowbar .cartpanel{top:auto;bottom:calc(100% + 10px);max-height:min(72vh,calc(100vh - 118px))}

/* ===== THE HEADER FIX: collapse at 1000px, where the assembled row actually runs out of room
   (the old build collapsed at 860 while the row needed ~900, so it jammed in between). ===== */
@media(max-width:1000px){
  .hnav{position:absolute;top:100%;left:0;right:0;display:none;flex-direction:column;align-items:stretch;
    gap:0;background:var(--surface);border-bottom:1px solid var(--rule);box-shadow:0 14px 24px rgba(51,38,27,.13)}
  .navtoggle:checked ~ .hnav{display:flex}
  .hnav a{padding:15px var(--pad);border-top:1px solid var(--rule);border-radius:0;font-size:.96rem}
  .navbtn{display:inline-flex}
  .packs,.shots,.shots.g3,.packs.g3{grid-template-columns:repeat(2,1fr)}
  .split{grid-template-columns:1fr;gap:26px}
  .split.flip .split-img{order:0}
  /* fr2 j03, 2026-09-08 walk: at one column the tall photo (Mike and Doug on home, Mike and
     the shop on /about/) was stretching edge to edge and blowing up. Cap it and center it;
     .split-img.tall img{aspect-ratio:4/5} stays untouched so shoot-site's ratio check holds. */
  .split-img.tall{max-width:min(100%,440px);margin-inline:auto}
  .areamap{grid-template-columns:1fr;gap:28px}
}
@media(max-width:720px){
  .pagehead.withimg .wrap{flex-direction:column;align-items:flex-start;gap:24px}
  
  .pagehead.withimg .pagehead-img{flex:none;width:min(320px,70%);align-self:center;margin-inline:auto}
  /* One column, and the step keeps the desktop stack: a full-width photograph with its words
     under it. The old phone treatment turned the row sideways around a 54px icon, which is the
     stamp this pass deleted. */
  .steps{grid-template-columns:1fr;gap:26px}
  /* The standing ruling is one option per row on a phone so the photograph is as large as it can
     be. */
  .opts,.addonpicker,
  .contactgrid{grid-template-columns:1fr}
  /* The full-bleed card treatment that used to live only in this block is the base rule now
     (items 26 and 42, 2026-08-26), so the phone and the desktop finally show the same card at
     two widths instead of two different cards. */
  footer.site .fgrid{gap:32px}
  footer.site .fbrand{flex:1 1 100%;max-width:46ch}
  /* The two pumpkins share the row rather than shrinking to a pair of chips in the middle of it:
     on a phone the control has to be the widest thing on the screen or it reads as decoration. */
  .swatch{max-width:none}
  .swatch img{width:88px;height:88px}
}
@media(max-width:560px){
  header.site .hdr-in{padding-block:11px;gap:9px}
  .navbtn em{display:none}
  .navbtn{padding:10px}
  .packs,.shots,.shots.g3,.packs.g3{grid-template-columns:1fr}
  .choices{grid-template-columns:1fr}
  /* Phase 7, and it is the same complaint again one screen earlier: "looks like trash on mobile -
     always have one option per row full width, so the image is as large as possible." Every choice
     card on the design step obeys it - the two fork doors, the three lettering tiles, the four
     ready-made tiles. The step gets long on a phone; that is his stated trade. */
  .fork,.styles,.presets{grid-template-columns:1fr}
  /* The mix cards carry a sentence each, so they stack for the same reason every other choice
     card on this flow does. The SWATCHES deliberately do NOT: three 64px circles and their names
     fit one row at 375 with room over, and stacking them would turn a colour picker into three
     screens of scrolling for a choice that is read at a glance. Measured, not assumed. */
  .mixes{grid-template-columns:1fr}
  
  .mobilebar,.flowbar{display:flex}
  /* Exactly ONE cart control at any width. Above this the header carries it; here the header
     hands it to the sticky bar, which has room for the running total beside it. */
  .cart-hd{display:none}
  /* FLOW-REVISION-6 Phase 7 (c6). The product page has no sticky flowbar to hand this off to -
     it never carried an order at all before this phase, so the rule above just meant "nothing
     to hide" there. Now that a saved order can show, it needs somewhere to surface on a phone;
     paintSavedCart() adds this class only once it has real lines to paint, so no other page's
     cart-hd (always backed by its own flowbar or mobilebar) is affected. */
  .cart-hd.pdp-cart-visible{display:block}
  .cta-row{gap:16px}
  .cta-row .btn{width:100%}
  footer.site .fcol{flex:1 1 100%;min-width:0}
  footer.site .fbase{justify-content:flex-start;gap:6px 18px}
  
  footer.site .fbase:has(.fmail){display:grid;grid-template-columns:1fr auto;gap:6px 18px}
  footer.site .fbase:has(.fmail) > span:first-child{grid-column:1;grid-row:1}
  footer.site .fbase .fmail{grid-column:1;grid-row:2;margin-left:0}
  footer.site .fbase .fdot{display:none}
  footer.site .fbase .flegal{grid-column:2;grid-row:2;text-align:right;margin-left:0}
}
@media(prefers-reduced-motion:reduce){*{transition-duration:.01ms !important}}
/* pre-order landing page (rebuilt 2026-08-26: too much text and too much explaining before the
   ask on the first pass). Price and form sit together in one split hero, photo one side, form
   the other, so both are visible together with as little scroll as the viewport allows. No
   separate deal-card paragraph, no step-by-step section: the numbers do the talking and one
   line of fine print under the button carries the only reassurance that matters.
   (No names in here: CSS comments are SERVED, and verify-site bans that one from the output.) */
/* THREE grid children, not two, so a phone can put the OFFER first and the photo second while a
   desktop keeps the photo down the whole left side. On a phone the previous build opened on a
   half-screen photograph with the headline and price below the fold, which is backwards for the
   traffic this page is built for: most of it arrives from a Facebook ad that ALREADY showed them
   a pumpkin, so the thing they have not seen yet is the price. */
/* The photo column is sized to land its box near the photos' own 0.75-0.80 ratio. The old
   1fr/1fr split made a ~700x700 square box and object-fit:cover blew every portrait photo up
   about 25% to fill it - the zoomed-in look reported 2026-08-26. 42% of 1240 against the
   ~660px-tall form column puts the box back in the photos' band, so the crop is a sliver. */
.prehero{display:grid;grid-template-columns:minmax(0,42%) 1fr;grid-template-areas:"img head" "img form";
  align-content:center;background:var(--surface);max-width:1240px;margin:0 auto}
.prehero-img{grid-area:img;position:relative;min-height:460px;overflow:hidden;background:var(--ink)}
.prehero-head{grid-area:head;display:flex;flex-direction:column;gap:13px;
  padding:clamp(26px,3.6vw,52px) clamp(26px,3.6vw,52px) 0}
.prehero-form{grid-area:form;display:flex;flex-direction:column;gap:15px;
  padding:15px clamp(26px,3.6vw,52px) clamp(26px,3.6vw,52px)}
.prehero-head h1{max-width:15ch}
.prehero-form .contactgrid{grid-template-columns:1fr;margin-bottom:0}
/* .smsopt carries margin-top:-10px for the wizard, where it follows a .contactgrid with 22px of
   bottom margin. On these panels it follows a radio pair instead, and that negative margin pulled
   the label up UNDER the radio cards - the clipped "Text me to finalize my design" line reported
   on 2026-08-26. Neutralised here rather than on .smsopt itself so the wizard is untouched. */
.prehero-form .smsopt,.postep .smsopt{margin-top:0}
.dealrow{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.dealrow .strike{color:var(--ink-2);text-decoration:line-through;font-size:1.2rem}
.dealrow .pricebig{font-family:var(--display);font-weight:700;color:var(--accent-dark);
  font-size:clamp(2.1rem,4vw,2.7rem);line-height:1}
.dealrow .pill{display:inline-block;background:var(--ribbon-bg);border:1px solid var(--accent);
  color:var(--accent-dark);border-radius:999px;padding:5px 14px;font-weight:700;font-size:.84rem}
/* Unscoped from .prehero-head so step 2 gets the same treatment; nothing else on the site
   uses .today. */
.today{font-family:var(--sans);font-weight:600;color:var(--ink);font-size:1.02rem}
.todaynote{display:block;margin-top:5px;font-weight:400;font-size:.9rem;line-height:1.45;color:var(--ink-2)}
.fineprint{color:var(--ink-2);font-size:.82rem}
.fieldnote{display:block;margin-top:5px;font-size:.78rem;color:var(--ink-2)}
@media(max-width:1100px){
  /* Offer first, photo second, form third. The breakpoint sits at 1100, not 820: between about
     830 and 1100 the split layout gives the photo column a box far narrower than the photos'
     own ratio, so the stacked card row, which never crops anything, takes over instead. */
  .prehero{grid-template-columns:1fr;grid-template-areas:"head" "img" "form"}
  /* The photo stops being a full-bleed band and becomes a row of 4:5 cards with the next one
     peeking, which does three things a full-width band could not: it holds the photos at their
     OWN ratio so nothing is cropped, it costs about 300px instead of half the screen, and the
     peek says "there are more of these" without needing the arrows. */
  .prehero-head{padding-top:12px;gap:9px}
  .prehero-form{padding-top:6px}
  .prehero-img{grid-area:img;min-height:0;height:auto;background:transparent;padding-block:0 4px}
  /* Form metrics, scoped to this panel only: the wizard's .contactgrid and .field must not move. */
  .prehero-form .contactgrid{gap:10px}
  .prehero-form .field label{margin-bottom:5px}
  .prehero-form .field input[type=text],
  .prehero-form .field input[type=email],
  .prehero-form .field input[type=tel]{padding:10px 13px}
  /* The engraved landing carries ONE photograph here, not the carousel, so it needs its own
     bound: without a ratio a portrait shot eats the whole phone screen between the price and
     the button, which is the exact thing this rebuild exists to stop. */
  .landing-offer .prehero-img img{height:auto;aspect-ratio:4/3;border-radius:var(--radius)}
  /* And it drops BELOW the card on a stacked screen, unlike /preorder/, where the price lives up
     in the head and the photo can sit between it and the form. Here the price is inside the card,
     so a photo above it pushed the whole offer down: measured at 390x844, the Start-your-order
     button landed 72px under the fold with the photo in between and clears it comfortably now.
     The photograph is still the next thing after, followed by the four-up strip. */
  .landing-offer{grid-template-areas:"head" "form" "img"}
}
.biz-sub{max-width:36em;margin:10px 0 0;color:var(--ink-2);font-size:1.05rem}
.biz-screen{display:block}
.biz-screen[hidden],.biz-prevwrap[hidden],.biz-words[hidden],.biz-dropwrap[hidden],.biz-error[hidden],.biz-verdict[hidden],.biz-closed[hidden],.biz-linewrap[hidden],#biz-form[hidden],#biz-next-1[hidden],
.biz-rest[hidden],.biz-load[hidden],.biz-drop[hidden],.biz-filecard[hidden]{display:none}

.biz-steps{list-style:none;display:flex;gap:8px;margin:0 0 22px;padding:0;counter-reset:bizstep}
.biz-steps li{flex:1;counter-increment:bizstep;display:flex;align-items:center;gap:8px;padding:10px 12px;border-radius:var(--btn-radius);
  background:var(--card);border:1px solid var(--rule);font-size:.9rem;font-weight:600;color:var(--ink-2);line-height:1.2}
.biz-steps li::before{content:counter(bizstep);flex:none;display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;
  border-radius:50%;background:var(--rule);color:var(--ink);font-weight:700;font-size:.85rem}
.biz-steps li.on{border-color:var(--accent);color:var(--ink)}
.biz-steps li.on::before,.biz-steps li.done::before{background:var(--accent);color:#fff}
@media(max-width:640px){.biz-steps li{font-size:0;gap:0;justify-content:center;padding:8px}.biz-steps li.on{font-size:.86rem;gap:8px;flex:3}}
.biz-split{display:grid;grid-template-columns:1fr;gap:26px;align-items:start}
@media(min-width:900px){.biz-split{grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);gap:44px}.biz-left{position:sticky;top:20px}}
.biz-stage{position:relative;border:1px solid var(--rule);border-radius:var(--radius);background:var(--card);overflow:hidden;box-shadow:var(--shadow)}
.biz-rest .showpix,.biz-load .showpix{aspect-ratio:4/5}
.biz-rest .showslide img,.biz-load .showslide img{object-fit:cover}
.biz-load{display:flex;flex-direction:column}
.biz-loadtop{display:flex;flex-direction:column;align-items:center;gap:10px;padding:20px 20px 16px;text-align:center}
.biz-loadhead{margin:0;font-family:var(--display);font-weight:700;font-size:1.25rem;color:var(--ink)}
.biz-bar{width:min(260px,80%);height:11px;border-radius:6px;background:var(--rule);overflow:hidden}
.biz-bar i{display:block;height:100%;width:0;background:var(--accent);border-radius:6px;transition:width .9s linear}
.biz-loadstage{margin:0;font-size:.9rem;color:var(--ink-2)}
.biz-prevwrap img{display:block;width:100%;height:auto;background:var(--bg)}
.biz-prevtext{padding:14px 18px 18px}
.biz-note{margin:0;color:var(--ink-2);font-size:.92rem}
.biz-verdict{margin:12px 0 0;padding:12px 14px;border-radius:var(--btn-radius);background:var(--bg);border-left:4px solid var(--accent);font-weight:600;color:var(--ink)}
.biz-split.on2 .biz-verdict,.biz-split.on3 .biz-verdict,.biz-split.on2 .biz-note,.biz-split.on3 .biz-note{display:none}
@media(max-width:899px){
  .biz-split.on2 .biz-prevwrap img,.biz-split.on3 .biz-prevwrap img{max-height:260px;width:auto;margin-inline:auto}
  .biz-split.on2 .biz-rest,.biz-split.on3 .biz-rest{display:none}
  .biz-split.on2 .biz-left:not(:has(.biz-prevwrap:not([hidden]))),.biz-split.on3 .biz-left:not(:has(.biz-prevwrap:not([hidden]))){display:none}
}
.biz-right .orderform{margin:0}
.biz-next-inline{width:100%;margin-top:16px}
@media(min-width:900px){.biz-next-inline{display:none}}
.biz-split.on2 .biz-next-inline,.biz-split.on3 .biz-next-inline{display:none}
.biz-price{display:flex;align-items:baseline;gap:8px;margin:-4px 0 18px}
.biz-price b{font-family:var(--display);font-size:1.6rem;color:var(--accent-dark)}
.biz-price span{color:var(--ink-2);font-size:.95rem}
.biz-drop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;text-align:center;
  min-height:190px;padding:26px 22px;border:2px dashed var(--rule);border-radius:var(--radius);background:var(--bg);cursor:pointer;
  color:var(--ink);transition:border-color .15s ease,background-color .15s ease}
.biz-drop:hover,.biz-drop.over{border-color:var(--accent);background:var(--card)}
.biz-drop svg{width:52px;height:52px;color:var(--accent)}
.biz-drop b{font-size:1.06rem;max-width:22em}
.biz-drop em{font-style:normal;color:var(--ink-2);font-size:.9rem}
.biz-file{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.biz-filecard{display:flex;align-items:center;gap:16px;padding:14px;border:2px solid var(--accent);border-radius:var(--radius);background:var(--card);
  animation:bizpop .25s ease-out}
@keyframes bizpop{from{transform:scale(.97);opacity:.4}to{transform:none;opacity:1}}
.biz-thumb{flex:none;width:132px;height:132px;border-radius:8px;border:1px solid var(--rule);display:flex;align-items:center;justify-content:center;overflow:hidden;
  background-color:#fff;background-image:linear-gradient(45deg,#eee 25%,transparent 25%),linear-gradient(-45deg,#eee 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#eee 75%),linear-gradient(-45deg,transparent 75%,#eee 75%);
  background-size:16px 16px;background-position:0 0,0 8px,8px -8px,-8px 0}
.biz-thumb img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
.biz-fileinfo{display:flex;flex-direction:column;gap:4px;min-width:0}
.biz-fname{margin:0;font-weight:700;color:var(--ink);word-break:break-all}
.biz-fsize{font-style:normal;color:var(--ink-2);font-size:.9rem}
.biz-change{margin-top:6px;color:var(--accent-dark);font-weight:700;text-decoration:underline;cursor:pointer;font-size:.95rem}
@media(max-width:480px){.biz-thumb{width:96px;height:96px}}
.biz-fieldlab{display:block;margin:24px 0 12px;font-family:var(--display);font-weight:700;font-size:1.15rem;color:var(--ink)}
#biz-s1 .biz-actions .btn,#biz-s2 #biz-next-2,#biz-s3 #biz-pay{flex:1 1 auto;min-width:200px}
.biz-see-busy{opacity:.7;cursor:progress}
#biz-see.biz-see-again{background:var(--card);color:var(--accent-dark);box-shadow:inset 0 0 0 2px var(--accent)}
.biz-toggle{display:inline-block;margin:14px 0 0;background:none;border:0;padding:0;color:var(--accent-dark);
  font:inherit;font-weight:700;text-decoration:underline;cursor:pointer}
.biz-styles{display:grid;gap:10px;margin:14px 0}
.biz-styles .opt{margin:0}
.biz-lines{display:grid;gap:12px;margin:0 0 6px}
.biz-lines .field input[type=text]{max-width:100%}
.biz-actions{margin:22px 0 0;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.biz-error{margin:14px 0 0;color:var(--timer);font-weight:700}
.biz-closed{padding:clamp(20px,2.4vw,32px);background:var(--card);border:1px solid var(--rule);border-radius:var(--radius);font-weight:700}
.biz-sizes{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:0 0 18px}
.biz-sizes .pack{margin:0}
.biz-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin:0 0 16px}
.biz-total{margin:16px 0 0;font-weight:700}
.biz-addr[hidden],.biz-soldout[hidden]{display:none}
.biz-addr{margin:6px 0 10px}
.biz-zipmsg{margin:6px 0 0;color:var(--ink-2);font-size:.92rem}
.biz-soldout{margin:6px 0 0;font-weight:700;color:var(--timer)}
.biz-sizes .pack.soldout{opacity:.55}
.biz-actions .biz-toggle{margin:0 12px 0 0}
.biz-step{display:flex;align-items:center;gap:10px}
.biz-step input{width:72px;min-height:44px;padding:10px 8px;border:1px solid var(--rule);border-radius:var(--btn-radius);background:var(--card);
  font:inherit;font-weight:700;text-align:center;color:var(--ink)}
.biz-stepbtn{min-width:44px;min-height:44px;padding:0 14px;border:1px solid var(--rule);border-radius:var(--btn-radius);background:var(--card);
  font:inherit;font-size:1.3rem;font-weight:700;line-height:1;color:var(--ink);cursor:pointer}
.biz-stepbtn:hover:not(:disabled){border-color:var(--accent)}
.biz-stepbtn:disabled{opacity:.45;cursor:default}
.biz-colornote{margin:0 0 16px;color:var(--ink-2);font-size:.96rem;max-width:34em}
.biz-colornote[hidden]{display:none}
.biz-colornote .biz-toggle{margin:0}
@media(max-width:640px){.biz-sizes{grid-template-columns:1fr}}
