/* ============================================================================
   Quick Return - Homepage
   Loaded by index.html only. Shared components live in css/style.css; this
   file must never redefine anything from there, because style.css is loaded
   by ~40 pages.

   Direction: the hero performs the product's own core interaction. A phone
   drawn in CSS acts as a scanner viewfinder, the range runs through it, and
   the octagon motif (from the Dharma-Initiative easter egg in the brand's
   origin story) is used structurally rather than as a small badge.
   ========================================================================= */

/* --- Self-hosted display type ------------------------------------------- --
   Poppins is already the brand face in design/flyer.html and across the
   social posts; it had just never reached the website. Self-hosted rather
   than pulled from Google so there is no third-party request and no visitor
   IP handed to another party, which matters alongside the consent banner.
   Latin subset only. -------------------------------------------------------- */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/poppins-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/fonts/poppins-800-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Caveat carries the handwritten sticker-pill lifted from the social posts.
   It is decorative and below the fold-line of attention, so it is never
   preloaded: swap means it can arrive late without blocking anything. */
@font-face {
  font-family: 'Caveat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/caveat-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- Homepage-only tokens ----------------------------------------------- --
   Scoped to .home so nothing here can leak into another page even if this
   stylesheet is ever loaded somewhere else by accident. ---------------------- */
.home {
  --font-display: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-script: 'Caveat', 'Segoe Script', cursive;

  /* a real spacing scale, which the codebase has never had */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px; --sp-9: 96px;

  --paper: #f8f6f2;
  --ink-soft: #454545;
  --rule: #e4e0d9;

  background: var(--clr-surface);
}

/* Centred content rail */
.home-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 var(--sp-5);
}

/* Display type. Body copy deliberately stays on the system stack: it reads
   well, costs nothing, and keeps the personality in the headings. */
.home h1, .home h2, .home h3,
.home .tier-price, .home .btn {
  font-family: var(--font-display);
  letter-spacing: -0.025em;
}

.section-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--clr-primary);
  background: var(--clr-primary-light);
  padding: 6px 14px;
  border-radius: var(--radius-pill);
}

/* ==========================================================================
   Top bar
   ========================================================================== */
.home .top-bar { padding: 14px var(--sp-5); position: relative; }

.home .top-bar-inner {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.top-bar-links { display: flex; align-items: center; gap: var(--sp-5); }

.top-bar-link {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--clr-text-muted);
  text-decoration: none;
  display: none;
}
.top-bar-link:hover { color: var(--clr-text); }

.hamburger {
  display: flex; flex-direction: column; gap: 4px;
  background: none; border: none; cursor: pointer; padding: 6px;
}
.hamburger span {
  display: block; width: 22px; height: 2px;
  background: var(--clr-text); border-radius: 2px;
  transition: all 0.2s ease;
}
.hamburger.open span:nth-child(1) { transform: rotate(45deg) translate(4px, 4px); }
.hamburger.open span:nth-child(2) { opacity: 0; }
.hamburger.open span:nth-child(3) { transform: rotate(-45deg) translate(4px, -4px); }

@media (min-width: 768px) { .hamburger { display: none; } }
@media (max-width: 767px) { .top-bar-links { display: none; } }

.top-bar-links.mobile-open {
  display: flex; flex-direction: column;
  position: absolute; top: 100%; left: 0; right: 0;
  background: var(--clr-surface);
  border-bottom: 1px solid var(--clr-border);
  padding: var(--sp-4) var(--sp-5);
  gap: var(--sp-4);
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  z-index: 100;
}
.top-bar-links.mobile-open .top-bar-link,
.top-bar-links.mobile-open .top-bar-action { display: block; font-size: 1rem; }

/* ==========================================================================
   HERO
   Type hangs left and ragged, the phone sits right and tilted, on a warm
   paper ground. The doodles are the brand's own social-post language.
   ========================================================================== */
.hero {
  position: relative;
  overflow: hidden;
  padding: var(--sp-7) 0 var(--sp-8);
  background:
    radial-gradient(90% 60% at 88% 8%, #fdf3ec 0%, rgba(253,243,236,0) 60%),
    repeating-linear-gradient(45deg, #f8f6f2 0 14px, #f4f1ec 14px 28px);
  border-bottom: 1px solid var(--rule);
}

.hero-grid {
  display: grid;
  gap: var(--sp-6);
  align-items: center;
  position: relative;
  z-index: 1;
}

.hero-copy { position: relative; }

.hero h1 {
  font-weight: 800;
  font-size: clamp(2.4rem, 7vw, 4.1rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  margin-top: var(--sp-4);
}
.hero h1 em { font-style: normal; color: var(--clr-primary); }

.hero-sub {
  font-size: clamp(1rem, 2vw, 1.1rem);
  line-height: 1.6;
  color: var(--ink-soft);
  margin-top: var(--sp-4);
  max-width: 36ch;
}

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
}
.hero-ctas .btn { padding: 14px 26px; font-size: 0.95rem; }

.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 6px var(--sp-5);
  margin-top: var(--sp-6);
  font-size: 0.82rem;
  color: var(--clr-text-muted);
}
.hero-trust b { color: var(--clr-primary); font-weight: 700; }

/* --- hand-drawn marks, used once and only here --- */
/* --w lives here, not on .scanner, so the doodles can position themselves
   against the phone's actual size. Anchoring them to a percentage of this
   column was the bug: the column also contains the swatches, the arrows and
   the dots, so "bottom: 8%" landed the pill on top of the left arrow. */
.hero-art {
  --w: 268px;
  position: relative;
  display: flex;
  justify-content: center;
}

.hero-doodle-sun {
  position: absolute;
  color: var(--clr-accent);
  opacity: 0.85;
  pointer-events: none;
}
.hero-pill {
  position: absolute;
  z-index: 4;
  font-family: var(--font-script);
  font-size: 1.55rem;
  line-height: 1;
  color: var(--clr-primary);
  background: var(--paper);
  padding: 10px 20px 12px;
  border-radius: var(--radius-pill);
  box-shadow: 0 4px 14px rgba(0,0,0,0.1);
  transform: rotate(-7deg);
  white-space: nowrap;
}

/* ==========================================================================
   THE SCANNER
   ========================================================================== */
/* --w is inherited from .hero-art. Do not redeclare it here: that would
   shadow the value the doodles measure themselves against. */
.scanner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-5);
}
.scanner:focus-visible { outline: 3px solid var(--clr-accent); outline-offset: 12px; border-radius: 8px; }

/* --- the phone, drawn entirely in CSS --- */
.phone {
  position: relative;
  width: var(--w);
  /* Not a true 9/19.5 handset. The photography is square, and a full-bleed
     viewfinder crops a square hard into a very tall frame, so the screen is
     shortened until the products still read. It is unmistakably a phone
     either way. */
  aspect-ratio: 9 / 17.5;
  padding: calc(var(--w) * 0.033);
  border-radius: calc(var(--w) * 0.15);
  transform: rotate(-5deg);
  background: linear-gradient(150deg, #43434a 0%, #17171a 26%, #0e0e10 55%, #2b2b31 82%, #101013 100%);
  box-shadow:
    0 1px 1px rgba(255,255,255,0.16) inset,
    0 34px 46px -22px rgba(45,106,90,0.45),
    0 16px 28px -14px rgba(0,0,0,0.34),
    0 3px 8px rgba(0,0,0,0.16);
}
/* thin bright rim along the top-left edge: the detail that stops it reading
   as a plain rounded rectangle */
.phone::before {
  content: '';
  position: absolute;
  inset: 1px;
  border-radius: inherit;
  pointer-events: none;
  padding: 1px;
  background: linear-gradient(150deg, rgba(255,255,255,0.5), rgba(255,255,255,0) 30%,
    rgba(255,255,255,0) 72%, rgba(255,255,255,0.22));
  -webkit-mask: linear-gradient(#000,#000) content-box, linear-gradient(#000,#000);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  /* Own stacking context, so the HUD, reticle, beam and reveal card stack
     against each other and never against anything outside the phone. */
  isolation: isolate;
  border-radius: calc(var(--w) * 0.118);
  /* Only ever seen in the moment before the photograph paints, and behind the
     rounded screen corners. Warm rather than white so that moment does not
     flash a bright rectangle. */
  background: radial-gradient(120% 80% at 50% 0%, #ffffff 0%, var(--clr-bg) 58%, #f1eee9 100%);
}
/* soft diagonal glare across the glass */
.phone-screen::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: linear-gradient(122deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0.07) 26%,
    rgba(255,255,255,0) 42%, rgba(255,255,255,0) 100%);
}

/* --- camera HUD --- */
/* A status chip, not bare text. The photograph now fills the screen, so the
   label needs its own ground to stay legible whatever is behind it, and a
   floating pill is what a real camera app does anyway. */
.scan-hud {
  position: absolute;
  z-index: 4;
  top: calc(var(--w) * 0.045);
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.84);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: 0 1px 4px rgba(0,0,0,0.09);
  font-size: calc(var(--w) * 0.038);
  font-weight: 600;
  color: #5a5a5a;
  white-space: nowrap;
}
.scan-hud-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--clr-accent);
  animation: scanPip 1.9s ease-out infinite;
}
@keyframes scanPip {
  0%   { box-shadow: 0 0 0 0 rgba(232,115,74,0.55); }
  70%  { box-shadow: 0 0 0 7px rgba(232,115,74,0); }
  100% { box-shadow: 0 0 0 0 rgba(232,115,74,0); }
}
.scanner.is-locked .scan-hud { color: var(--clr-primary); }
.scanner.is-locked .scan-hud-dot { background: var(--clr-primary); animation: none; }

/* --- product stage --- --
   A camera shows a full frame, not a floating cutout, so the photograph fills
   the screen edge to edge. This replaced an earlier mix-blend-mode knockout,
   which could only ever erase pure white and left a grey rectangle around
   products shot on light grey seamless. Nothing blends now, so there is also
   nothing to break if a stacking context appears above this. */
.scan-stage { position: absolute; inset: 0; overflow: hidden; }

.scan-product {
  display: block;
  width: 100%;
  /* height must be set here. The markup carries width/height attributes for
     LCP, and those act as presentational hints: with no author height the box
     would take the attribute's value literally and the product would float in
     an oversized box that the reticle could never frame. */
  height: 100%;
  object-fit: cover;
  object-position: var(--focus, 50% 50%);
  transform: scale(var(--zoom, 1));
  /* The opacity figure is load-bearing: js/home.js waits 165ms before swapping
     the photograph, so the fade must be finished by then. A longer fade meant
     the incoming colour appeared at part opacity and cross-faded with the
     outgoing one, which read as a flicker. */
  transition: opacity 0.15s ease, transform 0.45s cubic-bezier(0.3,0.8,0.3,1);
}
.scanner.is-turning .scan-product,
.scanner.is-swatching .scan-product {
  opacity: 0;
  transform: scale(calc(var(--zoom, 1) * 1.04));
}
/* A broken image URL would otherwise paint its alt text across the phone.
   Hide it and leave the warm screen and the brackets, which reads as a camera
   that has not found anything yet. Set by js/home.js on the image's error. */
.scanner.is-noimg .scan-product { visibility: hidden; }

/* --- the reticle: four corner brackets --- --
   Position and size come from the slide, because the code sits in a different
   place in every product photograph. See SLIDE_VIEW in js/carouselMath.js.
   The brackets animate between products, so changing slide reads as the
   camera re-framing rather than as a jump.

   Positioning uses the independent `translate` and `scale` properties rather
   than `transform`. The breathe and seek animations need to scale the
   brackets, and a `transform: scale()` keyframe would wipe out the centring
   translate; keeping them on separate properties lets both apply at once. */
.scan-reticle {
  position: absolute;
  z-index: 3;
  /* --ret-x defaults to 50% so the brackets stay centred on any product that
     has never had them placed by hand. */
  left: var(--ret-x, 50%);
  top: var(--ret-y, 54%);
  width: var(--ret-w, 76%);
  height: var(--ret-h, 46%);
  translate: -50% -50%;
  animation: scanBreathe 3.4s ease-in-out infinite;
  transition: left 0.4s cubic-bezier(0.3,1,0.4,1),
              top 0.4s cubic-bezier(0.3,1,0.4,1),
              width 0.4s cubic-bezier(0.3,1,0.4,1),
              height 0.4s cubic-bezier(0.3,1,0.4,1);
}
.scan-reticle i {
  position: absolute;
  width: calc(var(--w) * 0.095);
  aspect-ratio: 1;
  border: 0 solid var(--clr-accent);
  transition: border-color 0.2s ease;
}
.scan-reticle i:nth-child(1) { top: 0; left: 0;  border-width: 3px 0 0 3px; border-radius: 9px 0 0 0; }
.scan-reticle i:nth-child(2) { top: 0; right: 0; border-width: 3px 3px 0 0; border-radius: 0 9px 0 0; }
.scan-reticle i:nth-child(3) { bottom: 0; right: 0; border-width: 0 3px 3px 0; border-radius: 0 0 9px 0; }
.scan-reticle i:nth-child(4) { bottom: 0; left: 0;  border-width: 0 0 3px 3px; border-radius: 0 0 0 9px; }

@keyframes scanBreathe { 0%,100% { scale: 1; } 50% { scale: 1.022; } }
.scanner.is-turning .scan-reticle { animation: scanSeek 0.34s cubic-bezier(0.3,1.4,0.4,1); }
@keyframes scanSeek { 0% { scale: 1; } 40% { scale: 1.13; } 100% { scale: 1; } }
.scanner.is-locked .scan-reticle { animation: none; scale: 0.965; }
.scanner.is-locked .scan-reticle i { border-color: var(--clr-primary); }

/* --- sweep beam --- */
.scan-beam {
  position: absolute;
  z-index: 3;
  left: 8%; right: 8%;
  height: 2px;
  /* A fixed sweep wide enough to cover every product's bracket band (the
     widest runs 23% to 86%). Deliberately not driven by the --ret-* vars:
     animating a calc() built from custom properties is inconsistent across
     browsers, and a beam that overshoots the brackets slightly looks right
     anyway. */
  top: 22%;
  opacity: 0;
  background: linear-gradient(90deg, transparent, var(--clr-accent) 22%, #fff 50%, var(--clr-accent) 78%, transparent);
  box-shadow: 0 0 14px 3px rgba(232,115,74,0.5);
}
.scanner.is-scanning .scan-beam { animation: scanSweep 0.62s cubic-bezier(0.45,0,0.4,1); }
@keyframes scanSweep {
  0%   { top: 22%; opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { top: 86%; opacity: 0; }
}

/* --- the whole screen as a tap target --- --
   The scan button alone was a 66px circle sitting in a row with two arrows,
   so it read as carousel chrome and got missed. This makes the target the
   entire screen instead, which is what people reach for anyway.
   z-index sits above the reticle but below the reveal card, so the card's
   "View in shop" link stays clickable. ---------------------------------------- */
.scan-tap {
  position: absolute;
  inset: 0;
  z-index: 4;
  border: 0;
  padding: 0 0 calc(var(--w) * 0.085);
  background: transparent;
  cursor: pointer;
  display: grid;
  align-content: end;
  justify-items: center;
}
.scan-tap:focus-visible {
  outline: 3px solid var(--clr-accent);
  outline-offset: -5px;
  border-radius: calc(var(--w) * 0.1);
}

.scan-tap-hint {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: calc(var(--w) * 0.044);
  color: #fff;
  background: rgba(26, 26, 26, 0.74);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  padding: 7px 15px;
  border-radius: var(--radius-pill);
  box-shadow: 0 2px 12px rgba(0,0,0,0.22);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
/* Retires itself the moment someone scans, and stays retired on later visits.
   An invitation that keeps nagging after it has been accepted is just noise. */
.scanner.is-used .scan-tap-hint,
.scanner.is-found .scan-tap-hint,
.scanner.is-scanning .scan-tap-hint {
  opacity: 0;
  transform: translateY(6px);
}

/* --- the reveal: the finder's screen --- --
   This used to be a product card sitting over the bottom third of the photo.
   It is now the whole viewfinder, because the interesting claim the homepage
   makes is "a stranger who finds your things can reach you in one tap", and a
   price tag does not demonstrate that. Structure deliberately mirrors
   item.html (banner, avatar, you-found, contacts) without being a fourth copy
   of its stylesheet: only what reads at 268px is reproduced.

   Everything is sized from --w so the whole screen scales with the phone. */
.scan-found {
  position: absolute;
  z-index: 6;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--clr-surface);
  /* 100%, not the old 130%: a full-height panel only has to clear its own
     height, and the extra 30% just made the slide start late. */
  transform: translateY(100%);
  transition: transform 0.36s cubic-bezier(0.2,0.85,0.25,1);
  /* Belt and braces. The screen clips this while it is parked off the bottom,
     but an off-screen full-height panel sitting over the tap target is not
     something to leave to overflow behaviour. */
  pointer-events: none;
}
.scanner.is-found .scan-found { transform: translateY(0); pointer-events: auto; }

/* --- banner --- */
.scan-found-head {
  position: relative;
  flex: 0 0 auto;
  height: calc(var(--w) * 0.30);
  padding: calc(var(--w) * 0.038);
  background: linear-gradient(135deg, #2d6a5a 0%, #1a4a3e 60%, #0f3029 100%);
}
.scan-found-brand {
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: calc(var(--w) * 0.045);
}
.scan-found-avatar {
  position: absolute;
  left: 50%;
  bottom: calc(var(--w) * -0.075);
  translate: -50% 0;
  display: grid;
  place-items: center;
  width: calc(var(--w) * 0.15);
  height: calc(var(--w) * 0.15);
  border-radius: 50%;
  background: #d8d5d0;
  color: #8e8a84;
  border: calc(var(--w) * 0.011) solid var(--clr-surface);
  overflow: hidden;
}
.scan-found-avatar svg { width: 68%; height: 68%; translate: 0 6%; }

/* --- who you found --- */
.scan-found-body {
  flex: 1 1 auto;
  /* Top padding clears the avatar, which hangs below the banner. */
  padding: calc(var(--w) * 0.098) calc(var(--w) * 0.055) calc(var(--w) * 0.03);
  overflow: hidden;
}
.scan-found-eyebrow {
  display: block;
  text-align: center;
  font-size: calc(var(--w) * 0.036);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--clr-accent);
}
.scan-found-name {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-size: calc(var(--w) * 0.066);
  line-height: 1.15;
  text-align: center;
  margin-top: 3px;
}
.scan-found-desc {
  font-size: calc(var(--w) * 0.038);
  font-style: italic;
  color: var(--clr-text-muted);
  line-height: 1.35;
  text-align: center;
  margin-top: calc(var(--w) * 0.022);
}

/* --- contacts --- --
   Spans, not buttons or tel: links. They dial nobody, so there is nothing to
   focus and nothing to press; the container is aria-hidden in the markup. */
.scan-found-contacts { margin-top: calc(var(--w) * 0.05); }
.scan-found-contact + .scan-found-contact { margin-top: calc(var(--w) * 0.042); }
.scan-found-who {
  display: block;
  font-weight: 700;
  font-size: calc(var(--w) * 0.04);
  margin-bottom: calc(var(--w) * 0.018);
}
.scan-found-actions { display: flex; gap: calc(var(--w) * 0.028); }
.scan-found-action {
  flex: 1;
  text-align: center;
  color: #fff;
  font-weight: 600;
  font-size: calc(var(--w) * 0.038);
  padding: calc(var(--w) * 0.028) 0;
  border-radius: var(--radius-pill);
}
.scan-found-action.is-call { background: var(--clr-accent); }
.scan-found-action.is-text { background: var(--clr-primary); }

/* --- the shop row, pinned to the bottom --- */
.scan-found-row {
  flex: 0 0 auto;
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: calc(var(--w) * 0.042) calc(var(--w) * 0.055);
  border-top: 1px solid var(--clr-border, rgba(0,0,0,0.08));
  background: var(--clr-surface);
}
.scan-found-buy { display: flex; flex-direction: column; line-height: 1.2; }
.scan-found-buy b {
  font-size: calc(var(--w) * 0.036);
  font-weight: 600;
  color: var(--clr-text-muted);
}
.scan-found-price {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(var(--w) * 0.05);
}
.scan-found-btn {
  background: var(--clr-primary);
  color: #fff;
  text-decoration: none;
  font-weight: 600;
  font-size: calc(var(--w) * 0.04);
  padding: calc(var(--w) * 0.032) calc(var(--w) * 0.055);
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.scan-found-btn:hover { background: var(--clr-primary-hover); }

/* --- colour options --- --
   Octagons rather than circles so the swatches carry the brand's own motif
   instead of reading as a generic ecommerce control. -------------------------- */
.scan-swatches {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
}
.scan-swatches[hidden] { display: none; }
.scan-swatch-label { font-size: 0.76rem; color: var(--clr-text-muted); }
.scan-swatch-label b { color: var(--clr-text); font-weight: 600; }
.scan-swatch-row { display: flex; gap: 9px; }

.scan-swatch {
  width: 28px; height: 28px;
  padding: 0; border: 0;
  cursor: pointer;
  position: relative;
  background: transparent;
  transition: transform 0.16s ease;
}
.scan-swatch::before {
  content: '';
  position: absolute;
  inset: 0;
  --oct: 7px;
  background: var(--swatch, #8d8d8d);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.16);
  clip-path: polygon(var(--oct) 0, calc(100% - var(--oct)) 0, 100% var(--oct),
    100% calc(100% - var(--oct)), calc(100% - var(--oct)) 100%, var(--oct) 100%,
    0 calc(100% - var(--oct)), 0 var(--oct));
}
/* selected ring: an octagon a size up, so the motif repeats rather than
   falling back to a generic circle outline */
.scan-swatch::after {
  content: '';
  position: absolute;
  inset: -5px;
  --oct: 9px;
  opacity: 0;
  background: var(--clr-primary);
  z-index: -1;
  transition: opacity 0.16s ease;
  clip-path: polygon(var(--oct) 0, calc(100% - var(--oct)) 0, 100% var(--oct),
    100% calc(100% - var(--oct)), calc(100% - var(--oct)) 100%, var(--oct) 100%,
    0 calc(100% - var(--oct)), 0 var(--oct));
}
.scan-swatch:hover { transform: translateY(-2px); }
.scan-swatch[aria-pressed="true"]::after { opacity: 1; }
.scan-swatch:focus-visible { outline: 3px solid var(--clr-accent); outline-offset: 4px; }

/* --- controls --- */
.scan-ctl { display: flex; align-items: center; gap: clamp(16px, 4vw, 26px); }

.scan-nav {
  width: 48px; height: 48px;
  display: grid; place-items: center;
  cursor: pointer;
  background: var(--clr-surface);
  border: 1px solid var(--clr-border);
  color: var(--clr-primary);
  box-shadow: var(--shadow);
  transition: var(--transition);
  clip-path: polygon(var(--oct) 0, calc(100% - var(--oct)) 0, 100% var(--oct),
    100% calc(100% - var(--oct)), calc(100% - var(--oct)) 100%, var(--oct) 100%,
    0 calc(100% - var(--oct)), 0 var(--oct));
}
.scan-nav:hover { background: var(--clr-primary); color: #fff; transform: translateY(-2px); }
.scan-nav:active { transform: scale(0.95); }
.scan-nav:focus-visible { outline: 3px solid var(--clr-accent); outline-offset: 3px; }

.scan-btn-wrap { display: flex; flex-direction: column; align-items: center; gap: 7px; }
.scan-btn {
  position: relative;
  width: 66px; height: 66px;
  border-radius: 50%;
  border: 0;
  cursor: pointer;
  display: grid; place-items: center;
  color: #fff;
  background: var(--clr-accent);
  box-shadow: 0 6px 18px rgba(232,115,74,0.42), 0 2px 4px rgba(0,0,0,0.12);
  transition: var(--transition);
}
/* A slow ring outwards, always running. Same idiom as the HUD dot, so it
   reads as part of the camera rather than as a badge stuck on. The "Tap to
   scan" hint inside the screen still retires after first use; this does not,
   because it is the only standing cue that the control is live. */
.scan-btn::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  animation: scanAttract 2.6s ease-out infinite;
}
@keyframes scanAttract {
  0%   { box-shadow: 0 0 0 0 rgba(232,115,74,0.55); }
  70%  { box-shadow: 0 0 0 18px rgba(232,115,74,0); }
  100% { box-shadow: 0 0 0 0 rgba(232,115,74,0); }
}
.scan-btn:hover {
  background: var(--clr-accent-hover);
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(232,115,74,0.5);
}
.scan-btn:active { transform: scale(0.93); }
.scan-btn:focus-visible { outline: 3px solid var(--clr-primary); outline-offset: 3px; }
.scanner.is-scanning .scan-btn { transform: scale(0.93); }

/* 0.78rem bold on the paper ground: --clr-primary, not --clr-accent, because
   #e8734a on white is only ~3.0:1 and fails AA for text at this size. */
.scan-btn-label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.78rem;
  color: var(--clr-primary);
}

.scan-dots { display: flex; gap: 7px; }
.scan-dots button {
  width: 8px; height: 8px;
  padding: 0; border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  background: #cfcac3;
  transition: 0.24s ease;
}
.scan-dots button[aria-current="true"] { width: 24px; background: var(--clr-accent); }
.scan-dots button:focus-visible { outline: 2px solid var(--clr-primary); outline-offset: 3px; }

/* ==========================================================================
   Emotion bar
   ========================================================================== */
.emotion-bar {
  background: var(--clr-primary-light);
  padding: var(--sp-6) var(--sp-5);
  text-align: center;
}
.emotion-bar p {
  max-width: 62ch;
  margin: 0 auto;
  font-size: 1.02rem;
  line-height: 1.6;
  color: var(--clr-primary);
  font-style: italic;
}

/* ==========================================================================
   How it works
   ========================================================================== */
.how-section { padding: var(--sp-8) 0; text-align: center; }
.how-section h2 { font-size: clamp(1.5rem, 3.4vw, 2.1rem); font-weight: 800; margin-top: var(--sp-3); }
.how-section > .home-inner > p { color: var(--clr-text-muted); margin-top: var(--sp-2); }

.steps-container {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-top: var(--sp-6);
  text-align: left;
  counter-reset: step;
}
.step {
  display: flex;
  gap: var(--sp-4);
  align-items: flex-start;
  background: var(--clr-surface);
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  padding: var(--sp-4);
}
/* The numbering is real: these are sequential steps, so the order carries
   information the reader needs. */
.step-num {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.1rem;
  color: #fff;
  background: var(--clr-primary);
  clip-path: polygon(20% 0%, 80% 0%, 100% 20%, 100% 80%, 80% 100%, 20% 100%, 0% 80%, 0% 20%);
}
/* The last step is what happens to someone else, not to you, so it is the
   one that changes colour. */
.step:last-child .step-num { background: var(--clr-accent); }
.step-text h3 { font-family: var(--font-display); font-size: 1rem; font-weight: 700; }
.step-text p { font-size: 0.87rem; color: var(--clr-text-muted); margin-top: var(--sp-1); line-height: 1.55; }

/* ==========================================================================
   Reviews
   Real named customers, quoted. Deliberately not the Trustpilot widget: that
   needs a third-party script through the consent banner, so it would be
   invisible to anyone who declines. This is visible to everyone.
   ========================================================================== */
.reviews-section { padding: var(--sp-8) 0; }
.reviews-head { text-align: center; margin-bottom: var(--sp-6); }
.reviews-head h2 {
  font-size: clamp(1.5rem, 3.4vw, 2.1rem);
  font-weight: 800;
  margin-top: var(--sp-3);
}
.reviews-head > p {
  color: var(--ink-soft);
  margin: var(--sp-3) auto 0;
  max-width: 52ch;
  line-height: 1.6;
}

.reviews-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-3);
}

.review {
  margin: 0;
  display: flex;
  flex-direction: column;
  background: var(--clr-surface);
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  padding: var(--sp-4);
}

.review-stars { display: flex; gap: 2px; margin-bottom: var(--sp-3); }
.review-stars svg {
  width: 15px;
  height: 15px;
  /* Our own green, not Trustpilot's. Their marks carry brand rules, and the
     eyebrow and the link do the attribution honestly enough without borrowing
     their colour. */
  fill: var(--clr-primary);
}

.review-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
}
.review-body {
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-soft);
  margin-top: var(--sp-2);
  /* Grows so every caption in a row sits on the same line, whatever the
     quote length. */
  flex: 1 1 auto;
}
.review-who {
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--clr-border);
  font-size: 0.82rem;
  font-weight: 600;
}
.review-who span {
  display: block;
  font-weight: 400;
  color: var(--clr-text-muted);
  margin-top: 1px;
}

.reviews-link { text-align: center; margin-top: var(--sp-5); font-size: 0.98rem; }
.reviews-link a { color: var(--clr-primary); font-weight: 600; }

/* ==========================================================================
   FAQ
   Native <details>/<summary>: keyboard support, screen-reader semantics and
   browser find-in-page all come for free, and there is no JS to go wrong.
   ========================================================================== */
.faq-section { padding: var(--sp-8) 0; background: var(--paper); border-top: 1px solid var(--rule); }
.faq-section .home-inner { max-width: 780px; }
.faq-head { text-align: center; margin-bottom: var(--sp-6); }
.faq-head h2 { font-size: clamp(1.5rem, 3.4vw, 2.1rem); font-weight: 800; margin-top: var(--sp-3); }
.faq-head p { color: var(--clr-text-muted); margin-top: var(--sp-2); }

.faq-item {
  background: var(--clr-surface);
  border: 1px solid var(--clr-border);
  border-radius: var(--radius-sm);
  margin-bottom: var(--sp-2);
  overflow: hidden;
}
.faq-item[open] { border-color: var(--clr-primary); box-shadow: var(--shadow); }

.faq-item summary {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  list-style: none;
  transition: var(--transition);
}
/* Safari still paints its own disclosure triangle without this */
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { background: var(--clr-primary-light); }
.faq-item summary:focus-visible { outline: 3px solid var(--clr-accent); outline-offset: -3px; }

.faq-mark {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  position: relative;
  background: var(--clr-primary-light);
  color: var(--clr-primary);
  --oct: 6px;
  clip-path: polygon(var(--oct) 0, calc(100% - var(--oct)) 0, 100% var(--oct),
    100% calc(100% - var(--oct)), calc(100% - var(--oct)) 100%, var(--oct) 100%,
    0 calc(100% - var(--oct)), 0 var(--oct));
  transition: var(--transition);
}
.faq-mark::before,
.faq-mark::after {
  content: '';
  position: absolute;
  background: currentColor;
  border-radius: 1px;
  transition: transform 0.22s ease, opacity 0.22s ease;
}
.faq-mark::before { width: 11px; height: 2px; }
.faq-mark::after  { width: 2px; height: 11px; }
.faq-item[open] .faq-mark { background: var(--clr-primary); color: #fff; }
.faq-item[open] .faq-mark::after { transform: rotate(90deg); opacity: 0; }

.faq-body { padding: 0 var(--sp-4) var(--sp-4) calc(var(--sp-4) + 26px + var(--sp-3)); }
.faq-body p { font-size: 0.88rem; line-height: 1.6; color: var(--clr-text-muted); }
.faq-body p + p { margin-top: var(--sp-3); }
.faq-body a { color: var(--clr-primary); }

/* Both this and .final-cta-note are the same kind of line: the way out for
   someone the page has not answered. They were 0.85rem and 0.8rem in muted
   grey, which is small enough to skip. Sized to match each other, and set in
   --ink-soft rather than --clr-text-muted, because grey compounds the problem
   at small sizes. */
.faq-more {
  text-align: center;
  margin-top: var(--sp-5);
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--ink-soft);
}
.faq-more a { color: var(--clr-primary); font-weight: 600; }

/* ==========================================================================
   Plans
   ========================================================================== */
.tiers-section { padding: var(--sp-8) 0; text-align: center; }
.tiers-section h2 { font-size: clamp(1.5rem, 3.4vw, 2.1rem); font-weight: 800; margin-top: var(--sp-3); }
.tiers-section > .home-inner > p {
  color: var(--clr-text-muted);
  margin: var(--sp-2) auto 0;
  max-width: 56ch;
}

.tiers-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-top: var(--sp-6);
  text-align: left;
}
.tier-card {
  background: var(--clr-surface);
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  padding: var(--sp-5);
  display: flex;
  flex-direction: column;
}
.tier-card-premium { border-color: var(--clr-primary); box-shadow: var(--shadow-md); }
.tier-header { margin-bottom: var(--sp-4); }
.tier-name { font-family: var(--font-display); font-size: 1.15rem; font-weight: 800; }
.tier-price { font-size: 0.9rem; color: var(--clr-text-muted); margin-top: 2px; }

.tier-offer {
  background: var(--clr-accent);
  color: #fff;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  font-weight: 700;
  margin-bottom: var(--sp-4);
  text-align: center;
}

.tier-features { list-style: none; display: flex; flex-direction: column; gap: var(--sp-2); }
.tier-features li {
  position: relative;
  padding-left: 24px;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--clr-text-muted);
}
.tier-features li::before {
  content: '';
  position: absolute;
  left: 0; top: 7px;
  width: 12px; height: 7px;
  border-left: 2px solid var(--clr-primary);
  border-bottom: 2px solid var(--clr-primary);
  transform: rotate(-45deg);
}
.tier-card .btn { width: 100%; margin-top: auto; }
.tier-cta-space { margin-top: var(--sp-4); }

/* ==========================================================================
   Promise
   ========================================================================== */
.promise-section { padding: var(--sp-8) 0; background: var(--paper); border-top: 1px solid var(--rule); text-align: center; }
.promise-section h2 { font-size: clamp(1.5rem, 3.4vw, 2.1rem); font-weight: 800; margin-top: var(--sp-3); }
.promise-section > .home-inner > p { color: var(--clr-text-muted); margin: var(--sp-2) auto 0; max-width: 56ch; }

.promise-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
  text-align: left;
}
.promise-item {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  font-size: 0.87rem;
  line-height: 1.5;
  color: var(--clr-text);
}
.promise-check {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  background: var(--clr-primary-light);
  color: var(--clr-primary);
  border-radius: 50%;
  font-size: 0.75rem;
}
.promise-item a { color: var(--clr-primary); }

/* ==========================================================================
   Final CTA
   The class matters: js/home.js relabels .final-cta .btn-ghost for signed-in
   visitors. The old markup was an unclassed div, so that relabel never ran.
   ========================================================================== */
.final-cta {
  background: var(--clr-surface);
  border-top: 1px solid var(--clr-border);
  padding: var(--sp-7) var(--sp-5) var(--sp-6);
  text-align: center;
}
.final-cta .home-inner { max-width: 520px; }
.final-cta h2 { font-size: 1.5rem; font-weight: 800; }
.final-cta > .home-inner > p { font-size: 0.9rem; color: var(--clr-text-muted); margin-top: var(--sp-2); }
.final-cta-btns {
  display: flex;
  gap: var(--sp-3);
  justify-content: center;
  flex-wrap: wrap;
  margin: var(--sp-5) 0 var(--sp-6);
}
.final-cta-btns .btn { padding: 14px 32px; font-size: 0.95rem; }
/* Matches .faq-more, see the note there. */
.final-cta-note { font-size: 0.98rem; line-height: 1.6; color: var(--ink-soft); }
.final-cta-note a { color: var(--clr-primary); font-weight: 600; }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (min-width: 600px) {
  .promise-list { grid-template-columns: repeat(2, 1fr); }
  .reviews-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 768px) {
  .top-bar-link { display: block; }
  .steps-container { grid-template-columns: repeat(2, 1fr); }
  .tiers-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 900px) {
  .hero { padding: var(--sp-8) 0 var(--sp-9); }
  .hero-grid { grid-template-columns: 1.02fr 0.98fr; gap: var(--sp-5); }
  .hero-art { --w: 288px; }
  .hero-doodle-sun { top: -4px; right: 6%; width: 66px; }
  /* Measured down the phone rather than up from the column, so it sits beside
     the handset and can never reach the controls underneath. The phone is
     --w * 17.5/9 tall, so this lands a little past halfway. */
  .hero-pill { left: 0; top: calc(var(--w) * 1.05); }
}

@media (min-width: 1024px) {
  .steps-container { grid-template-columns: repeat(4, 1fr); }
  .step { flex-direction: column; }
  .reviews-grid { grid-template-columns: repeat(4, 1fr); gap: var(--sp-4); }
}

/* Below 900px the hero centres, so the doodles have to be repositioned or
   they collide with the copy. The arrow is dropped entirely: it points from
   the text to the scan button, and that relationship does not survive the
   stack. */
@media (max-width: 899px) {
  .hero { text-align: center; }
  .hero-sub { max-width: 100%; margin-left: auto; margin-right: auto; }
  .hero-ctas, .hero-trust { justify-content: center; }
  .hero-doodle-sun { top: -16px; right: 3%; width: 48px; }
  /* The pill comes off below 900px. The column is barely wider than the phone
     here, so there is nowhere for it to sit that does not cover either the
     product or a control. The sun stays as the one accent. */
  .hero-pill { display: none; }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .scan-reticle,
  .scan-hud-dot { animation: none; }
  /* The attention ring becomes a static halo rather than a pulse. It still
     says "this is pressable", which is its whole job, without animating at
     someone who has asked the system not to. Needs !important because the
     global block in css/style.css kills all animation with !important. */
  .scan-btn::after {
    animation: none !important;
    box-shadow: 0 0 0 6px rgba(232,115,74,0.22);
  }
  .scanner.is-turning .scan-reticle,
  .scanner.is-scanning .scan-beam { animation: none; }
  /* The brackets still move between products, they just arrive instantly. */
  .scan-reticle,
  .scan-product,
  .scan-found,
  .faq-mark::before,
  .faq-mark::after { transition: none; }
}
