/* ============================================================
   DEMO ONLY — NOT FOR THE REPO.
   Styles for mockup blocks. Nothing in this file ships.
   ============================================================ */

/* the visible mockup label, legible, never small grey type */
.skin-home .demo-tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border: 2px dashed var(--cheese-gold);
  border-radius: 8px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--cheese-gold);
  background: rgba(232, 163, 58, 0.08);
}
.skin-home .demo-tag--ink { color: #7B4A2A; border-color: #7B4A2A; background: rgba(123, 74, 42, 0.06); }

/* section headings for the mockup blocks */
.skin-home .reviews-h, .skin-home .rewards-h {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(clamp(2rem, 6vw, 3.2rem) * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  line-height: 0.95;
  color: #FFF8EC;
}
.skin-home .caterform-h {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(clamp(1.8rem, 5vw, 2.6rem) * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  line-height: 0.95;
  color: #FFF8EC;
}

/* ============================================================
   THE CHECKOUT (demo only, never the repo). Everything around the
   card slot is portable; .pay-cardslot is replaced wholesale by
   Clover's hosted field in production.
   ============================================================ */
.skin-home .cart-checkout { margin-top: 18px; display: grid; gap: 8px; }
.skin-home .cart-checkout-btn {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 12px 22px;
  background: var(--sign-red);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(1.05rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  border: 3px solid var(--outline);
  border-radius: 999px;
  box-shadow: 3px 3px 0 var(--outline);
  cursor: pointer;
}
.skin-home .cart-checkout-note {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--patty);
  text-align: center;
}
.skin-home .checkout {
  position: fixed;
  inset: 0;
  z-index: 75;
  overflow-y: auto;
  background: #100c0a;
  padding-bottom: 80px;
  /* ══ THE iOS KEYBOARD BLEED-THROUGH (S91, item 5) ═══════════════════════════
     🔴 UNVERIFIED FIX. This cannot be reproduced in this container: it needs a
     real iPhone with a software keyboard, and no desktop browser shrinks the
     visual viewport the way iOS does. Carlos must check it on his phone.

     ⚠️ THE FIRST TRACE WAS WRONG AND IS CORRECTED HERE, because a wrong
     mechanism written into a comment outlives the night it was guessed in.

     I wrote that iOS pans the visual viewport past this fixed element's edge and
     reveals the page underneath. Then I measured, and TWO things came back:

       - the overlay's z-order is FINE. elementsFromPoint at the middle of the
         screen returns div.checkout ABOVE button.prow, so the menu is behind it
         exactly as intended. Verified on the deployed sha too, in a frozen
         worktree - so nothing about this was a regression either.
       - THE CHECKOUT RENDERS ITS OWN COPY OF THE SITE FOOTER, inside its own
         scroll. footer.footer returns chk.contains() === TRUE, at scrollTop 436
         of scrollHeight 1304. What appears below the card panel is not the page
         bleeding through: it is this screen's own footer, scrolled into view.

     So the likeliest explanation for what Carlos saw - "menu artwork and the
     cheese graphic under the keyboard area" - is the badge and links in THAT
     footer, not a viewport bug. It is not established either way: I have no
     iPhone here and cannot watch what the keyboard does.

     WHAT THIS SHADOW IS, HONESTLY. A precaution against a mechanism that is NOT
     established, kept only because it cannot cost anything: box-shadow affects
     no layout, no scrolling and no hit-testing, so if a real bleed-through does
     happen on hardware, any revealed edge is the overlay's own ground. It is
     NOT described as the fix for what Carlos reported, and block 158 says the
     same thing in its own header.

     WHY A SHADOW AND NOT dvh OR A SCROLL LOCK.
       - `100dvh` describes the visual viewport, which is the thing that SHRANK.
         It cannot paint ground the element does not occupy. Kept below as a
         floor because it costs nothing, but it is not the fix.
       - a body scroll-lock is the textbook modal answer and is probably right,
         but it changes scroll restoration on close - a regression I cannot test
         here, the night before a client demo. Named as the next step if this
         is not enough rather than shipped blind.
       - a spread box-shadow paints an opaque ring 100vmax beyond this element
         in EVERY direction, in the overlay's own colour, and box-shadow does
         not affect layout, scrolling or hit-testing at all. Whatever iOS
         reveals on any edge is then the same flat #100c0a as the overlay.
         It cannot regress anything; the worst case is that it was unnecessary.

     The colour is the SAME token as the background above, deliberately: two
     places painting one ground is how they drift. Block 158 asserts they match
     rather than asserting a hex. */
  box-shadow: 0 0 0 100vmax #100c0a;
  min-height: 100dvh;
}
.skin-home .checkout-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 22px;
  padding-bottom: 30px;
}
.skin-home .checkout-brand { display: flex; align-items: center; gap: 12px; }
.skin-home .checkout-back {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(0.95rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: var(--cheese-gold);
}
.skin-home .checkout-back:hover { color: var(--butter); }
.skin-home .checkout-body { max-width: 480px; margin: 0 auto; padding: 0 20px; display: grid; gap: 18px; }
.skin-home .checkout-h {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(clamp(1.9rem, 6vw, 2.6rem) * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  line-height: 0.95;
  color: #FFF8EC;
}
.skin-home .checkout-sub { margin: 0; font-family: var(--font-body); font-size: 0.95rem; line-height: 1.55; color: #E8DFCE; }
/* the step card: the shop's sticker construction on the dark ground */
.skin-home .checkout-card {
  background: var(--butter);
  border: 3px solid var(--outline);
  border-radius: 16px;
  box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.5);
  padding: 22px 20px;
  display: grid;
  gap: 14px;
}
.skin-home .checkout-form { display: grid; gap: 14px; }
.skin-home .checkout-field { display: grid; gap: 6px; }
.skin-home .checkout-label { font-family: var(--font-body); font-weight: 700; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--patty); }
.skin-home .checkout-input {
  min-height: 48px;
  padding: 10px 14px;
  border: 2px solid var(--outline);
  border-radius: 10px;
  background: #FFF8EC;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--outline);
}
.skin-home .checkout-input[readonly] { color: #4A3A2C; background: #FBF2DC; }
/* ── THE ALLERGY NOTE (S133b) ─────────────────────────────────────────────────
   It IS a .checkout-input - same border, ground, radius and type - so it reads
   as one of the fields rather than a bolted-on box. Only what a <textarea>
   needs differs:
     resize: vertical    a customer with three allergies can see all three;
                         horizontal resize would break the panel's own width.
     min-height          two rows plus the padding, and comfortably past the
                         44px tap floor the whole checkout is held to.
     line-height         .checkout-input sets none, and a textarea without one
                         renders its second line tight against the first.
   NO transition and NO animation anywhere in this block, which is what makes
   it reduced-motion safe: there is no motion to suppress, so there is nothing
   for a @media (prefers-reduced-motion) rule to turn off. A rule that exists
   only to disable motion nobody added is a comment pretending to be a guard. */
.skin-home .checkout-note {
  display: block;
  width: 100%;
  min-height: 72px;
  line-height: 1.45;
  resize: vertical;
}
/* the live count. Quiet, because it is a convenience for the eye - the LIMIT
   is what the screen reader gets, once, through aria-describedby. */
.skin-home .checkout-count {
  margin: 6px 0 0;
  font-variant-numeric: tabular-nums;
}
.skin-home .checkout-seg { display: flex; gap: 8px; }
.skin-home .checkout-seg-btn {
  flex: 1 1 0;
  min-height: 48px;
  padding: 8px 14px;
  background: #FFF8EC;
  border: 2px solid var(--outline);
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(0.9rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: var(--outline);
  cursor: pointer;
}
.skin-home .checkout-seg-btn.is-on {
  background: var(--outline);
  border-color: var(--outline);
  color: var(--cheese-gold);
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.35);
}
.skin-home .checkout-err {
  margin: 0;
  padding: 12px 14px;
  border: 2px dashed var(--sign-red);
  border-radius: 10px;
  font-family: var(--font-body);
  font-size: 0.9rem;
  line-height: 1.5;
  color: #7B1F1A;
  background: rgba(200, 50, 42, 0.08);
}
.skin-home .checkout-cta {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 12px 22px;
  background: var(--sign-red);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(1.05rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  border: 3px solid var(--outline);
  border-radius: 999px;
  box-shadow: 3px 3px 0 var(--outline);
  cursor: pointer;
}
.skin-home .checkout-demo-note { margin: 0; font-family: var(--font-body); font-size: 0.78rem; line-height: 1.5; color: var(--patty); text-align: center; }
.skin-home .checkout-backlink {
  justify-self: center;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--patty);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.skin-home .checkout-summary {
  border: 2px dashed rgba(20, 16, 14, 0.35);
  border-radius: 12px;
  padding: 14px 16px;
  display: grid;
  gap: 6px;
  background: #FFF8EC;
}
.skin-home .checkout-sumline { margin: 0; display: flex; justify-content: space-between; gap: 12px; font-family: var(--font-body); font-size: 0.92rem; color: #3A2A1C; }
.skin-home .checkout-sumrule { border-top: 2px dashed rgba(20, 16, 14, 0.35); margin: 4px 0; }
.skin-home .checkout-sumtotal { font-family: var(--font-display); font-size: calc(1.05rem * var(--type-scale)); letter-spacing: calc(0.5px + var(--type-tracking)); text-transform: uppercase; color: var(--outline); }
.skin-home .pay-cardslot {
  border: 2px dashed #B77E22;
  border-radius: 12px;
  padding: 14px 16px;
  display: grid;
  gap: 12px;
  background: rgba(232, 163, 58, 0.12);
}
.skin-home .pay-cardslot-h {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(0.9rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: #8A5A14;
}
.skin-home .pay-cardslot .isheet-mods-sub { color: #8A5A14; opacity: 0.9; }
.skin-home .pay-cardrow { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.skin-home .checkout-ticket { margin: 0; }
/* the receipt feeds out of the printer once, then sits still */
@keyframes st-print {
  from { clip-path: inset(0 0 100% 0); transform: translateY(-8px); }
  to { clip-path: inset(0 0 0 0); transform: translateY(0); }
}
.skin-home .checkout-ticket .cart-ticket-paper {
  animation: st-print 1100ms cubic-bezier(0.16, 0.84, 0.3, 1) both;
}
@media (prefers-reduced-motion: reduce) {
  .skin-home .checkout-ticket .cart-ticket-paper { animation: none; }
}
.skin-home .checkout-ticket .cart-ticket-paper { max-width: 420px; margin: 0 auto; }
.skin-home .ticket-meta { margin: 0; font-size: 0.82rem; }
.skin-home .ticket-lines { display: grid; gap: 8px; text-align: left; }
.skin-home .ticket-line-head { margin: 0; display: flex; justify-content: space-between; gap: 10px; font-weight: 700; font-size: 0.88rem; }
.skin-home .ticket-line-mod { margin: 0; padding-left: 12px; font-size: 0.78rem; opacity: 0.85; }
.skin-home .ticket-sum { margin: 0; display: flex; justify-content: space-between; gap: 10px; font-size: 0.85rem; }
.skin-home .ticket-total { font-weight: 800; font-size: 0.95rem; }
.skin-home .ticket-quiet { margin: 0 0 6px; font-size: 0.8rem; }
/* THE KEY TO THE ASTERISK (E6). Quiet, but not under the legibility floor:
   0.75rem is 12px, and the receipt's paper is cream, so this follows the
   cream colour law - patty brown, 5.6:1 secondary - rather than a grey that
   would measure fine on the dark screen and vanish on the paper. */
.skin-home .ticket-prov-note {
  margin: 8px 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--patty);
  text-align: left;
}
.skin-home .ticket-quiet a { text-decoration: underline; }
.skin-home .checkout-cta--done { max-width: 420px; margin: 0 auto; }
.skin-home .checkout .footer { margin-top: 84px; }

/* ============================================================
   THE CREST HEADER - one primitive, two surfaces (slice 31)
   ============================================================
   The smash-list popup is the quality bar on this site: a dark card with the
   cheese drip across the top, the badge, a gold kicker, a big two-tone display
   headline and a warm sub-line. The franchise page needed exactly that
   treatment, and the instruction was explicit about NOT copy-pasting the
   popup's CSS into a second place, because this codebase has paid twice for
   duplicated styling (the per-element override written into both home.css and
   demo-only.css, and the six-way red button that had grown a sixth private
   copy in .franchise-send).

   So the shared part was pulled out rather than copied. What is shared:
     .st-sheet        the card surface itself - ground, keyline, radius, offset
     .st-crest-drip   the cheese drip that hangs off the top edge
     .st-crest        the header block: badge, kicker, headline, sub-line
   What stays private to each surface is only what genuinely differs: the popup
   is FIXED and centred in the viewport with a close button; the franchise
   sheet is in the flow with a form under it.

   The popup's own .joinpop-drip / -badge / -kicker / -h / -p rules are GONE,
   not left behind beside these - the point of an extraction is that there is
   one home, and two homes that agree today are two homes that drift. */

.skin-home .st-sheet {
  background: linear-gradient(180deg, #1c0a08, #120b09);
  border: 3px solid var(--outline);
  border-radius: 16px;
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.55);
}
.skin-home .st-crest-drip {
  position: absolute;
  top: 0; left: 0; right: 0;
  line-height: 0;
  pointer-events: none;
  /* The cheese drip is a full-bleed decorative SVG and it rounded 2.5px past
     the card's right edge at 320 (only at 320 - the phone pass caught it).
     Block 17 already carries a 4px sub-pixel exemption for exactly this kind
     of art; making it FIT costs one line and needs no exemption at all, so the
     guard stays strict instead.

     overflow:hidden alone was not the fix and the reason is worth keeping: it
     clips the PAINT but leaves the child's getBoundingClientRect untouched, so
     the element still measures past the edge. Same family as the closed
     <details> that still reports 48px rows. The art has to actually be the
     right width. */
  overflow: hidden;
}
.skin-home .st-crest-drip svg { display: block; width: 100%; }
.skin-home .st-crest { display: block; text-align: center; }
.skin-home .st-crest-badge {
  display: block;
  width: 58px; height: 58px;
  margin: 0 auto 12px;
  /* no circular mask: the v2 badge has clean transparent margins (see the
     note in part1-repo/home-additions.css where the same mask was removed) */
}
.skin-home .st-crest-kicker {
  margin: 0 0 8px;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cheese-gold);
}
.skin-home .st-crest-h {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(clamp(1.9rem, 6vw, 2.7rem) * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  line-height: 0.95;
  color: #FFF8EC;
}
.skin-home .st-crest-sub {
  margin: 0 auto 20px;
  max-width: 400px;
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.55;
  color: #E8DFCE;
}

/* the smash list popup: the crest above, plus the only things that are its own */
.skin-home .joinpop {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 80;
  width: min(520px, 94vw);
  max-height: 92vh;
  overflow-y: auto;
  padding: 58px 34px 28px;
  text-align: center;
}
.skin-home .joinpop .joinform { flex-direction: column; align-items: stretch; gap: 10px; }
.skin-home .joinpop .joinform input { width: 100%; text-align: center; flex: 0 0 auto; }
.skin-home .joinpop .joinform .btn { width: 100%; flex: 0 0 auto; }
.skin-home .joinpop .consent { margin-top: 16px; text-align: left; }
.skin-home .joinpop-x {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 2;
  width: 44px; height: 44px;
  border: 3px solid var(--outline);
  border-radius: 50%;
  background: var(--butter);
  color: var(--outline);
  font-size: 1.3rem;
  line-height: 1;
}

/* 2.1 WATCH THE SMASH */
.skin-home .watchbtn { position: relative; }
.skin-home .watchbtn-tag {
  position: absolute;
  top: -12px; right: -10px;
  padding: 3px 9px;
  background: var(--cheese-gold);
  color: var(--outline);
  border: 2px solid var(--outline);
  border-radius: 999px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.62rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  transform: rotate(3deg);
}
.skin-home .watchmodal {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  pointer-events: none;
}
.skin-home .watchmodal-card {
  pointer-events: auto;
  position: relative;
  width: min(720px, 94vw);
  background: var(--butter);
  border: 3px solid var(--outline);
  border-radius: 14px;
  box-shadow: 7px 7px 0 var(--outline);
  overflow: hidden;
  max-height: 92vh;
  overflow-y: auto;
}
.skin-home .watchmodal-poster { position: relative; aspect-ratio: 16 / 9; background: #14100E; }
.skin-home .watchmodal-poster img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: 0.82; }
.skin-home .watchmodal-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(1.5rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: #FFF8EC;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.7);
}
/* A SMALL MARK WITH A LARGE TARGET (Carlos, slice 28: it "competes with the
   video it is supposed to sit quietly on top of").
   The two sizes are now DIFFERENT NUMBERS and must not be confused:
     TAP   44 x 44 - the button's own box, untouched, still the floor
     MARK  28 x 28 - the painted disc, a ::before inside that box
   The button itself is transparent, so what a customer sees is 28px and what a
   thumb hits is 44px. Block 72.4 asserts both separately at 320/390/1280 for
   exactly that reason - measuring the box and calling it "the visible size" is
   the confusion this splits apart.
   The ink ring and the butter fill stay: they are what keeps the glyph legible
   over a bright frame AND over the ink letterbox, and forced-colors keeps a
   border where it would drop a background. */
.skin-home .watchmodal-x {
  position: absolute;
  top: 8px; right: 8px;
  width: 44px; height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--outline);
  font-size: 1.05rem;
  line-height: 1;
  padding: 0;
}
.skin-home .watchmodal-x { z-index: 2; }
.skin-home .watchmodal-x::before {
  content: "";
  position: absolute;
  z-index: -1;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--butter);
  border: 2px solid var(--outline);
  box-sizing: border-box;
}


/* 2.2 WHAT THE BLOCK SAYS — example reviews + the real photo strip, one section */
.skin-home .reviews { padding: 72px 0 64px; }
.skin-home .reviews-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  margin-bottom: 30px;
}
.skin-home .reviews-h-gold { color: var(--cheese-gold); }
.skin-home .reviews-grid {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
@media (max-width: 820px) { .skin-home .reviews-grid { grid-template-columns: 1fr; } }
.skin-home .review-card {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 14px;
  padding: 24px 22px;
  display: grid;
  align-content: start;
}
/* THE GAP WAS INERT, and it looked like it was working. `gap: 14px` lived on
   .review-card — whose only grid child is the anchor wrapping all three parts
   — so stars, body and attribution measured 0px apart while the declaration
   sat there reading as if it spaced them. The layout belongs on the ANCHOR,
   which is the element that actually has three children. Measured before:
   starsToQuote 0, quoteToName 0. (slice 3b3) */
.skin-home .review-card > .review-link {
  display: grid;
  gap: 14px;
  align-content: start;
  height: 100%;
}
/* the mark is on the CARD, never on the sentence: a gold outline drawn
   INSIDE the border box so it cannot shift the grid by a pixel */
.skin-home .review-card:hover,
.skin-home .review-card:focus-within {
  border-color: var(--cheese-gold);
  box-shadow: inset 0 0 0 1px var(--cheese-gold);
}
.skin-home .review-stars { display: block; line-height: 0; }
.skin-home .review-quote { margin: 0; font-family: var(--font-body); font-size: 1.05rem; line-height: 1.6; color: #E8DFCE; }
.skin-home .review-name {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9C948A;
}
.skin-home .reviews-ask {
  margin: 24px 0 44px;
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.55;
  color: #E8DFCE;
  max-width: 560px;
}

/* 2.3 SMASHTIME REWARDS */
.skin-home .rewards { padding: 64px 0; }
.skin-home .rewards-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center; }
@media (max-width: 820px) { .skin-home .rewards-grid { grid-template-columns: 1fr; } }
.skin-home .rewards-copy { display: grid; gap: 14px; justify-items: start; }
.skin-home .rewards-p { margin: 0; font-family: var(--font-body); font-size: 1.02rem; line-height: 1.6; color: #E8DFCE; max-width: 480px; }
.skin-home .rewards-card {
  background: var(--butter);
  border: 3px solid var(--outline);
  border-radius: 14px;
  box-shadow: 7px 7px 0 var(--outline);
  padding: 24px 22px;
  display: grid;
  gap: 14px;
  transform: rotate(-0.8deg);
}
.skin-home .rewards-card-h {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(1.3rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: var(--outline);
}
.skin-home .rewards-field { display: grid; gap: 6px; }
.skin-home .rewards-label { font-family: var(--font-body); font-weight: 700; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: #7B4A2A; }
.skin-home .rewards-input {
  min-height: 48px;
  padding: 10px 14px;
  border: 2px solid var(--outline);
  border-radius: 10px;
  background: #FFF8EC;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--outline);
}
.skin-home .rewards-usual { margin: 0; padding: 12px 14px; border: 2px dashed #7B4A2A; border-radius: 10px; font-family: var(--font-body); font-size: 0.92rem; line-height: 1.5; color: #3A2A1C; }

/* 2.4 the catering form, its own dark page */
.skin-home .caterpage {
  position: fixed;
  inset: 0;
  z-index: 70;
  overflow-y: auto;
  background: #100c0a;
  /* 80px of breathing room under the LAST CONTENT of a subpage. Four of the
     five subpages (about, suggest, franchise, gallery) end in a paragraph or a
     form and want it. */
  padding-bottom: 80px;
}

/* ...but the catering page ends in a FOOTER, and a footer is the base of a
   page: it is supposed to reach the floor. Scrolled to the end of the panel it
   stopped 80.31px short at 1280x800 and 79.98px short on iPhone 14, with the
   panel's flat #100c0a below it - the "footer sits too high" report.

   THE 80px WAS RESERVING AGAINST NOTHING HERE. .caterpage is
   position:fixed; inset:0 with an opaque background at z-index 70, and every
   piece of fixed chrome that could have justified a reserve sits BELOW it:
   .mobilebar at z-index 55, .cartbtn at 65 (auto on a laptop). Measured in all
   four states - laptop and phone, cart empty and cart loaded - nothing visible
   occupies that band: document.elementFromPoint at the bottom-centre returns
   .caterpage every time. The panel hides the very chrome the padding was
   clearing.

   Scoped with :has() rather than by removing the padding outright, so the four
   footer-less subpages keep the breathing room they actually use. A browser
   without :has() simply keeps today's behaviour, which is the safe direction. */
.skin-home .caterpage:has(> footer.footer) { padding-bottom: 0; }

/* THE CHECKOUT IS THE SECOND PANEL OF THAT SET (fix 2, 2026-08-23), and it was
   missed the first time because the sweep found the constant on .caterpage and
   fixed the instance rather than the set.

   Identical shape: .checkout is fixed, inset:0, opaque and z-index 75, while
   .mobilebar is 55 and .cartbtn is 65 — so both fixed controls sit UNDERNEATH
   it and the 80px band reserved room for chrome no customer can see through the
   panel. Measured with the cart loaded: the footer's bottom stopped 79.72px
   above the floor on iPhone 14 and 80.13px on a 1280x800 laptop, and
   elementFromPoint at the bottom-centre of the screen returned div.checkout —
   the panel's own ground, nothing else.

   ⚠️ IT WAS DEAD SPACE, NOT AN OBSTRUCTION, and that distinction is the
   correction to make. "Tell us how we did" was ALREADY reachable before this
   change: 111.59 x 44px, inside the viewport, with elementFromPoint at its
   centre returning a.footsuggest itself on both profiles. Nothing was
   unreachable and nothing is being rescued here — the panel simply ended 80px
   early.

   0 rather than a new constant, because that is what the catering panel above
   settled on and its footer reaches the floor with both links still
   hit-testable (block 97.1). A second number here would be a second decision
   about the same question. Block 97.3 asserts it the same way. */
.skin-home .checkout:has(> footer.footer) { padding-bottom: 0; }
.skin-home .caterpage-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-top: 22px;
  padding-bottom: 34px;
  position: relative;
}
.skin-home .caterpage-brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.skin-home .caterpage-back {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(0.95rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: var(--cheese-gold);
}
.skin-home .caterpage-back:hover { color: var(--butter); }
/* SIDE PADDING, and it was MISSING here and on .subpage-body until slice 7A
   measured it. globals.css gives .wrap `padding: 0 24px`, but the skin opens
   with an `all: unset` neutralizer, so inside .skin-home a .wrap has no
   padding at all unless the skin re-declares it — and neither page body did.
   Measured at 320px: the About page had 10 elements sitting at left: 0 and the
   Gallery page 7, headings and body copy touching the glass. 20px matches
   .e1-wrap, which is the house value everywhere else in this skin. */
.skin-home .caterpage-body { max-width: 780px; margin: 0 auto; padding-left: 20px; padding-right: 20px; }
.skin-home .caterpage .caterform-head { justify-items: center; text-align: center; }
.skin-home .caterpage .caterform-grid { margin: 0 auto; }
.skin-home .caterpage .caterform-actions { justify-content: center; }
/* REUSED RATHER THAN RE-DECLARED (slice 32). This rule was written for the
   catering page's quiet call line and had no markup left using it; the
   franchise and suggestion pages now carry the one TRUE sentence out of their
   retired gates, and both are .caterpage surfaces, so the rule fits with
   nothing new written. Margin flipped to sit ABOVE the form rather than under
   a page's last block. */
.skin-home .caterpage-call {
  margin: 0 0 22px;
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.5;
  color: #CFC7BA;
}
.skin-home .caterpage-call a { text-decoration: underline; text-underline-offset: 3px; }
.skin-home .caterpage .footer { margin-top: 84px; }
.skin-home .caterform-head { display: grid; gap: 14px; justify-items: start; margin-bottom: 26px; }
.skin-home .caterform-sub { margin: 0; font-family: var(--font-body); font-size: 1rem; line-height: 1.55; color: #E8DFCE; max-width: 560px; }
.skin-home .caterform-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px 18px; max-width: 720px; }
@media (max-width: 640px) { .skin-home .caterform-grid { grid-template-columns: 1fr; } }
.skin-home .caterform-field { display: grid; gap: 6px; }
.skin-home .caterform-field--wide { grid-column: 1 / -1; }
.skin-home .caterform-label { font-family: var(--font-body); font-weight: 700; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: #CFC7BA; }
.skin-home .caterform-input, .skin-home .caterform-select, .skin-home .caterform-textarea {
  min-height: 48px;
  padding: 10px 14px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 10px;
  background: #1a1512;
  font-family: var(--font-body);
  font-size: 1rem;
  color: #fff;
  color-scheme: dark;
}
.skin-home .caterform-input::placeholder, .skin-home .caterform-textarea::placeholder { color: #8a8782; }
.skin-home .caterform-textarea { min-height: 110px; resize: vertical; }
/* THE SELECT'S BOX IS OURS IN EVERY ENGINE (S127/1, docs/S122-mac-env.md finding 1).
   The Mac seat measured select#cf-mode at 25px on WebKit against a 44px tap
   floor, while every sibling control in this same form was 48px in both
   engines. The mechanism is not the markup and not the numbers above: a native
   <select> with `appearance: auto` is drawn by the PLATFORM, so WebKit sized
   it from the font and reported `min-height: 18px; padding: 0px` - the rule
   above never applied to it at all.

   `appearance: none` takes the control off the native widget path in EVERY
   engine, so the min-height and padding already declared above size this the
   same way they size the inputs beside it. Deliberately NOT an
   `@supports (-webkit-...)` block or a second number: a rule that only holds
   on the engine somebody remembered to special-case is the defect wearing a
   fix, and it goes stale the first time the design moves.

   Losing the native control loses its arrow, so one is drawn back - an inline
   SVG chevron as a data URI, never a text glyph or an emoji - with right
   padding to hold it clear of the longest option. background-color is restated
   because the shared rule above sets `background` as a SHORTHAND, and this
   project has already paid for a longhand losing to an earlier shorthand.
   Block 211 measures all four properties, in both engines. */
.skin-home .caterform-select {
  appearance: none;
  -webkit-appearance: none;
  background-color: #1a1512;
  background-image: url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'12\' height=\'8\' viewBox=\'0 0 12 8\' fill=\'none\'%3E%3Cpath d=\'M1 1.5 6 6.5 11 1.5\' stroke=\'%23CFC7BA\' stroke-width=\'1.75\' stroke-linecap=\'round\' stroke-linejoin=\'round\'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
  padding-right: 38px;
}
.skin-home .caterform-actions { grid-column: 1 / -1; display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.skin-home .caterpage .btn {
  display: inline-block;
  min-height: 52px;
  padding: 12px 22px;
  background: var(--sign-red);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(1rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  border: 3px solid var(--outline);
  border-radius: 999px;
  box-shadow: 3px 3px 0 var(--outline);
  cursor: pointer;
}
.skin-home .caterform-demo-state {
  grid-column: 1 / -1;
  margin: 0;
  padding: 16px 18px;
  border: 2px dashed var(--cheese-gold);
  border-radius: 10px;
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.55;
  color: #E8DFCE;
  background: rgba(232, 163, 58, 0.07);
}
.skin-home .caterform-demo-state a { text-decoration: underline; }


/* ---- THE DEMONSTRATION MARKER IS GONE --------------------------------
   .footdemo styled the "Demonstration build" line that slice 14 removed on
   Carlos's instruction, and .footmade styled "Made in the Bronx" beside it.
   Both rules outlived their markup by a slice; deleted in slice 15's cleanup
   on his ruling. The note is kept so nobody re-adds the rule wondering where
   the marker went: it was removed on purpose, not lost.
   ------------------------------------------------------------------- */

/* ---- CART BUTTON vs THE STICKY MOBILE BAR (fixed at deploy time) ------
   MEASURED BUG, not a preference. At 390x844 with the cookie notice
   dismissed:

     .cartbtn    fixed, bottom: 18px, height 56px  -> occupies 18..74px
     .mobilebar  fixed, bottom: 0,   height 63px   -> occupies  0..63px
                 z-index 60, and .cartbtn declares none (auto = 0)

   So 45 of the cart button's 56 pixels sit UNDER the bar, and the bar wins
   the stack. document.elementFromPoint at the button's centre returns the
   bar's DIRECTIONS link. On a phone the client taps YOUR CART and gets
   directions instead. That is the whole journey blocked at step five, and
   it is the reason to check a demo with a thumb rather than an eye.

   The lift is 70px because that is exactly what the bar already reserves
   for itself (`body:has(.mobilebar){padding-bottom:70px}`), so the button
   lands on the line the layout already treats as the floor. Scoped to the
   same max-width:780px query that creates the bar, so nothing moves on a
   laptop, where no bar exists.
   ------------------------------------------------------------------- */
@media (max-width: 780px) {
  .skin-home .cartbtn {
    /* THE LIFT READS THE BAR'S OWN HEIGHT, and the safe-area term is GONE on
       purpose (2026-08-23). The 70 here was never the bar's height - the note
       above says it was chosen because it is "exactly what the bar already
       reserves for itself", so it was chained to a second typed constant rather
       than to the bar. Three numbers described one bar: 63, 70, and 70 + the
       inset.

       DOUBLE-COUNT REMOVED: --mobilebar-h is the bar's MEASURED rect, and that
       rect already contains its own `padding-bottom: calc(10px +
       env(safe-area-inset-bottom))`. Adding env() again on top would lift the
       pill by the inset TWICE on any device that has one - invisible here,
       where this harness reports the inset as 0, and wrong on Carlos's phone.

       AND THE +12px IS DELIBERATE, where the old separation was not. Deriving
       the lift put the pill's bottom edge exactly ON the bar's top edge - gap
       0.00 - because the 70 had been carrying 7px of accidental clearance that
       only existed while the two constants disagreed. 12px is the site's own
       breathing room, the same number the footer reserves use, chosen rather
       than inherited from a mismatch. */
    bottom: calc(var(--cookienote-h, 0px) + var(--mobilebar-h, 70px) + 12px);
    z-index: 65;
  }
}

/* ---- TICKET A CONTRAST (fixed at deploy time) -------------------------
   MEASURED BUG on the demo's payoff screen.

   .cart-ticket-paper sets `background: #FCF8EC` but declares no `color`,
   and neither did the receipt's own line classes. Inside the checkout overlay (`.checkout{background:#100c0a}`)
   they therefore inherit that dark screen's light type: computed
   rgb(233,228,218) on #FCF8EC paper. Cream on cream. Every line of the
   receipt was effectively invisible except .cart-ticket-close, which is the
   one element that declares its own colour.

   It reads fine in the CART, where the surrounding context is already dark
   type, which is why it survived the design session: the bug only appears
   when the same component is placed on the dark checkout screen.

   Fixing it on the PAPER rather than on each line means anything added to
   the receipt later inherits a legible colour by default. Outline black on
   this cream is 14.4:1 per the colour law in CLAUDE.md, and the existing
   `opacity: .85` on .ticket-line-mod still gives the modifier lines their
   quieter step without going under the floor.
   ------------------------------------------------------------------- */
.skin-home .cart-ticket-paper {
  color: var(--outline);
}

/* ---- THE WATCH THE SMASH OVERLAY (rebuilt at deploy time) -------------
   FOUR DEFECTS, ONE ROOT CAUSE PLUS TWO CONTRAST MISSES.

   ROOT CAUSE, measured. `.watchmodal-poster` is `aspect-ratio: 16/9`, so at
   a 714px-wide card its box is 714x402. The poster image is 841x635, which
   at 714 wide wants to be 539 tall. `height: 100%` on the img does not
   resolve against an aspect-ratio-sized parent, so the image rendered at its
   own ratio and hung 137px BELOW the frame. `.watchmodal-poster` was
   `overflow: visible` and `.watchmodal-body` had NO background of its own
   (computed rgba(0,0,0,0)), so the burger painted straight through the
   caption panel: a dense scan of the panel found 12.7% burger pixels, and
   the strip under "VIDEO DOES NOT EXIST YET" was solid bun tone (184,139,83).
   That is the same class of bug as the Ticket A one: a container that looks
   like it has a surface but never declares it.

   Fixes, in the order the Director listed them:
   1. The poster is a clipped box: fixed ratio, absolutely-filled image,
      object-fit cover, overflow hidden, and its own top radius so the card's
      corners actually cut it.
   2. The caption panel gets a real opaque ground and sits above the poster,
      so nothing can ever paint through it again.
   3. The DEMO label is the honesty string, so it stops being the faintest
      thing on screen: solid outline-black plate, cream type. 14.4:1.
   4. FOOTAGE COMING moves onto a solid plate rather than sitting on a
      photograph. Its old worst case measured 1.94:1 at desktop and 1.00:1 at
      390px, where the type crossed the lit bun and vanished exactly.
   ------------------------------------------------------------------- */

/* 1. a properly clipped poster */
.skin-home .watchmodal-poster {
  overflow: hidden;
  border-radius: 11px 11px 0 0;   /* 14px card radius minus its 3px border */
}
.skin-home .watchmodal-poster img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.66;                  /* was .82; the plates do the work now */
}
/* a scrim so the plates sit on a settled ground rather than on chaos */
.skin-home .watchmodal-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(20,16,14,0.35) 0%, rgba(20,16,14,0.55) 60%, rgba(20,16,14,0.72) 100%);
}

/* 2. the caption panel owns its surface */

/* the play affordance: a slot, visibly inert, in the sticker language */
.skin-home .watchmodal-slot {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.skin-home .watchmodal-playmark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 62px;
  padding-left: 4px;              /* optical centring of a triangle */
  background: var(--cheese-gold);
  color: var(--outline);          /* 8.8:1 black on gold, the sticker device */
  border: 3px solid var(--outline);
  border-radius: 50%;
  box-shadow: 4px 4px 0 var(--outline);
}

/* 4. FOOTAGE COMING on a solid plate, never on the photograph */
.skin-home .watchmodal-play {
  position: static;
  display: inline-block;
  padding: 7px 16px;
  background: var(--outline);
  color: #F5DFA8;                 /* 14.4:1 on outline black */
  border-radius: 999px;
  font-size: calc(1.15rem * var(--type-scale));
  text-shadow: none;
}

/* 3. the honesty label, made the clearest thing in the overlay */
.skin-home .watchmodal .demo-tag--ink {
  background: var(--outline);
  color: #F5DFA8;                 /* 14.4:1 */
  border-color: var(--cheese-gold);
  border-style: dashed;
  opacity: 1;
}

/* ---- CHECKOUT HORIZONTAL OVERFLOW (fixed 2026-07-30, urgent) ----------
   THE BUG the Director photographed on a real iPhone: on the payment step
   the summary's figures were off the right edge, so Subtotal, Tax and TOTAL
   showed their LABELS WITH NO NUMBERS. The card border, the card dashed box,
   the CVC field and the PAY button were all cut off too.

   WHY EVERY CHECK MISSED IT. `globals.css:97` sets `body{overflow-x:hidden}`
   and `home.css:110` adds `overflow-x:clip`, so the page can never report a
   horizontal scrollbar: documentElement.scrollWidth stays EQUAL to
   clientWidth at every width, in every state. The overflow is not absent, it
   is silently CLIPPED. Measuring scrollWidth — the obvious probe, and the one
   the brief prescribed — reports "clean" on this site forever. The journey
   suite passed 21/21 for the same reason: it asserted the totals TEXT was
   present, and the text is present, just painted past the right edge.
   The only instrument that sees it is a per-element rect.right vs clientWidth
   walk, which is what the new guard does.

   THE CULPRIT was NOT a fixed pixel width. `.checkout-body` is already
   `max-width: 480px`. It is the CSS Grid / flex `min-width: auto` blowout:
   grid and flex items refuse to shrink below their min-content size, so the
   widest row propagated outward through every ancestor, each adding its own
   padding and border:

       .checkout-sumline  450px  (min-content of the row)
       .checkout-summary  486px  (450 + 16/16 padding + 2/2 border)
       .checkout-card     532px  (486 + 20/20 padding + 3/3 border)

   532px on a 320px phone put the figures at right: 511px. The max-width on
   the parent could not help, because the CHILDREN were the ones refusing to
   shrink. The cure is `min-width: 0` at every level of that chain, which
   restores the parent's authority, plus letting a totals row wrap so a figure
   that cannot fit beside its label drops UNDER it rather than off the screen.
   ------------------------------------------------------------------- */
.skin-home .checkout-body { width: 100%; }
.skin-home .checkout-body > *,
.skin-home .checkout-card,
.skin-home .checkout-card > *,
.skin-home .checkout-summary,
.skin-home .checkout-summary > *,
.skin-home .pay-cardslot,
.skin-home .pay-cardslot > *,
.skin-home .checkout-field,
.skin-home .checkout-ticket,
.skin-home .cart-ticket-paper { min-width: 0; }

/* a figure wraps under its label rather than off the screen */
.skin-home .checkout-sumline { flex-wrap: wrap; min-width: 0; }
.skin-home .checkout-sumline > span { min-width: 0; overflow-wrap: anywhere; }

/* expiry and CVC flex instead of holding two fixed columns */
.skin-home .pay-cardrow { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
.skin-home .checkout-input { width: 100%; min-width: 0; }

/* the receipt is the other place a long line could push wide */
.skin-home .ticket-line-head,
.skin-home .ticket-sum { flex-wrap: wrap; min-width: 0; }
.skin-home .ticket-line-head > span,
.skin-home .ticket-sum > span { min-width: 0; overflow-wrap: anywhere; }

/* ---- SKIP LINK (a11y, approved 2026-07-30) ----------------------------
   Off-screen until focused, not display:none — a display:none element is
   not focusable, and a skip link that cannot be focused is decoration.
   The class selector outscores the skin's `all: unset` on `.skin-home a`. */
.skin-home .e1-skiplink {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 12px 18px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  background: var(--outline);
  color: var(--butter);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.9rem;
  border: 2px solid var(--cheese-gold);
  border-radius: 0 0 10px 0;
  text-decoration: underline;
}
.skin-home .e1-skiplink:focus,
.skin-home .e1-skiplink:focus-visible { left: 0; }

/* 8. CARLOS'S TYPE WHEEL — REMOVED IN SLICE 23, and this note stands in its
   place so nobody re-adds it as a convenience. Carlos chose Anton on
   2026-08-16 and the tool's job ended there. It had covered customer UI TWICE
   before it went: it intercepted the cart pill at 320px (2026-08-15) and it
   sat entirely on top of the footer's suggestion link (2026-08-17), each time
   found by a test rather than by looking. A fixed demo control floats over
   whatever is in its band, and its band is always somebody's content.

   Its space reservation went with it. `.skin-home:has(.fontwheel)` no longer
   matches anything, so the foot of a phone page falls back to reserving only
   the phone bar — home.css's `.skin-home .footer` clearance. Block 67.4
   measures that fallback at 320/360/390/430 rather than assuming it. */

/* 9. THE ABOUT AND GALLERY PAGES (2026-08-16) — the caterpage chrome plus a
   /v2-shaped body: crumb, gold eyebrow, big cream display heading, subtitle,
   then alternating text-and-photo splits (About) or the photo wall (Gallery). */
.skin-home .subpage-body { max-width: 980px; margin: 0 auto; padding-left: 20px; padding-right: 20px; display: grid; gap: 34px; }
.skin-home .subpage-crumb { margin: 0; font-family: var(--font-body); font-size: 0.82rem; color: var(--muted); }
.skin-home .subpage-crumb a { color: var(--cheese-gold); }
.skin-home .subpage-head { display: grid; gap: 8px; }
.skin-home .subpage-eyebrow {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.76rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cheese-gold);
}
.skin-home .subpage-h {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(clamp(2.4rem, 8vw, 4rem) * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  line-height: 0.95;
  color: #FFF8EC;
}
.skin-home .subpage-sub { margin: 0; font-family: var(--font-body); font-size: 1rem; line-height: 1.55; color: #E8DFCE; }
.skin-home .subpage-h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(clamp(1.5rem, 4.5vw, 2.1rem) * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  line-height: 0.95;
  color: #FFF8EC;
}
.skin-home .subpage-p { margin: 0; font-family: var(--font-body); font-size: 0.98rem; line-height: 1.6; color: #E8DFCE; }
.skin-home .subpage-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  align-items: center;
}
.skin-home .subpage-split--flip .subpage-copy { order: 2; }
.skin-home .subpage-split--flip .subpage-photo { order: 1; }
.skin-home .subpage-copy { display: grid; gap: 10px; justify-items: start; }
.skin-home .subpage-photo { margin: 0; }
.skin-home .subpage-photo img {
  display: block;
  width: 100%;
  height: auto;
  border: 3px solid var(--outline);
  border-radius: 14px;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.45);
}
.skin-home .subpage-band {
  display: grid;
  gap: 10px;
  justify-items: center;
  text-align: center;
  padding: 30px 22px;
  border: 3px solid var(--outline);
  border-radius: 16px;
  background: var(--surface);
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.45);
}
.skin-home .subpage-band-btns { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
@media (max-width: 760px) {
  .skin-home .subpage-split { grid-template-columns: 1fr; }
  .skin-home .subpage-split--flip .subpage-copy { order: 1; }
  .skin-home .subpage-split--flip .subpage-photo { order: 2; }
}

/* the photo wall: cards clip their image, the name rides a scrim at the foot */
.skin-home .gwall-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}
.skin-home .gwall-card {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  border: 3px solid var(--outline);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.45);
}
.skin-home .gwall-card img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A LINKED TILE IS A BUTTON FILLING THE CARD (D3). It has to carry the whole
   card, or the tap target is the caption strip and the picture above it does
   nothing - which is worse than no link, because it looks like one.

   width/height 100% in CSS rather than on the element: the skin opens with an
   `all: unset` neutralizer, which cancels the width and height ATTRIBUTES on a
   replaced element, so any sizing that matters has to be a declaration. */
.skin-home .gwall-open {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  text-align: inherit;
  color: inherit;
  font: inherit;
}
/* the focus ring rides the card's own radius, and it is NOT removed - a tile
   the keyboard can reach must show where it is */
.skin-home .gwall-open:focus-visible {
  outline: 3px solid var(--cheese-gold);
  outline-offset: -6px;
  border-radius: 11px;
}
.skin-home .gwall-name {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 26px 14px 12px;
  background: linear-gradient(180deg, rgba(10, 10, 10, 0) 0%, rgba(10, 10, 10, 0.82) 70%);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: calc(1.05rem * var(--type-scale));
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  color: #FFF8EC;
}
/* A CLIP TILE LOOKS LIKE A CLIP BEFORE IT IS TAPPED (2026-08-23). The tile is
   a poster frame - there is no <video> in the grid at all - so nothing about
   the picture says it moves. The play mark is the only thing that does, which
   makes it load-bearing rather than decorative.

   IT IS SVG, NOT A GLYPH AND NOT AN EMOJI: CLAUDE.md's rule, and a triangle
   drawn in a font is at the mercy of whether that font arrived. */
.skin-home .gwall-playmark {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: block;
  width: 44px;
  height: 44px;
  pointer-events: none;
  /* the ground behind it is a photograph, so the mark carries its own contrast
     in the SVG (a 0.62 ink disc under a #FFF8EC triangle) rather than trusting
     whatever frame it lands on */
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.55));
}
/* the tile lifts on hover the way a control should, but ONLY where hovering is
   a thing that happens - a phone would leave it stuck in the hovered state */
@media (hover: hover) {
  .skin-home .gwall-play:hover .gwall-playmark { transform: translate(-50%, -50%) scale(1.08); }
}
@media (prefers-reduced-motion: reduce) {
  .skin-home .gwall-play:hover .gwall-playmark { transform: translate(-50%, -50%); }
}

/* THE CARD SLOTS (slice 38). Each is an empty box that Clover's SDK fills with
   an iframe from its own origin, so the height is OURS to reserve and the
   contents are not ours to style. A slot with no min-height collapses to 0
   before the SDK answers and the step reflows under the customer's thumb when
   it does. */
/* ══ THE CARD FORM — THE 7TH STREET PATTERN (S91) ═══════════════════════════
   Carlos's reference: one plain bordered rectangle per field, the label ABOVE
   it in small caps, expiry and security code side by side, ZIP full width.
   The joined box, the shared border and the two dividers are gone; so is the
   cardholder name field.

   NOTHING HERE IS A CLOVER CONSTRAINT. Each field is a separate iframe in a
   separate div of ours, and their inputs carry border:'none', so every border
   on this form is drawn by these rules. */
.skin-home .paycard { display: grid; gap: 14px; margin: 0 0 12px; }
.skin-home .paycard-field { display: grid; gap: 6px; }
/* OUR LABEL IS ON THE SCREEN AGAIN (S91). It was clipped to 1x1px because
   Clover paints its own placeholder inside the frame and two captions per field
   read as a mistake. The reference form puts the NAME outside and the VALUE
   inside, which is what every other checkout does - so Clover's placeholder
   stops being the field's name and becomes its example value.
   patty brown on cream is 5.6:1, the same secondary weight .checkout-sech uses;
   the value inside the frame is outline black at 14.4:1. Contrast, not colour
   names - block 157 asserts the ratio. */
.skin-home .paycard-label {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  overflow: visible;
  clip-path: none;
  white-space: normal;
}
.skin-home .paycard-slot {
  /* A DEFINITE HEIGHT, NOT A FLOOR (2026-08-25). min-height alone let Clover's
     iframe fall back to the HTML default of 150px and the slot grew to meet it,
     so a single-line card field became a 150px box whose bottom ~110px was the
     frame's empty document. A click there focuses the IFRAME and types nothing:
     visible, plausible, and dead. Block 121.1.

     🔴 48px, AND THE INPUT INSIDE THE FRAME IS TOLD 44 (S91). The frame fills
     this box; 48 less the 2px border top and bottom is 44, which is what
     CARD_FIELD_STYLES.input asks Clover for. Those two numbers are a PAIR: if
     this height or the border changes, that constant changes with it, and
     block 157 fails if they drift apart. Before S91 the input was told
     `height: 100%`, which resolves to `auto` with no definite parent inside the
     frame - measured at 18px sitting at top 0 of a 46px frame, which is the
     "double height, text at the top" Carlos reported. */
  height: 48px;
  min-height: 48px;
  border: 2px solid var(--outline);
  border-radius: 8px;
  background: #FFF8EC;
  /* NO HORIZONTAL PADDING HERE ANY MORE: the frame runs edge to edge and the
     text inset is the input's own padding, inside the frame, where it can
     actually centre against the border. */
  padding: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
}
/* the frame fills the slot exactly: no strip that looks like the field and is
   not. box-sizing is border-box site-wide, so 100% here is the 44px content box. */
.skin-home .paycard-slot iframe { display: block; width: 100%; height: 100%; border: 0; }
/* EXPIRY | SECURITY CODE, two columns.
   🔴 align-items: end IS LOAD-BEARING, and it was measured. At 320 the two
   labels wrap to DIFFERENT line counts - "Expiry date" to two lines (29.73px)
   and "Security code" to three (39.66px) - so top-aligned columns put the two
   boxes at different heights and the row read as broken. Bottom alignment makes
   the BOXES line up whatever the labels do, which is what the eye follows, and
   it holds for any label length and in either font-load state (CLAUDE.md: a
   layout that only holds once the webfont arrives is not a layout).
   flex-wrap stays as the last resort if even that will not fit. */
.skin-home .paycard-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: end; }
/* min-width:0, NOT a floor. A floor here is what wrapped the row at 320 - the
   two columns need 120 each plus the gap and the card has less than that. The
   labels now sit ABOVE the boxes rather than inside the frames, so the column
   only has to hold its own caption; the squash rule that put ZIP on its own
   line still applies and ZIP is still on its own line. Measured at 320 by
   121.5. */
.skin-home .paycard-col { flex: 1 1 0; min-width: 0; }

/* THE TWO NOTES UNDER THE FIELDS, ONE HOOK EACH (S91).
   They can never both render - one is the not-live state, one is the live
   state - but they stay separate elements because they are separate facts, and
   sharing a selector between two facts is what made two guards ambiguous in
   S89. The declaration is shared; the hooks are not. */
.skin-home .paycard-testnote,
.skin-home .paycard-reqnote {
  margin: 10px 0 0;
  font-family: var(--font-body);
  font-size: 0.78rem;
  line-height: 1.45;
  /* patty brown, 5.6:1 on cream. Secondary: it is a note about the form, not a
     correction of something the customer can see wrongly - which is why the
     old star note was outline black and this is not. */
  color: var(--patty);
}

/* THE WALL GETS THE ROOM, THE READING PAGES DO NOT (D4a, 2026-08-23).
   .subpage-body is 980px because that is a comfortable measure for a column of
   TEXT, and About, Catering, Franchise and Suggest all keep it — a 2000px line
   of prose is unreadable and widening them would be a regression dressed as an
   improvement.

   A wall of photographs is the opposite: it is a grid, not a measure, and 980px
   held it to THREE columns at 1280, 1440, 1920 and 2560 alike. Measured: an
   identical 940px grid of 301.3px tiles on every one of those screens, so two
   thirds of a 2560 monitor showed nothing at all.

   THE max() AROUND IT IS NOT DECORATION - IT IS THE BUG THIS RULE SHIPPED WITH
   FOR ABOUT TEN MINUTES. Written as a bare `min(1440px, 100vw - 80px)` the cap
   also applies going DOWN: at a 320px viewport it resolved to 240px, which is
   NARROWER than the 280px the grid's own 240px minimum needs, and the wall
   painted 20px past its own body. A rule meant to widen a container must be
   incapable of narrowing it, so 980 - the shared measure every other subpage
   keeps - is the floor.

   Read it as: never below what it was, never above 1440, and in between the
   viewport less the 80px the page already breathes on either side. Scoped by
   #gallery, the id the panel already carries — no new class, nothing else
   moves, and About / Catering / Franchise / Suggest stay on 980 at every width
   (measured, all four). */
.skin-home #gallery .subpage-body { max-width: max(980px, min(1440px, 100vw - 80px)); }

/* THE TILE FLOOR RISES WITH THE ROOM. Left at 240px the extra width bought
   more, smaller tiles; the point of the space is bigger pictures as well as
   more of them, so the floor goes up and the column count follows the screen
   instead of the constant. */
@media (min-width: 1100px) {
  .skin-home #gallery .gwall-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
}

/* THE PLAYER HAS NO ACTION ROW (Carlos, 2026-08-23). .gclip-actions and
   .gclip-menubtn styled a "See it on the menu" button that has been removed -
   the player is the video and nothing else. Deleted rather than left behind:
   dead CSS for a control that no longer exists is the thing somebody restores
   by accident when they see a class with no markup.

   ⚠️ AND THE TWO RULES ABOVE WERE COLLATERAL WHEN THAT DELETION WAS MADE. The
   edit took a RANGE - from this comment's old position to .gwall-card--more -
   and the D4a width rules sat inside it. The suite caught it immediately
   (3 columns at 1920, 301.3px tiles at 1280 - the pre-D4 numbers exactly), but
   a range edit that names its endpoints and not its contents is how a fix
   disappears while its comment survives. */

.skin-home .gwall-card--more {
  aspect-ratio: 1 / 1;
  min-height: 240px;
  display: grid;
  gap: 10px;
  align-content: center;
  justify-items: center;
  text-align: center;
  padding: 22px;
}

/* 10. THE REAL VIDEO in the watch modal: portrait 720x1280 framed on an ink
   letterbox ground instead of cropped by the old 16/9 poster clip. */
/* THE CARD SHRINKS TO THE CLIP, so there is no ink left over to read as bars
   (D1, 2026-08-22). .watchmodal-card is `width: min(720px, 94vw)`, sized for the
   landscape 16/9 poster this modal used to carry. The clip is 720x1280 - 9:16,
   confirmed from the mp4's own tkhd and avc1 boxes, no rotation matrix - so at
   1280x800 the video resolved to 288px inside a 714px ink ground and left
   213.00px of #14100E down each side. On iPhone 14 it was 52.48px a side. At
   320 there is none, because max-width:100% has already clamped the frame to
   the card.

   OBJECT-FIT IS THE WRONG LEVER AND CANNOT HELP. It governs how the picture
   fits inside the VIDEO ELEMENT's box, and that box is already exactly 9:16 -
   so `cover` and `contain` are the same picture here. Measured: switching to
   `object-fit: cover` crops 0% and leaves all 213px of ink exactly where it
   was. The bars are the PARENT's background beside a correctly-sized child, so
   only the container's width can move them.

   Nor can the ground shrink instead of the card: `grid-template-columns: auto`
   sizes the track to the item's max-content, which is the blowout the note
   below already records - measured again here, the video came out 288px inside
   a 274px ground at 320 and hung 7px off each edge.

   This does NOT reverse slice 24's ruling. That choice was ink letterbox VERSUS
   a 16/9 crop, and it was right: the clip is still never cropped, and the ink
   is still its ground. What goes is only the empty ink the card's old landscape
   width was holding open. The card carries no caption and no body - its single
   child is this ground - so nothing else pays for the narrower box. */
.skin-home {
  /* ONE SOURCE FOR THE CLIP'S BOX. The frame takes its height from this and the
     card takes its width from the same number through the clip's 9:16 ratio, so
     the two cannot drift. Written as a token rather than repeating
     min(64vh, 620px) in two rules - a second copy is a second thing to forget. */
  --watchclip-h: min(64vh, 620px);
}

/* WHY THE CARD IS SIZED AND NOT LEFT TO SHRINK-WRAP. `width: auto` worked while
   the <video> carried a src in the markup and so had an intrinsic size for
   max-content to resolve against. D2 moved the src onto watchVideoRef to stop
   the clip being fetched during the page parse, and a source-less <video> falls
   back to the replaced-element default - at which point the ground's
   minmax(0, 1fr) track had nothing to shrink to and the card blew out to the
   viewport: measured 1246px at 1920x1080, against the 354.75px the clip
   actually occupies.

   Deriving the width from --watchclip-h instead makes the card's box a fact
   about the CLIP's declared shape rather than about whether the media element
   happens to know its own size yet. box-sizing is border-box globally, so the
   3px border is already inside this number. */
.skin-home .watchmodal-card:has(> .watchmodal-poster--video) {
  width: calc(var(--watchclip-h) * 9 / 16 + 6px);
  max-width: 94vw;
}

.skin-home .watchmodal-poster--video {
  aspect-ratio: auto;
  display: grid;
  /* minmax(0, 1fr), NOT the implicit auto column. An auto grid column sizes to
     its item's max-content, so the frame's `max-width: 100%` resolved against
     ITSELF and clamped nothing: at 320 the video came out 324px wide inside a
     320px screen, ran 27px off the right edge, and took the close button with
     it - elementFromPoint at the button's centre returned the backdrop because
     the button was no longer where the layout said. Same min-width:auto family
     as the checkout blowout in invariant 17: a track that refuses to shrink
     below its content and a parent whose max-width therefore cannot bite. */
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  background: var(--ink);
}
/* SIZED BEFORE ANY METADATA ARRIVES, and this is the playback fix: with
   preload="none" a video element knows nothing about its file, so without an
   explicit shape it renders at the UA default 300x150 — a collapsed sliver
   with the poster squeezed into it, which reads as "the video is broken"
   before anyone taps play. The aspect-ratio + height give the element its
   real 9/16 frame from the first paint. */
/* THE ✕ IS ANCHORED TO THE VIDEO, NOT THE CARD (slice 24).
   It always measured 44x44 inside the card - 13px in from its top and right at
   every width - so "hanging off the panel" was not literally true, and the
   screenshot showed what Carlos was actually seeing. The clip is PORTRAIT on an
   ink letterbox: at 390 the card is 350 wide and the video 280.78, leaving ~34px
   of ink each side, so a 44px button inset 10px from the CARD straddled the
   video's right edge - half on a bright photograph, half on black. It read as
   detached because it belonged to neither surface.
   This frame shrinks to the video's own box, so the button sits on the picture
   at every width and there is no edge to straddle. Block 68.2 measures
   containment against the VIDEO rather than the panel. */
.skin-home .watchmodal-frame {
  position: relative;
  display: block;
  /* THE FRAME CARRIES THE VIDEO'S BOX, and the video then fills it. The first
     version used width:fit-content, which does NOT track a replaced element
     sized by aspect-ratio + height - measured 21.61px wider than the video at
     390, so the button still spilled past its right edge. Declaring the same
     shape on the frame makes the two boxes identical by construction rather
     than by the layout happening to agree. */
  aspect-ratio: 9 / 16;
  height: var(--watchclip-h);
  width: auto;
  max-width: 100%;
}

.skin-home .watchmodal-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--ink);
}

/* 11. THE FRANCHISE ENQUIRY — the footer column is GONE (slice 7A). It was
   ".footcol--franchise" plus the footer copies of the input/button styles;
   both the markup and these rules moved to the franchise page in section 12
   below. ".demo-tag--sm" went with them: it existed only for that column's
   tag, and the page carries a standing gate paragraph instead. */

/* ============================================================
   12. THE FRANCHISE PAGE (slice 7A, 2026-08-16)
   ============================================================
   The four fields were a footer column on every page; they are one page now,
   reached by a footer LINK weighted as a peer of the legal links. The input
   and button styling below is the footer version, moved rather than rewritten
   — the subpage ground is #100c0a, the same dark the footer was, so the
   light-on-dark treatment carries over unchanged and nothing was re-derived.
   What is new is the page shape: a two-column field grid that collapses on a
   phone, a full-width textarea, and the STANDING gate paragraph. */
.skin-home .checkout-brand { text-decoration: none; color: #FFF8EC; }

/* THE PAGE'S CARD (slice 31). Everything visual here comes from .st-sheet and
   .st-crest-* above, shared with the smash-list popup; these are only the
   things a page-in-the-flow needs that a fixed dialog does not.

   position:relative lives HERE and not in .st-sheet on purpose. .joinpop is
   position:fixed, and .skin-home .joinpop and .skin-home .st-sheet are both
   (0,2,0) - so a `position` inside the shared rule would be decided by which
   one happens to be written later in the file. That is the exact trap
   CLAUDE.md records for the footer clearance rule, and it is avoided by not
   putting the contested property in the shared rule at all.

   overflow:hidden so the drip is cut by the card's own radius rather than
   squaring off its top corners. */
.skin-home .franchise-sheet {
  position: relative;
  overflow: hidden;
  display: grid;
  gap: 22px;
  padding: 58px 26px 30px;
}
@media (max-width: 420px) {
  .skin-home .franchise-sheet { padding-left: 18px; padding-right: 18px; }
}


.skin-home .franchise-form--page {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 18px;
  margin-top: 0;
}
.skin-home .franchise-field--wide,
.skin-home .franchise-actions,
.skin-home .franchise-form--page .franchise-state { grid-column: 1 / -1; }
.skin-home .franchise-field { display: grid; gap: 4px; min-width: 0; }
.skin-home .franchise-label {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #A8A6A2;
}
.skin-home .franchise-input,
.skin-home .franchise-textarea {
  width: 100%;
  min-width: 0;                    /* the blowout guard: a grid column */
  min-height: 44px;                /* the tap floor */
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 2px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 0.92rem;
  line-height: 1.5;
  color: #FFF8EC;
}
.skin-home .franchise-textarea { resize: vertical; }
/* THE SIXTH COPY OF THE RED BUTTON IS GONE (slice 31). This rule used to
   restate the whole sticker construction - fill, keyline, radius, offset, the
   display face - at a smaller size and a shallower offset than the five
   buttons it was supposed to match, so the franchise page's one confident
   action was quietly the weakest button on the site: 10px/20px padding against
   15px/28px, 0.95rem against 1rem, a 3px offset against --lift.

   .franchise-send now joins the canonical selector list in home.css that
   .e1-btn, .isheet-order, .cater-call, .footbrand .btn and .joinform .btn
   already share, hover and active included. What is left here is only what is
   true of this button and no other: where it sits in the form grid, and the
   tap floor. */
.skin-home .franchise-send {
  min-height: 44px;
  cursor: pointer;
}
/* CENTRED UNDER A CENTRED HEADER, and it needed the ACTIONS row to do it.
   The old rule said `justify-self: start` on the button, which never applied:
   .franchise-actions is a grid ITEM, not a grid container, so its child is an
   ordinary block and justify-self has nothing to resolve against. Measured at
   1280 before this line went in - the button sat at x=199, hard against the
   card's left padding, with `justify-self: center` already written on it.
   Scoped to the page form so the suggestion box, which shares these field
   classes but has its own .suggest-form, keeps the alignment it has today. */
.skin-home .franchise-form--page .franchise-actions {
  display: flex;
  justify-content: center;
}
.skin-home .franchise-state {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.88rem;
  line-height: 1.6;
  color: #CFC7BA;
}
.skin-home .franchise-state a { color: var(--cheese-gold); }

@media (max-width: 760px) {
  .skin-home .franchise-form--page { grid-template-columns: 1fr; gap: 14px; }
}

/* ============================================================
   PICKUP OR DELIVERY, AND THE CARD STEP (slice 16, 2026-08-17)
   ============================================================ */
.skin-home .checkout-mode { margin-bottom: 4px; }
/* ⚠️ RENAMED FROM .checkout-optional (S92). The name was already inaccurate
   before this slice - it carries "required" twice on the franchise form and
   "if you have one" elsewhere - and it is a generic FIELD HINT, not a marker of
   optionality. It cost a real guard: 77.3 asserted the email label no longer
   says "optional" and matched the CLASS NAME instead of the visible text, so it
   failed on a label that reads "your receipt and updates go here".
   A class that states a fact must not state a fact that is not always true. */
.skin-home .checkout-fieldhint {
  font-family: var(--font-body);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.78rem;
  color: #A9A093;
  margin-left: 6px;
}
.skin-home .checkout-address { display: contents; }
.skin-home .checkout-mode-line {
  margin: 0 0 2px;
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: calc(1px + var(--type-tracking));
  text-transform: uppercase;
  font-size: 0.95rem;
  color: var(--cheese-gold);
}
.skin-home .checkout-addr-line {
  margin: 0 0 14px;
  font-family: var(--font-body);
  font-size: 0.9rem;
  line-height: 1.5;
  /* R4: WAS #CFC7BA, THE DARK-GROUND GREY, ON CREAM PAPER - the same class of
     mistake as the card note. This is the address the order SHIPS TO: it is
     data the customer has to be able to check, not decoration, so it takes the
     body colour. Outline black on cream is 14.4:1 by the colour law. */
  color: var(--outline);
}
.skin-home .checkout-prov-note,
.skin-home .checkout-timeprov-note {
  margin: 10px 0 0;
  font-family: var(--font-body);
  font-size: 0.8rem;
  line-height: 1.5;
  color: #A9A093;
}
.skin-home .checkout-summary--tight { margin-bottom: 16px; }

/* THE MOUNT REGION. A bordered, clearly-bounded space with a real minimum
   height, because that is what a provider's iframe drops into — sizing it now
   means the layout does not jump the day it is wired. It holds ONE paragraph
   and no controls; block 59.2 fails if anything typeable appears anywhere on
   this step.

   The dashed rule is doing work rather than decoration: it reads as an empty
   slot rather than as a panel that failed to load. */
.skin-home .pay-mount {
  display: flex;
  /* a COLUMN, not a row: the note centres alone when payments are off, and
     stacks under the field grid when the SDK mounts. The row version put the
     sentence BESIDE the slots and painted them into each other. */
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  align-items: stretch;
  /* ⚠️ THE DASHED BOX IS GONE (S91). It framed the card area with a 2px gold
     dashed border and a 168px floor, which read as "unfinished" - a dashed
     outline is the universal shorthand for a placeholder. Carlos's reference
     form has no container at all around the fields: just labels and plain
     bordered rectangles on the card's own ground.

     THE min-height WENT WITH IT for the same reason it existed: it reserved a
     tall box so the "payments are off" sentence had something to sit in the
     middle of. With no box to fill, a sentence needs no floor, and reserving
     168px of empty ground for copy that may not render is the constant-that-
     reserves-against-nothing trap this project already has a rule for. */
  padding: 0;
  margin: 0 0 18px;
  box-sizing: border-box;
  border: 0;
  background: none;
}
.skin-home .pay-mount-note {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.9rem;
  line-height: 1.6;
  /* the mount sits INSIDE the cream .checkout-card, so the note follows the
     cream colour law: patty brown, 5.6:1 secondary. #CFC7BA was the
     dark-ground value and measured near-invisible here. */
  color: var(--patty);
}
.skin-home .ticket-addr-to { white-space: normal; }

/* ---- RICK'S ASKS (slice 19, 2026-08-17) -------------------------------
   THE UNCLE LOUIE G LINE sits on the receipt, above the phone line, in the
   receipt's own quiet register rather than shouting: it is a nudge to walk
   next door, not a second call to action competing with the order that was
   just placed. Outline black on the cream paper is 14.4:1 per the colour law,
   and .cart-ticket-paper already sets that colour, so this inherits it rather
   than declaring a second source of truth.

   THE DELIVERY NOTE sits under the totals beside the * legend and reads in the
   same quiet tone, because it states a fact about the charge rather than
   selling anything.
   ------------------------------------------------------------------- */
/* ── WHAT BECAME OF THE ORDER (S147) ─────────────────────────────────────────
   Two states on one class, and the WARNING is the one that had to earn its
   colour. "The kitchen has your order" is a reassurance and reads like the
   rest of the receipt; "we couldn't reach the kitchen" is an instruction the
   customer has to act on, so it gets the sign red, a rule down the side and
   the weight - the same treatment the shop's own email gives an allergy.

   ⚠️ COLOUR LAW. Sign red on cream is 4.06:1 and FAILS body text (CLAUDE.md,
   measured, never re-derived), so the red here is the RULE and the outline
   black is the type. The warning is carried by the bar, the weight and the
   words - never by a colour a customer may not be able to read. */
/* ⚠️ `.ticket-rail`, NOT `.ticket-kitchen`, AND DO NOT TIDY IT BACK. The first
   draft used `class="ticket-kitchen ticket-kitchen--warn"`, and the space
   between those two names spells the literal string "kitchen ticket" - which
   block 154.1 bans, correctly, as a claim about where an order ends up. The
   guard was right and the class name was the accident, so the NAME changed.
   Renaming it back re-trips a live guard on a substring nobody would think to
   look for. */
.skin-home .ticket-rail {
  margin: 0 0 6px;
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--outline);
}
.skin-home .ticket-rail--warn {
  padding: 8px 10px;
  border-left: 4px solid var(--sign-red);
  font-weight: 700;
}
.skin-home .ticket-rail--warn a { text-decoration: underline; color: var(--outline); }
.skin-home .ticket-louie {
  margin: 0 0 6px;
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.5;
}
.skin-home .checkout-delivery-note {
  margin: 10px 0 0;
  font-family: var(--font-body);
  font-size: 0.8rem;
  line-height: 1.5;
  /* R5: SAME GREY, SAME CREAM, AND THIS ONE IS ABOUT MONEY. "Delivery is paid
     by you in full and is included in the total above" exists to stop a
     billing argument before it starts, which makes it the worst sentence on
     the screen to render at 1.9:1. Outline black, 14.4:1 - a fee disclosure
     is not secondary copy. */
  color: var(--outline);
}
.skin-home .checkout-delivery-note + .checkout-prov-note { margin-top: 4px; }

/* ---- THE SUGGESTION BOX (slice 21) -----------------------------------
   It borrows the franchise page's field and state styling rather than
   declaring a second set: the two pages are the same shape (a short form and
   one honest state underneath) and two stylesheets for one shape is how they
   drift.

   THE GATE RULE IS GONE (slice 32), with the paragraph it styled. Both pages
   carried a standing note saying the form was not connected and that nothing
   was sent, stored or read; Supabase has been configured since 2026-08-18, so
   every clause of it was false while a real row was being written. Removed on
   the Director's ruling. Block 75 fails if the wording returns from any
   source. */
.skin-home .suggest-form { display: grid; gap: 14px; }

/* the suggestion box's way in, in the footer's base strip beside the copyright:
   on every page, visible, and not disturbing the 3x2 link block slice 11
   measured (a seventh label there puts back the staircase it removed) */
.skin-home .footsuggest {
  /* IT IS A STANDALONE TAP TARGET, so it carries the 44px floor. Block 54
     caught it at 111.59 x 19.83 on all four phone widths the moment it existed:
     the text sets the height of an inline anchor, and 0.8rem of text is 19.83px
     no matter how reachable the link is otherwise. inline-flex + min-height
     makes the BOX the target rather than the glyphs. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: #A9A093;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.skin-home .footsuggest:hover { color: var(--cheese-gold); }

/* ---- THE IN-FLIGHT STATE, VISIBLE (slice 32c) ------------------------
   Every submit button on the site now goes disabled from the moment it is
   pressed until the request settles. The lock that actually prevents a second
   write is a synchronous instance flag in index.html - this is the half the
   customer can see, so a second tap reads as "already going" rather than as
   nothing happening. Not a substitute for the lock: a disabled attribute is a
   courtesy, and a refresh beats it. */
.skin-home button[disabled] {
  opacity: 0.6;
  cursor: default;
  pointer-events: none;
}

/* ============================================================
   THE SEALED ENVELOPE (slice 32c)
   ============================================================
   Carlos: "Got it. Somebody reads these and comes back to you." is flat. The
   Director picked the line and asked for the filled form to fold closed and
   seal. Four constraints, and every one of them is a rule rather than a
   preference:

     THE ANIMATION FOLLOWS THE RESULT. This block only exists inside the "ok"
     branch, so it cannot render before the server has acknowledged the row. A
     failed write shows the error paragraph and the envelope never seals -
     there is no code path that can seal it optimistically.

     REDUCED MOTION GETS THE END STATE, not a shorter animation and not a
     missing one. The rules below start from the SEALED state and the keyframes
     move INTO it, so switching the animation off leaves a sealed envelope
     rather than a half-open one. Animating from a hidden start would have made
     "no motion" mean "no envelope".

     IT IS ANNOUNCED REGARDLESS. The line carries role="status" and is never
     the animated thing; the flap and the wax are aria-hidden, because an
     envelope closing is decoration and a screen reader does not need it.

     CSS ONLY. No dependency, no script, nothing to load. */

.skin-home .sealbox {
  display: grid;
  justify-items: center;
  gap: 18px;
  padding: 26px 0 6px;
}

/* the envelope body: a squat rectangle in the card's own materials */
.skin-home .sealbox-env {
  position: relative;
  width: 168px;
  height: 108px;
  border: 3px solid var(--outline);
  border-radius: 8px;
  background: var(--butter);
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.45);
  overflow: hidden;
}

/* THE FLAP, drawn as a triangle by borders so it needs no asset. Its resting
   state is DOWN (closed) - see the reduced-motion note above. */
.skin-home .sealbox-flap {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 0;
  border-left: 84px solid transparent;
  border-right: 84px solid transparent;
  border-top: 62px solid var(--cheese-gold);
  transform-origin: top center;
  transform: rotateX(0deg);
  animation: sealbox-fold 620ms cubic-bezier(0.2, 0.8, 0.3, 1) both;
}

/* the wax seal, landing after the flap is down */
.skin-home .sealbox-wax {
  position: absolute;
  top: 44px; left: 50%;
  width: 38px; height: 38px;
  margin-left: -19px;
  border: 3px solid var(--outline);
  border-radius: 50%;
  background: var(--sign-red);
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.45);
  transform: scale(1);
  opacity: 1;
  animation: sealbox-stamp 260ms cubic-bezier(0.3, 1.6, 0.5, 1) 560ms both;
}

.skin-home .sealbox-line {
  margin: 0;
  max-width: 30ch;
  text-align: center;
  font-family: var(--font-body);
  font-size: 1.05rem;
  line-height: 1.5;
  color: #FFF8EC;
}

/* the suggestion box's way back, and nowhere else's */
.skin-home .sealbox-again {
  min-height: 44px;
  padding: 10px 22px;
  background: transparent;
  color: var(--cheese-gold);
  border: 2px solid var(--cheese-gold);
  border-radius: 999px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
}

@keyframes sealbox-fold {
  from { transform: rotateX(-168deg); }
  to   { transform: rotateX(0deg); }
}
@keyframes sealbox-stamp {
  from { transform: scale(2.4); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

/* THE END STATE, AND NOTHING MOVING. Both elements already rest sealed, so
   this only has to stop the keyframes running. */
@media (prefers-reduced-motion: reduce) {
  .skin-home .sealbox-flap,
  .skin-home .sealbox-wax { animation: none; }
}

/* THE REPRICED NOTE ON THE TICKET (slice 34, 2026-08-20). Shown only when the
   board moved while the customer was ordering, so the receipt says why its
   total is not the one on the previous screen. Outline black on the ticket's
   own paper, not sign red: this is information, not an error, and the order
   stands. */
.ticket-pricenote {
  margin: 10px 0 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--ink, #1A1A1A);
  border-top: 1px dashed rgba(26, 26, 26, 0.35);
  padding-top: 8px;
}


/* ══ INLINE VALIDATION ════════════════════════════════════════════════════════
   The message sits under the input it is about, and the input goes red.

   ⚠️ THE RED OUTLINE IS DERIVED FROM THE MESSAGE, not from a second class hole.
   :has() lets the field wrapper see whether its own error paragraph has any
   text, so there is exactly ONE source of truth for "this field is wrong" and
   no way for the outline and the message to disagree. It also sidesteps a
   measured dc-runtime trap: a class hole rendered once and never updated when
   the state behind it changed (2026-08-30).

   :has() is Safari 15.4+ and Chrome 105+. Where it is unsupported the message
   still renders in red and the outline simply does not appear - the customer is
   still told what is wrong, by name, under the right field. Degrades to
   informative, never to silent. */
.skin-home .checkout-fielderr {
  margin: 6px 0 0;
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--sign-red, #C8322A);
}
/* 🔴 PRESENCE, NOT :empty - AND A SCREENSHOT CAUGHT WHY. The first version
   rendered the paragraph always and keyed the outline off
   .checkout-fielderr:not(:empty). Every field came up RED BEFORE THE CUSTOMER
   HAD SUBMITTED ANYTHING, because CSS :empty means "no child nodes at all" and
   the runtime leaves an empty TEXT NODE in the hole - so the element is never
   :empty and the negation is always true.

   The paragraph is now rendered only when there is something to say, so the
   selector asks whether it EXISTS. One condition, no way to be subtly wrong,
   and it cost a screenshot rather than a customer. */
.skin-home .checkout-field:has(.checkout-fielderr) .checkout-input {
  border-color: var(--sign-red, #C8322A);
  box-shadow: 0 0 0 2px rgba(200, 50, 42, 0.18);
}

/* ══ CHECKOUT PROGRESS ════════════════════════════════════════════════════════
   Three steps, ours. A position indicator, not navigation - there are no links
   and no handlers, because the steps validate in order and a crumb that looks
   tappable and is not is worse than no crumb at all.

   ⚠️ IT WRAPS RATHER THAN SHRINKS. Measured on this project 2026-08-18: a grid
   whose free space goes negative does not wrap or overflow, it SQUASHES - a
   34px badge crushed to 20.2px with nothing looking broken. So the labels have
   a floor and the row is allowed to go to two lines at the narrowest widths
   instead of quietly compressing. */
/* 🔴 IT LIVES INSIDE .checkout-body NOW, AND THAT IS THE WHOLE FIX. It used to
   be a SIBLING of the card's column, so on desktop it stranded at the far-left
   page edge and the first step marker was clipped at x=0 - invisible on a phone
   only because the page is narrow enough that the column is the page.

   NO SECOND WIDTH LITERAL. .checkout-body already carries `max-width: 480px;
   margin: 0 auto`, so being a child of it IS being in the column: one number,
   one place, and the two can never drift apart. A `max-width: 480px` copied
   onto the crumb would have looked identical today and been the bug the day
   somebody changed the card.

   NO margin-bottom: .checkout-body is a grid with an 18px gap, which now does
   that job. Keeping the margin would have doubled it. */
.skin-home .checkout-crumb { margin: 0; }
/* ⚠️ MORE AIR UNDER THE CRUMB ON A PHONE (S91), AND THE MEASUREMENT FIRST:
   the gap was ALREADY IDENTICAL at every width - 18px at 390, 430, 768, 1280 and
   1920, because it is .checkout-body's grid gap and nothing overrides it. So
   this is NOT a fix to a mobile-only discrepancy; there was none. It is a
   deliberate mobile-only increase, because Carlos reads the strip as crowding
   the heading on a phone and as correct on a laptop, and the same 18px does sit
   differently under a 30.4px heading than under a 41.6px one.

   Said plainly so nobody later "restores consistency" by deleting it: the
   asymmetry is the point.

   margin-bottom, not a changed gap: a grid item's margin box is placed in its
   track, so this ADDS to the 18px rather than replacing it, and it cannot
   affect any other row. 600px, not 769: 768 must stay exactly as it is. */
@media (max-width: 600px) {
  .skin-home .checkout-crumb { margin-bottom: 12px; }
}
.skin-home .crumb-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.skin-home .crumb {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(233, 228, 218, 0.5);
}
/* the connector, drawn between steps and never before the first */
.skin-home .crumb + .crumb::before {
  content: "";
  width: 14px;
  height: 2px;
  background: rgba(233, 228, 218, 0.22);
  flex: 0 0 auto;
  margin-right: 3px;
}
.skin-home .crumb-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: 0 0 22px;              /* a floor: this is the badge that got crushed */
  border-radius: 50%;
  border: 2px solid rgba(233, 228, 218, 0.28);
  font-size: 0.72rem;
  line-height: 1;
}
.skin-home .crumb-tick { display: none; }
.skin-home .crumb-label { white-space: nowrap; }
/* S112: a completed step is a real button. It inherits the crumb's face
   (colour, size, uppercase) so the row still reads as one strip, and it is a
   comfortable thumb target - 44px tall, the mark and label inside - without
   the visible circle growing. The current and future steps are spans in the
   same box so the three stay aligned. Focus is visible, and only when it came
   from a keyboard. */
.skin-home .crumb-btn, .skin-home .crumb-static {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px; margin: -11px 0; padding: 0 2px;
  background: transparent; border: 0; border-radius: 8px;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
}
.skin-home .crumb-btn { cursor: pointer; }
.skin-home .crumb-btn:hover .crumb-label { text-decoration: underline; text-underline-offset: 3px; }
.skin-home .crumb-btn:focus-visible { outline: 3px solid #F5DFA8; outline-offset: 2px; }

/* DONE: the tick replaces the number, so the mark says "finished" without
   needing the colour to carry the whole meaning */
.skin-home .crumb.is-done { color: rgba(233, 228, 218, 0.72); }
.skin-home .crumb.is-done .crumb-mark {
  border-color: var(--sign-red, #C8322A);
  background: var(--sign-red, #C8322A);
  color: #FFF;
}
.skin-home .crumb.is-done .crumb-tick { display: block; }
.skin-home .crumb.is-done .crumb-num { display: none; }

/* NOW: the one being worked on, emphasised by weight and contrast rather than
   by colour alone */
.skin-home .crumb.is-now { color: #F5DFA8; font-weight: 700; }
.skin-home .crumb.is-now .crumb-mark {
  border-color: #F5DFA8;
  color: #F5DFA8;
}

@media (max-width: 359px) {
  /* at the very narrowest the labels go and the numbers carry it, rather than
     the row squashing every mark below its 22px floor */
  .skin-home .crumb-label { display: none; }
}

/* ══ NAMED SECTIONS ═══════════════════════════════════════════════════════════
   A header and a hairline above each group of fields. The checkout used to be
   an unstructured stack of inputs, which is most of why it read as unfinished
   next to a polished one. */
.skin-home .checkout-sech {
  margin: 0 0 12px;
  padding: 0 0 8px;
  border-bottom: 1px solid rgba(233, 228, 218, 0.14);
  font-family: var(--font-display, var(--font-body));
  font-size: 0.86rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* 🔴 PATTY BROWN, NOT CREAM. The first version used #F5DFA8 - which is the
     CARD'S OWN COLOUR - so the header rendered invisible on the cream panel and
     left a gap where a title should be. Caught in a screenshot, not by a test.
     The colour law: on cream, outline black is 14.4:1 and patty brown 5.6:1;
     cheese gold at 1.64:1 is never text on cream. Section headers are
     secondary, so patty brown, exactly like .checkout-label. */
  color: var(--patty);
}
/* a header that follows content needs air above it; the first one does not */
.skin-home .checkout-sech + * { margin-top: 0; }

/* ══ LEGAL MICROCOPY ══════════════════════════════════════════════════════════
   Under the button that commits the order. Quiet, but not invisible: patty
   brown on cream is 5.6:1, which clears AA for this size, and the links carry
   an underline so they are not colour-only. */
.skin-home .checkout-legal {
  margin: 12px 0 0;
  font-family: var(--font-body);
  font-size: 0.74rem;
  line-height: 1.45;
  text-align: center;
  color: var(--patty);
}
.skin-home .checkout-legal a {
  color: var(--patty);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* what "soonest" means, under the time control. Patty brown on the cream card,
   the same weight as the optional-field hint: it is an answer, not a warning. */
.skin-home .checkout-soonnote {
  margin: 8px 0 0;
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--patty);
}
/* ⚠️ THE TIMES LEGEND SHARES THE PRICE LEGEND'S LOOK AND NOT ITS CLASS. It
   carried .checkout-prov-note first, which made two different facts
   indistinguishable and broke two existing guards that locate the PRICE legend
   by that selector - both hit a strict-mode violation resolving to two
   elements. The declaration above is shared; the selector is not. */
.skin-home .checkout-when-line {
  margin: 4px 0 0;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--patty);
}

/* ══ REQUIRED CHOICE GROUPS (S97/1.1, 1.3) ═══════════════════════════════════
   The error treatment is the CHECKOUT'S, reused rather than reinvented: the
   message renders only when there is something to say, and the red outline is
   DERIVED from that element's presence with :has(), exactly as
   .checkout-field:has(.checkout-fielderr) does. One fact, one place - a
   separate "is-invalid" class could disagree with the text, and on this runtime
   a class hole has been measured rendering once and never updating. */
.skin-home .isheet-choices { display: flex; flex-direction: column; gap: 14px; margin: 0 0 14px; }
.skin-home .isheet-choice-h {
  margin: 0 0 8px;
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-family: var(--font-display);
  font-size: 1rem; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--outline);
}
.skin-home .isheet-choice-req {
  font-family: var(--font-body); font-weight: 700; font-size: 0.68rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: #FFF8EC; background: var(--sign-red);
  padding: 2px 7px; border-radius: 999px;
}
.skin-home .isheet-choice-err {
  margin: 7px 0 0;
  font-family: var(--font-body); font-size: 0.82rem; line-height: 1.35;
  color: var(--sign-red, #C8322A);
}
.skin-home .isheet-choice:has(.isheet-choice-err) .isheet-choice-opt {
  border-color: var(--sign-red, #C8322A);
  box-shadow: 0 0 0 2px rgba(200, 50, 42, 0.18);
}
/* COUNTED GROUPS (S109b, the egg rolls). The progress reads beside the "Pick
   two" badge in the same quiet weight as the size hint; the count sits inside
   the pill it belongs to; the minus control is a real button of tap size next
   to that pill, shown only once there is something to take off. Same cream
   ground, same outline, nothing new in the palette. */
.skin-home .isheet-choice-progress {
  font-family: var(--font-body); font-weight: 700; font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--patty);
}
/* S112: the minus stepper is gone (Carlos: it read as an empty rounded box
   with a floating dash on a phone). The count badge stays on the row itself,
   from ×2 up, and a one-line hint under the group says what a tap does now. */
.skin-home .isheet-opt-count {
  margin-left: 8px;
  padding: 1px 7px; border-radius: 999px;
  background: var(--outline); color: #FFF8EC;
  font-family: var(--font-body); font-weight: 700; font-size: 0.74rem; letter-spacing: 0.04em;
}
.skin-home .isheet-choice-hint {
  margin: 7px 0 0;
  font-family: var(--font-body); font-size: 0.82rem; line-height: 1.35;
  color: var(--patty);
}

/* ══ THE COUNTER SUGGESTS (S110) ══════════════════════════════════════════
   Two cards at most, under the subtotal, in the cart's own cream and outline.
   The line reads in the body face like the counter note; the item and its
   price sit on one row like a cart line; the whole card is the tap target,
   44px tall at the least. Nothing new in the palette. */
.skin-home .cart-suggests { margin: 18px 0 0; }
.skin-home .cart-suggests-h {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 1rem; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--outline);
}
.skin-home .cart-suggest-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.skin-home .cart-suggest {
  display: block; width: 100%; min-height: 56px; text-align: left;
  padding: 10px 12px;
  border: 2px solid var(--outline); border-radius: 12px;
  background: transparent; color: var(--outline);
  cursor: pointer;
}
.skin-home .cart-suggest:focus-visible { outline: 3px solid var(--sign-red, #C8322A); outline-offset: 2px; }
.skin-home .cart-suggest-line {
  display: block; margin: 0 0 4px;
  font-family: var(--font-body); font-size: 0.86rem; line-height: 1.35; font-style: italic;
  color: var(--patty);
}
.skin-home .cart-suggest-row { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.skin-home .cart-suggest-name { font-family: var(--font-display); font-size: 1.05rem; letter-spacing: 0.02em; text-transform: uppercase; }
.skin-home .cart-suggest-price { font-family: var(--font-body); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ══ S113: THE ADD-ONS WALL IS THE BOARD'S OWN TAXONOMY (2026-09-06) ═════════
   Three labelled groups inside ADD ONS, each a GRID of chips: two columns on a
   phone, three once the sheet is the 520px desktop dialog (home.css's own
   700px breakpoint, so the two never disagree). This is not the 2026-08-16
   pill row that home-additions.css retired - those were ragged, intrinsic-
   width pills that wrapped into a staircase; these are equal-width grid cells,
   so every row lines up by construction. A chip is still an .isheet-opt:
   the drawn box-and-tick marker, the inverted is-on state and the focus ring
   all come from the rules that already style the S112 picker. Only the shape
   changes here. 44px is the tap floor; the grid's `minmax(0, 1fr)` is what
   lets a long label wrap instead of blowing the track (the 2026-08-18 rule). */
.skin-home .isheet-tier { margin: 0 0 12px; }
.skin-home .isheet-tier:last-child { margin-bottom: 0; }
.skin-home .isheet-tier-h {
  margin: 0 0 8px;
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  font-family: var(--font-display);
  font-size: 0.92rem; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--outline);
}
.skin-home .isheet-tier-sep,
.skin-home .isheet-tier-price {
  font-family: var(--font-body); font-weight: 700; font-size: 0.74rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--patty);
}
.skin-home .isheet-chips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.skin-home .isheet-chips > li { min-width: 0; }
.skin-home .isheet-chips > li + li { border-top: 0; }
.skin-home .isheet-chip {
  min-height: 44px; height: 100%;
  padding: 6px 10px;
  gap: 8px;
  border: var(--key-soft) solid rgba(20, 16, 14, 0.28);
  border-radius: 10px;
  font-size: 0.86rem; line-height: 1.2;
}
.skin-home .isheet-chip.is-on { border-color: var(--outline); }
.skin-home .isheet-chip .isheet-opt-price { min-width: 0; padding-left: 6px; }
.skin-home .isheet-chip .isheet-opt-price:empty { display: none; }
@media (min-width: 700px) {
  .skin-home .isheet-chips { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
