/* ══════════════════════════════════════════════════════════════════════════
   Veteran Vends — MOBILE LAYER
   9:16 phone (390×844 reference). Generated from the ten section pages in
   design_handoff/mobile/, which are the same markup with this stylesheet
   inlined — so the pages are the proof, and THIS FILE is the deliverable.

   HOW TO SHIP IT
   Append this file to site.css, or link it after site.css and video.css.
   That is the whole integration. There is no second set of pages to deploy,
   no template fork and no user-agent check: the markup is identical at every
   width, so the phone layout is these media queries and nothing else.

   WHY ONE BREAKPOINT AT 560px
   Every rule below is inside @media (max-width:560px) — deliberately BELOW all
   nine breakpoints the site already uses (600/720/760/820/860/900/980/1080).
   Those still do the column stacking exactly as they do today; this layer only
   tightens what they leave sized for a 1440px measure. Nothing here fires above
   560px, so the desktop build is byte-for-byte unchanged.

   WHAT IT DOES NOT TOUCH
   · #trust's height — the hero's scroll-scrub range is computed from it
     (video.js 941-958), so its padding is left alone at every width.
   · Any copy, any markup, any :root token.
   · The hero itself. pages/hero/ already carries the site's own phone rules,
     including a 380px query and a (max-width:720px) and (max-height:740px)
     pair for short phones. It needed nothing.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── SCOPING NOTE (Divine Built, applied on paste) ──────────────────────────
   Every group below is section-scoped with a zero-specificity
   :where(root, root *) guard on each selector's subject compound. Nothing about
   the cascade changes — a rule simply cannot reach outside the section it was
   written for.

   It is needed because the vv-* names are not unique across sections, the same
   way they were not in the desktop blocks: `.vv-plate img` is authored once for
   #statement (object-position:78% 42%) and again for the footer (72% 100%);
   `.vv-head` is #trust's read head and also #machines' board header; `.vv-ret`
   is the machine cards' corner reticle and also about.html's portrait corners.

   The `shared` group is deliberately left global — rhythm, gutters and type are
   meant to apply site-wide. Every rule, here and there, is still inside
   @media (max-width:560px): nothing fires above that width.

   Source, unmodified, with the full authoring notes: design_handoff/mobile.css
   ────────────────────────────────────────────────────────────────────────── */


/* ── shared: rhythm, gutters, type ─────────────────────────────────────── */
/* global by design — rhythm, gutters and type apply site-wide */
/* ══ MOBILE LAYER — 9:16 phone (390×844) ═══════════════════════════════════
   ADDITIVE. Paste this block AFTER the page's other style blocks; change nothing
   above it. Every rule is inside @media (max-width:560px), so desktop is
   untouched byte for byte — this is why the mobile set needs no separate markup
   and no second copy of the page on the live site.
   560px is deliberately BELOW every breakpoint the source already uses
   (600/720/760/820/860/900/980/1080), so the stacking those do still happens
   first and this layer only tightens what they leave desktop-sized. */
@media (max-width:560px){
  /* 1 · vertical rhythm. calc(nav-h + 24px) = 96px of dead air above every
     section — 11% of a 844px screen, and there are ten sections.
     .trust-strip REMOVED from this selector on paste (Divine Built). Source shipped
     it here, which contradicts this file's own "WHAT IT DOES NOT TOUCH" note and
     both READMEs: it took #trust from 55px to 114px on a phone, and video.js
     941-958 computes the hero's scroll-scrub range from #trust's bottom edge, so
     the machine would fill on a different schedule on phones than on desktop.
     Its own group below still tightens the marquee type, which costs no height. */
  .vv-section{padding-top:calc(var(--nav-h) * 0.55 + var(--spacing-16));padding-bottom:var(--spacing-24)}
  /* 2 · gutters. width:90% gives proportional margins that read as 19px here and
     72px on a desktop; a fixed inset is what a phone wants.
     .trust-strip keeps this one — width and inline padding do not change the
     strip's height, so the scrub range is unaffected. */
  .wrap,.wrap--narrow,.trust-strip{width:100%;max-width:none;padding-inline:18px}
  /* 3 · type. The clamps bottom out at their desktop minimum, which is still set
     for a 1440px measure — ch caps do nothing once the column is 354px. */
  .vv-section h2,#worlds h2,.reason-head{max-width:none;font-size:clamp(20px,6vw,25px);line-height:1.2;text-wrap:pretty}
  .section-sub{max-width:none;font-size:15px;line-height:1.72}
  .section-badge{font-size:10px;padding:5px 12px;letter-spacing:0.08em}

  /* 4 · touch targets. Measured every button/link/input/summary on all ten sections at
     390px against a 44px floor; these are the ones that failed, each fixed by the cause
     rather than by padding everything.
     .btn-ghost is already display:flex + align-items:center with min-height:auto, so one
     property lifts it (40 → 44) and the label stays centred.
     .footer-logo is sized by the flip's --logo-h (26px), so it gets the floor plus a
     negative margin that cancels the growth — the brand line does not move.
     The inline links are left INLINE on purpose: making a long mailto address inline-flex
     turns it into an unbreakable box that overflows a 354px column. They get the hit area
     as a pseudo-element instead, which is what source already does for .vend-line__key
     (§4b.3, @media pointer:coarse) — reproduced here keyed on WIDTH, because several touch
     devices report pointer:fine and that query then never fires. */
  .btn-ghost{min-height:44px}
  .footer-logo{min-height:44px;align-items:center;margin-block:-9px}
  /* .vend-line__key REMOVED from this selector on paste (Divine Built). The B4 key is
     already position:absolute — it is pinned inside the machine graphic at right:7px /
     top:52px. Forcing it to relative dropped it out of the machine and left it sitting
     loose under the artwork on phones, which read as the button being gone. Absolute is
     already a containing block, so the ::after hit area below works either way. */
  .overlay-card--footer a[href^="mailto:"],.built-by a{position:relative}
  .overlay-card--footer a[href^="mailto:"]::after{content:"";position:absolute;inset:-12px;z-index:1}
  /* the credit is only 14px tall, so -12px reaches 38 — this one needs -15 to clear 44 */
  .built-by a::after{content:"";position:absolute;inset:-15px;z-index:1}
  .vend-line__key::after{content:"";position:absolute;inset:-8px}
}

/* ── #what — The 3:00 PM Slump ───────────────────────────────────── */
/* scoped to #what */
@media (max-width:560px){
  /* .vv-fig stacks at 820 and .vv-rig caps at 210px there — both already right.
     What is left is the figure's own chrome, still sized for a 1440px column. */
  .vv-fig:where(#what, #what *){padding:18px}.vv-fig__title:where(#what, #what *){flex-direction:column;align-items:flex-start;gap:6px;font-size:12px}.vv-cost:where(#what, #what *){margin-inline:0}.vv-lost b:where(#what, #what *){font-size:clamp(34px,11vw,46px)}.vv-lost span:where(#what, #what *){font-size:11px;letter-spacing:0.1em}.card-grid--3:where(#what, #what *){gap:14px}
  /* same problem as the reasons list: three stacked cards are taller than the screen, so a
     single group reveal fires while cards 2 and 3 are still below the fold */
  .vv-seq-on > *:where(#what, #what *){opacity:0;transform:translateY(16px)}.vv-seq-on > *.vv-in:where(#what, #what *){opacity:1;transform:none;transition:opacity .55s var(--ease-out-quart),transform .55s var(--ease-out-quart)}
}

/* ── #machines — Four Machines ───────────────────────────────────── */
/* scoped to #machines */
@media (max-width:560px){
  /* one card per row from 600 down; the board's two pulse rows were spaced for a
     four-across grid, so on a single column they land between cards 1-2 only */
  .vv-pulse--r1:where(#machines, #machines *){top:18%}.vv-pulse--r2:where(#machines, #machines *){top:62%;display:block}.vv-readout:where(#machines, #machines *){gap:8px;font-size:10px}.machine-card__media:where(#machines, #machines *){height:200px}.machine-card h3:where(#machines, #machines *){font-size:11px}
  /* the corner reticles are hover affordances; on a touch screen nothing hovers,
     so they sit at their lit state instead of never appearing */
  .vv-ret:where(#machines, #machines *){opacity:.55}
  /* the cards' whole interaction vocabulary was :hover — reticles, gold wash, image scale —
     so on a phone a tap did nothing at all. Re-hung on :active. */
  .machine-card:where(#machines, #machines *){transition:box-shadow .2s var(--ease-out-quart)}.machine-card:active:where(#machines, #machines *){box-shadow:inset 0 0 56px rgba(var(--gold-rgb),0.25)}.machine-card:active .vv-ret:where(#machines, #machines *){opacity:1}.machine-card:active img:where(#machines, #machines *){transform:scale(1.06);filter:brightness(1.12);transition:transform .3s var(--ease-out-quart),filter .3s var(--ease-out-quart)}.machine-card:active .machine-card__copy h3:where(#machines, #machines *){letter-spacing:0.12em}

  /* ── quad, not a column (Scott, 2026-08-19) ────────────────────────────
     site.css stacks this grid to 1fr under 720px, which on a phone gave four
     339x509 cards and a 2762px section — 3.3 screens to scroll past four
     product shots. Two across halves it. This rule is (0,1,0) like the one it
     replaces and simply lands later in the cascade; the :where() guard adds
     nothing to specificity, so nothing here out-weighs site.css by accident.
     Everything below it is the card's own furniture re-sized for a card half
     as wide — the head was the binding constraint, because "Combination" at
     15px plus the 40px toggle is wider than the card now is. ── */
  .machine-grid:where(#machines, #machines *){grid-template-columns:1fr 1fr;gap:10px}
  .vv-plate__head:where(#machines, #machines *){padding:10px 10px 2px;gap:6px}
  .vv-plate__name:where(#machines, #machines *){font-size:11px;letter-spacing:0.05em}
  .vv-sw:where(#machines, #machines *){width:28px;height:13px}
  .vv-sw i:where(#machines, #machines *){top:2px;right:3px;width:5px;height:9px}
  .machine-card__media:where(#machines, #machines *){padding:4px 8px 0}
  .vv-plate__foot:where(#machines, #machines *){padding:8px 10px 10px;font-size:9.5px;letter-spacing:0.06em}
  /* the corner reticles were inset 8px/5px from a full-width card; at half the
     width that reads as a box drawn around the name, so pull them to the edge */
  .vv-ret--tl:where(#machines, #machines *){top:5px;left:4px}
  .vv-ret--tr:where(#machines, #machines *){top:5px;right:4px}
  .vv-ret--bl:where(#machines, #machines *){bottom:4px;left:4px}
  .vv-ret--br:where(#machines, #machines *){bottom:4px;right:4px}
}

/* ── #statement — Not Corporate Vending ──────────────────────────── */
/* scoped to #statement */
@media (max-width:560px){
  /* the 860px rule already pushes the art to 72% and frees the copy width; at
     390px the quote still lands on the marine's chest, so the scrim carries more
     of the load rather than the type getting smaller */
  .vv-plate:where(#statement, #statement *)::after{background:linear-gradient(180deg,rgba(10,9,8,0.72) 0%,rgba(10,9,8,0.46) 46%,rgba(10,9,8,0.86) 100%)}.vv-plate img:where(#statement, #statement *){object-position:78% 42%}.vv-statement-headline:where(#statement, #statement *){font-size:clamp(28px,8.6vw,36px) !important;max-width:none !important}.statement-copy > *:where(#statement, #statement *){max-width:100%}.who-close:where(#statement, #statement *){font-size:11px !important;letter-spacing:0.14em}
}

/* ── #qualify — The Process ──────────────────────────────────────── */
/* scoped to #qualify */
@media (max-width:560px){
  /* the 980 rule already moves the step nodes onto a left rail at -32px; that
     rail needs the padding to survive an 18px gutter */
  .vv-steps:where(#qualify, #qualify *){padding-left:34px}.vv-step:where(#qualify, #qualify *){padding:20px 0}.qualify-verdict:where(#qualify, #qualify *){max-width:none;font-size:14.5px}
  /* the ≤980 node positions are calc(clamp(22px,2.4vw,30px) + n) — pinned to the
     card padding this layer just changed to 20px, so both dots sat ~7px low */
  .vv-step:where(#qualify, #qualify *)::before{top:27px}.vv-steps .vv-step:where(#qualify, #qualify *)::after{top:30.5px}

  /* ── power down the rail ───────────────────────────────────────────────────
     .vv-railv's ::after is the gold fill and its background is the dashed track,
     but ::before was unused — so it carries a pulse that runs the rail on a loop.
     Plain CSS, no scroll dependency, so this is the movement every browser gets. */
  .vv-railv:where(#qualify, #qualify *)::before{content:"";position:absolute;left:-2px;right:-2px;top:-56px;height:56px;background:linear-gradient(180deg,rgba(var(--gold-rgb),0) 0%,rgba(var(--gold-rgb),0.9) 62%,rgba(var(--gold-rgb),0) 100%);animation:vv-m-run 3.6s linear infinite}
  @keyframes vv-m-run{0%{top:-56px;opacity:0}9%{opacity:1}88%{opacity:1}100%{top:100%;opacity:0}}

  /* ── the rail and the cards track the finger ──────────────────────────────
     Source lights everything on one timed cascade the moment .is-on lands: on a
     phone the strip is taller than the screen, so that cascade finishes while step
     3 is still below the fold and scrolling down to it shows a static card.
     Driven by mobile.js, which adds .is-near per step and writes --vv-fill on the
     rail — the same IntersectionObserver / rAF-scroll pattern the rest of this
     project already uses (about.html's paint(), the .is-on observers everywhere).
     animation-timeline:view() was tried here first and pulled back out: it does not
     tick against every scroller (measured frozen mid-scroll, playState "finished"),
     and because each of its from-keyframes was the EMPTY state, one that failed to
     sample left the rail permanently two-thirds drawn — worse than shipping nothing.
     Everything below therefore defaults to the FINISHED state and the script only
     ever walks it backwards, so with no JS at all the design is simply source's. */
  .is-on .vv-railv:where(#qualify, #qualify *)::after{transform:scaleY(var(--vv-fill,1));transition:transform .3s linear}.vv-steps .vv-step:where(#qualify, #qualify *){transition:opacity .6s var(--ease-out-quart),transform .6s var(--ease-out-quart),box-shadow .25s var(--ease-out-quart)}.vv-steps .vv-step:where(#qualify, #qualify *)::after{transition:opacity .5s var(--ease-fade),transform .4s var(--ease-out-quart),box-shadow .4s var(--ease-out-quart)}
  /* the step you are reading: faint ring, node flared, icon lit */
  .vv-steps .vv-step.is-near:where(#qualify, #qualify *){box-shadow:inset 0 1px 1px rgba(233,228,220,0.1),inset 0 0 0 1px rgba(var(--gold-rgb),0.3),inset 0 0 0 0 rgba(var(--gold-rgb),0)}.vv-steps .vv-step.is-near:where(#qualify, #qualify *)::after{transform:scale(1.4);box-shadow:0 0 11px 1px rgba(var(--gold-rgb),0.5)}.vv-steps .vv-step.is-near .vv-icon:where(#qualify, #qualify *){filter:drop-shadow(0 0 26px rgba(var(--gold-rgb),0.42))}

  /* ── tap feedback ─────────────────────────────────────────────────────────
     :hover never fires on a touch screen, so every hover treatment on this page
     was dead weight on a phone. Re-hung on :active, and declared AFTER .is-near so
     a press still reads on the step you are already on. */
  .vv-steps .vv-step:active:where(#qualify, #qualify *){box-shadow:inset 0 1px 1px rgba(233,228,220,0.1),inset 0 0 0 1px rgba(var(--gold-rgb),0.6),inset 0 0 46px 0 rgba(var(--gold-rgb),0.18)}
  /* an animation, not a transition — the icon's own transition is 0.9s on an 0.8s
     delay (the entrance), far too slow to read as a press */
  .vv-steps .vv-step:active .vv-icon:where(#qualify, #qualify *){animation:vv-m-pop .36s var(--ease-out-quart);filter:drop-shadow(0 0 30px rgba(var(--gold-rgb),0.5))}
  @keyframes vv-m-pop{0%{transform:none}42%{transform:scale(1.075)}100%{transform:none}}.vv-steps .vv-step:active:where(#qualify, #qualify *)::after{transform:scale(2);box-shadow:0 0 14px 3px rgba(var(--gold-rgb),0.6)}.vv-steps .vv-step:active .vv-wp:where(#qualify, #qualify *){color:var(--silver)}
}
@media (max-width:560px) and (prefers-reduced-motion:reduce){.vv-railv:where(#qualify, #qualify *)::before{display:none}.vv-steps .vv-step:active .vv-icon:where(#qualify, #qualify *){animation:none}.vv-steps .vv-step:where(#qualify, #qualify *),.vv-steps .vv-step:where(#qualify, #qualify *)::after{transition:none}.is-on .vv-railv:where(#qualify, #qualify *)::after{transform:scaleY(1)}
}

/* ── #area — Service Area ────────────────────────────────────────── */
/* scoped to #area */
@media (max-width:560px){
  /* THE defect on this page. At 720px .area-route gets max-height:280px, and a
     220×420 viewBox meeting a 354×280 box scales to 0.667 — so the 11px city
     labels render at 7.3px, letterboxed in the middle of a wide empty band.
     Sizing by HEIGHT instead lets the SVG take its natural 220px width and the
     labels sit at their authored size. */
  .area-route:where(#area, #area *){max-height:none;min-height:0;height:420px;width:auto;margin-inline:auto}/* source zeroes the marquee's -5.5vw at 720 (site.css 2068); the reproduction relies on #area:where(#area, #area *){overflow-x:clip} instead, which hides the bleed but still
     lets the track start off-screen. Zero it properly. */
  .marquee:where(#area, #area *){margin-inline:0}.vv-corridor:where(#area, #area *){font-size:9px;letter-spacing:0.1em;padding:8px 10px}.reason-list li:where(#area, #area *){padding-left:34px;font-size:14.5px}
  /* the ten reasons stagger off one .is-reasons class with nth-child delays topping out at
     .59s. In one column on a phone the list is ~1400px tall, so that stagger is over before
     item 4 is on screen and the rest arrive pre-revealed. mobile.js marks the list and
     reveals each item as it arrives; without the script the list is simply visible. */
  .vv-seq-on > li:where(#area, #area *){opacity:0;transform:translateY(14px)}.vv-seq-on > li.vv-in:where(#area, #area *){opacity:1;transform:none;transition:opacity .5s var(--ease-out-quart),transform .5s var(--ease-out-quart)}.reason-list li:active span:where(#area, #area *){color:var(--gold)}
}

/* ── #faq + #contact ─────────────────────────────────────────────── */
/* scoped to #faq, #contact */
@media (max-width:560px){
  /* ── the ANSWERED stamp, brought back ────────────────────────────────────
     .vv-stamp is display:none from 860px down, which on a phone removed the only
     payoff a tap has — the answer just opened, silently. It is shown again here,
     smaller and with tighter tracking so "ANSWERED" clears a 354px column.
     The clear band is added to the answer's LAST PARAGRAPH rather than to
     .faq-answer itself: site.js writes an explicit pixel height on .faq-answer to
     animate the open, so padding on that element would fall outside the measured
     content and clip. Padding on the paragraph is inside it. */
  .vv-stamp:where(#faq, #faq *, #contact, #contact *){display:block;right:16px;bottom:12px;font-size:13px;letter-spacing:0.22em;padding:3px 8px;transform:rotate(-7deg) scale(1.4)}.faq-item[open] .vv-stamp:where(#faq, #faq *, #contact, #contact *){transform:rotate(-7deg) scale(1)}.faq-answer p:last-of-type:where(#faq, #faq *, #contact, #contact *){padding-bottom:46px}

  /* .place-form is 1fr 1fr with no phone breakpoint of its own (source 1871-1898),
     so at 390px each field is ~160px — too narrow to read a typed address in. */
  .place-form:where(#faq, #faq *, #contact, #contact *){grid-template-columns:1fr;gap:14px}
  /* ── touch targets ───────────────────────────────────────────────────────
     Every interactive element on this page, measured at 390px, against a 44px
     floor. Two were under it and one rule was aimed at an element that does not
     exist: .vv-filter has no <button> — its children are a <label>, the search
     <input> and .vv-count — so a min-height written against .vv-filter button
     matched nothing while #faqFilter sat at 42px.
     The tel:/sms: anchors are bare inline-flex text at 12px with no padding, so
     they had a 16px hit area — on a phone layout, where the sms link is one of the
     likeliest taps on the page. Both are grown here WITHOUT moving the layout, the
     same way footer.html already does it for .built-by a: pad the box out and pull
     the same amount back off the margin. */
  .faq-item summary:where(#faq, #faq *, #contact, #contact *){min-height:48px;padding-block:14px;font-size:14px}.vv-filter input:where(#faq, #faq *, #contact, #contact *){min-height:44px}.place-form input:where(#faq, #faq *, #contact, #contact *),.place-form select:where(#faq, #faq *, #contact, #contact *),.place-form textarea:where(#faq, #faq *, #contact, #contact *){min-height:48px;font-size:16px}
  /* 16px on inputs specifically — iOS Safari zooms the whole page on focus below that */
  /* a summary row is the most-tapped thing on the page and had no pressed state */ .faq-item summary:active:where(#faq, #faq *, #contact, #contact *){background:rgba(var(--gold-rgb),0.07)}.faq-item summary:active:where(#faq, #faq *, #contact, #contact *)::marker,.faq-item summary:active:where(#faq, #faq *, #contact, #contact *)::-webkit-details-marker{color:var(--gold)}.vv-board__head:where(#faq, #faq *, #contact, #contact *){font-size:10px}.vv-slot:where(#faq, #faq *, #contact, #contact *){font-size:10px}.vv-receipt:where(#faq, #faq *, #contact, #contact *){font-size:12px}
  /* a phone number is worth reaching for; margin-block cancels the growth so the
     line sits exactly where it did. Same pattern as footer.html §5.6, but keyed on
     WIDTH rather than (pointer:coarse): at 390px the device is a phone whatever it
     reports, and several report "fine" — gating on pointer left the rule inert. */
  a[href^="tel:"]:where(#faq, #faq *, #contact, #contact *),a[href^="sms:"]:where(#faq, #faq *, #contact, #contact *){display:inline-flex;align-items:center;min-height:44px;margin-block:-14px}
}

/* ── #trust — Trust Strip ────────────────────────────────────────── */
/* scoped to #trust */
@media (max-width:560px){
  /* the read head's falloff is max(120, width/3); at 354px that is 120px, wide
     enough to light two phrases at once and read as a wash rather than a head.
     Narrowing the strip's own padding is not enough — the JS reads the box, so
     the CSS floor is what changes here. */
  .trust-strip .marquee__track:where(#trust, #trust *){gap:22px}.trust-strip .marquee__track span:where(#trust, #trust *){font-size:11px;letter-spacing:0.07em}.vv-head:where(#trust, #trust *){opacity:.55}
}

/* ── #worlds — Two Worlds ────────────────────────────────────────── */
/* scoped to #worlds */
@media (max-width:560px){
  /* the 720 rule pins .worlds-copy under the nav at full width; at 390 the two
     video halves behind it are 195px each, so the copy needs its own ground */
  .worlds-copy:where(#worlds, #worlds *){top:calc(var(--nav-h) * 0.55 + 8px);max-width:calc(100% - 24px);padding:16px}#worlds h2:where(#worlds, #worlds *){max-width:none;font-size:clamp(23px,7.4vw,30px)}#worlds .section-sub:where(#worlds, #worlds *){max-width:none;font-size:14.5px}
}

/* ── footer — site footer ────────────────────────────────────────── */
/* scoped to .overlay-card--footer */
@media (max-width:560px){
  /* the tweaks panel is a design tool, not site furniture — it has no business
     on a phone, where it would cover a third of the screen */
  .vvt:where(.overlay-card--footer, .overlay-card--footer *),.vvt__toggle:where(.overlay-card--footer, .overlay-card--footer *){display:none !important}
  /* --vv-plate-h is a min-height, and the footer's own content (footer-top + the B4 machine
     + the legal row) already exceeds it — the plate measures 390×995 on a phone, not the
     700 asked for. So 700 is only a floor for the case where content shrinks. */
  .overlay-card--footer:where(.overlay-card--footer, .overlay-card--footer *){--vv-plate-h:700px;--vv-lift:32px;--vv-nav-top:0px;--vv-brand-w:100%}

  /* ── framing the portrait plate ───────────────────────────────────────────
     The box is 390×995 (0.392) against an image of 941×1672 (0.563), so object-fit:cover
     scales to fit HEIGHT — 0.5951 — and the crop falls entirely on the WIDTH: 286 image px,
     30% of the frame, gone. Centring it (50%) cut the machine's right edge and the two men
     on that side of the table, which is the subject.
     72% pans the window right: visible range becomes image x 206..861 of 941, which holds
     the machine (centre 765) and the whole table, and still keeps the walking figure
     (~490-680) well inside. Nothing crops vertically at all. */
  .vv-plate img:where(.overlay-card--footer, .overlay-card--footer *){object-position:72% 100%}
  /* Re-aimed against that VISIBLE RANGE rather than the raw image — the distinction is the
     whole bug: a bloom placed at the machine's image coordinate (81%) resolves to 71.6% of
     the box once 206px of the left is cropped away, ~10pp off target.
       machine  image x 765 → (765-206)/655 = 85%,  y 700/1672  = 42%
       pendant  image x 810 → (810-206)/655 = 92%,  y 540/1672  = 32%
     Radii are box-relative too: the machine is ~190 image px wide → 113 displayed → 29% of
     390, so a 20% radius covers it with spill; 8% of 995 ≈ 80px matches its height. */
  .vv-glow:where(.overlay-card--footer, .overlay-card--footer *){background:radial-gradient(20% 8% at 85% 42%,rgba(var(--gold-rgb),0.3) 0%,rgba(var(--gold-rgb),0.09) 45%,rgba(var(--gold-rgb),0) 72%)}.vv-lamp:where(.overlay-card--footer, .overlay-card--footer *){background:radial-gradient(6% 3% at 92% 32%,rgba(var(--gold-rgb),0.34) 0%,rgba(var(--gold-rgb),0) 70%)}.footer-top:where(.overlay-card--footer, .overlay-card--footer *){gap:28px}.footer-nav:where(.overlay-card--footer, .overlay-card--footer *){margin-top:0;gap:2px}.footer-nav a:where(.overlay-card--footer, .overlay-card--footer *){min-height:46px;display:flex;align-items:center}.built-by:where(.overlay-card--footer, .overlay-card--footer *){position:static;margin-top:24px}
  /* B4 keeps its 46px key (source 4b.3) and clears the fixed Call Now band */
  .footer-machine:where(.overlay-card--footer, .overlay-card--footer *){padding-bottom:108px}.vend-line:where(.overlay-card--footer, .overlay-card--footer *){gap:12px}.vend-line__hint:where(.overlay-card--footer, .overlay-card--footer *){font-size:9px;letter-spacing:0.08em}
}

/* ── about.html ──────────────────────────────────────────────────── */
/* scoped to main */
@media (max-width:560px){.about-portraits:where(main, main *){max-width:100%}.about-story p:where(main, main *){font-size:15px;line-height:1.74}

  /* ── the stock gauge, re-hung as a fixed rail on the right edge ──────────
     Below 900px .about-portraits is unpinned, so the gauge panel scrolled out of
     view long before the story was read — a progress meter you scroll past is not
     measuring anything. Fixed to the edge it stays with the reader for the whole
     piece, which is the one job it has.
     Same element, same script, same fill order: paint() fills from the END of the
     row list (`i >= rows.length - full`), so in normal vertical order the bottom
     segment lights first and the rail still stocks BOTTOM-UP, exactly like the
     shelves do on desktop. Only the geometry changes — no markup, no JS. */
  .vv-stock:where(main, main *){position:fixed;top:calc(var(--nav-h) + 8px);right:9px;bottom:104px;width:3px;padding:0;z-index:40;display:flex;pointer-events:none;background:rgba(var(--steel-rgb),0.10);box-shadow:none;-webkit-backdrop-filter:none;backdrop-filter:none}.vv-stock:where(main, main *)::before{display:none}          /* liquid-glass edge makes no sense at 3px */
  .vv-stock__head:where(main, main *),.vv-stock__foot:where(main, main *){display:none}
  /* eight segments kept rather than one bar — the shelf count is the metaphor */
  .vv-stock__rows:where(main, main *){flex:1 1 auto;width:100%;display:flex;flex-direction:column;gap:2px}.vv-stock__row:where(main, main *){flex:1 1 0;height:auto;background:none;box-shadow:none;overflow:hidden}
  /* fill along the rail instead of across a shelf */
  .vv-stock__row:where(main, main *)::after{transform-origin:50% 100%;transform:scaleY(0);background:linear-gradient(180deg,rgba(var(--gold-rgb),0.55),rgba(var(--gold-rgb),0.95))}.vv-stock__row.is-full:where(main, main *){box-shadow:none}.vv-stock__row.is-full:where(main, main *)::after{transform:scaleY(1)}
}
