/* ============================================================================
   GOLDEN DRAGON — the store page
   ---------------------------------------------------------------------------
   Four services, taken one at a time: clustered on the landing, carried through
   a chapter each, docked into a label card that can be added to a quote list,
   and gathered at the end. The 3D and its choreography are the page; the chrome
   around them speaks the company's own vernacular — asset tags, cable labels,
   a quote list — in the navy and gold of the mark.

   Sizing is vw throughout, so the composition scales as one drawing instead of
   reflowing. The mobile block at the foot is the one place that stops being
   true, because a 9.8vw headline on a phone is six characters wide.
   ========================================================================= */

:root {
  /* --- the type ramp, measured at 1440x900 ------------------------------ */
  --p1: 9.8vw;    /* 141.12px — the one giant headline per screen */
  --p2: 2.7vw;    /* 38.88px  — straplines, secondary headlines */
  --p4: .84vw;    /* 12.096px — body copy, lead, chrome. Set solid (lh 1). */
  --p5: .6vw;     /* 8.6px    — inline annotations, card ingredients */
  --p6: .49vw;    /* 7px      — the card spec table, and nothing else */
  --s1: .76vw;    /* 10.944px — receipt monospace */

  --f-store: 'Inter Tight', 'Helvetica Neue', Arial, sans-serif;
  --f-mono:  'PT Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* body copy: sentence case, readable at every width */
  --body:  max(15px, 1vw);

  --ink:   #1D2D4A;   /* the navy of the mark */
  --scan:  #F2AE1C;   /* the gold of the mark: added-to-quote, focus, tags */
  --mute:  #5B6474;
}

/* Wrapped in :where() so this contributes no specificity at all.

   Written plainly as `.store h1, .store p { margin: 0 }` it scores one class
   plus one element, which beats the single class of `.tit` and `.lead` further
   down and silently cancelled their `margin: … auto`. The headline and the
   body copy both lost their centring and hung off the left edge. A reset must
   never outrank the components it is resetting. */
:where(.store h1, .store h2, .store h3, .store h4, .store p,
       .store figure, .store ul, .store table) { margin: 0; padding: 0; }
:where(.store ul) { list-style: none; }

.store {
  /* Positioned so the left rail can size itself against the document rather
     than against the viewport — see .rail. */
  position: relative;
  font-family: var(--f-store);
  font-weight: 300;
  color: var(--ink);
  background: #FFF;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* White at the top, warm grey by the bottom of every screen. Fixed rather than
   scrolling: the products are lit by one studio environment that does not move,
   and a gradient travelling under them breaks that illusion immediately. */
.store__bg {
  position: fixed;
  inset: 0;
  z-index: -2;
  background: linear-gradient(180deg, #FFFFFF 0%, #F9F9F7 34%, #EEEFEC 72%, #E3E6E4 100%);
}

.skip-link {
  position: absolute; left: -9999px; top: 0;
  background: var(--ink); color: #fff; padding: .8rem 1.2rem; z-index: 200;
}
.skip-link:focus { left: 0; }

a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* ---------------------------------------------------------------------------
   Stacking

   One canvas, fixed, full-viewport. Where it sits in the stack is a design
   decision that changes per screen, and the reference changes it deliberately:

     landing   type IN FRONT of the products — the headline is the subject
     chapter   product IN FRONT of the type — the product is the subject

   That second one is what makes the objects read as things standing in a space
   rather than a picture behind the words. On chapter 01 the reference lets the
   can cover two letters of its own headline.
   ------------------------------------------------------------------------ */

.hero__canvas {
  position: fixed;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 5;
  pointer-events: none;
}

main { position: relative; }

.landing   { position: relative; z-index: 10; }   /* type over product */
.manifesto { position: relative; z-index: 10; }
.chapter   { position: relative; z-index: 1;  }   /* product over type */
.label     { position: relative; z-index: 10; }
.clients   { position: relative; z-index: 20; }   /* opaque: hides the canvas */
/* The checkout is the one screen whose parts are on both sides of the canvas,
   so the section itself must NOT make a stacking context — `z-index: 1` here
   trapped its children below the canvas however high they were raised, and the
   call to action ended up behind a chrome rail. Positioned, index auto. */
.checkout  { position: relative; }
.checkout .tit { position: relative; z-index: 1;  }   /* basket crops the headline */
.checkout .talk,
.talk__alt,
.checkout__links,
.store__credit { position: relative; z-index: 10; font-style: normal; }   /* nothing crops the button */
.store__credit b { font-weight: 500; letter-spacing: .03em; color: var(--ink); }

/* ===========================================================================
   FIXED CHROME
   ======================================================================== */

/* --- the left rail ------------------------------------------------------
   Not a ticker. It is a column as tall as the document holding a series of
   near-headline-sized statements reading bottom-to-top, which scroll past with
   the page. `writing-mode: vertical-rl` plus a 180deg turn gets that without
   the usual trick of rotating a fixed strip and swapping its width for the
   viewport height — and unlike that trick, each block keeps its natural length,
   so the copy can be edited without re-measuring anything. */
.rail {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 3.9vw;
  z-index: 30;
  pointer-events: none;
  /* Clipped, and the body is positioned, so the strip is exactly as tall as the
     page. Without both, it is laid out against the viewport instead, its blocks
     overflow past the footer, and that overflow adds a screen and a half of
     blank scroll to the end of the document. */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.rail__lg, .rail__sm {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  line-height: 1;
}
.rail__lg { font-size: var(--p2); font-weight: 200; letter-spacing: -.01em; text-transform: uppercase; }
.rail__sm { font-size: var(--p4); }
.rail img { width: 1.9vw; }

/* Gaps, not a rhythm. The reference scatters these irregularly down 16 000px,
   and the irregularity is what stops the column reading as a list.

   Sized so the last block lands just above the footer. The rail is clipped to
   the document, so gaps that are too generous do not lengthen the page any
   more — they silently cut the last line off instead. Measured at 1440x900:
   page ~18 850px, rail content ~18 600. */
.rail > * { flex: none; }
.rail__gap-s { height: 28vh; }
.rail__gap-m { height: 63vh; }
.rail__gap-l { height: 132vh; }
.rail__gap-x { height: 250vh; }

/* --- top-right: WhatsApp and the menu, side by side --- */
.topbar {
  position: fixed;
  top: .9vh; right: .9vw;
  z-index: 72;
  display: flex; gap: .5vw;
}
.corner {
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  display: flex; align-items: center; gap: .5vw;
  padding: .5vw 1vw;
  font-size: max(14px, .95vw);
  font-weight: 400;
  border: 1px solid currentColor;
  border-radius: 99px;
  background: rgba(255, 255, 255, .7);
  backdrop-filter: blur(6px);
}
.corner img { width: .9vw; }

/* The WhatsApp button: solid WhatsApp green so it is found by colour before it
   is read, fixed with the menu so it follows the reader down the page. */
.wa-cta {
  display: flex; align-items: center; gap: .55vw;
  padding: 0 max(18px, 1.25vw) 0 max(12px, .85vw);
  height: max(44px, 3.1vw);
  box-sizing: border-box;
  border-radius: 99px;
  background: #25D366;
  color: #fff;
  font-size: max(15px, 1vw);
  font-weight: 600;
  letter-spacing: -.005em;
  box-shadow: 0 8px 22px rgba(37, 211, 102, .35);
  position: relative;
  transition: background .3s, transform .35s cubic-bezier(.16, 1, .3, 1), box-shadow .35s;
}
.wa-cta img { width: max(22px, 1.45vw); height: auto; }
.wa-cta:hover { background: #1EBE5A; transform: translateY(-1px); box-shadow: 0 12px 28px rgba(37, 211, 102, .45); }
.wa-cta:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
/* A soft ring that breathes out a few times after load, then stops. */
.wa-cta::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 0 rgba(37, 211, 102, .55);
  animation: wa-ring 2.4s 1.6s ease-out 3;
}
@keyframes wa-ring {
  0%   { box-shadow: 0 0 0 0 rgba(37, 211, 102, .55); }
  70%  { box-shadow: 0 0 0 14px rgba(37, 211, 102, 0); }
  100% { box-shadow: 0 0 0 0 rgba(37, 211, 102, 0); }
}
.topbar { align-items: center; }
.menu-btn { height: max(44px, 3.1vw); box-sizing: border-box; }
/* The mark carries the meaning here, so it is set a shade larger than the
   arrow it replaced and pulled up by its own optical centre — the glyph's
   bubble sits high in its box and hangs below the cap height otherwise. */
.corner img.corner__wa { width: 1.15vw; margin-top: -.08vw; }

/* --- the menu button and its panel --------------------------------------
   The panel is a card pinned under the pill that opened it, the same white
   stock and navy print as the label cards, so it reads as part of the page
   rather than an overlay dropped on top of it. */
.menu-btn { font-size: max(14px, .95vw); font-weight: 400; }
.menu-btn__bars { display: grid; gap: 3px; width: 14px; }
.menu-btn__bars i { display: block; height: 1.5px; background: currentColor; transition: transform .35s cubic-bezier(.16, 1, .3, 1); }
.menu-btn[aria-expanded="true"] { background: var(--ink); color: #fff; border-color: var(--ink); }
.menu-btn[aria-expanded="true"] .menu-btn__bars i:first-child { transform: translateY(2.25px) rotate(45deg); }
.menu-btn[aria-expanded="true"] .menu-btn__bars i:last-child  { transform: translateY(-2.25px) rotate(-45deg); }
.corner:focus-visible, .menu a:focus-visible { outline: 3px solid var(--scan); outline-offset: 3px; }

.menu {
  position: fixed;
  top: calc(.9vh + max(44px, 3.2vw)); right: .9vw;
  z-index: 71;
  width: max(340px, 27vw);
}
.menu[hidden] { display: none; }
.menu__card {
  background: #fff;
  border: 1px solid rgba(29, 45, 74, .18);
  border-radius: .8vw;
  box-shadow: 0 2vw 4vw rgba(29, 45, 74, .16);
  padding: max(22px, 1.7vw);
  display: flex; flex-direction: column; gap: max(16px, 1.2vw);
  transform-origin: 100% 0;
  animation: menu-in .35s cubic-bezier(.16, 1, .3, 1);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } to { opacity: 1; transform: none; } }
.menu__head { display: flex; align-items: center; gap: 10px; font-size: max(14px, .95vw); font-weight: 500; }
.menu__head img { width: 32px; height: 32px; }
.menu__pages { display: flex; flex-direction: column; gap: 2px; }
.menu__pages a {
  display: inline-block;
  font-size: max(30px, 2.5vw);
  font-weight: 200;
  line-height: 1.12;
  letter-spacing: -.02em;
  text-transform: uppercase;
  background: linear-gradient(var(--scan), var(--scan)) 0 92% / 0 3px no-repeat;
  transition: background-size .4s cubic-bezier(.16, 1, .3, 1);
}
.menu__pages a:hover, .menu__pages a:focus-visible { background-size: 100% 3px; }
.menu__sub {
  padding-top: max(14px, 1vw);
  border-top: 1px solid rgba(29, 45, 74, .14);
  font-family: var(--f-mono);
  font-size: max(12px, .8vw);
  color: var(--mute);
}
.menu__jump { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 16px; margin-top: -6px; }
.menu__jump a { font-size: max(14px, .95vw); display: flex; flex-direction: column; gap: 2px; }
.menu__jump a:hover { text-decoration: underline; text-decoration-color: var(--scan); text-decoration-thickness: 2px; text-underline-offset: 4px; }
.menu__jump sup { font-family: var(--f-mono); font-size: max(10px, .65vw); color: var(--mute); }
.menu__also { font-size: max(13px, .85vw); color: var(--mute); margin-top: -4px; }
.menu__also:hover { color: var(--ink); }
.inline-link { border-bottom: 1px solid currentColor; }
.inline-link:hover { color: var(--scan); }
.menu__mail { font-family: var(--f-mono); font-size: max(12px, .8vw); color: var(--mute); }
.menu__mail:hover { color: var(--ink); }
@media (prefers-reduced-motion: reduce) { .menu__card { animation: none; } }

/* --- the quote list -----------------------------------------------------
   A till roll in the bottom-right corner. It feeds up out of the page edge in
   stepper-motor jerks once the reader is past the landing and then stays fully
   out: every line, the running count and the WhatsApp button are always
   readable. A line prints as its chapter is read; "Add to quote" marks it. */
.receipt {
  position: fixed;
  right: .55vw; bottom: 0;
  width: max(190px, 12.4vw);
  box-sizing: border-box;
  padding: .8vw .8vw 1.6vw;
  background: #fff;
  color: var(--ink);
  /* White paper on an ivory page needs an edge or it is not there at all. */
  border: 1px solid rgba(29, 45, 74, .18);
  border-bottom: 0;
  box-shadow: 0 -6px 30px rgba(29, 45, 74, .08);
  font-family: var(--f-mono);
  font-size: max(11.5px, .76vw);
  line-height: 1.3;
  z-index: 55;
  pointer-events: none;

  /* Two mask layers, unioned: all of the paper except the last strip, plus a
     conic wedge tiled across that strip to cut the zig-zag tear edge. */
  --tear:
    linear-gradient(#000 0 0) 0 0 / 100% calc(100% - 7px) no-repeat,
    conic-gradient(from 135deg at 50% 0, #0000, #000 1deg 89deg, #0000 90deg)
      0 100% / 14px 7px repeat-x;
  -webkit-mask: var(--tear);
  mask: var(--tear);

  transform: translateY(105%);
  transition: transform .9s;
  transition-timing-function: steps(7, end);
}
.receipt.is-open { transform: translateY(0); }

.receipt__head { display: flex; align-items: center; gap: .45vw; }
.receipt__head img { width: max(20px, 1.35vw); height: auto; }
.receipt__head span:first-of-type { flex: 1; }
.receipt__rule { letter-spacing: .04em; overflow: hidden; white-space: nowrap; color: #8A93A3; }

.receipt__items { margin: .55vw 0; }
.receipt__item {
  position: relative;
  padding: .28vw .3vw;
  margin: 0 -.3vw;
  border-radius: 3px;
  color: #A3AAB6;
  transition: color .6s;
}
.receipt__item.is-on { color: var(--ink); }
.receipt__line { display: flex; gap: .45em; align-items: baseline; }
.receipt__box {
  flex: none;
  width: .9em; height: .9em;
  box-sizing: border-box;
  border: 1px solid currentColor;
  border-radius: 2px;
  transform: translateY(.1em);
  position: relative;
}
.receipt__ref { display: flex; justify-content: space-between; padding-left: 1.35em; opacity: .75; }
.receipt__qty { font-weight: 400; opacity: 0; }

/* Added: the box fills gold with a tick, the quantity prints, and the line
   keeps a gold rule down its left edge like a highlighter pass. */
.receipt__item.is-added { box-shadow: inset 2px 0 0 var(--scan); }
.receipt__item.is-added .receipt__box { background: var(--scan); border-color: var(--scan); }
.receipt__item.is-added .receipt__box::after {
  content: '';
  position: absolute; left: 32%; top: 8%;
  width: 28%; height: 55%;
  border-right: 1.5px solid var(--ink);
  border-bottom: 1.5px solid var(--ink);
  transform: rotate(45deg);
}
.receipt__item.is-added .receipt__qty { opacity: 1; color: var(--ink); }

.receipt__total { display: flex; justify-content: space-between; font-weight: 700; }
.receipt__count { display: inline-block; min-width: 2ch; text-align: right; }
.receipt__note { margin-top: .3vw; }

.receipt__send {
  pointer-events: auto;
  display: flex; align-items: center; justify-content: center; gap: .45vw;
  margin-top: .55vw;
  min-height: 40px;
  padding: .45vw .5vw;
  border-radius: 99px;
  background: #25D366;
  color: #fff;
  font-family: 'Inter Tight', sans-serif;
  font-size: max(13px, .82vw);
  font-weight: 600;
  line-height: 1.15;
  text-align: center;
  position: relative;
  overflow: hidden;
  transition: background .3s;
}
.receipt__send img { width: max(17px, 1.1vw); height: auto; flex: none; }
.receipt__send:hover { background: #1EBE5A; }
.receipt__send:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

/* --- the moment something is added ---------------------------------------
   Four beats, in order: the paper jolts as the printer feeds, the line prints
   left to right, a gold band passes over it, and the count ticks over. The
   WhatsApp button then catches a sweep of light, pointing at the next step. */
.receipt.is-feeding { animation: paper-feed .42s steps(3, end); }
@keyframes paper-feed {
  0%   { transform: translateY(0); }
  35%  { transform: translateY(-9px); }
  70%  { transform: translateY(-3px); }
  100% { transform: translateY(0); }
}
.receipt__item.is-rung .receipt__line,
.receipt__item.is-rung .receipt__ref { animation: print-line .55s steps(18, end) both; }
.receipt__item.is-rung .receipt__ref { animation-delay: .2s; }
@keyframes print-line {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
.receipt__item.is-rung::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 3px;
  background: var(--scan);
  z-index: -1;
  animation: ring-flash 1.4s cubic-bezier(.2, .7, .3, 1) both;
}
@keyframes ring-flash {
  0%, 30% { opacity: .55; }
  100%    { opacity: 0; }
}
.receipt__count.is-ticking { animation: count-tick .5s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes count-tick {
  0%   { transform: translateY(-70%); opacity: 0; color: var(--scan); }
  100% { transform: none; opacity: 1; }
}
.receipt__send.is-glint::after {
  content: '';
  position: absolute; top: 0; bottom: 0; left: -40%;
  width: 35%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .65), transparent);
  animation: glint .9s .35s ease-out both;
}
@keyframes glint { to { left: 110%; } }

/* The chip that detaches from a scanned barcode and files itself to the till.
   Fixed, because it crosses from the card to the receipt — two different
   stacking worlds — and must be above both for the length of the flight. */
.scan-chip {
  position: fixed;
  z-index: 60;
  transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px 7px 7px;
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  border: 1px solid rgba(29, 45, 74, .18);
  box-shadow: 0 14px 30px rgba(29, 45, 74, .22);
  font-size: max(13px, .85vw);
  white-space: nowrap;
  pointer-events: none;
  will-change: transform, opacity;
}
.scan-chip b {
  font-family: var(--f-mono);
  font-weight: 400;
  padding: 2px 6px;
  border-radius: 3px;
  background: var(--scan);
}

/* ===========================================================================
   SHARED TYPE
   ======================================================================== */

.tit {
  font-size: var(--p1);
  font-weight: 200;
  line-height: .9;
  letter-spacing: -.025em;
  text-transform: uppercase;
}
.strap {
  font-size: var(--p2);
  font-weight: 200;
  line-height: .9;
  letter-spacing: -.012em;
  text-transform: uppercase;
}
/* Body copy is set solid and flush both edges. The justified block is a
   signature of this design: at 0.84vw across a 28vw column the word spacing
   opens just enough to look printed rather than typed. */
.copy, .lead {
  font-size: var(--body);
  font-weight: 400;
  line-height: 1.5;
  text-align: left;
  hyphens: none;
}
.copy p + p { margin-top: 1.1em; }

/* ===========================================================================
   LANDING
   ======================================================================== */

.landing {
  height: 100vh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
}
/* The registered company name, and the only place on the page it is set at any
   size. The reference runs a small wordmark here; this one is deliberately
   heavier, because "Golden Dragon Technology Sdn. Bhd." is what the client is
   signing a contract with and it should not read as a caption. */
.landing__mark {
  position: absolute; top: 2.2vh; left: 0; right: 0;
  display: flex; flex-direction: row; justify-content: center; align-items: center; gap: 1vw;
  font-size: 1.9vw;
  font-weight: 500;
  letter-spacing: .04em;
  line-height: 1.1;
}
/* The one piece of colour on a page that is otherwise ink on paper, so it is
   kept small and given room rather than scaled up to fill the corner. */
.landing__logo { width: 3.8vw; height: auto; }
.landing .tit   { width: 60vw; }
.landing .strap {
  width: 60vw; margin-top: 1.5vw;
  display: flex; align-items: center; justify-content: center; gap: .9vw;
}
.landing .lead  { width: 30vw; margin-top: 1.8vw; text-align: center; color: var(--mute); }

.scroll-cue {
  position: absolute; bottom: 6vh; left: 0; right: 0;
  font-size: var(--p4);
  color: var(--mute);
}

/* ===========================================================================
   MANIFESTO
   ======================================================================== */

.manifesto { min-height: 90vh; padding: 9vh 0 6vh; }

/* The four services as a shelf index, evenly spread across the same 54vw the
   reference uses. Each carries its number as a raised superscript. */
.index {
  width: 54vw; margin: 0 auto;
  display: flex; justify-content: space-between;
  font-size: max(14px, 1vw);
}
.index sup { font-family: var(--f-mono); font-size: max(10px, .6vw); margin-left: .3vw; color: var(--mute); }

/* Each entry jumps to its chapter. The rule under it is the whole affordance —
   drawn from the left on hover and on keyboard focus, in the scan colour the
   beam and the receipt already use, so the index reads as part of the same
   machine rather than as a navigation bar bolted to the top. */
.index a {
  position: relative;
  color: inherit;
  text-decoration: none;
  padding-bottom: .35vw;
  transition: opacity .35s ease;
}
.index a::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--scan);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .45s cubic-bezier(.16, 1, .3, 1);
}
.index a:focus-visible::after { transform: scaleX(1); }
.index a:focus-visible { outline: none; opacity: 1; }

/* The one being pointed at stays lit; the other three step back, which is the
   same trick the shelf uses to tell you which pack you have picked up.

   Behind `hover: hover`, because a touch browser leaves :hover latched on the
   last thing tapped — on a phone the reader would arrive at chapter 02 and find
   the index still dimmed behind them, with no way to clear it. */
@media (hover: hover) {
  .index a:hover::after { transform: scaleX(1); }
  .index:hover a { opacity: .42; }
  .index:hover a:hover { opacity: 1; }
}

.manifesto__say {
  width: 60vw; margin: 12vh auto 0;
  font-size: var(--p2);
  font-weight: 200;
  line-height: 1.0;
  letter-spacing: -.012em;
  text-transform: uppercase;
  text-align: center;
  text-wrap: balance;
}

.manifesto__cols {
  width: 54vw; margin: 6vh auto 0;
  display: flex; justify-content: space-between;
}
.manifesto__cols > div { width: 24vw; }

/* ===========================================================================
   CHAPTERS

   Four chapters, four compositions. The reference does not repeat a layout, and
   copying only its components without copying that variation produces a page
   that is recognisably the same design running four times. The modifiers below
   are the differences, and they are the point.
   ======================================================================== */

/* Everything a chapter has to say lives in the top 45% of the screen. Measured
   off the reference: headline at y8, strapline at y160, body columns ending by
   y375, and the bottom half deliberately empty — that is the space the label
   card slides up into, and the space the product is centred in.

   78, not 100: the copy is top-aligned and the product lives in the fixed
   canvas, so the last fifth of the screen was empty in the layout *and* in the
   render — scroll distance with nothing standing in it. The tallest chapter's
   copy runs to 59vh, so this still clears it with room to spare; the floor is
   the copy, not the number. */
.chapter { min-height: 78vh; padding: 1.5vh 0 0; }

/* --- the numbered badge — a 7vw box, per the reference's 101px --- */
.ch__badge {
  width: 7vw; height: 7vw;
  padding: .55vw;
  border: 1px solid currentColor;
  border-radius: .5vw;
  display: flex; flex-direction: column; justify-content: space-between;
  font-size: var(--p4);
  font-weight: 400;
  line-height: 1.1;
}
.ch__badge b { font-size: 3.2vw; font-weight: 200; line-height: .8; }

.ch__tit   { margin-top: 0; }
.ch__strap { margin-top: 2.4vh; }
.ch__cols  { margin-top: 8vh; display: flex; }

/* --- 01 · centred, two columns, badge above the headline --- */
.chapter--a { text-align: center; }
.chapter--a .ch__badge { margin: 0 auto 2vh; text-align: left; }
.chapter--a .ch__tit   { width: 82vw; margin-left: auto; margin-right: auto; }
.chapter--a .ch__cols  { width: 60vw; margin-left: auto; margin-right: auto; gap: 3vw; }
.chapter--a .ch__cols > div { width: 28.5vw; }

/* --- 02 · left-aligned, three columns, badge out to the right --- */
.chapter--b .ch__badge { margin-left: 78vw; }
.chapter--b .ch__tit   { width: 76vw; margin-left: 12vw; text-align: left; }
.chapter--b .ch__strap { width: 76vw; margin-left: 12vw; text-align: right; }
.chapter--b .ch__cols  { width: 71vw; margin: 5vh auto 0; gap: 3.9vw; }
.chapter--b .ch__cols > div { width: 21vw; }

/* --- 03 · centred, strapline first, two tight columns, headline last --- */
.chapter--c { text-align: center; }
.chapter--c .ch__strap {
  margin-top: 0;
  display: flex; align-items: center; justify-content: center; gap: 1.2vw;
}
.chapter--c .ch__cols  { width: 56vw; margin: 6vh auto 0; gap: 0; }
.chapter--c .ch__cols > div { width: 28vw; padding: 0 .8vw; }
.chapter--c .ch__tit   { width: 50vw; margin: 8vh auto 0; font-size: var(--p2); }

/* --- 04 · left-aligned strapline, a single column out to the right --- */
.chapter--d .ch__strap {
  width: 34vw; margin: 0 0 0 12vw;
  text-align: left; font-size: var(--p2);
}
.chapter--d .ch__cols  { width: 30vw; margin: -4vh 0 0 42vw; }
.chapter--d .ch__cols > div { width: 30vw; }
.chapter--d .ch__tit   { display: none; }

/* ===========================================================================
   TAGS

   The one claim a chapter makes loudly is printed on a gold asset tag — the
   thing every machine we supply leaves with. Absolutely placed in the margin
   the copy leaves, tilted the way a tag hangs, with the punched hole drawn in.
   ======================================================================== */

.seal, .burst {
  position: absolute;
  display: flex; flex-direction: column;
  align-items: flex-start; justify-content: center;
  gap: .25vw;
  padding: 1.1vw 1.1vw .9vw 1.1vw;
  min-width: 6.2vw;
  background: var(--scan);
  color: var(--ink);
  border-radius: .45vw;
  font-family: var(--f-mono);
  font-size: max(10px, .62vw);
  line-height: 1.2;
  text-align: left;
  box-shadow: 0 1.2vw 2.4vw rgba(29, 45, 74, .16);
}
.seal::before, .burst::before {
  content: '';
  position: absolute; top: .55vw; right: .55vw;
  width: .55vw; height: .55vw;
  border-radius: 50%;
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--ink);
}
.seal b, .burst b {
  font-family: var(--f-store);
  font-size: 2vw; font-weight: 300;
  line-height: 1; letter-spacing: -.03em;
}
.seal  { left: 9vw;  top: 14vh; transform: rotate(-5deg); }
.burst { left: 28vw; top: 30vh; transform: rotate(4deg); }

/* ===========================================================================
   LABEL CARDS

   Each chapter ends by being scanned. The card is pinned — sticky for the whole
   of a tall spacer — so it holds still while the page runs past it, which is
   the reference's ScrollTrigger pin done in CSS and without a library.

   The cards are deliberately not the same size or in the same place. Building
   one component and repeating it four times is the single most obvious way this
   page stops looking like the reference.

   The spacers used to run 300/200/300/400vh — twelve screens of scrolling for
   four cards, and the card was empty while you did it. The product now docks
   into the frame (hero.js, the `card` pose), so the pin only has to last long
   enough to arrive and be read.

   Trimmed twice: from 165/150/165/165 to 120, then to these.

   How long a sticky element actually holds still is the *difference* between the
   spacer and the sticky box, not the spacer on its own — so a 100vh sticky in a
   120vh spacer stands still for 20vh and spends the other 100 travelling. That
   made shortening the spacer expensive: at 107vh the pause collapsed to 7vh,
   which is under one notch of a mouse wheel, and the card carrying the page's
   one interaction — CLICK TO SCAN — would have gone by before it could be hit.

   Shrinking the sticky box instead costs nothing. The tallest card is 72vh, so
   the box only ever needed 76 of the 100vh it was holding; the 24vh it was
   wasting are given back to the pause. Offset by `top: 12vh` the card's centre
   stays exactly where it was, at half the viewport height.

     spacer 165vh, sticky 100vh  ->  65vh still, 165vh total   (original)
     spacer 104vh, sticky  76vh  ->  28vh still, 104vh total   (here)

   Two fifths of the scrolling for nearly half the pause.
   ======================================================================== */

.label--1 { height: 104vh; }
.label--2 { height: 104vh; }
.label--3 { height: 104vh; }
.label--4 { height: 104vh; }

.label__stick {
  position: sticky; top: 12vh;
  /* Sized to the tallest card, not to the viewport — see above. */
  height: 76vh;
  display: flex; align-items: center;
}

.label__card {
  position: relative;
  width: 18.1vw; height: 63vh;
  border: 1px solid rgba(29, 45, 74, .5);
  border-radius: .5vw;
  margin: 0 auto;
}
/* per the baseline: 261x565 / 405x646 / 261x556 / 261x556, and only two of the
   four are centred */
.label--2 .label__card { width: 28.1vw; height: 72vh; margin: 0 14.7vw 0 auto; }
.label--4 .label__card { width: 18.1vw; height: 62vh; margin: 0 auto 0 26.6vw; }

/* #04 sits left of centre, so the right half of its screen carries the
   explanation: how a server is sized, which is the part buyers most often get
   wrong. It arrives with the card and rides the same pin. */
.label__aside {
  position: absolute;
  left: 49vw; top: 50%;
  width: 31.5vw;
  color: var(--ink);
  opacity: 0;
  transform: translateY(calc(-50% + 24px));
  transition: opacity .8s ease, transform 1s cubic-bezier(.16, 1, .3, 1);
}
.label__aside.is-shown { opacity: 1; transform: translateY(-50%); }
.aside__ref { font-size: max(13px, .85vw); color: var(--mute); margin-bottom: 1.2vw; }
.aside__ref span {
  font-family: var(--f-mono);
  padding: .15vw .45vw;
  margin-right: .4vw;
  background: var(--scan);
  color: var(--ink);
  border-radius: 3px;
}
.aside__tit {
  font-size: max(26px, 2.35vw);
  font-weight: 300;
  line-height: 1.04;
  letter-spacing: -.02em;
  margin-bottom: 1.2vw;
}
.aside__lead { font-size: var(--body); line-height: 1.5; color: #3A4760; max-width: 30em; }
.aside__list { margin: 1.3vw 0; border-top: 1px solid var(--rule, #D5D8DD); }
.aside__list > div {
  display: grid; grid-template-columns: 10.5vw 1fr; gap: 1.2vw;
  padding: .7vw 0;
  border-bottom: 1px solid #D5D8DD;
}
.aside__list dt { margin: 0; font-size: max(14px, .9vw); font-weight: 500; }
.aside__list dd { margin: 0; font-size: max(14px, .9vw); line-height: 1.45; color: #3A4760; }
.aside__foot { font-size: max(14px, .9vw); line-height: 1.5; color: var(--mute); }
.aside__wa {
  pointer-events: auto;
  display: inline-flex; align-items: center; gap: .5vw;
  margin-top: 1vw;
  padding: .55vw 1.1vw .55vw .8vw;
  border: 1.5px solid #25D366;
  border-radius: 99px;
  color: var(--ink);
  font-weight: 500;
  transition: background .3s;
}
.aside__wa img { width: max(20px, 1.25vw); height: auto; }
.aside__wa:hover { background: rgba(37, 211, 102, .12); }
.aside__wa:focus-visible { outline: 3px solid var(--scan); outline-offset: 3px; }

.label__num {
  position: absolute; top: .3vw; left: .55vw;
  width: 6vw; height: 2.5vw;
  overflow: hidden;
  font-size: var(--p2);
  font-weight: 200;
  line-height: .9;
}
.label__num-in { transition: transform .7s cubic-bezier(.16, 1, .3, 1); }
/* Half, not whole. The strip holds exactly two numbers, so -100% of the strip
   is two lines and carries both of them out of the window — the card showed a
   blank corner where its number should be. */
.is-shown .label__num-in { transform: translateY(-50%); }

/* Both long edges carry a line of the chapter, turned to run down the card the
   way small print runs down the side of a pack. */
.label__side, .label__edge {
  position: absolute;
  writing-mode: vertical-rl;
  font-size: max(10px, .62vw);
  color: var(--mute);
  white-space: nowrap;
  line-height: 1;
}
.label__side { top: .6vw;  right: .5vw; }
.label__edge { bottom: 9vw; left: .5vw; }

.label__foot { position: absolute; left: .6vw; right: .6vw; bottom: .55vw; }

.barcode { position: relative; width: 8.3vw; overflow: hidden; }
.barcode img { width: 100%; }

/* The beam sweeps the barcode, then the button is replaced by its result. */
.beam {
  position: absolute; left: -.3vw; width: calc(100% + .6vw); top: 0;
  height: 3px; background: var(--scan);
  filter: blur(1px); opacity: 0;
}
.is-scanning .beam { animation: beam-sweep 1.1s cubic-bezier(.5, 0, .5, 1) forwards; }
@keyframes beam-sweep {
  0%   { top: 0;    opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}

/* Barcode left, pill right, on one line just above the table — the row a
   supermarket label always has. Both used to be absolutely placed and landed on
   top of the ingredients. */
.label__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: .8vw;
}

.label__scan, .label__added {
  padding: .45vw 1vw;
  border: 1px solid var(--ink);
  border-radius: 99px;
  color: var(--ink);
  font-size: max(13px, .9vw);
  font-weight: 400;
  white-space: nowrap;
}
.label__scan { pointer-events: auto; background: var(--scan); border-color: var(--scan); transition: background .3s; }
.label__scan:hover { background: #F7CB63; }
.label__scan:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }
.label__added { display: none; }
.is-added .label__scan  { display: none; }
.is-added .label__added { display: block; }

/* The spec table and the ingredients sit side by side along the foot, at 7px.
   That size is not a mistake — it is the size real nutrition print is, and
   setting it any larger is the difference between a label and a caption. */
.label__grid {
  display: flex; justify-content: space-between; gap: .8vw;
  margin-top: .5vw;
  font-size: var(--p6);
  line-height: 1.24;
}
.spec { width: 8.3vw; }
.spec table { width: 100%; border-collapse: collapse; }
.spec th, .spec td { text-align: left; font-weight: 400; }
.spec th + th, .spec td + td { text-align: right; }
.spec th { font-weight: 500; }
.spec--nil { color: var(--mute); }

/* Sentence case, unlike everything around it — the way an ingredients list is
   the one block on a pack that is not shouting. */
.label__ing { width: 8vw; }
.label__ing em { display: block; margin: .5em 0; font-style: normal; }
.label__ing b  { display: block; font-weight: 500; }

/* ===========================================================================
   CLIENTS — inverted

   The one dark screen. It is opaque and above the canvas, so the products are
   genuinely off-stage here rather than dimmed behind it.
   ======================================================================== */

.clients {
  background: var(--ink);
  color: #fff;
  padding: 18vh 0 14vh;
  overflow: hidden;
}
.clients .tit { width: 76vw; margin: 0 auto; text-align: center; }
.clients__cols {
  width: 62vw; margin: 8vh auto 0;
  display: flex; justify-content: space-between;
}
.clients__cols > div { width: 27vw; }

/* --- the brands we work with --------------------------------------------
   Nine brands on the navy, three to a row, each with its mark on a white plate
   (so the mark keeps its own colour against the navy), the name in light type
   under it, and one line on what we do with it. The plate is landscape, not
   square: Hikvision and Panduit are wordmarks seven and four times wider than
   they are tall, and a square tile shrinks them to a line nobody can read. */
.brands { width: 66vw; margin: 14vh auto 0; }   /* clear of the receipt on the right */
.brands__tit {
  font-size: var(--p2);
  font-weight: 200;
  line-height: 1;
  letter-spacing: -.012em;
  text-transform: uppercase;
  text-align: center;
}
.brands__lead {
  margin: 1.4vh auto 0;
  max-width: 38em;
  font-size: var(--body);
  line-height: 1.5;
  text-align: center;
  color: rgba(255, 255, 255, .72);
}
.brands__list {
  list-style: none;
  margin: 6vh 0 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 2.6vw;
}
.brands__list li {
  padding: 2vw 0 2.4vw;
  border-top: 1px solid rgba(255, 255, 255, .22);
}
.brands__list b {
  display: block;
  font-size: max(24px, 2.1vw);
  font-weight: 200;
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--scan);
}
.brands__logo {
  display: block;   /* not grid: a grid cell leaves the img's 100% height unresolved */
  box-sizing: border-box;
  width: 100%; height: max(76px, 5.4vw);
  margin-bottom: 1.3vw;
  padding: max(16px, 1.15vw) 24%;
  background: #fff;
  border-radius: max(12px, .8vw);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
}
.brands__logo img { display: block; width: 100%; height: 100%; object-fit: contain; }
/* Wordmarks, wider than tall: less padding at the sides so the lettering
   gets the width it needs. */
.brands__logo:has(img[src$="hikvision.svg"]),
.brands__logo:has(img[src$="panduit.svg"]) { padding-left: 11%; padding-right: 11%; }
.brands__logo:has(img[src$="dell.svg"]) { padding-top: max(10px, .7vw); padding-bottom: max(10px, .7vw); }
.brands__list span {
  display: block;
  margin-top: 1.2vw;
  font-size: max(13px, .82vw);
  line-height: 1.5;
  color: rgba(255, 255, 255, .78);
}

/* --- the handover records -----------------------------------------------
   Three sheets of paper on the navy, slightly off-square the way documents
   land on a desk. Real paper colour and real print sizes: these are meant to
   be read, not recognised. */
.handover { width: 72vw; margin: 14vh auto 0; }
.handover__tit {
  font-size: var(--p2);
  font-weight: 200;
  line-height: 1;
  letter-spacing: -.012em;
  text-transform: uppercase;
  text-align: center;
}
.handover__lead {
  margin: 1.4vh auto 0;
  max-width: 34em;
  font-size: var(--body);
  line-height: 1.5;
  text-align: center;
  color: rgba(255, 255, 255, .72);
}
.handover__docs {
  margin-top: 7vh;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2.4vw;
  align-items: start;
}
.doc {
  margin: 0;
  background: #fff;
  color: var(--ink);
  border-radius: .35vw;
  padding: max(16px, 1.3vw);
  box-shadow: 0 1.6vw 3.2vw rgba(0, 0, 0, .28);
}
.doc--1 { transform: rotate(-2deg); }
.doc--2 { transform: rotate(1.4deg) translateY(2.4vh); }
.doc--3 { transform: rotate(-.8deg); }
.doc figcaption {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1em;
  padding-bottom: max(10px, .7vw);
  margin-bottom: max(10px, .7vw);
  border-bottom: 2px solid var(--ink);
  font-size: max(15px, 1.05vw);
  font-weight: 500;
}
.doc figcaption em {
  font-style: normal;
  font-family: var(--f-mono);
  font-size: max(11px, .7vw);
  font-weight: 400;
  color: var(--ink);
  background: var(--scan);
  padding: 2px 8px;
  border-radius: 3px;
}
.doc table { width: 100%; border-collapse: collapse; font-family: var(--f-mono); font-size: max(11px, .76vw); }
.doc th { text-align: left; font-weight: 400; color: var(--mute); padding-bottom: 6px; }
.doc td { padding: 5px 0; border-top: 1px solid rgba(29, 45, 74, .12); }
.doc td.ok { color: #1F6B3A; }
.doc svg { display: block; width: 100%; height: auto; }
.doc p {
  margin-top: max(10px, .8vw);
  font-size: max(13px, .85vw);
  line-height: 1.4;
  color: var(--mute);
}

/* Waiting on photography — the markup is commented out in index.html until
   there are real job-site pictures to put here, because four empty outlined
   boxes on a black field read as a broken page rather than a reserved space.
   The rules stay so that uncommenting is the only step needed. */
.shots { position: relative; height: 46vh; margin-top: 6vh; }
.shot {
  position: absolute;
  border-radius: .45vw;
  overflow: hidden;
}
.shot img { width: 100%; height: 100%; object-fit: cover; }
.shot--1 { left: 11vw;  top:  2vh;  width: 12vw; height: 22vh; }
.shot--2 { left: 27vw;  top: 16vh;  width: 16vw; height: 30vh; }
.shot--3 { left: 48vw;  top:  0;    width: 16vw; height: 30vh; }
.shot--4 { left: 69vw;  top: 12vh;  width: 12vw; height: 22vh; }
.clients__tag {
  position: absolute; font-family: var(--f-mono); font-size: max(13px, .84vw);
  color: rgba(255, 255, 255, .7);
}
.clients__tag--a { left: 6vw;  top: 40vh; }
.clients__tag--b { right: 6vw; top: 8vh;  }

/* ===========================================================================
   CHECKOUT
   ======================================================================== */

/* One screen, like the reference: headline at the top, basket in the middle,
   the button across the bottom. It used to run 200vh, which put the headline
   and the button a viewport and a half apart and left the basket alone in the
   gap between them. */
/* A flex column, because this is the one screen where a fixed stack cannot
   work. Everything in it is sized in vw — the headline, the button, the
   links — and it has to fit inside a height measured in vh, so the leftover is
   different at every window shape. Authored as a fixed gap it was right at
   1440x900 and cropped the top of the headline at 1280x720.

   So the gap is the flexible one and everything else is `flex: none`. The
   section is then exactly one viewport at any size, the headline is never cut,
   and the basket — which measures this gap and fits itself to it, see
   `measureCart` in hero.js — gets whatever the type did not need. */
.checkout {
  /* border-box, or the padding is added on top of the 100vh and the section is
     its own padding taller than the screen — which is precisely the amount the
     headline was cut off by. */
  box-sizing: border-box;
  min-height: 100vh;
  padding: 1.5vh 0 0;
  text-align: center;
  display: flex;
  flex-direction: column;
}
.checkout > * { flex: none; }
.checkout .tit { width: 72vw; margin: 0 auto; }

/* The basket renders in here. It is 3D, so no height is reserved for it beyond
   the gap itself; the floor is for the short-viewport case, where the type
   alone fills the screen and the goods would otherwise be given nothing. */
.checkout__space { flex: 1 1 auto; min-height: 12vh; }

.talk {
  display: block;
  width: 56vw; margin: 0 auto;
  padding: .8vw 0 1.3vw;
  border: 1px solid currentColor;
  border-radius: 99px;
  font-size: var(--p1);
  font-weight: 200;
  line-height: 1;
  letter-spacing: -.025em;
  text-transform: uppercase;
  /* Barely there. At .35 with a blur it read as a white panel laid over the
     basket and washed out everything behind it; the pill wants to be an
     outline that happens to sit in front, not a lightbox. */
  background: rgba(255, 255, 255, .10);
  transition: background .4s, color .4s;
}
.talk:hover { background: var(--ink); color: #fff; }
.talk:focus-visible { outline: 3px solid var(--scan); outline-offset: 4px; }

/* Second door, deliberately quieter than the pill above it. */
.talk__alt {
  width: 56vw; margin: 1.4vh auto 0;
  font-size: max(14px, .95vw);
  text-align: center;
  color: var(--mute);
}
.talk__alt a { color: var(--ink); border-bottom: 1px solid currentColor; }

/* Below the fold on purpose. The checkout screen itself is the button; the
   links and the licence line are a footer you reach by scrolling past it. */
.checkout__links {
  width: 56vw; margin: 2vh auto 0;
  display: flex; justify-content: space-between;
  font-size: max(14px, .95vw);
}
.checkout__links a { border-bottom: 1px solid transparent; padding-bottom: .2vw; }
.checkout__links a:hover { border-bottom-color: currentColor; }

/* Every text link on this page, on any touch device and at any width. The type
   here is small by design — .84vw is a 12px body — which leaves a 17px-tall
   target where WCAG 2.2 asks for 24. Vertical padding on an inline box grows
   the hit area without growing the line box, so the composition is untouched
   and only the finger notices. The index gets its padding on top alone: the
   scan rule underneath it is positioned off the bottom edge, and padding there
   would push the rule away from the word it belongs to. */
@media (pointer: coarse) {
  .checkout__links a, .talk__alt a, .store__credit a { padding-block: 5px; }
  .index a { padding-top: 9px; }
}

.store__credit {
  width: 60vw; margin: 3vh auto 1.5vh;
  font-size: max(12px, .75vw);
  line-height: 1.5;
  text-align: center;
  color: var(--mute);
  font-style: normal;   /* <address> is italic by default in every browser */
}
.store__credit a { border-bottom: 1px solid currentColor; }

/* ===========================================================================
   ENTRANCES

   Headlines rise a letter at a time, body copy a word at a time. Both are split
   in store.js, because the counts depend on the copy and a fixed list of
   nth-child delays stops staggering the moment somebody edits a sentence.
   ======================================================================== */

.ltr, .w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* The clip has to sit below the baseline or it cuts the tail off every g, y
     and p. Padding opens the box, the negative margin gives the space back to
     the line, so leading is unchanged. */
  padding-bottom: .14em;
  margin-bottom: -.14em;
}

/* Letters are grouped back into words in store.js. Without this the line can
   break between any two glyphs, because each one is its own inline-block. */
.ltr-word { display: inline-block; white-space: nowrap; }
.ltr > span, .w > span {
  display: inline-block;
  transform: translateY(105%);
  transition: transform .9s cubic-bezier(.16, 1, .3, 1);
}
.is-shown .ltr > span,
.is-shown .w > span { transform: none; }

/* A space between two inline-block letters collapses to nothing, so word breaks
   have to be carried by the element itself. */
.ltr--sp { width: .28em; }

@media (prefers-reduced-motion: reduce) {
  .ltr > span, .w > span { transform: none; transition: none; }
  .receipt { transition: none; }
  .receipt.is-feeding, .receipt__item.is-rung .receipt__line, .receipt__item.is-rung .receipt__ref,
  .receipt__item.is-rung::before, .receipt__count.is-ticking, .receipt__send.is-glint::after,
  .wa-cta::before { animation: none; }
  .label__aside { transition: none; }
  .is-scanning .beam { animation: none; }
}

/* ===========================================================================
   NARROW SCREENS

   The vw ramp stops making sense well before phone width: 9.8vw is 38px on a
   390px screen, which is smaller than the body copy is meant to feel. Below
   860px the page switches to a straightforward single column, drops the
   ornaments that need margins to live in, and releases the pinned cards — a
   400vh spacer with a sticky card inside it is four blank screens on a phone.
   ======================================================================== */

@media (max-width: 860px) {
  :root {
    --p1: 15vw;
    --p2: 6.4vw;
    --p4: 12px;
    --body: 16px;
    --p5: 10px;
    --p6: 9px;
    --s1: 3vw;
  }

  .rail, .seal, .burst, .receipt { display: none; }
  /* The till roll has no margin to hang in on a phone, but a card's "Add to
     quote" still has to lead somewhere. Once something is on the list, the
     paper's WhatsApp button alone comes up as a bar along the bottom edge. */
  .receipt.has-items {
    display: block;
    left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom));
    width: auto; padding: 0;
    background: none; border: 0; box-shadow: none;
    -webkit-mask: none; mask: none;
    transform: none; animation: none;
  }
  .receipt.has-items > :not(.receipt__send) { display: none; }
  .receipt.has-items .receipt__send {
    margin: 0; min-height: 50px; gap: 9px;
    font-family: var(--f-store); font-size: 16px; font-weight: 600;
    box-shadow: 0 8px 24px rgba(29, 45, 74, .22);
  }
  .receipt.has-items .receipt__send img { width: 22px; }
  .wa-cta { height: 40px; padding: 0 14px 0 10px; gap: 7px; font-size: 15px; }
  .wa-cta img { width: 20px; }
  .menu-btn { height: 40px; }
  .label--4 .label__stick { flex-direction: column; }
  .label__aside { position: static; width: 86vw; margin: 4vh auto 0; transform: none; opacity: 1; }
  .label__aside.is-shown { transform: none; }
  .aside__ref { margin-bottom: 14px; }
  .aside__tit { margin-bottom: 14px; }
  .aside__list { margin: 20px 0; }
  .aside__list > div { grid-template-columns: 1fr; gap: 4px; padding: 12px 0; }
  .aside__wa { gap: 8px; margin-top: 14px; padding: 10px 18px 10px 12px; }

  .topbar { top: 10px; right: 10px; gap: 8px; }
  .corner { padding: 8px 14px; gap: 7px; min-height: 40px; box-sizing: border-box; }
  .menu { top: 58px; left: 10px; right: 10px; width: auto; }
  .menu__card { border-radius: 14px; }
  .corner img { width: 12px; }
  .corner img.corner__wa { width: 15px; margin-top: -1px; }

  .landing .tit, .landing .strap, .landing .lead,
  .manifesto__say, .clients .tit, .checkout .tit { width: 86vw; }
  /* 1.55vw is 6px on a phone. The company name is the one thing on this screen
     that must never shrink out of legibility. */
  .landing__mark { flex-direction: column; gap: 10px; font-size: 19px; top: 7vh; padding: 0 16px; text-align: center; }
  /* Same reason as the name beside it: 4.4vw is 17px on a phone. */
  .landing__logo { width: 54px; }
  .landing .lead { text-align: left; }
  /* Back to a plain line of text. As a flex row the strapline puts "for growing"
     and "businesses" in two items side by side, and at this width the first one
     wraps internally and runs into the second. */
  .landing .strap { display: block; }

  .index, .manifesto__cols, .clients__cols, .checkout__links,
  .talk__alt, .store__credit { width: 86vw; }
  .index { flex-wrap: wrap; gap: 10px 18px; justify-content: flex-start; }
  .manifesto__cols, .clients__cols { flex-direction: column; gap: 18px; }
  .manifesto__cols > div, .clients__cols > div { width: 100%; }

  .chapter { padding: 8vh 0; }
  .ch__badge { width: 74px; height: 74px; margin: 0 0 0 7vw !important; padding: 8px; }
  .ch__badge b { font-size: 26px; }
  .chapter--a, .chapter--c { text-align: left; }
  .ch__tit, .ch__strap, .ch__cols {
    width: 86vw !important;
    margin-left: 7vw !important;
    margin-right: 0 !important;
    text-align: left !important;
  }
  .chapter--c .ch__strap { justify-content: flex-start; }
  .ch__cols { flex-direction: column; gap: 16px !important; margin-top: 5vh !important; }
  .ch__cols > div { width: 100% !important; padding: 0 !important; }

  /* Released from the pin: the card becomes an ordinary block in the flow. */
  .label--1, .label--2, .label--3, .label--4 { height: auto; }
  .label__stick { position: static; height: auto; padding: 8vh 0; }
  /* The upper face of the card holds the product (PhoneShowcase in hero.js), so it
     needs the height for one: 62vh leaves it about a third of the screen.
     Without the script the face is left blank, the way a pack has one. */
  .label__card,
  .label--2 .label__card,
  .label--4 .label__card { width: 86vw; height: 62vh; margin: 0 auto; }
  .label__num { width: 30vw; height: 9vw; }
  .barcode, .spec { width: 40vw; }
  .label__ing { width: 38vw; }
  .label__scan, .label__added { bottom: 16vw; padding: 5px 12px; }
  .label__foot { left: 4vw; right: 4vw; bottom: 3vw; }
  .label__side, .label__edge { display: none; }

  .brands { width: 86vw; margin-top: 8vh; }
  .brands__list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 14px; margin-top: 4vh; }
  .brands__list li { padding: 16px 0 20px; }
  .brands__logo { height: 64px; margin-bottom: 12px; padding: 13px 20%; border-radius: 12px; }
  .brands__logo:has(img[src$="hikvision.svg"]),
  .brands__logo:has(img[src$="panduit.svg"]) { padding-left: 8%; padding-right: 8%; }
  .brands__logo:has(img[src$="dell.svg"]) { padding-top: 9px; padding-bottom: 9px; }
  .brands__list b { font-size: 21px; }
  .brands__list span { margin-top: 6px; font-size: 13px; }
  .handover { width: 86vw; margin-top: 8vh; }
  .handover__docs { grid-template-columns: 1fr; gap: 22px; }
  .doc--1, .doc--2, .doc--3 { transform: none; }
  .shots { height: 34vh; }
  .shot--1 { left: 7vw;  top: 0;    width: 38vw; height: 15vh; }
  .shot--2 { left: 52vw; top: 4vh;  width: 40vw; height: 18vh; }
  .shot--3 { left: 7vw;  top: 18vh; width: 40vw; height: 15vh; }
  .shot--4, .clients__tag { display: none; }

  /* A fixed gap rather than the flexed one. hero.js draws the delivery pallet
     in it, so this number is how big the closing delivery is on a phone. */
  .checkout { min-height: 0; display: block; }
  .checkout__space { flex: none; height: 32vh; }
  .talk { width: 86vw; font-size: 13vw; padding: 4vw 0 5vw; }
  .checkout__links { flex-direction: column; gap: 12px; }
}
