/* Milk Bikis retailer microsite.

   Every colour is lifted from the artwork itself -- the cyan band, the
   Britannia red, the cream of the biscuit -- rather than invented. Baloo 2
   echoes the rounded Milk Bikis wordmark; Figtree keeps the fields plain and
   legible on a cheap Android screen in daylight.

   Built mobile-first. Retailers open this on a phone, standing in their shop.
*/

:root {
  --cyan:       #00b4cf;
  --cyan-deep:  #00768f;
  --cyan-ink:   #024e60;
  --red:        #d0202f;
  --red-deep:   #a5141f;
  --cream:      #fdf6e9;
  --cream-2:    #f6ebd6;
  --biscuit:    #d9a441;
  --ink:        #16323a;
  --ink-soft:   #4e6b74;
  --line:       #e2d6bf;
  --white:      #ffffff;

  --shadow-sm: 0 1px 2px rgba(22,50,58,.08);
  --shadow:    0 2px 4px rgba(22,50,58,.06), 0 12px 28px -14px rgba(22,50,58,.35);
  --shadow-lg: 0 8px 20px -6px rgba(22,50,58,.22), 0 28px 60px -30px rgba(22,50,58,.5);

  --display: "Baloo 2", system-ui, sans-serif;
  --body:    "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;

  --pad: clamp(1rem, 4vw, 1.6rem);
}

* { box-sizing: border-box; }

/* The panels below set display:flex, which would otherwise beat the hidden
   attribute and leave every screen stacked on the page at once. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background:
    radial-gradient(120% 60% at 50% 0%, var(--cream) 0%, var(--cream-2) 100%)
    fixed;
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

:where(a, button, input, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--cyan-deep);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ── demo flag ──────────────────────────────────────────────────────────── */
.demo-flag {
  background: var(--ink);
  color: var(--cream);
  font-size: .78rem;
  letter-spacing: .02em;
  text-align: center;
  padding: .5rem var(--pad);
}

/* ── masthead ───────────────────────────────────────────────────────────── */
/* Logos left, one link right. No divider rule: the border under the bar is
   already the separation, and a second line through the middle just read as
   a stray stroke. */
.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .7rem var(--pad);
  background: var(--white);
  border-bottom: 1px solid var(--line);
}
.masthead-brand {
  display: flex;
  align-items: center;
  gap: .7rem;
  text-decoration: none;
}
.masthead-brand img { height: 2rem; width: auto; display: block; }
.masthead-brand .masthead-bikis { height: 2.75rem; }

/* Hidden on desktop: the real headline sits above the poster there. The mobile
   block below brings this out to fill the empty half of the bar. */
.masthead-punch { display: none; }

/* Britannia and Milk Bikis alternating in one slot.

   Both faces are stacked and the box is rotated on its Y axis; the back face
   is pre-rotated 180deg so it reads the right way round when it comes into
   view.

   4s round trip: each logo holds still for 1.5s and each half-turn takes 0.5s,
   eased in and out so the logo gathers speed and settles rather than snapping.
   A 0.25s turn read as a flicker between two states; at half a second the eye
   follows the rotation itself, which is the point of turning at all.

   Width is fixed to the wider of the two so the header does not jump as they
   swap, and both faces are centred inside it. */
.brandflip {
  position: relative;
  display: block;
  width: 6.5rem;
  height: 2.75rem;
  perspective: 600px;
}
.brandflip-face {
  position: absolute;
  inset: 0;
  margin: auto;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  backface-visibility: hidden;
  animation: brandturn 4s ease-in-out infinite;
}
.brandflip-face.is-back { transform: rotateY(180deg); }

@keyframes brandturn {
  0%,   37.5%  { transform: rotateY(0deg); }
  50%,  87.5%  { transform: rotateY(180deg); }
  100%         { transform: rotateY(360deg); }
}
.brandflip-face.is-back { animation-name: brandturn-back; }
@keyframes brandturn-back {
  0%,   37.5%  { transform: rotateY(180deg); }
  50%,  87.5%  { transform: rotateY(360deg); }
  100%         { transform: rotateY(540deg); }
}

/* Someone who has asked their phone to stop animating things gets both logos
   held still, side by side, rather than a frozen half-flip showing neither. */
@media (prefers-reduced-motion: reduce) {
  .brandflip { width: auto; display: flex; align-items: center; gap: .6rem; perspective: none; }
  .brandflip-face { position: static; animation: none; transform: none; height: 2rem; }
  .brandflip-face.is-back { transform: none; height: 2.4rem; }
}

.masthead-nav a {
  font-size: .86rem;
  font-weight: 600;
  color: var(--cyan-ink);
  text-decoration: none;
  padding: .45rem .8rem;
  border: 1.5px solid var(--line);
  border-radius: 99px;
  white-space: nowrap;
  transition: border-color .15s, background .15s;
}
.masthead-nav a:hover { border-color: var(--cyan); background: var(--cream); }

@media (max-width: 26rem) {
  .masthead-brand img { height: 1.5rem; }
  .masthead-brand .masthead-bikis { height: 1.9rem; }
  .masthead-nav a { font-size: .78rem; padding: .4rem .6rem; }
}

/* ── shell ──────────────────────────────────────────────────────────────── */
.shell {
  max-width: 30rem;
  margin: 0 auto;
  padding: .8rem var(--pad) .6rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}



/* ── the stage: poster with one empty panel ─────────────────────────────── */
.stage { display: flex; flex-direction: column; gap: .9rem; }

.stage-title {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.6rem, 7vw, 2.1rem);
  line-height: 1.12;
  letter-spacing: -.015em;
  text-wrap: balance;
  color: var(--ink);
}
.stage-title em {
  display: block;
  font-style: normal;
  color: var(--red);
}

.poster {
  position: relative;
  margin: 0;
  aspect-ratio: 1126 / 682;
  border-radius: 12px;
  overflow: hidden;
  background: var(--white);
  box-shadow: var(--shadow-lg);
}
.poster-art, .poster-brand {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.poster-brand { pointer-events: none; }

/* Panel 2 -- the middle one -- measured off the artwork: x=376, 374x447 inside
   1126x682. The retailer's photo sits here, under the branding overlay: the
   same three-layer stack the compositor uses, so the preview is honest.
   Warm biscuit tan rather than cyan, so the empty slot reads as a gap waiting
   to be filled instead of blending into the brand band below it. */
.poster-slot {
  position: absolute;
  left: var(--slot-x, 33.393%); top: 0;
  transition: left .28s cubic-bezier(.2,.7,.3,1);
  width: 33.215%;
  height: 65.543%;
  overflow: hidden;
  background: linear-gradient(160deg, #e8bd6b 0%, var(--biscuit) 55%, #b88230 100%);
  display: grid;
  place-items: center;
}
.poster-slot img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: none;
}
.poster-slot.has-photo img { display: block; }
.poster-slot.has-photo .slot-hint { display: none; }

.slot-hint {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .3rem;
  color: #5b3c12;
  font-size: clamp(.6rem, 2.6vw, .78rem);
  font-weight: 600;
  text-align: center;
  padding: .4rem;
}
.slot-hint svg {
  width: clamp(1.5rem, 7vw, 2.1rem);
  height: clamp(1.5rem, 7vw, 2.1rem);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
}
/* A slow breath so the empty panel reads as waiting, not broken. */
@media (prefers-reduced-motion: no-preference) {
  .poster-slot:not(.has-photo) .slot-hint { animation: breathe 2.8s ease-in-out infinite; }
}
@keyframes breathe {
  0%, 100% { opacity: .78; transform: translateY(0); }
  50%      { opacity: 1;   transform: translateY(-2px); }
}

.stage-note {
  margin: 0;
  color: var(--ink-soft);
  font-size: .93rem;
}

/* Phones: drop the poster thumbnail only. The logo and the headline stay --
   they are what tells the retailer what this is. The thumbnail costs about 40%
   of a small screen and both form steps have to fit without scrolling.
   This block sits AFTER the base .stage rules on purpose: a media query adds
   no specificity, so placed earlier it simply loses the cascade. */
@media (max-width: 61.99rem) {
  /* The poster used to be hidden here entirely, to keep both form steps on
     one screen. That cost too much: the poster IS the offer, and every
     retailer is on a phone, so the one audience that never saw the artwork
     was the whole audience. It comes back small and above the headline --
     enough to show what they are joining, cheap enough in height that Shop
     Name still lands near the top of the screen. */
  .stage-note { display: none; }

  /* The headline moves up into the masthead, beside the logo, filling the half
     of that bar that was empty. Its own row below then disappears, and the
     poster is untouched -- same size, same place. */
  .masthead {
    justify-content: flex-start;
    gap: 40px;
    padding: .5rem var(--pad);
  }
  .masthead-brand { flex: 0 0 auto; }
  .brandflip { width: 4.4rem; height: 2.1rem; }
  .masthead-punch {
    display: block;
    margin: 0;
    font-family: var(--display);
    font-weight: 700;
    font-size: 1.02rem;
    line-height: 1.14;
    letter-spacing: -.01em;
    color: var(--ink);
  }
  .masthead-punch em { display: block; font-style: normal; color: var(--red); }

  /* The real heading stays in the document and stays announced -- only its
     pixels go. display:none here would leave the page with no heading at all
     for anyone using a screen reader, since the copy above is aria-hidden. */
  .stage-title {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }

  /* align-self, NOT margin: 0 auto. .stage is a column flex container, and auto
     side margins on a flex item make the margins eat the free space instead of
     the item stretching -- so the figure sizes to its content. Every child here
     is position:absolute, so that content is zero wide, aspect-ratio then gives
     it zero height, and the poster silently vanishes. It measured 0x0 at 375px
     and rendered fine at desktop, where this rule does not apply. */
  /* No cap: the poster spans the column like the form card and the reference
     pair, so all three blocks share one left and right edge. It was 304px
     against the form's 345px, which read as a misalignment rather than a
     deliberate inset. */
  .poster { width: 100%; align-self: stretch; margin: 0; }
  .stage { gap: 0; }

  /* Two blocks left on the page -- poster, then form -- so the padding that
     used to separate three can come down. */
  .shell { padding-top: 1.1rem; gap: 1.3rem; padding-bottom: .9rem; }

  /* On a phone the poster comes first: it is the offer, and a retailer
     scrolling in has to see what they are joining before they are shown how
     to photograph it. The references follow, then the form. The DOM order is
     the desktop one, so flex order swaps the first two here rather than
     duplicating the markup. */
  .stage { order: 1; }
  .kvref { order: 2; }
  .panel { order: 3; }
  .panel { padding: 1.15rem 1.1rem; }
  .step  { gap: .8rem; }
  .steps { margin-bottom: 1rem; }
  .field { gap: .3rem; }
  .field input, .field textarea { padding: .72rem .85rem; }
}

/* ── panels ─────────────────────────────────────────────────────────────── */
.panel {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: clamp(.95rem, 4vw, 1.35rem);
  box-shadow: var(--shadow);
}

/* ── steps ──────────────────────────────────────────────────────────────── */
.steps {
  display: flex;
  gap: .5rem;
  list-style: none;
  margin: 0 0 .7rem;
  padding: 0;
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.steps li {
  flex: 1;
  display: flex;
  align-items: center;
  gap: .45rem;
  padding-top: .6rem;
  border-top: 3px solid var(--line);
  transition: color .2s, border-color .2s;
}
.steps li span {
  display: grid;
  place-items: center;
  width: 1.4rem; height: 1.4rem;
  border-radius: 50%;
  background: var(--cream-2);
  color: var(--ink-soft);
  font-size: .75rem;
  font-weight: 700;
}
.steps li.is-current { color: var(--ink); border-top-color: var(--cyan); }
.steps li.is-current span { background: var(--cyan); color: var(--white); }
.steps li.is-done { border-top-color: var(--cyan-deep); }
.steps li.is-done span { background: var(--cyan-deep); color: var(--white); }

/* ── fields ─────────────────────────────────────────────────────────────── */
.step { border: 0; margin: 0; padding: 0; display: none; flex-direction: column; gap: .58rem; }
.step.is-active { display: flex; }

@media (prefers-reduced-motion: no-preference) {
  .step.is-active { animation: slide-in .28s cubic-bezier(.2,.7,.3,1) both; }
}
@keyframes slide-in {
  from { opacity: 0; transform: translateX(14px); }
  to   { opacity: 1; transform: none; }
}

.field { display: flex; flex-direction: column; gap: .2rem; }
.field-label {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--cyan-ink);
}
.field input, .field textarea {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: 10px;
  padding: .58rem .8rem;
  width: 100%;
  resize: vertical;
  transition: border-color .15s, background .15s;
}
.field input::placeholder, .field textarea::placeholder { color: #a99c86; }
.field input:focus, .field textarea:focus {
  background: var(--white);
  border-color: var(--cyan);
}
.field-help { font-size: .8rem; color: var(--ink-soft); }
.field.is-bad input, .field.is-bad textarea { border-color: var(--red); }

/* ── upload ─────────────────────────────────────────────────────────────── */
.drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .3rem;
  text-align: center;
  padding: 1.15rem 1rem;
  border: 2px dashed var(--cyan);
  border-radius: 12px;
  background: linear-gradient(180deg, #f2fbfd 0%, var(--cream) 100%);
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .1s;
}
.drop:active { transform: scale(.99); }
.drop.is-over { border-color: var(--cyan-deep); background: #e6f7fb; }
.drop.has-photo { border-style: solid; border-color: var(--cyan-deep); }
.drop strong { font-family: var(--display); font-size: 1.18rem; color: var(--ink); }
.drop-sub { font-size: .87rem; color: var(--ink-soft); }
.drop-icon {
  width: 2.4rem; height: 2.4rem;
  fill: none; stroke: var(--cyan-deep); stroke-width: 1.5; stroke-linejoin: round;
}

/* ---- the photo box --------------------------------------------------------
   Two buttons and a label. No thumbnail: the poster above already shows the
   photo in the chosen panel, so a preview here was the same image twice and
   it was the tallest thing on the screen.

   Two controls on a phone, because a phone can genuinely do both: capture=
   opens the camera directly but offers no route to the gallery, so the gallery
   needs its own input. A desktop ignores capture= entirely, so a Camera button
   there is just a second file picker wearing a different label -- hidden, and
   the remaining button says Choose photo. */

.shot-brief {
  margin: 0 0 .1rem;
  font-size: .92rem;
  line-height: 1.45;
  color: var(--ink-soft);
}
.shot-brief strong { color: var(--ink); }

.shot {
  display: flex;
  flex-direction: column;
  gap: .55rem;
  padding: .8rem;
  border: 2px dashed var(--cyan);
  border-radius: 12px;
  background: linear-gradient(180deg, #f2fbfd 0%, var(--cream) 100%);
  transition: border-color .15s, background .15s;
}
.shot.is-over { border-color: var(--cyan-deep); background: #e6f7fb; }
.shot.has-photo { border-style: solid; border-color: var(--cyan-deep); }

.shot-hint {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: .05rem;
  line-height: 1.25;
  color: var(--ink);
}
.shot-hint strong { font-family: var(--display); font-size: 1.05rem; }
.shot.has-photo .shot-hint strong { color: var(--cyan-deep); }
.shot-sub { font-size: .8rem; color: var(--ink-soft); }

/* Side by side. Stacked, these were the two tallest rows on the screen. */
.shot-acts { display: flex; gap: .5rem; }
.shot-btn {
  flex: 1 1 0;
  min-width: 0;
  font: inherit;
  font-size: .88rem;
  font-weight: 600;
  padding: .6rem .4rem;
  border: 1.5px solid var(--cyan);
  border-radius: 9px;
  background: #fff;
  color: var(--cyan-deep);
  cursor: pointer;
  transition: background .15s, border-color .15s;
}
.shot-btn:hover { background: #f2fbfd; border-color: var(--cyan-deep); }
.shot-btn:active { transform: scale(.98); }

/* Shown while shrink() runs. A 48MP capture takes a beat to downscale, and
   without this the buttons look like the tap did nothing. */
.shot.is-busy .shot-acts { opacity: .5; pointer-events: none; }
.shot.is-busy .shot-sub::after { content: " 6"; }

.shot-clear {
  align-self: center;
  font: inherit;
  font-size: .8rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-soft);
  text-decoration: underline;
  cursor: pointer;
}

/* Desktop has no camera, so one button, and it says what it does. */
@media (hover: hover) and (pointer: fine) {
  .shot-btn.is-cam { display: none; }
  .shot-btn.is-lib { font-size: 0; }
  .shot-btn.is-lib::after { content: "Choose photo"; font-size: .88rem; }
}

/* Panels the retailer has not filled stay transparent, so the stock model in
   the artwork below shows through -- the preview then matches the poster.
   The chosen one carries the cream gap and follows the picker. */
.poster-slot:not(.has-photo) { background: none; }
.poster-slot:not(.has-photo) .slot-hint { display: none; }
.poster-slot.is-target:not(.has-photo) {
  background: linear-gradient(160deg, #e8bd6b 0%, var(--biscuit) 55%, #b88230 100%);
}
.poster-slot.is-target:not(.has-photo) .slot-hint { display: flex; }

.go[disabled] { opacity: .45; cursor: not-allowed; }

/* Panels the retailer has not filled stay transparent, so the stock model in
   the artwork below shows through -- the preview then matches the poster.
   The middle one keeps its biscuit-tan gap: it is the panel the headline
   points at, and an invitation there is worth more than an accurate third
   thumbnail. */
.poster-slot:not(.has-photo) { background: none; }
.poster-slot:not(.has-photo) .slot-hint { display: none; }

/* The chosen panel, and only that one: cream-tan gap with "Your photo" in it.
   It follows the Left/Middle/Right picker, so pressing a button moves the gap
   there. The other two stay transparent and show the stock models underneath,
   which is exactly what the printed poster will have in those panels. */
.poster-slot.is-target:not(.has-photo) {
  background: linear-gradient(160deg, #e8bd6b 0%, var(--biscuit) 55%, #b88230 100%);
}
.poster-slot.is-target:not(.has-photo) .slot-hint { display: flex; }

/* ---- reference key visuals -----------------------------------------------
   The campaign's own artwork, sitting at the foot of the page so a retailer
   can see what they are about to be part of. Static: no interaction, no
   dependence on which step they are on.

   Loaded lazily -- they are below everything else and no retailer needs them
   before the form, so they must not compete with the poster preview for the
   first bytes of a shop's 4G connection. */
.kvref {
  display: flex;
  flex-direction: column;
  gap: .7rem;
}
.kvref-title {
  margin: 0;
  font-family: var(--body);
  font-size: .88rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--ink-soft);
  text-align: center;
  text-wrap: balance;
}
/* Side by side at every size. The sources are 333px wide, and half of this
   column is about 230px -- so they render at close to native resolution and
   stay sharp. Stretched full width they would be visibly soft. */
/* Both images share the row equally and the pair fills the column, so the
   block lines up edge to edge with the form card and the poster. Fixed pixel
   widths made this the odd one out: narrower than the form, wider than the
   poster, so nothing down the page shared a left edge. */
.kvref-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .6rem;
}

.kvref-grid img {
  width: 100%;
  height: auto;
  display: block;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 10px;
  box-shadow: var(--shadow);
  background: var(--cream-2);
}

.tip { margin: 0; font-size: .84rem; color: var(--ink-soft); }

/* ── consent ────────────────────────────────────────────────────────────── */
/* Two separate targets on purpose: the tick agrees, the link reads. With the
   link inside the label a single tap did both at once. */
/* Tickbox in one column, sentence in the other. The terms link is part of that
   sentence rather than a row of its own -- as a separate line it cost a third
   line on a screen that has none spare. It is a sibling of the <label>, not
   inside it, so tapping the link opens the terms instead of toggling consent. */
.consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .7rem;
  align-items: start;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: .85rem;
}
.consent.is-bad { border-color: var(--red); }

.consent > input[type="checkbox"] {
  width: 1.3rem; height: 1.3rem;
  margin: .1rem 0 0;
  accent-color: var(--cyan-deep);
  cursor: pointer;
}

.consent-text {
  margin: 0;
  font-size: .88rem;
  line-height: 1.45;
  color: var(--ink-soft);
}
.consent-text label { cursor: pointer; }

.consent-link {
  font-size: inherit;
  font-weight: 600;
  color: var(--cyan-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.consent-link:hover { color: var(--cyan-deep); }

/* ── buttons ────────────────────────────────────────────────────────────── */
.go {
  display: block;
  width: 100%;
  font-family: var(--display);
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--white);
  background: var(--red);
  border: 0;
  border-radius: 11px;
  padding: .82rem 1rem;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 3px 0 var(--red-deep);
  transition: transform .08s, box-shadow .08s, background .15s;
}
.go:hover { background: #c01b29; }
.go:active { transform: translateY(3px); box-shadow: 0 0 0 var(--red-deep); }
.go[disabled] { background: #c9bda8; box-shadow: 0 3px 0 #b3a68f; cursor: not-allowed; }

.back {
  font: inherit;
  font-weight: 600;
  color: var(--cyan-ink);
  background: none;
  border: 1.5px solid var(--line);
  border-radius: 11px;
  padding: .85rem 1.1rem;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.back:hover { border-color: var(--cyan); background: var(--cream); }

.actions { display: grid; grid-template-columns: auto 1fr; gap: .7rem; }

.error {
  margin: 0;
  min-height: 0;
  font-size: .87rem;
  font-weight: 600;
  color: var(--red);
}

/* ── working ────────────────────────────────────────────────────────────── */
.working { text-align: center; display: flex; flex-direction: column; gap: .8rem; align-items: center; }
.working h2, .done h2 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--ink);
}
.working-note { margin: 0; color: var(--ink-soft); font-size: .95rem; }

/* Three biscuits dipping in turn -- the campaign's own gesture, used as the
   wait. Placeholder until the real pack shots arrive. */
.biscuit { display: flex; gap: .5rem; padding: .4rem 0; }
.biscuit span {
  width: 1.1rem; height: 1.55rem;
  border-radius: 3px;
  background: linear-gradient(180deg, #f0d9a8 0%, var(--biscuit) 100%);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.45), var(--shadow-sm);
}
@media (prefers-reduced-motion: no-preference) {
  .biscuit span { animation: dip 1.25s ease-in-out infinite; }
  .biscuit span:nth-child(2) { animation-delay: .16s; }
  .biscuit span:nth-child(3) { animation-delay: .32s; }
}
@keyframes dip {
  0%, 100% { transform: translateY(0)      rotate(-4deg); }
  50%      { transform: translateY(.55rem) rotate(4deg); }
}

.meter {
  width: 100%;
  height: 7px;
  border-radius: 99px;
  background: var(--cream-2);
  overflow: hidden;
}
.meter i {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--cyan) 0%, var(--cyan-deep) 100%);
  transition: width .4s ease;
}

/* ── done ───────────────────────────────────────────────────────────────── */
.done { display: flex; flex-direction: column; gap: 1rem; text-align: center; }
.done img {
  width: 100%;
  border-radius: 10px;
  box-shadow: var(--shadow);
}
@media (prefers-reduced-motion: no-preference) {
  .done img { animation: reveal .5s cubic-bezier(.2,.7,.3,1) both; }
}
@keyframes reveal {
  from { opacity: 0; transform: scale(.965); }
  to   { opacity: 1; transform: none; }
}

/* ── foot ───────────────────────────────────────────────────────────────── */
.foot {
  padding: 1.5rem var(--pad) 2.5rem;
  text-align: center;
  font-size: .8rem;
  color: var(--ink-soft);
}

/* ── wider screens: poster beside the form ──────────────────────────────── */
@media (min-width: 62rem) {
  .shell {
    max-width: 64rem;
    display: grid;
    grid-template-columns: 1.05fr .95fr;
    align-items: start;
    gap: 2.5rem;
  }
  /* Left column is the poster alone -- it is the hero and it gets the height.
     The right column reads top to bottom: what a good photo looks like, then
     the form to fill in. Both halves of "here is the example, now do it".

     Explicit rows on every item. .stage was already pinned to row 1, so
     anything left on grid-row:auto gets placed after it rather than where the
     column is meant to read. */
  .stage { grid-column: 1; grid-row: 1 / span 2; position: sticky; top: 2rem; }
  .kvref { grid-column: 2; grid-row: 1; align-items: center; }
  .panel { grid-column: 2; grid-row: 2; }

  /* ONE NUMBER controls the reference images on a PC: how wide the pair is
     allowed to get. Each image is half of it, minus the gap.

     It also decides where the form starts. The pair sits above the form in
     this column, so anything taken off here lifts the whole card -- which is
     what keeps the Next button on screen instead of below the fold. */
  .kvref-grid { max-width: 300px; }
}

/* ── pack shot, once the brand supplied it ──────────────────────────────── */
.masthead-back {
  font-size: .84rem;
  font-weight: 600;
  color: var(--cyan-ink);
  text-decoration: none;
  border-bottom: 1.5px solid var(--line);
  padding-bottom: 1px;
}
.masthead-back:hover { border-bottom-color: var(--cyan); }

/* The real pack, tilting gently, rather than the CSS biscuits it replaced. */
.working-pack {
  width: min(13rem, 70%);
  height: auto;
  display: block;
  filter: drop-shadow(0 10px 18px rgba(22,50,58,.18));
}
@media (prefers-reduced-motion: no-preference) {
  .working-pack { animation: sway 2.4s ease-in-out infinite; }
}
@keyframes sway {
  0%, 100% { transform: rotate(-3deg) translateY(0); }
  50%      { transform: rotate(3deg)  translateY(-6px); }
}


/* ── terms page ─────────────────────────────────────────────────────────── */
.shell--doc { max-width: 44rem; display: block; }
.doc { display: flex; flex-direction: column; gap: .35rem; }
.doc h1 {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.6rem, 6vw, 2.1rem);
  font-weight: 700;
  line-height: 1.15;
}
.doc-date { margin: 0 0 .8rem; color: var(--ink-soft); font-size: .9rem; }
.doc h2 {
  margin: 1.5rem 0 .1rem;
  font-family: var(--display);
  font-size: 1.12rem;
  font-weight: 700;
  color: var(--cyan-ink);
}
.doc p { margin: .3rem 0; color: var(--ink-soft); max-width: 62ch; }
.doc ul { margin: .4rem 0; padding-left: 1.2rem; color: var(--ink-soft); max-width: 62ch; }
.doc li { margin: .28rem 0; }
.doc li::marker { color: var(--cyan); }
.doc strong { color: var(--ink); }
.doc .go { margin-top: 1.8rem; }

.callout {
  margin-top: 1.8rem;
  border-left: 3px solid var(--red);
  background: #fdeeef;
  border-radius: 0 8px 8px 0;
  padding: .9rem 1.1rem;
}
.callout strong { color: var(--red); display: block; margin-bottom: .25rem; }
.callout p { margin: 0; }

/* ── panel picker ───────────────────────────────────────────────────────── */
/* Which of the three panels the photo lands in. Pressing one moves the
   cream-tan gap in the poster above to that panel, so the choice is visible
   rather than described.

   Centred, and stacked label over buttons: left-aligned it read as a stray
   form field, and the poster it controls is centred above it. */
.picker {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .35rem;
  margin-top: -.1rem;
  text-align: center;
}
.picker-label {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--cyan-ink);
}
.picker-opts {
  display: flex;
  gap: .3rem;
  background: var(--cream);
  border: 1.5px solid var(--line);
  border-radius: 99px;
  padding: .2rem;
}
.picker-opts button {
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-radius: 99px;
  padding: .35rem .8rem;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.picker-opts button:hover { color: var(--ink); }
.picker-opts button[aria-pressed="true"] {
  background: var(--cyan-deep);
  color: var(--white);
}

/* Hotspots over the artwork: click a panel to send your photo there. */
.poster-picks {
  position: absolute;
  inset: 0 0 34.457% 0;   /* the photo band only, above the cyan strip */
  display: grid;
  grid-template-columns: 33.215% 33.393% 33.392%;
}
.poster-picks button {
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0;
  display: grid;
  place-items: end center;
  padding-bottom: .5rem;
  transition: box-shadow .15s;
}
.poster-picks button span {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--white);
  background: rgba(12,31,36,.62);
  border-radius: 99px;
  padding: .18rem .55rem;
  opacity: 0;
  transition: opacity .15s;
}
.poster-picks button:hover span,
.poster-picks button:focus-visible span { opacity: 1; }
.poster-picks button[aria-pressed="true"] { cursor: default; }
.poster-picks button[aria-pressed="true"] span { opacity: 0; }

/* ── admin ──────────────────────────────────────────────────────────────── */
/* Scanned and operated, not read top to bottom: the summary tiles carry state
   in colour as well as number, so anything needing attention shows up before
   the table does. */
.admin-body { background: #f2f5f6; }

.admin {
  max-width: 78rem;
  margin: 0 auto;
  padding: 1.5rem var(--pad) 4rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.admin h1 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.8rem;
  font-weight: 700;
}
.admin h2 {
  margin: 0 0 .7rem;
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--cyan-ink);
}

.chip {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  padding: .28rem .6rem;
  border-radius: 99px;
  white-space: nowrap;
}
.chip--demo { background: #e7eef0; color: var(--ink-soft); }
.chip--live { background: #fdeeef; color: var(--red); }
.masthead-nav { display: flex; align-items: center; gap: .8rem; }

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .8rem;
}
.tile {
  background: var(--white);
  border: 1px solid #dde5e7;
  border-top: 3px solid var(--cyan);
  border-radius: 10px;
  padding: .85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .1rem;
}
.tile--warn  { border-top-color: var(--biscuit); }
.tile--bad   { border-top-color: var(--red); }
.tile--money { border-top-color: var(--ink); }
.tile-label {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.tile-value {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-variant-numeric: tabular-nums;
  font-size: 1.7rem;
  font-weight: 600;
  line-height: 1.15;
  color: var(--ink);
}
.tile-note { font-size: .76rem; color: var(--ink-soft); }

.block {
  background: var(--white);
  border: 1px solid #dde5e7;
  border-radius: 12px;
  padding: 1.1rem;
}
.block .note { margin: .8rem 0 0; font-size: .84rem; color: var(--ink-soft); }
.block code {
  font-family: ui-monospace, Consolas, monospace;
  font-size: .82em;
  background: #eef3f4;
  padding: .1rem .35rem;
  border-radius: 4px;
}

table.grid { width: 100%; border-collapse: collapse; min-width: 46rem; }
table.grid th, table.grid td { padding: .55rem .7rem; text-align: left; vertical-align: middle; }
table.grid thead th {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-bottom: 1px solid #dde5e7;
  white-space: nowrap;
}
table.grid tbody tr + tr td { border-top: 1px solid #eef2f3; }
table.grid .num {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-variant-numeric: tabular-nums;
  font-size: .86rem;
  white-space: nowrap;
}
table.grid .empty { text-align: center; color: var(--ink-soft); padding: 2rem; }

.thumb {
  display: block;
  width: 5.5rem;
  height: auto;
  border-radius: 4px;
  border: 1px solid #dde5e7;
}
.thumb--empty { width: 5.5rem; height: 3.3rem; background: #eef2f3; }

.pill {
  display: inline-block;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .04em;
  padding: .2rem .55rem;
  border-radius: 99px;
  text-transform: capitalize;
}
.pill--done    { background: #e4f3ec; color: var(--good, #16624a); }
.pill--pending { background: #fdf2e0; color: #8a6114; }
.pill--failed  { background: #fdeeef; color: var(--red); }
.pill--warn    { background: #fdf2e0; color: #8a6114; }

/* ── admin sign in ──────────────────────────────────────────────────────── */
/* Same cream ground, same card, same fields and button as the retailer form.
   The browser's own Basic-auth dialog could not be branded or signed out of. */
.shell--login {
  /* The base .shell becomes a two-column grid on desktop and would drop this
     card into the narrow right column. Force it back to a single centred box. */
  display: flex !important;
  flex-direction: column;
  grid-template-columns: none;
  max-width: 24rem;
  min-height: calc(100dvh - 9rem);
  justify-content: center;
  padding-bottom: 4rem;
}
.shell--login .panel { grid-column: auto; }
.login { display: flex; flex-direction: column; gap: 1.1rem; }
.login-title {
  margin: 0;
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.15;
  color: var(--ink);
}
.login-title em {
  display: block;
  font-style: normal;
  font-family: var(--body);
  font-size: .85rem;
  font-weight: 500;
  color: var(--ink-soft);
  margin-top: .2rem;
}
.login-form { display: flex; flex-direction: column; gap: .85rem; }
.login-note { margin: 0; font-size: .82rem; color: var(--ink-soft); }
.login-note a { color: var(--cyan-ink); font-weight: 600; }

/* ── admin: filter bar, exports ─────────────────────────────────────────── */
.bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: .8rem;
  background: var(--white);
  border: 1px solid #dde5e7;
  border-radius: 12px;
  padding: .9rem 1.1rem;
}
.filter { display: flex; align-items: flex-end; gap: .6rem; flex-wrap: wrap; }
.filter-field { display: flex; flex-direction: column; gap: .2rem; }
.filter-field span {
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.filter-field input {
  font: inherit;
  font-size: .9rem;
  color: var(--ink);
  background: var(--white);
  border: 1.5px solid #dde5e7;
  border-radius: 8px;
  padding: .42rem .6rem;
}
.filter-field input:focus { border-color: var(--cyan); outline: none; }
.exports { display: flex; gap: .5rem; flex-wrap: wrap; }

.btn {
  display: inline-block;
  font: inherit;
  font-size: .86rem;
  font-weight: 600;
  color: var(--cyan-ink);
  background: var(--white);
  border: 1.5px solid #dde5e7;
  border-radius: 8px;
  padding: .45rem .85rem;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color .15s, background .15s;
}
.btn:hover { border-color: var(--cyan); background: #f4fbfc; }
.btn--go { background: var(--cyan-deep); border-color: var(--cyan-deep); color: var(--white); }
.btn--go:hover { background: var(--cyan-ink); border-color: var(--cyan-ink); }
.btn--sm { font-size: .76rem; padding: .3rem .6rem; }
.btn--danger { background: var(--red); border-color: var(--red); color: var(--white); }
.btn--danger:hover { background: var(--red-deep); border-color: var(--red-deep); }

/* ── admin: charts ──────────────────────────────────────────────────────── */
.charts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: 1rem;
}

/* Ranked magnitude, one measure -- a single brand hue, no legend, values
   direct-labelled. Rounded data-ends, recessive track. */
.bars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .55rem; }
.bars-row {
  display: grid;
  grid-template-columns: minmax(4.5rem, 8rem) 1fr auto;
  align-items: center;
  gap: .7rem;
}
.bars-name {
  font-size: .86rem;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bars-track { height: 12px; background: #eef3f4; border-radius: 99px; overflow: hidden; }
.bars-fill {
  display: block;
  height: 100%;
  min-width: 4px;
  background: var(--cyan-deep);
  border-radius: 0 4px 4px 0;
}
.bars-value {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-variant-numeric: tabular-nums;
  font-size: .86rem;
  font-weight: 600;
  color: var(--ink);
  min-width: 1.6rem;
  text-align: right;
}

.days {
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  gap: 4px;
  height: 8.5rem;
  overflow-x: auto;
  padding-bottom: .2rem;
}
.days-col {
  /* Capped, so a single day does not render as one solid block across the
     card. Bars grow in count, not in width. */
  flex: 0 1 2.2rem;
  max-width: 3rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: .3rem;
  height: 100%;
}
.days-bar {
  width: 100%;
  min-height: 3px;
  background: var(--cyan);
  border-radius: 4px 4px 0 0;
}
.days-label {
  font-family: ui-monospace, Consolas, monospace;
  font-size: .62rem;
  color: var(--ink-soft);
  white-space: nowrap;
}

table.grid .wrap { max-width: 16rem; font-size: .84rem; color: var(--ink-soft); }

.danger { border-color: #f0d6d6; }
.danger h2 { color: var(--red); }
.confirm-input {
  font: inherit;
  font-size: .9rem;
  letter-spacing: .08em;
  border: 1.5px solid #f0d6d6;
  border-radius: 8px;
  padding: .45rem .7rem;
  width: 8rem;
}
.confirm-input:focus { border-color: var(--red); outline: none; }
