/* ============================================================
   A reseller's storefront.

   Deliberately its own stylesheet with nothing of our own site in it. This
   page belongs to the reseller: their name, their colour, their contact
   details, and no route back to our own prices.

   One colour drives the page. The reseller picks it, and js/reseller-shop.js
   turns it into a small set (--brand-ink always readable on white,
   --brand-deep for the dark end of a gradient) before anything is drawn, so
   a pale colour chosen for its look can never leave white text unreadable.
   Network colours get the same treatment per card (--net and friends).

   Light only, as before: the shop has no theme switch, and every surface
   paints its own background so a phone in dark mode still shows it as
   designed.
   ============================================================ */
/* The bottom inset, capped. Zero in a browser tab, and the real inset only
   once the page is running from a home screen. Reading env() raw is what
   put a white band under a bar before (9 Oct 2026); everything pinned to
   the bottom edge reads this instead. */
:root { --lb-safe-b: 0px; }
@media (display-mode: standalone) {
  :root { --lb-safe-b: env(safe-area-inset-bottom, 0px); }
}
html.is-app { --lb-safe-b: env(safe-area-inset-bottom, 0px); }

:root {
  --brand: #0164ce;
  --brand-ink: #0164ce;
  --brand-deep: #0059c4;
  --brand-soft: #e7f0fb;
  --brand-ring: rgba(1, 100, 206, 0.2);

  --ink: #16181d;
  --soft: #5d6470;
  --faint: #8a919c;
  --line: #e4e7eb;
  --line-strong: #d2d7de;
  --page: #f4f6f8;
  --card: #fff;

  --green: #127a3e;
  --green-bg: #e7f6ed;
  --red: #b42318;
  --red-bg: #fdecea;
  --amber: #8f5500;
  --amber-bg: #fff3d6;
  --blue: #0059c4;
  --blue-bg: #e7f0fb;
  --star: #e0a100;

  --radius: 16px;
  --shadow: 0 1px 2px rgba(22, 24, 29, 0.05), 0 8px 24px rgba(22, 24, 29, 0.07);
  --figure: "Space Grotesk", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }
/* This page carries no site-wide reset, and .sheet sets display:flex, which
   would otherwise beat the hidden attribute and leave the buy sheet open on
   load. Everything here toggles visibility with el.hidden, so this one rule
   has to win. */
[hidden] { display: none !important; }

/* A page even 1px wider than the screen makes iPhone Safari shrink it. */
html, body { overflow-x: clip; }
body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}
body.is-locked { overflow: hidden; }
body.has-pending { padding-bottom: 88px; }

img { max-width: 100%; }
h1, h2, h3 { letter-spacing: -0.01em; }
.mono { font-family: var(--mono); }
.sr-only {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- loading, closed and offline ---- */
.state {
  min-height: 72vh; display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 40px 16px; color: var(--soft);
}
.state h1 { margin: 16px 0 6px; font-size: 21px; color: var(--ink); }
.state p { margin: 0 0 18px; max-width: 34ch; }
.state__icon {
  width: 68px; height: 68px; border-radius: 50%; display: grid; place-items: center;
  background: var(--card); border: 1px solid var(--line); color: var(--soft);
}
.state__icon svg { width: 30px; height: 30px; }
.spinner {
  width: 36px; height: 36px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--brand-ink);
  animation: shop-spin 0.8s linear infinite; margin-bottom: 14px;
}
@keyframes shop-spin { to { transform: rotate(360deg); } }

/* ---- the bar at the top of the shop ----
   The seller's mark and name stay in view as the customer reads. On a wide
   screen the sections sit beside them; on a phone one button opens them. */
:root { --bar: 58px; }
.bar {
  position: sticky; top: 0; z-index: 30; color: #fff;
  background: var(--brand-ink);
  padding-top: env(safe-area-inset-top, 0px);
}
.bar__inner {
  max-width: 1080px; margin: 0 auto; height: var(--bar);
  display: flex; align-items: center; gap: 12px; padding: 0 12px;
}
.bar__brand {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  display: flex; align-items: center; gap: 10px; min-width: 0; padding: 4px; margin-right: auto;
  color: inherit; border-radius: 12px;
}
.bar__ring {
  width: 34px; height: 34px; flex: none; border-radius: 50%; overflow: hidden;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.18); border: 1px solid rgba(255, 255, 255, 0.45);
}
.bar__ring img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bar__initials { font-family: var(--figure); font-size: 14px; font-weight: 700; line-height: 1; }
.bar__name {
  font-size: 16.5px; font-weight: 700; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bar__links { display: none; }
.bar__links button {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  color: rgba(255, 255, 255, 0.86); font-size: 14.5px; font-weight: 600;
  padding: 8px 10px; border-radius: 10px;
}
.bar__links button:hover { color: #fff; background: rgba(255, 255, 255, 0.14); }
.bar__links button.is-on { color: #fff; background: rgba(255, 255, 255, 0.2); }
.bar__menu {
  appearance: none; -webkit-appearance: none; background: none; cursor: pointer;
  width: 42px; height: 42px; flex: none; display: grid; place-items: center;
  border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.4); color: #fff;
}
.bar__menu svg { width: 20px; height: 20px; }
.bar__brand:focus-visible, .bar__menu:focus-visible, .bar__links button:focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
}

/* ---- the menu a phone opens ---- */
.menu {
  position: fixed; inset: 0; z-index: 60; display: flex; justify-content: flex-end;
  background: rgba(16, 24, 40, 0.5);
}
.menu__box {
  width: min(320px, 86vw); background: #fff; color: var(--ink);
  padding: 16px 16px calc(24px + env(safe-area-inset-bottom, 0px));
  display: flex; flex-direction: column; gap: 6px; overflow-y: auto;
  box-shadow: -18px 0 40px rgba(16, 24, 40, 0.18);
}
.menu__head { display: flex; align-items: center; gap: 12px; padding-bottom: 10px; }
.menu__head strong { font-size: 16px; min-width: 0; overflow-wrap: anywhere; }
.menu__close {
  appearance: none; -webkit-appearance: none; background: var(--page); border: 0; cursor: pointer;
  margin-left: auto; width: 40px; height: 40px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: var(--soft);
}
.menu__close svg { width: 18px; height: 18px; }
.menu__list { list-style: none; margin: 0; padding: 8px 0; display: grid; gap: 2px; border-top: 1px solid var(--line); }
.menu__list button {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  width: 100%; min-height: 48px; padding: 10px 12px; border-radius: 12px;
  display: flex; align-items: center; gap: 10px; text-align: left;
  font-size: 16px; font-weight: 600; color: var(--ink);
}
.menu__list button svg { width: 20px; height: 20px; flex: none; color: var(--soft); }
.menu__list button.is-on { background: var(--brand-soft); color: var(--brand-ink); }
.menu__list button.is-on svg { color: var(--brand-ink); }
.menu__talk { display: grid; gap: 8px; padding-top: 12px; border-top: 1px solid var(--line); }
.menu__talk a {
  min-height: 48px; padding: 10px 12px; border-radius: 12px; text-decoration: none;
  display: flex; align-items: center; gap: 10px;
  font-size: 15.5px; font-weight: 600; color: var(--brand-ink); background: var(--brand-soft);
}
.menu__talk a svg { width: 18px; height: 18px; flex: none; }

/* ---- the front of the shop ----
   The name is the headline, the line under it is the seller's own, and the
   two buttons go where most customers are heading. */
.hero {
  position: relative; overflow: hidden; color: #fff;
  background: linear-gradient(135deg, var(--brand-ink) 0%, var(--brand-deep) 100%);
  padding: 40px 16px 44px;
}
.hero::before, .hero::after {
  content: ""; position: absolute; border-radius: 50%; pointer-events: none;
  background: rgba(255, 255, 255, 0.08);
}
.hero::before { width: 320px; height: 320px; top: -170px; right: -100px; }
.hero::after { width: 240px; height: 240px; bottom: -150px; left: -80px; background: rgba(255, 255, 255, 0.06); }
.hero__inner {
  position: relative; z-index: 1; max-width: 1080px; margin: 0 auto;
  display: flex; align-items: center; gap: 32px;
}
.hero__text { min-width: 0; flex: 1 1 auto; }
.hero__name {
  margin: 0; font-weight: 750; line-height: 1.04; letter-spacing: -0.03em;
  font-size: clamp(34px, 9vw, 60px); overflow-wrap: anywhere;
}
.hero__desc {
  margin: 14px 0 0; max-width: 42ch; white-space: pre-line; overflow-wrap: anywhere;
  font-size: 16px; line-height: 1.55; color: rgba(255, 255, 255, 0.88);
}
.hero__notice {
  display: inline-flex; align-items: flex-start; gap: 8px; margin: 14px 0 0; padding: 8px 14px;
  border-radius: 999px; font-size: 14.5px; font-weight: 600; overflow-wrap: anywhere;
  background: rgba(255, 255, 255, 0.16); border: 1px solid rgba(255, 255, 255, 0.38);
}
.hero__notice svg { width: 18px; height: 18px; flex: none; margin-top: 1px; }
.hero__acts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
.hero__btn {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 48px; padding: 10px 20px; border-radius: 14px;
  font-size: 15.5px; font-weight: 700;
  background: rgba(255, 255, 255, 0.14); border: 1px solid rgba(255, 255, 255, 0.45); color: #fff;
  transition: transform 0.15s ease, background-color 0.15s ease;
}
.hero__btn svg { width: 18px; height: 18px; flex: none; }
.hero__btn:hover { transform: translateY(-1px); background: rgba(255, 255, 255, 0.24); }
.hero__btn--go { background: #fff; color: var(--brand-ink); border-color: #fff; }
.hero__btn--go:hover { background: #fff; }
.hero__btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.hero__art { flex: none; display: none; }
/* The scene has depth rather than a flat drawing: the phone sits back, the
   mark sits in front of it, and both move a little, which is what makes it
   read as one object seen at an angle. */
.scene { position: relative; width: 300px; height: 250px; perspective: 900px; }
.scene__svg {
  width: 100%; height: 100%; display: block;
  transform: rotateY(-16deg) rotateX(6deg); transform-origin: 60% 50%;
  filter: drop-shadow(0 22px 34px rgba(0, 0, 0, 0.22));
  animation: shop-float 7s ease-in-out infinite;
}
.scene__wave { opacity: 0.22; animation: shop-wave 2.6s ease-in-out infinite; }
.scene__wave:nth-of-type(2) { animation-delay: 0.32s; }
.scene__wave:nth-of-type(3) { animation-delay: 0.64s; }
.scene__dot { animation: shop-drift 5.5s ease-in-out infinite; }
.scene__dot:nth-of-type(2) { animation-delay: 0.7s; animation-duration: 6.4s; }
.scene__dot:nth-of-type(3) { animation-delay: 1.3s; animation-duration: 7.1s; }
.hero__ring {
  position: absolute; left: 6px; bottom: 6px;
  width: 112px; height: 112px; padding: 5px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.2); border: 1.5px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.24);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  animation: shop-float 7s ease-in-out infinite reverse;
}
.hero__ring img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; background: #fff; }
.hero__ring .head__initials { font-size: 34px; }

@keyframes shop-float {
  0%, 100% { transform: rotateY(-16deg) rotateX(6deg) translateY(0); }
  50% { transform: rotateY(-16deg) rotateX(6deg) translateY(-10px); }
}
@keyframes shop-wave {
  0%, 100% { opacity: 0.18; }
  40% { opacity: 0.85; }
}
@keyframes shop-drift {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-6px, -9px); }
}

/* The page arrives once: the name, then the line under it, then the buttons.
   Nothing repeats, so the shop is still after a second. */
.hero__name, .hero__desc, .hero__acts, .head__links { animation: shop-rise 0.5s ease-out both; }
.hero__desc { animation-delay: 0.06s; }
.hero__acts { animation-delay: 0.12s; }
.head__links { animation-delay: 0.18s; }
@keyframes shop-rise {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .net, .bundle, [role="tabpanel"].is-fresh { animation: none !important; }
  .net, .net__tile, .net__count, .tab svg, .tab::after, .bundle, .bundle__buy { transition: none !important; }
  .net.is-picked .net__tile::after { display: none; }
  .scene__svg, .hero__ring, .scene__wave, .scene__dot,
  .hero__name, .hero__desc, .hero__acts, .head__links { animation: none; }
  .scene__wave { opacity: 0.5; }
}
.head__initials { font-family: var(--figure); font-size: 52px; font-weight: 700; line-height: 1; }
.head__links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.pill {
  display: inline-flex; align-items: center; gap: 7px; min-height: 40px; padding: 7px 15px;
  border-radius: 999px; text-decoration: none; color: #fff;
  font-size: 14.5px; font-weight: 600;
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.38);
  transition: background-color 0.15s ease;
}
.pill svg { width: 17px; height: 17px; flex: none; }
/* The share pill is a button among links, so it needs the bits a button
   does not inherit. */
button.pill { appearance: none; -webkit-appearance: none; cursor: pointer; font-family: inherit; }
.pill:hover { background: rgba(255, 255, 255, 0.26); }
.pill:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (min-width: 900px) {
  .bar__links { display: flex; align-items: center; gap: 2px; }
  .bar__menu { display: none; }
  /* The bar already lists the sections on a wide screen, so the strip under
     the header would say the same thing twice. */
  .tabs { display: none; }
  .hero { padding: 72px 24px 78px; }
  .hero__art { display: block; }
  .hero__ring { left: 0; bottom: 0; }
}

/* ---- the shop's scrolling message line ---- */
.ticker { background: #fff; border-bottom: 2px solid var(--brand); color: var(--brand-deep); }
.ticker__viewport {
  overflow: hidden; padding: 9px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 28px, #000 calc(100% - 28px), transparent);
}
.ticker__track {
  display: flex; width: max-content;
  animation: shop-ticker var(--ticker-time, 30s) linear infinite;
}
.ticker:hover .ticker__track { animation-play-state: paused; }
.ticker__group { display: flex; align-items: center; flex: none; white-space: nowrap; }
.ticker__item { font-size: 14.5px; font-weight: 600; }
.ticker__dot {
  width: 5px; height: 5px; margin: 0 18px; flex: none; border-radius: 50%;
  background: var(--brand-ink); opacity: 0.6;
}
/* The two copies are identical, so moving by half the track lands exactly
   where it started and the loop has no visible jump. */
@keyframes shop-ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.ticker.is-still .ticker__viewport { -webkit-mask-image: none; mask-image: none; padding: 9px 16px; }
.ticker.is-still .ticker__track { animation: none; width: auto; justify-content: center; }
.ticker.is-still .ticker__group { flex: 1 1 auto; min-width: 0; white-space: normal; flex-wrap: wrap; justify-content: center; row-gap: 4px; text-align: center; }
.ticker.is-still .ticker__dot:last-child { display: none; }

/* ---- tabs ---- */
.tabs-anchor { height: 0; }
.tabs {
  /* Under the bar, which is sticky too, so both stay in view together. */
  position: sticky; top: calc(var(--bar) + env(safe-area-inset-top, 0px)); z-index: 20;
  background: rgba(255, 255, 255, 0.97);
  -webkit-backdrop-filter: saturate(1.4) blur(10px);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}
.tabs__inner { max-width: 1080px; margin: 0 auto; display: flex; padding: 0 6px; }
.tab {
  appearance: none; -webkit-appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  position: relative; flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  padding: 10px 4px 9px; color: var(--soft);
  font-size: 13px; font-weight: 600; white-space: nowrap;
  transition: color 0.15s ease;
}
/* The icon leads the change: it lifts and grows a little as its tab becomes
   the live one, so the eye follows the move rather than noticing the colour
   afterwards. (4 Oct 2026, on the owner's word.) */
.tab svg {
  width: 21px; height: 21px; flex: none;
  transition: transform .34s cubic-bezier(.22, 1.3, .36, 1), opacity .2s ease;
}
.tab:active svg { transform: scale(.9); }
/* The underline grows out of the middle instead of appearing whole, which
   is what makes moving between tabs read as one thing sliding rather than
   two things blinking. */
.tab::after {
  content: ""; position: absolute; left: 16%; right: 16%; bottom: -1px; height: 3px;
  border-radius: 3px 3px 0 0; background: var(--brand-ink);
  transform: scaleX(0); transform-origin: center;
  transition: transform .3s cubic-bezier(.22, .9, .3, 1);
}
.tab:hover { color: var(--ink); }
.tab:hover svg { transform: translateY(-1px); }
.tab.is-on { color: var(--brand-ink); }
.tab.is-on svg { transform: translateY(-2px) scale(1.08); }
.tab.is-on::after { transform: scaleX(1); }
/* The panel the tab opens arrives rather than appearing. Set on the section
   by js/reseller-shop.js each time a tab is chosen. */
[role="tabpanel"].is-fresh { animation: panelIn .36s cubic-bezier(.22, .8, .3, 1) both; }
@keyframes panelIn { from { opacity: 0; transform: translateY(10px); } }
.tab:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: -4px; border-radius: 8px; }

.wrap { max-width: 1080px; margin: 0 auto; padding: 18px 16px 48px; }
.narrow { max-width: 640px; margin: 0 auto; }
.mid { max-width: 820px; margin: 0 auto; }

.notice {
  display: flex; align-items: flex-start; gap: 10px; margin: 0 0 16px; padding: 12px 14px;
  border-radius: 12px; background: var(--amber-bg); color: var(--amber);
  border: 1px solid #f0dcaa; font-size: 14.5px; overflow-wrap: anywhere;
}
.notice svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }

/* ---- network pills ---- */
/* ---- the networks ----
   One row of pills, one of them on. The motion here was added 4 Oct 2026 on
   the owner's word, and all of it is doing a job: the pills arrive in order
   so the eye lands on the first one, the chosen pill lifts and its colour
   fills from the left rather than snapping, its count bumps so you can see
   which number just became the live one, and a press gives way under the
   finger. Transform and opacity only, so none of it costs a layout. */
/* ---- the network chooser: a tile, a name under it ----
   The owner, 13 Oct 2026, with a picture of the layout he wanted: the
   network's own logo as a square tile, its name beneath, and how many
   sizes it sells in the corner of the tile.

   One scrolling row, never wrapped: five networks used to take two lines
   on a phone and push the bundles down the page. */
/* THEY SHARE THE ROW. The owner, 13 Oct 2026: "let it be large ... when I
   add a new network you just squeeze or arrange it to the right size".

   So the tiles divide whatever width there is rather than each holding a
   fixed size: four networks are big, seven are smaller, and past the floor
   they wrap onto a second line instead of shrinking into nothing. */
.nets {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0 0 16px;
  /* Room at the top for the count, which sits on the corner of the tile. */
  padding: 10px 2px 6px;
  perspective: 600px;
}

.net {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 0; border: 0; background: none; color: var(--muted, #64748b);
  /* An equal share of the row, never under 74px and never over 124px: one
     network does not become a billboard and eight do not become dots. */
  /* The basis is what decides how many share a line: wrapping happens
     against the basis, and only then do the ones on a line grow to fill
     it. 64px means four fit on a small phone and five on a big one, each
     grown to whatever is left over. Past that they wrap rather than
     shrink into dots. */
  flex: 1 1 64px;
  min-width: 64px;
  max-width: 124px;
  transition: color .2s ease;
  animation: netIn .42s cubic-bezier(.22, .8, .3, 1) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}

/* The tile itself. The ring is drawn outside it, so the picture is never
   covered and the chosen one reads at a glance. */
/* The tile takes the width its column was given, so it squeezes with it. */
.net__tile {
  position: relative;
  width: 100%;
  max-width: 104px;
  aspect-ratio: 1 / 1;
  border-radius: 22px;
  background: #fff;
  box-shadow: 0 1px 2px rgba(16, 20, 30, .06);
  outline: 2px solid transparent;
  outline-offset: 2px;
  transition: outline-color .2s ease, transform .32s cubic-bezier(.22, 1.2, .36, 1), box-shadow .32s ease;
}

.net__art {
  width: 100%; height: 100%;
  border-radius: inherit;
  object-fit: cover;
  background: #fff;
  border: 1px solid rgba(16, 20, 30, 0.08);
  display: block;
}

/* A network with no picture of its own: its short mark on its own colour,
   so the row is never left with a gap where the others have a logo. */
.net__art--text {
  display: grid;
  place-items: center;
  background: var(--net);
  color: var(--net-on);
  border-color: transparent;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.01em;
}

/* How many sizes this network sells, on the corner of its tile. */
.net__count {
  position: absolute;
  top: -7px; right: -7px;
  min-width: 22px; height: 22px; padding: 0 6px;
  border-radius: 999px;
  background: var(--net);
  color: var(--net-on);
  border: 2px solid #fff;
  font-size: 11.5px; font-weight: 800; line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Two lines kept for every name, whether it uses them or not, so a long
   one ("AirtelTigo iShare") does not make its tile sit higher than the
   rest. A third line is clipped rather than pushing the row about. */
.net__name {
  font-size: 13px;
  font-weight: 700;
  line-height: 1.2;
  height: 2.4em;
  text-align: center;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.net:hover .net__tile { transform: translateY(-2px); }

.net.is-on { color: var(--net-ink); }
.net.is-on .net__tile {
  outline-color: var(--net);
  box-shadow: 0 10px 20px -14px var(--net);
}

@media (max-width: 599px) {
  .nets { gap: 10px; }
  .net__name { font-size: 12px; }
}

@keyframes netIn { from { opacity: 0; transform: translateY(8px) scale(.96); } }
.net:active .net__tile { transform: scale(.95); }
/* A ring that leaves the tile once, on the tap that chose it. Added by
   js/reseller-shop.js and removed when it finishes, so it can play again. */
.net.is-picked .net__tile::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit;
  border: 2px solid var(--net);
  animation: netRing .55s ease-out forwards;
  pointer-events: none;
}
@keyframes netRing {
  from { opacity: .7; transform: scale(1); }
  to { opacity: 0; transform: scale(1.22); }
}
.net:focus-visible { outline: 2px solid var(--net-ink); outline-offset: 2px; }

/* ---- find a size ----
   Added 4 Oct 2026. A shop with twenty sizes on one network is a list a
   customer scrolls twice and then messages the seller about instead. The
   box only appears once there are enough of them to be worth it. */
.find {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 10px; padding: 0 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(22, 24, 29, .04);
}
.find[hidden] { display: none; }
.find svg { width: 17px; height: 17px; flex: none; color: var(--soft); }
.find input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  font: inherit; font-size: 16px; color: var(--ink);
  padding: 11px 0;
}
.find input::-webkit-search-cancel-button { display: none; }
.find button {
  appearance: none; border: 0; background: none; cursor: pointer;
  font-size: 22px; line-height: 1; color: var(--soft); padding: 2px 2px 4px;
}
.find button:hover { color: var(--ink); }
.empty--find { padding: 18px 12px; }
.linkish {
  appearance: none; border: 0; background: none; cursor: pointer; font: inherit;
  color: var(--brand-ink); font-weight: 700; text-decoration: underline;
}

/* ---- the bundles, which are the whole point of the page ---- */
/* Four across on a phone and more on wider screens. Three was the owner's
   first call and he changed it on 4 Oct 2026 looking at his own shop: the
   cards were still too big, and a customer should take in a whole network's
   sizes in one look rather than scroll past a few giant tiles. Everything
   inside the card is sized against the card itself below, or four columns
   only produces four cramped ones. */
/* THE COLUMNS FOLLOW THE SCREEN, NOT A NUMBER. The owner, 13 Oct 2026:
   "no matter how you zoom in, you zoom out, it's four. So fix that."

   repeat(4, 1fr) is exactly four columns at every width, so pinching the
   page only made four narrower cards: at four across on a phone the card
   is 78px, which is why the size, the days and the price all had to be
   shrunk to fit and why he read them as too small.

   auto-fill with a floor means the CARD keeps a readable width and the
   count falls out of it: two columns zoomed right in, three on a normal
   phone, four or five on a tablet. The figures below are sized for a card
   of at least 104px and never have to shrink again. */
.grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
.bundle {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden; cursor: pointer;
  /* They arrive in order, a few milliseconds apart, so a switch of network
     reads as a new set landing rather than as a flicker. Capped at the
     tenth, or the last card of twenty would be most of a second late. */
  animation: bundleIn .34s cubic-bezier(.22, .8, .3, 1) both;
  animation-delay: calc(min(var(--i, 0), 9) * 32ms);
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
@keyframes bundleIn { from { opacity: 0; transform: translateY(10px) scale(.97); } }
.bundle:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.bundle:active { transform: scale(.98); }
.bundle__buy { transition: transform .18s ease, filter .18s ease; }
.bundle:hover .bundle__buy { filter: brightness(1.04); }
.bundle__buy:active { transform: scale(.97); }
/* A fixed band. The uploaded network logos are complete branded tiles with
   their own background, so the picture fills the band; if the picture set
   the size, a 1600px square would decide how tall every card is. The
   network colour behind is the fallback for a network with no picture. */
/* Four by three, like the panel the owner's resellers know: a square logo
   tile cropped to this still shows the whole oval. The size comes from the
   card's width, never from the image, so a 1600px tile cannot stretch it. */
.bundle__band {
  position: relative; flex: none; aspect-ratio: 4 / 3; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--net), var(--net-deep));
}
.bundle__band img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.bundle__mark {
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  border: 2px solid currentColor; background: rgba(255, 255, 255, 0.14); color: var(--net-on);
  font-family: var(--figure); font-size: 12px; font-weight: 700; padding: 0 2px;
  overflow: hidden; white-space: nowrap;
}
/* Five or six letters (a network with no logo text of its own) need a
   smaller size to stay inside the circle. */
.bundle__mark--long { font-size: 10px; padding: 0; letter-spacing: -0.02em; }
.bundle__body { display: flex; flex-direction: column; flex: 1; padding: 8px; }
.bundle__size {
  margin: 0; font-family: var(--figure); font-size: 17px; font-weight: 700; line-height: 1.15;
  color: var(--net-ink); overflow-wrap: anywhere;
}
.bundle__val { margin: 1px 0 0; font-size: 11.5px; color: var(--soft); }
.bundle__price {
  margin: auto 0 8px; padding-top: 5px;
  font-family: var(--figure); font-size: 13.5px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.bundle__buy {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  width: 100%; min-height: 32px; border: 0; border-radius: 8px;
  font-size: 13px; font-weight: 700; color: var(--net-on);
  background: linear-gradient(135deg, var(--net), var(--net-deep));
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1);
}
.bundle__buy:focus-visible { outline: 3px solid var(--net-ink); outline-offset: 2px; }

/* ---- checker cards ---- */
/* Drawn here and never from a picture: a coloured band with the exam's name
   on it. WASSCE takes the shop's own colour; BECE and anything else get a
   fixed colour of their own, each dark enough to carry white text. */
.vhead { margin: 0 0 14px; }
.vhead h2 { margin: 0; font-size: 18px; font-weight: 700; line-height: 1.3; }
.vhead p { margin: 4px 0 0; max-width: 60ch; color: var(--soft); font-size: 14.5px; }
.vgrid { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.voucher {
  --v: var(--brand-ink); --v-deep: var(--brand-deep);
  display: flex; flex-direction: column; min-width: 0; overflow: hidden; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.voucher--bece { --v: #0f7a6c; --v-deep: #0a5a50; }
.voucher--other { --v: #475467; --v-deep: #344054; }
.voucher:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.voucher.is-out { cursor: default; }
.voucher.is-out:hover { transform: none; box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04); border-color: var(--line); }
.voucher__band {
  position: relative; flex: none; aspect-ratio: 16 / 10; overflow: hidden;
  display: flex; flex-direction: column; justify-content: space-between; padding: 12px;
  color: #fff; background: linear-gradient(135deg, var(--v), var(--v-deep));
}
/* Two soft circles, like the header, so a band is not a flat block. */
.voucher__band::before, .voucher__band::after {
  content: ""; position: absolute; border-radius: 50%; pointer-events: none; background: rgba(255, 255, 255, 0.1);
}
.voucher__band::before { width: 120px; height: 120px; top: -58px; right: -34px; }
.voucher__band::after { width: 84px; height: 84px; bottom: -48px; left: -26px; background: rgba(255, 255, 255, 0.07); }
.voucher.is-out .voucher__band { filter: grayscale(0.7); opacity: 0.75; }
.voucher__icon {
  position: relative; width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.16); border: 1px solid rgba(255, 255, 255, 0.35);
}
.voucher__icon svg { width: 22px; height: 22px; }
.voucher__exam {
  position: relative; font-family: var(--figure); font-size: 22px; font-weight: 700; line-height: 1.1;
  letter-spacing: 0.02em; overflow-wrap: anywhere;
}
.voucher__body { display: flex; flex-direction: column; flex: 1; padding: 10px; }
.voucher__name { margin: 0; font-size: 15px; font-weight: 700; line-height: 1.25; overflow-wrap: anywhere; }
.voucher__note { margin: 2px 0 0; font-size: 12.5px; color: var(--soft); }
.voucher__price {
  margin: auto 0 8px; padding-top: 8px;
  font-family: var(--figure); font-size: 17px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.voucher__buy {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  width: 100%; min-height: 40px; border: 0; border-radius: 9px;
  font-size: 14px; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--v), var(--v-deep));
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.12);
}
.voucher__buy:focus-visible { outline: 3px solid var(--v); outline-offset: 2px; }
.voucher__soldout {
  display: grid; place-items: center; min-height: 40px; border-radius: 9px;
  background: var(--page); border: 1px dashed var(--line-strong);
  color: var(--soft); font-size: 14px; font-weight: 700;
}

.empty {
  display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center;
  padding: 48px 16px; color: var(--soft);
  background: var(--card); border: 1px dashed var(--line-strong); border-radius: var(--radius);
}
.empty svg { width: 34px; height: 34px; color: var(--faint); }
.empty p { margin: 0; max-width: 32ch; }

/* ---- cards and forms ---- */
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px; box-shadow: 0 1px 2px rgba(22, 24, 29, 0.04);
}
.card__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin: 0 0 4px; }
.card__head h2 { margin: 0; font-size: 18px; font-weight: 700; line-height: 1.3; }
.card__lead { margin: 0 0 4px; color: var(--soft); font-size: 14.5px; }
.fee { margin: 0; display: flex; flex-direction: column; align-items: flex-end; flex: none; text-align: right; }
.fee small { font-size: 12.5px; color: var(--soft); font-weight: 500; }
.fee span { font-family: var(--figure); font-size: 21px; font-weight: 700; color: var(--brand-ink); line-height: 1.15; }

.fields { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: 16px; }
.field { margin-top: 14px; min-width: 0; }
.lbl { display: block; margin: 0 0 6px; font-size: 14px; font-weight: 600; color: var(--ink); }
.req { color: var(--red); margin-left: 2px; }
.input {
  display: block; width: 100%; min-height: 48px; margin: 0;
  font: inherit; font-size: 16px; /* 16px keeps iPhone from zooming */
  padding: 11px 13px; border-radius: 11px;
  border: 1px solid var(--line-strong); background: #fff; color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
textarea.input { min-height: 96px; resize: vertical; line-height: 1.5; }
.input::placeholder { color: var(--faint); }
.input:focus { outline: none; border-color: var(--brand-ink); box-shadow: 0 0 0 3px var(--brand-ring); }
.input[aria-invalid="true"] { border-color: var(--red); }
.input.mono { font-family: var(--mono); letter-spacing: 0.02em; }
.hint { margin: 6px 0 0; font-size: 13px; color: var(--soft); }
.sub { margin: 22px 0 0; padding-top: 18px; border-top: 1px solid var(--line); font-size: 16px; font-weight: 700; }
.sub + .hint { margin-top: 2px; }
.muted { color: var(--soft); font-size: 14.5px; margin: 12px 0 0; }

.err, .ok { margin: 14px 0 0; padding: 10px 12px; border-radius: 10px; font-size: 14.5px; }
.err { background: var(--red-bg); color: var(--red); }
.ok { background: var(--green-bg); color: var(--green); }

.btn {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; min-height: 50px; margin-top: 18px; padding: 12px 18px;
  border: 0; border-radius: 12px; text-decoration: none;
  font-size: 16px; font-weight: 700; line-height: 1.2;
}
.btn svg { width: 19px; height: 19px; flex: none; }
.btn--solid {
  color: #fff; background: linear-gradient(135deg, var(--brand-ink), var(--brand-deep));
  box-shadow: 0 6px 16px var(--brand-ring);
}
.btn--ghost { color: var(--ink); background: #fff; border: 1px solid var(--line-strong); }
.btn--ghost:hover { background: var(--page); }
.btn--auto { width: auto; }
.btn:disabled { opacity: 0.55; cursor: default; box-shadow: none; }
.btn:focus-visible { outline: 3px solid var(--brand-ring); outline-offset: 2px; }
.linkish {
  appearance: none; background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  color: var(--brand-ink); font-weight: 600; text-decoration: underline; text-underline-offset: 2px;
}

.inline { display: flex; gap: 8px; }
.inline .input { flex: 1; min-width: 0; }
.inline .btn { flex: none; width: auto; margin-top: 0; min-height: 48px; padding: 10px 16px; }

/* How many checker cards: a number between two round buttons. */
.qty { display: flex; align-items: stretch; gap: 8px; max-width: 220px; }
.qty__btn {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  width: 48px; min-height: 48px; flex: none; border-radius: 11px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong); background: #fff; color: var(--brand-ink);
}
.qty__btn svg { width: 20px; height: 20px; }
.qty__btn:hover { background: var(--brand-soft); }
.qty__btn:disabled { opacity: 0.45; cursor: default; background: #fff; }
.qty__btn:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
.qty .qty__input { flex: 1; min-width: 0; text-align: center; font-family: var(--figure); font-weight: 700; }

/* ---- how to pay: radio cards ---- */
.pay { margin-top: 20px; }
.pay__title { margin: 0 0 10px; font-size: 15.5px; font-weight: 700; }
.pay-cards { display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); }
.pay-card { position: relative; display: block; cursor: pointer; }
.pay-card input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.pay-card__box {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border: 1.5px solid var(--line-strong); border-radius: 13px; background: #fff;
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}
.pay-card:hover .pay-card__box { border-color: var(--brand); }
.pay-card input:checked + .pay-card__box {
  border-color: var(--brand-ink); background: var(--brand-soft); box-shadow: 0 0 0 3px var(--brand-ring);
}
.pay-card input:focus-visible + .pay-card__box { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
.pay-card__badge {
  width: 44px; height: 44px; flex: none; border-radius: 12px; display: grid; place-items: center;
  background: var(--badge, var(--brand-ink)); color: var(--badge-on, #fff);
}
.pay-card__badge svg { width: 22px; height: 22px; }
.pay-card__main { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.35; }
.pay-card__main strong { font-size: 15px; }
.pay-card__main span { font-size: 13.5px; color: var(--soft); overflow-wrap: anywhere; }
.pay-card__main .pay-card__num { font-family: var(--figure); font-weight: 600; color: var(--ink); letter-spacing: 0.02em; }
.pay-card__tick {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  border: 2px solid var(--line-strong); background: #fff;
}
.pay-card input:checked + .pay-card__box .pay-card__tick {
  border-color: var(--brand-ink); background: var(--brand-ink); box-shadow: inset 0 0 0 4px #fff;
}
.pay-empty {
  display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: 12px;
  background: var(--amber-bg); color: var(--amber); font-size: 14.5px;
}
.pay-empty svg { width: 20px; height: 20px; flex: none; margin-top: 2px; }
.pay-empty strong { display: block; }
/* On a line of its own under the message, only as wide as its label. */
.pay-empty .pay-retry {
  display: flex; width: fit-content; margin-top: 10px; min-height: 42px; padding: 8px 16px; font-size: 15px;
}

/* ---- the popup ---- */
.sheet {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(17, 19, 24, 0.55);
  animation: shop-fade 0.18s ease;
}
.sheet__box {
  position: relative; width: 100%; max-width: 500px;
  max-height: 92vh; max-height: 92dvh; overflow-y: auto; overscroll-behavior: contain;
  background: #fff; border-radius: 20px 20px 0 0;
  padding: 20px 16px calc(22px + env(safe-area-inset-bottom, 0px));
  animation: shop-rise 0.22s ease;
}
.sheet__box:focus { outline: none; }
@keyframes shop-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes shop-rise { from { transform: translateY(24px); opacity: 0.6; } to { transform: none; opacity: 1; } }
.sheet__x {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  width: 38px; height: 38px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--page); color: var(--soft);
}
.sheet__x svg { width: 18px; height: 18px; }
.sheet__x:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
/* Locked while an order is being placed, and it should look it. */
.sheet__x:disabled { opacity: 0.45; cursor: default; }
.sheet__title { margin: 2px 48px 14px 0; font-size: 19px; font-weight: 700; }

.pick {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  border-radius: 14px; background: var(--page); border: 1px solid var(--line);
}
.pick__tile {
  width: 54px; height: 54px; flex: none; border-radius: 12px; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--net, var(--brand-ink)), var(--net-deep, var(--brand-deep)));
  color: var(--net-on, #fff); font-family: var(--figure); font-size: 14px; font-weight: 700;
}
.pick__tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pick__tile svg { width: 26px; height: 26px; }
.pick__main { flex: 1; min-width: 0; }
.pick__main strong { display: block; font-size: 16px; overflow-wrap: anywhere; }
.pick__main span { display: block; font-size: 13.5px; color: var(--soft); overflow-wrap: anywhere; }
.pick__price { font-family: var(--figure); font-size: 18px; font-weight: 700; white-space: nowrap; }
/* A checker card's tile takes its exam's colour, as on the card itself. */
.pick__tile--wassce { background: linear-gradient(135deg, var(--brand-ink), var(--brand-deep)); color: #fff; }
.pick__tile--bece { background: linear-gradient(135deg, #0f7a6c, #0a5a50); color: #fff; }
.pick__tile--other { background: linear-gradient(135deg, #475467, #344054); color: #fff; }

/* ---- pay to ---- */
.payto__lead { margin: 16px 0 10px; font-size: 14.5px; color: var(--soft); }
.payto { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
.payto__amount {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 14px 16px;
  color: #fff; background: linear-gradient(135deg, var(--brand-ink), var(--brand-deep));
}
.payto__amount span { font-size: 14px; opacity: 0.92; }
.payto__amount strong { font-family: var(--figure); font-size: 24px; line-height: 1.1; white-space: nowrap; }
.payto__rows { margin: 0; padding: 2px 16px; }
.payto__row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.payto__row:last-child { border-bottom: 0; }
.payto__row dt { flex: none; font-size: 13.5px; color: var(--soft); }
.payto__row dd {
  margin: 0; min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
  text-align: right; font-size: 15px; font-weight: 600; overflow-wrap: anywhere;
}
.payto__num { font-family: var(--figure); letter-spacing: 0.03em; }
.payto__note {
  margin: 0; padding: 12px 16px; border-top: 1px solid var(--line);
  background: var(--amber-bg); color: var(--amber); font-size: 14px; overflow-wrap: anywhere;
}
.copy {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  min-height: 34px; padding: 5px 10px; border-radius: 9px;
  border: 1px solid var(--line-strong); background: #fff; color: var(--brand-ink);
  font-size: 13px; font-weight: 700;
}
.copy svg { width: 15px; height: 15px; }
.copy:hover { background: var(--brand-soft); }
.copy.is-done { color: var(--green); border-color: #bfe3cc; background: var(--green-bg); }
.copy:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }

/* ---- paying online ---- */
/* The amount and the reference sit in the same panel as Pay on Confirmation,
   so both ways to pay read as one checkout. A prompt gateway adds the two
   fields above; a card gateway adds nothing and only opens its own page. */
#step-online .payto { margin-top: 16px; }
#step-online .btn--ghost { margin-top: 10px; }
select.input { padding-right: 34px; cursor: pointer; background-color: #fff; }
.wait {
  display: flex; align-items: center; gap: 12px; margin-top: 16px; padding: 12px 14px;
  border-radius: 12px; background: var(--blue-bg); color: var(--blue);
}
.wait .spinner {
  width: 26px; height: 26px; flex: none; margin: 0; border-width: 2.5px;
  border-color: rgba(1, 100, 206, 0.25); border-top-color: var(--blue);
}
.wait__text { min-width: 0; font-size: 14.5px; }
.wait__text strong { display: block; }

/* ---- proof of payment ---- */
.proof { margin-top: 20px; padding-top: 18px; border-top: 1px dashed var(--line-strong); }
.proof h3 { margin: 0 0 2px; font-size: 17px; font-weight: 700; }
.proof > .hint { margin-top: 0; }
.drop {
  position: relative; display: flex; align-items: center; gap: 12px; padding: 12px 14px; cursor: pointer;
  border: 1.5px dashed var(--line-strong); border-radius: 13px; background: var(--page);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.drop:hover, .drop:focus-within { border-color: var(--brand-ink); background: var(--brand-soft); }
.drop.is-invalid { border-color: var(--red); }
.drop__icon {
  width: 48px; height: 48px; flex: none; overflow: hidden; border-radius: 11px;
  display: grid; place-items: center; background: #fff; color: var(--brand-ink); border: 1px solid var(--line);
}
.drop__icon svg { width: 24px; height: 24px; }
.drop__icon img { width: 100%; height: 100%; object-fit: cover; display: block; }
.drop__text { min-width: 0; }
.drop__text strong { display: block; font-size: 15px; overflow-wrap: anywhere; }
.drop__text span { display: block; font-size: 13px; color: var(--soft); }

/* ---- done ---- */
.done { text-align: center; padding: 10px 0 0; }
.done__icon {
  width: 68px; height: 68px; margin: 6px auto 14px; border-radius: 50%;
  display: grid; place-items: center; background: var(--green-bg); color: var(--green);
}
.done__icon svg { width: 36px; height: 36px; }
.done h2 { margin: 0 0 6px; font-size: 20px; }
.done p { margin: 0 auto; max-width: 36ch; color: var(--soft); font-size: 15px; }
.done__ref {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px 10px;
  margin: 18px 0 0; padding: 12px; border-radius: 12px; background: var(--page); border: 1px solid var(--line);
}
.done__ref span { font-size: 13.5px; color: var(--soft); }
.done__ref strong { font-size: 16px; overflow-wrap: anywhere; }
.done p.done__keep { margin-top: 10px; font-size: 14px; }
.done__keep:empty { display: none; }
.done .btn--ghost { margin-top: 10px; }

/* ---- order status ---- */
.result { margin-top: 14px; }
.result__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding-bottom: 14px; border-bottom: 1px solid var(--line);
}
.result__kind { display: block; font-size: 13px; color: var(--soft); }
.result__head strong { display: block; font-size: 16px; overflow-wrap: anywhere; }
.result__note { margin: 12px 0 0; font-size: 14.5px; color: var(--soft); }
.result__note--bad { color: var(--red); }
.result__note--good { color: var(--green); font-weight: 600; }
/* The line that says the card is watching for the payment by itself. The dot
   keeps it honest: it only pulses while a check is actually running. */
.result__live { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.result__live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--blue-600, #0164ce); animation: result-live 1.6s ease-in-out infinite; }
@keyframes result-live { 0%, 100% { opacity: 0.25; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .result__live::before { animation: none; opacity: 0.9; } }
.rows { margin: 4px 0 0; }
.rows > div { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.rows > div:last-child { border-bottom: 0; padding-bottom: 0; }
.rows dt { color: var(--soft); font-size: 14px; flex: none; }
.rows dd { margin: 0; font-weight: 600; text-align: right; overflow-wrap: anywhere; min-width: 0; }

.status {
  display: inline-flex; align-items: center; gap: 6px; flex: none; white-space: nowrap;
  padding: 5px 11px; border-radius: 999px; font-size: 13px; font-weight: 700;
}
.status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
/* ---- the live status on the done screen ----
   The order moves on its own, so the screen it was placed on says where it
   is rather than sending the customer to a form to ask (owner, 13 Oct
   2026). The dot breathes while the order is still moving and holds still
   once it has arrived or stopped. */
.done__live { display: grid; gap: 6px; justify-items: center; margin: 2px 0 2px; }
.done__live[hidden] { display: none; }
.done__livenote { margin: 0; font-size: 12.5px; color: var(--soft); text-align: center; }
.done__live .status.is-moving::before { animation: done-live 1.8s ease-in-out infinite; }
@keyframes done-live {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .35; transform: scale(.72); }
}
@media (prefers-reduced-motion: reduce) {
  .done__live .status.is-moving::before { animation: none; }
}

.status--green { background: var(--green-bg); color: var(--green); }
.status--red { background: var(--red-bg); color: var(--red); }
.status--amber { background: var(--amber-bg); color: var(--amber); }
.status--blue { background: var(--blue-bg); color: var(--blue); }

/* ---- free code ---- */
.opt { margin-left: 8px; font-size: 12.5px; font-weight: 500; color: var(--soft); }
/* Capitals as they are typed, spaced a little so look-alike characters are
   easier to read back off a message. */
.code-input { text-transform: uppercase; letter-spacing: 0.08em; }
.code-input::placeholder { letter-spacing: 0.02em; }
.code-confirm .pick { margin-top: 12px; }
.code-confirm .hint { margin-top: 12px; font-size: 14px; }
.code-out.result { margin-top: 0; }
.code-out .hint { margin-top: 14px; }
.code-confirm .btn--ghost,
.code-out .btn--ghost { margin-top: 10px; }
/* Focus lands on these cards when they appear, for screen readers; the
   buttons inside keep their own outline. */
.code-confirm:focus,
.code-out:focus { outline: none; }
.rows dd.rows__ref { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px 8px; }

/* ---- a checker card order's codes, under Order status ---- */
.vcodes-form, .vcodes { margin-top: 20px; padding-top: 18px; border-top: 1px dashed var(--line-strong); }
.vcodes-form h3, .vcodes h3 { margin: 0 0 2px; font-size: 17px; font-weight: 700; }
.vcodes-form > .hint { margin-top: 0; }
.vcodes__list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.vcode { padding: 4px 12px; border-radius: 13px; background: var(--page); border: 1px solid var(--line); }
.vcode__num { display: block; padding-top: 8px; font-size: 13px; font-weight: 600; color: var(--soft); }
.vcode__row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.vcode__row:last-child { border-bottom: 0; }
.vcode__lbl { flex: none; width: 50px; font-size: 13.5px; color: var(--soft); }
/* Big enough to read off to someone, and one tap selects all of it. */
.vcode__val {
  flex: 1; min-width: 0; font-size: 16px; font-weight: 700; letter-spacing: 0.05em;
  overflow-wrap: anywhere; -webkit-user-select: all; user-select: all;
}
.vcodes__safe {
  display: flex; align-items: flex-start; gap: 10px; margin: 14px 0 0; padding: 12px 14px; border-radius: 12px;
  background: var(--amber-bg); color: var(--amber); border: 1px solid #f0dcaa; font-size: 14px;
}
.vcodes__safe svg { width: 19px; height: 19px; flex: none; margin-top: 1px; }

/* Five tabs. On a small phone the words get smaller and may take two
   lines, or the longest label runs out of its tab; just past the phone
   layout the tabs sit a little closer. */
@media (max-width: 479px) {
  .tabs__inner.is-crowded { padding: 0 2px; }
  .tabs__inner.is-crowded .tab {
    font-size: 12px; line-height: 1.15; white-space: normal; text-align: center;
    padding-left: 2px; padding-right: 2px;
  }
}
@media (min-width: 640px) and (max-width: 767px) {
  .tabs__inner.is-crowded .tab { padding-left: 10px; padding-right: 10px; }
}
/* Six tabs, with Checker cards. On phones every label may take two lines;
   past the phone layout six labels side by side would run off the edge
   until 900px, so the phone layout of words under icons holds till then. */
@media (max-width: 639px) {
  .tabs__inner.is-packed { padding: 0 2px; }
  .tabs__inner.is-packed .tab {
    white-space: normal; text-align: center; line-height: 1.15;
    padding-left: 2px; padding-right: 2px;
  }
}
@media (min-width: 480px) and (max-width: 639px) {
  .tabs__inner.is-packed .tab { font-size: 12.5px; }
}
@media (max-width: 379px) {
  .tabs__inner.is-packed .tab { font-size: 11.5px; }
  .tabs__inner.is-packed .tab svg { width: 19px; height: 19px; }
}
@media (min-width: 640px) and (max-width: 899px) {
  .tabs__inner.is-packed { justify-content: flex-start; gap: 0; }
  .tabs__inner.is-packed .tab {
    flex: 1 1 0; flex-direction: column; gap: 3px; padding: 10px 4px 9px; font-size: 13.5px;
  }
  .tabs__inner.is-packed .tab svg { width: 21px; height: 21px; }
  .tabs__inner.is-packed .tab::after { left: 16%; right: 16%; }
}

/* ---- reviews ---- */
.reviews { display: grid; gap: 14px; grid-template-columns: minmax(0, 1fr); }
.rev-sum { display: flex; align-items: center; gap: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--line); }
.rev-sum__num { font-family: var(--figure); font-size: 42px; font-weight: 700; line-height: 1; }
.rev-sum__side { display: flex; flex-direction: column; gap: 3px; font-size: 14px; color: var(--soft); }
.rev-sum__side strong { color: var(--ink); font-size: 17px; }
.stars { display: inline-flex; gap: 2px; color: var(--star); }
.stars svg { width: 17px; height: 17px; }
.stars .is-off { color: var(--line-strong); }
.rev { display: flex; gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--line); }
.rev:last-child { border-bottom: 0; padding-bottom: 0; }
.rev__avatar {
  width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand-ink); font-weight: 700;
}
.rev__main { flex: 1; min-width: 0; }
.rev__top { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 2px 10px; }
.rev__name { font-weight: 650; font-size: 15px; overflow-wrap: anywhere; }
.rev__date { font-size: 12.5px; color: var(--faint); }
.rev__body { margin: 4px 0 0; font-size: 14.5px; color: #353a43; white-space: pre-line; overflow-wrap: anywhere; }

/* Five radio buttons drawn as stars. They sit in the page highest first
   and are shown lowest first, so "this one and every one before it" is a
   plain sibling selector. */
.stars-pick { display: flex; flex-direction: row-reverse; justify-content: flex-end; gap: 2px; }
.stars-pick input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.stars-pick label {
  display: grid; place-items: center; padding: 4px; border-radius: 8px; cursor: pointer;
  color: var(--line-strong); transition: color 0.12s ease;
}
.stars-pick label svg { width: 32px; height: 32px; }
.stars-pick input:checked ~ label { color: var(--star); }
.stars-pick:hover input ~ label { color: var(--line-strong); }
.stars-pick:hover label:hover,
.stars-pick:hover label:hover ~ label { color: var(--star); }
.stars-pick input:focus-visible + label { outline: 2px solid var(--brand-ink); outline-offset: 1px; }
.stars-pick__text { margin: 4px 0 0; font-size: 13.5px; color: var(--soft); }

/* ---- an order still waiting for payment ---- */
.pending {
  position: fixed; left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); z-index: 40;
  max-width: 560px; margin: 0 auto;
  display: flex; align-items: center; gap: 10px; padding: 10px 10px 10px 16px;
  background: #fff; border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 14px 36px rgba(22, 24, 29, 0.2);
}
.pending__text { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.pending__text span { font-size: 13px; color: var(--soft); }
.pending__text strong { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pending .btn { margin: 0; width: auto; flex: none; min-height: 42px; padding: 8px 14px; font-size: 14.5px; }
.icon-btn {
  width: 36px; height: 36px; flex: none; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--page); color: var(--soft);
}
.icon-btn svg { width: 16px; height: 16px; }

/* ---- the foot of the shop ----
   Where the customer is told again what is on sale here and how to reach
   the seller. Built as a column on a phone and as four across once there is
   width for it. */
.foot {
  position: relative;
  overflow: hidden;
  padding: 44px 16px calc(28px + env(safe-area-inset-bottom, 0px));
  color: var(--soft); font-size: 14px;
  border-top: 1px solid var(--line); background: #fff; overflow-wrap: anywhere;
}

/* ---- the shop's waves ----
   The owner, 13 Oct 2026: "let them have that waves that i have in my main
   page but in the different style in that section". The main site's run
   white over navy and fill half the footer; these run in the SHOP's own
   colour along the top edge only, so a white footer keeps its light. Two
   runs at different speeds, each holding its shape twice and moving by
   exactly half its width, which is what makes the loop seamless. */
.foot__waves {
  position: absolute; left: 0; right: 0; top: 0; height: 120px;
  overflow: hidden; pointer-events: none; z-index: 0;
  transform: translateZ(0);
  -webkit-mask-image: linear-gradient(to bottom, #000 35%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 35%, transparent 100%);
}
.foot__waves-run {
  position: absolute; left: 0; top: 0; display: flex; width: 200%; height: 100%;
  animation: foot-wave 30s linear infinite;
  color: color-mix(in srgb, var(--brand, #0164ce) 12%, transparent);
  will-change: transform; backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.foot__waves-run--front {
  animation-duration: 19s;
  color: color-mix(in srgb, var(--brand, #0164ce) 7%, transparent);
}
.foot__waves-run svg { flex: 0 0 50%; width: 50%; height: 100%; display: block; transform: scaleY(-1); }
.foot__waves-run path { fill: currentColor; }
@keyframes foot-wave { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-50%, 0, 0); } }
@media (prefers-reduced-motion: reduce) { .foot__waves-run { animation: none; } }

/* The words sit above the waves. */
.foot__grid, .foot__base { position: relative; z-index: 1; }

/* ---- the footer on a phone ----
   The owner, 13 Oct 2026: on a phone it "moves" rather than stacking the
   way his main site does. Every link was a row of its own, so five lists
   ran to a screen and a half of single words with 28px of air each. The
   lists sit two across now, which is how the main site reads, and the rows
   are tight enough that a column is a block rather than a ladder. */
.foot__grid { display: grid; gap: 22px; max-width: 1040px; margin: 0 auto; }
.foot__who { display: flex; align-items: center; gap: 12px; min-width: 0; }
.foot__ring {
  width: 44px; height: 44px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand-ink); overflow: hidden;
  border: 1px solid var(--brand-ring);
}
.foot__ring img { width: 100%; height: 100%; object-fit: cover; }
.foot__initials { font-family: var(--figure); font-weight: 700; font-size: 16px; }
.foot__whotext { min-width: 0; }
.foot__whotext strong { display: block; color: var(--ink); font-size: 16px; font-weight: 700; }
.foot__whotext p { margin: 2px 0 0; font-size: 13.5px; color: var(--soft); }
.foot__h {
  margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--ink);
}
.foot__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.foot__list a {
  color: var(--soft); text-decoration: none; font-size: 14px; font-weight: 600;
  display: inline-block; padding: 3px 0; min-height: 28px;
}
@media (max-width: 719px) {
  .foot__grid { gap: 18px; }
  /* Two links across instead of one under another. A tap target is still
     40px tall, which is what the single column was really buying. */
  .foot__list { grid-template-columns: 1fr 1fr; gap: 4px 14px; }
  .foot__list a { font-size: 13.5px; min-height: 34px; display: flex; align-items: center; }
  .foot__h { margin-bottom: 6px; }
}
.foot__list a:hover, .foot__list a:focus-visible { color: var(--brand-ink); text-decoration: underline; }
.foot__base {
  margin: 22px auto 0; max-width: 1040px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 13px; color: var(--faint); text-align: center;
}
@media (min-width: 720px) {
  .foot { padding-left: 24px; padding-right: 24px; }
  .foot__grid { grid-template-columns: 1.3fr 1fr 1fr 1fr; gap: 28px; align-items: start; }
}

/* ---- the card on a phone ----
   The card is at least 104px now, so the figures are the size they should
   always have been: the owner reported them as too small to read on his
   customers' phones. A shorter band pays for the taller text, so the tile
   does not grow much: at three across it stands 180px against the 156 it
   stood at four, and the whole network still fits one screen. */
@media (max-width: 599px) {
  .bundle__band { aspect-ratio: 16 / 9; }
  .bundle__body { padding: 8px; }
  .bundle__size { font-size: 19px; line-height: 1.1; }
  .bundle__val { font-size: 11.5px; }
  .bundle__price { font-size: 15px; margin-bottom: 8px; padding-top: 4px; }
  .bundle__buy { min-height: 34px; font-size: 13px; border-radius: 8px; }
  .bundle__mark { width: 40px; height: 40px; font-size: 11px; border-width: 1.5px; }
  .bundle__mark--long { font-size: 9px; }
}

/* ---- wider screens ---- */
@media (min-width: 620px) {
  .sheet { align-items: center; padding: 24px; }
  .sheet__box { border-radius: 20px; padding: 24px; }
  .fields--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fields--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pay-cards--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card { padding: 24px; }
}
@media (min-width: 640px) {
  .head { padding-top: calc(46px + env(safe-area-inset-top, 0px)); padding-bottom: 40px; }
  .head__ring { width: 108px; height: 108px; }
  .head__name { font-size: 32px; }
  .head__desc { font-size: 16px; }
  .tabs__inner { justify-content: center; gap: 4px; }
  .tab { flex: 0 0 auto; flex-direction: row; gap: 8px; padding: 15px 18px 14px; font-size: 15px; }
  .tab svg { width: 19px; height: 19px; }
  .tab::after { left: 12px; right: 12px; }
}
@media (min-width: 720px) {
  .wrap { padding: 26px 24px 64px; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
  .bundle__body { padding: 10px; }
  .bundle__size { font-size: 20px; }
  .bundle__price { font-size: 15px; }
  .bundle__buy { min-height: 36px; font-size: 14px; }
  .vgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
  .voucher__body { padding: 12px; }
  .voucher__exam { font-size: 26px; }
}
@media (min-width: 900px) {
  .reviews { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); align-items: start; gap: 20px; }
  .reviews__form { position: sticky; top: 80px; }
}
@media (min-width: 980px) {
  .grid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }
  .vgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .bundle:hover, .voucher:hover { transform: none; }
}

/* ---- announcements ----
   What this shop is telling its own customers. The shop's own colour, the
   shop's own words, and nothing from anywhere else. A notice stays until the
   seller takes it down; only a popup can be put away, and only on this
   device, because a shop's customer has no account to remember it against.
   Nothing is drawn when there is nothing to say. */
.anns { display: grid; gap: 10px; margin: 0 0 16px; }
.anns[hidden] { display: none; }
.ann {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px;
  border: 1px solid var(--line); border-left: 3px solid var(--line-strong);
  border-radius: 12px; background: var(--card); box-shadow: var(--shadow);
}
.ann--care { border-left-color: var(--amber); }
.ann--important { border-left-color: var(--brand-ink); }
.ann__icon { flex: none; width: 20px; height: 20px; margin-top: 2px; color: var(--brand-ink); }
.ann__icon svg { width: 20px; height: 20px; display: block; }
.ann--care .ann__icon { color: var(--amber); }
.ann__text { flex: 1 1 auto; min-width: 0; }
.ann__flag { display: block; margin-bottom: 2px; font-size: 12px; font-weight: 700; color: var(--amber); }
.ann--important .ann__flag { color: var(--brand-ink); }
.ann__title { display: block; font-size: 14.5px; font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.ann__body { margin: 3px 0 0; font-size: 14px; line-height: 1.55; color: var(--soft); white-space: pre-line; overflow-wrap: anywhere; }

.annpop {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(22, 24, 29, 0.5);
}
.annpop[hidden] { display: none; }
.annpop__box { width: 100%; max-width: 400px; padding: 20px; background: var(--card); border-radius: var(--radius); box-shadow: var(--shadow); }
.annpop__flag { display: block; margin-bottom: 4px; font-size: 12px; font-weight: 700; color: var(--amber); }
.annpop--important .annpop__flag { color: var(--brand-ink); }
.annpop__title { margin: 0 0 6px; font-size: 17px; color: var(--ink); overflow-wrap: anywhere; }
.annpop__body { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--soft); white-space: pre-line; overflow-wrap: anywhere; }
.annpop__foot { display: flex; justify-content: flex-end; margin-top: 16px; }
.annpop__ok {
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit; font-size: 15px; font-weight: 600;
  min-height: 44px; padding: 0 18px; border: 0; border-radius: 999px; background: var(--brand); color: #fff;
}

/* ---- An order taking longer, on Order status ---------------------------
   The question to the customer who paid, in the shop's own colour, with
   Continue and wait and Request refund side by side on a desk and stacked
   on a phone. Fields are 16px and buttons 50px (.input, .btn). */
.wip { margin-top: 20px; padding: 16px; border-radius: 14px; background: var(--brand-soft); border: 1px solid var(--brand-ring); overflow-wrap: anywhere; }
.wip h3 { margin: 0; font-size: 16.5px; font-weight: 700; line-height: 1.45; color: var(--ink); }
.wip .hint { margin-top: 8px; }
.wip__btns { display: flex; flex-wrap: wrap; gap: 0 10px; }
.wip__btns .btn { flex: 1 1 180px; width: auto; min-width: 0; }
.wip__refund { margin-top: 4px; }
textarea.input.wip__why { min-height: 72px; }

/* ---- The five seconds before an order is placed -------------------------
   Nothing has been sent while this is on screen: the clock counts down, the
   rows are what is about to be bought, and Cancel order calls it off. It is
   built by js/reseller-shop.js under the button that was pressed, in the
   shop's own colour. Fields stay 16px and the button 50px (.btn). */
.hold {
  margin-top: 16px; padding: 16px; border-radius: 14px;
  background: var(--brand-soft); border: 1px solid var(--brand-ring); overflow-wrap: anywhere;
}
.hold__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.hold__head strong { font-size: 15.5px; font-weight: 700; color: var(--ink); }
.hold__clock {
  min-width: 58px; min-height: 32px; padding: 3px 12px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--card); border: 1px solid var(--line);
  font-family: var(--figure); font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink);
}
.hold__clock.is-urgent { color: var(--amber); border-color: var(--amber); }
.hold.is-going .hold__clock { color: var(--soft); }
.hold__lead { margin: 9px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--soft); }
.hold__facts { margin: 12px 0 0; display: grid; gap: 7px; }
.hold__facts > div {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 16px;
}
.hold__facts dt { font-size: 13.5px; color: var(--soft); }
.hold__facts dd {
  margin: 0; font-size: 15px; font-weight: 700; color: var(--ink);
  text-align: right; overflow-wrap: anywhere;
}
.hold__acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px; }
.hold__acts .btn { flex: 1 1 170px; width: auto; min-width: 0; margin: 0; }
.hold__note { flex: 1 1 200px; font-size: 13.5px; line-height: 1.55; color: var(--soft); }

/* ---- About ----
   The seller's own details, read by a customer deciding whether to buy from
   a link someone sent them. */
.about { display: grid; gap: 18px; }
.about__card {
  display: flex; align-items: center; gap: 16px; padding: 20px;
  border-radius: var(--radius); background: var(--brand-soft); border: 1px solid var(--brand-ring);
}
.about__mark {
  width: 64px; height: 64px; flex: none; border-radius: 16px; overflow: hidden;
  display: grid; place-items: center; background: var(--brand-ink); color: #fff;
}
.about__mark img { width: 100%; height: 100%; object-fit: cover; display: block; }
.about__initials { font-family: var(--figure); font-size: 24px; font-weight: 700; line-height: 1; }
.about__who { min-width: 0; }
.about__who h2 { margin: 0; font-size: 22px; font-weight: 750; overflow-wrap: anywhere; }
.about__who p { margin: 6px 0 0; font-size: 14.5px; color: var(--soft); white-space: pre-line; overflow-wrap: anywhere; }
.about__h { margin: 4px 0 0; font-size: 16px; font-weight: 700; }
.about__list {
  margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); overflow: hidden;
}
.about__row { display: flex; gap: 12px; padding: 14px 16px; border-top: 1px solid var(--line); }
.about__row:first-child { border-top: 0; }
.about__row > svg { width: 20px; height: 20px; flex: none; margin-top: 2px; color: var(--soft); }
.about__row dt { font-size: 12.5px; font-weight: 600; color: var(--soft); }
.about__row dd { margin: 2px 0 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
.about__row dd a { color: var(--brand-ink); text-decoration: none; }
.about__row dd a:hover { text-decoration: underline; }
.about__ask {
  display: grid; gap: 14px; padding: 18px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--card);
}
.about__askwords h3 { margin: 0; font-size: 16px; font-weight: 700; }
.about__askwords p { margin: 4px 0 0; font-size: 14.5px; color: var(--soft); }
.about__askacts { display: flex; flex-wrap: wrap; gap: 10px; }
.about__askacts .btn { flex: 1 1 140px; margin: 0; }
@media (min-width: 620px) {
  .about__ask { grid-template-columns: 1fr auto; align-items: center; }
  .about__askacts .btn { flex: 0 0 auto; }
}

/* ---- checking a number ---- */
.ck_note { margin: 16px 0 0; font-size: 15px; font-weight: 600; }
.ck_counts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.ck_chip {
  padding: 6px 12px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
  background: var(--page); color: var(--soft); border: 1px solid var(--line);
}
.ck_chip.is-ok { background: var(--green-bg); color: var(--green); border-color: transparent; }
.ck_chip.is-warn { background: var(--amber-bg); color: var(--amber); border-color: transparent; }
.ck_lists { display: grid; gap: 12px; margin-top: 14px; }
.ck_list { border: 1px solid var(--line); border-radius: 12px; padding: 14px; }
.ck_list.is-ok { border-color: #bde8cc; background: #f6fcf8; }
.ck_list.is-warn { border-color: #f4dfa0; background: #fffdf6; }
.ck_list h3 { margin: 0 0 8px; font-size: 14px; }
.ck_list ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.ck_list li { font-family: var(--mono); font-size: 14.5px; }
.ck_list p { margin: 0; font-size: 13.5px; color: var(--soft); }
#ck_send { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
@media (min-width: 620px) { .ck_lists { grid-template-columns: 1fr 1fr; } }

/* ---- chat with the shop ----
   A button in the corner, and a panel above it. On a phone the panel fills
   the screen, because anything smaller is unusable with a keyboard up. */
/* ---- the chat button ----
   The owner, 13 Oct 2026: "their chat with their customer pattern is long
   it's very very long there and i just want it in the way of a chat button
   which will be blinking". A shop called Able God Data Hub made this
   275px wide and it sat across the bundles.

   A circle with the speech mark in it, and a ring that breathes out of it
   every few seconds so the eye finds it without the name being spelled
   out. The name is still on the button for a screen reader, and still in
   the panel's own header once it opens. */
.chatfab {
  position: fixed; right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); z-index: 55;
  appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; min-height: 56px; padding: 0;
  border: 0; border-radius: 50%; color: #fff; background: var(--brand-ink);
  box-shadow: 0 10px 26px rgba(16, 24, 40, 0.28);
  transition: transform .2s cubic-bezier(.22, 1.2, .36, 1);
}
.chatfab:active { transform: scale(.94); }
.chatfab svg { width: 24px; height: 24px; flex: none; }
/* The name is read out, not drawn. */
.chatfab #chatFabText {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* The ring. It leaves the button every five seconds rather than pulsing
   without pause, which reads as a notification instead of a loading state,
   and it costs one transform on its own layer. */
.chatfab::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--brand-ink);
  animation: chatfab-ping 5s cubic-bezier(.22, .8, .3, 1) infinite;
  pointer-events: none;
}
@keyframes chatfab-ping {
  0% { opacity: .55; transform: scale(1); }
  28% { opacity: 0; transform: scale(1.5); }
  100% { opacity: 0; transform: scale(1.5); }
}
@media (prefers-reduced-motion: reduce) {
  .chatfab::before { animation: none; opacity: 0; }
}
.chatfab__dot {
  position: absolute; top: 2px; right: 2px; width: 13px; height: 13px; border-radius: 50%;
  background: #f04438; border: 2px solid #fff;
}
body.has-pending .chatfab { bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }

/* The chat covers the screen, exactly as it always did. */
.chatbox {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  background: var(--card);
}
/* ONLY while the keyboard is up does it follow the visible part of the
   window instead, so the composer rides on top of the keyboard rather than
   being pushed under it.
   
   Both numbers are needed. Height alone left the box short of the bottom
   of the screen, with the page showing underneath it, which is what the
   owner filmed on 13 Oct 2026. The visible part of the window also starts
   partway down it, so the top is read as well. */
.chatbox.is-typing {
  top: var(--chat-top, 0px);
  bottom: auto;
  height: var(--chat-vh, 100%);
  /* Safari's address pill and the bar above the keyboard are see-through,
     and they sit just below the box. The page used to show through them
     (owner's recording, 21 Oct 2026: network tiles under the composer). A
     solid shadow the colour of the chat fills everything around the box,
     so only the chat is ever behind the browser's own glass. */
  box-shadow: 0 0 0 100vh var(--card);
}
.chatbox__head {
  display: flex; align-items: center; gap: 12px;
  padding: calc(14px + env(safe-area-inset-top, 0px)) 16px 14px;
  background: var(--brand-ink); color: #fff;
}
.chatbox__who { min-width: 0; }
.chatbox__who strong { display: block; font-size: 16px; overflow-wrap: anywhere; }
.chatbox__who span { font-size: 13px; opacity: 0.85; }
.chatbox__close {
  appearance: none; -webkit-appearance: none; cursor: pointer; margin-left: auto;
  width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.18); border: 1px solid rgba(255, 255, 255, 0.4); color: #fff;
}
.chatbox__close svg { width: 18px; height: 18px; }
.chatbox__intro { padding: 18px 16px; overflow-y: auto; }
.chatbox__intro p { margin: 0 0 14px; font-size: 14.5px; color: var(--soft); }
.chatbox__body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.chatbox__msgs {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px;
  display: flex; flex-direction: column; gap: 10px; background: var(--page);
}
.chatmsg { max-width: 80%; align-self: flex-start; }
.chatmsg p {
  margin: 0; padding: 10px 14px; border-radius: 16px 16px 16px 4px;
  background: #fff; border: 1px solid var(--line); font-size: 15px; overflow-wrap: anywhere;
}
.chatmsg span { display: block; margin-top: 4px; font-size: 11.5px; color: var(--faint); }
.chatmsg.is-mine { align-self: flex-end; }
.chatmsg.is-mine p { background: var(--brand-ink); border-color: transparent; color: #fff; border-radius: 16px 16px 4px 16px; }
.chatmsg.is-mine span { text-align: right; }
/* Somebody is writing back. Three dots in a bubble on the shop's side,
   shown only while the server says a reply really is coming: the owner,
   11 Oct 2026, wanted "a live texting back animation to see that someone
   is texting back", and a box that shows dots when nothing is coming is
   worse than one that shows nothing at all. */
.chatmsg--typing p.chat-dots {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 13px 14px; min-height: 42px;
}
.chat-dots i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--soft, #8b9099); display: block;
  animation: chat-dot 1.2s ease-in-out infinite;
}
.chat-dots i:nth-child(2) { animation-delay: .18s; }
.chat-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes chat-dot {
  0%, 60%, 100% { transform: translateY(0); opacity: .45; }
  30% { transform: translateY(-4px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-dots i { animation: none; opacity: .6; }
}

/* Ending a chat, and clearing it off this device. The shopkeeper has had
   "Mark finished" all along; the customer had nothing but an X that hides
   the box. */
.chatbox__more {
  appearance: none;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  opacity: .85;
  /* The first auto takes the free space, so this goes right and the X
     sits beside it rather than both fighting for the same gap. */
  margin-left: auto;
}

.chatbox__more svg { width: 18px; height: 18px; }
.chatbox__more:hover { background: rgb(255 255 255 / 18%); opacity: 1; }

.chatbox__menu {
  position: absolute;
  /* Under the header, which is taller on a phone with a notch. */
  top: calc(56px + env(safe-area-inset-top, 0px));
  right: 46px;
  z-index: 5;
  min-width: 220px;
  padding: 6px;
  border-radius: 14px;
  background: var(--card, #fff);
  border: 1px solid var(--line, #e6e9ef);
  box-shadow: 0 18px 40px -18px rgb(11 18 32 / 35%);
  color: var(--ink, #0b1220);
}

.chatbox__menu button {
  appearance: none;
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14.5px;
  padding: 11px 12px;
  min-height: 44px;
  border-radius: 10px;
  cursor: pointer;
  color: inherit;
}

.chatbox__menu button:hover { background: var(--page, #f3f5f9); }

/* The honest part, said where the choice is made and not in a help page. */
.chatbox__menunote {
  margin: 4px 2px 2px;
  padding: 0 10px 6px;
  font-size: 12px;
  color: var(--soft, #6b7280);
}

.chatbox__closed { margin: 0; padding: 10px 16px; font-size: 13.5px; color: var(--soft); background: var(--page); }
.chatbox__write {
  display: flex; align-items: flex-end; gap: 10px;
  /* Through --lb-safe-b, never env() directly: the raw inset is the whole
     home-indicator height and put a white band under the composer. */
  padding: 12px 16px calc(12px + var(--lb-safe-b, 0px));
  border-top: 1px solid var(--line); background: var(--card);
}
/* With the keyboard up there is no home indicator to clear. */
.chatbox.is-typing .chatbox__write { padding-bottom: 12px; }
.chatbox__write textarea {
  flex: 1 1 auto; min-height: 48px; max-height: 140px; resize: none; overflow-y: auto;
  padding: 13px 14px; border-radius: 14px; border: 1px solid var(--line-strong);
  font: inherit; font-size: 16px; line-height: 1.3;
}
.chatbox__send {
  appearance: none; -webkit-appearance: none; cursor: pointer; flex: none;
  width: 48px; height: 48px; border-radius: 50%; border: 0; display: grid; place-items: center;
  background: var(--brand-ink); color: #fff;
}
.chatbox__send svg { width: 20px; height: 20px; }
.chatbox .err { margin: 0; padding: 0 16px 12px; }

@media (min-width: 620px) {
  .chatbox {
    inset: auto 16px 86px auto; width: 380px; height: min(560px, calc(100vh - 140px));
    border-radius: 18px; overflow: hidden; box-shadow: 0 24px 60px rgba(16, 24, 40, 0.28);
    border: 1px solid var(--line);
  }
  .chatbox__head { padding: 14px 16px; }
}

/* ---- what a chat message can carry ---- */
.chatmsg.has-piece p { margin-top: 6px; }
.chatmsg__sticker { display: block; width: 92px; height: 92px; color: var(--brand-ink); }
.chatmsg.is-mine .chatmsg__sticker { color: var(--brand-deep); margin-left: auto; }
.chatmsg__sticker svg { width: 100%; height: 100%; }
.chatmsg__pic { display: block; }
.chatmsg__pic img { display: block; max-width: 220px; border-radius: 14px; }
.chatmsg__audio { width: 220px; max-width: 100%; }
.chatmsg__video { max-width: 220px; border-radius: 14px; display: block; }
.chatmsg__file {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 14px;
  background: #fff; border: 1px solid var(--line); text-decoration: none; color: var(--ink); font-size: 14.5px;
}
.chatmsg__file small { margin-left: auto; color: var(--brand-ink); font-weight: 700; }
.chatbox__staged { margin: 0; padding: 8px 16px; font-size: 13px; color: var(--soft); background: var(--page); }
.chatbox__stickers {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px;
  padding: 10px 16px; border-top: 1px solid var(--line); background: var(--card);
}
.chatbox__stickers button {
  appearance: none; -webkit-appearance: none; cursor: pointer; border: 0; background: none;
  padding: 6px; border-radius: 10px; color: var(--brand-ink);
}
.chatbox__stickers button:hover { background: var(--brand-soft); }
.chatbox__stickers svg { width: 100%; height: auto; display: block; }
.chatbox__icon {
  appearance: none; -webkit-appearance: none; cursor: pointer; flex: none;
  width: 42px; height: 42px; border-radius: 50%; border: 1px solid var(--line-strong);
  display: grid; place-items: center; background: var(--card); color: var(--soft);
}
.chatbox__icon svg { width: 19px; height: 19px; }
.chatbox__icon.is-on { background: var(--red-bg); color: var(--red); border-color: transparent; }
@media (max-width: 380px) { .chatbox__stickers { grid-template-columns: repeat(4, 1fr); } }

/* ---- one of the shop's own pages ---- */
.policy {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 22px;
}
.policy h2 { margin: 0 0 6px; font-size: 22px; font-weight: 750; }
.policy h3 { margin: 22px 0 6px; font-size: 16px; font-weight: 700; }
.policy p { margin: 0 0 12px; font-size: 15.5px; line-height: 1.65; color: var(--soft); overflow-wrap: anywhere; }
.policy p:last-child { margin-bottom: 0; }
.policy__wait { color: var(--faint); }
.policy__when { margin: 10px 0 0; font-size: 13px; color: var(--faint); }
.policy__more { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; margin: 16px 0 0; font-size: 14px; color: var(--soft); }
.policy__more a { color: var(--brand-ink); text-decoration: none; font-weight: 600; margin-right: 12px; }
.policy__more a:hover { text-decoration: underline; }

/* Shop-chat: a message the shop edited or took back. */
.chatmsg.is-deleted p.chatmsg__gone { background: transparent; border: 1px dashed var(--line, #d7dbe3); color: var(--faint, #667085); font-style: italic; }
.chatmsg.is-mine.is-deleted p.chatmsg__gone { background: transparent; color: rgba(255,255,255,.85); border-color: rgba(255,255,255,.5); }

/* ---- Background media behind the shop hero (migrate-194) ----------------
   The owner sets a default for every shop; a reseller may choose their own.
   A picture or a clip sits behind the name and buttons, with a scrim over it
   so the white text keeps its contrast on any image. ---- */
.hero { position: relative; isolation: isolate; overflow: hidden; }
.hero__inner { position: relative; z-index: 2; }
.hero__media { position: absolute; inset: 0; z-index: 0; background-size: cover; background-position: center; pointer-events: none; }
.hero__media video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The scrim keeps the brand colour present and the copy readable. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(4, 22, 52, 0.35) 0%, rgba(4, 22, 52, 0.62) 100%),
    radial-gradient(120% 90% at 50% 30%, rgba(4, 22, 52, 0) 35%, rgba(4, 22, 52, 0.5) 100%);
}

/* ---- Hero background controls -------------------------------------------
   Hold the picture, or turn the sound on. Small and out of the way at the
   corner of the hero, never over the shop's name or its buttons. ---- */
.hero__ctrls {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 3;
  display: flex;
  gap: 8px;
}
/* Icons, not words (owner, 9 Oct 2026). Round, 38px, which is still a
   comfortable tap and about a third of the room the worded pills took
   over the shop's own name. The words live in aria-label and title. */
.hero__ctrl {
  appearance: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: rgba(8, 24, 54, 0.45);
  color: #fff;
  border-radius: 999px;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.hero__ctrl svg { width: 18px; height: 18px; fill: currentColor; }
.hero__ctrl:hover { background: rgba(8, 24, 54, 0.62); }
.hero__ctrl:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (max-width: 560px) {
  .hero__ctrls { right: 10px; bottom: 10px; gap: 6px; }
  /* 36px is still over the 32px a thumb needs, and two of them take less
     width than one of the old worded buttons. */
  .hero__ctrl { width: 36px; height: 36px; }
  .hero__ctrl svg { width: 17px; height: 17px; }
}

/* A checker card with no codes in stock is still on sale: the order waits
   for the next load. The customer is told before paying, not after. */
.voucher__wait { margin: 0 0 8px; font-size: 11.5px; line-height: 1.35; color: var(--muted, #64748b); }

/* One background fading into the next, for a shop's own pictures and
   clips. The new one is laid over the old and brought up, so the brand
   colour never flashes through between two of them. */
.hero__media { transition: opacity .42s ease; }
.hero__media--in { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .hero__media { transition: none; }
  .hero__media--in { opacity: 1; }
}

/* ---- buy again, sorting and the two tags --------------------------------
   Added 8 Oct 2026. A shop with thirty bundles was one long scroll with
   nothing to steer by, and a returning customer had to find the same
   bundle and type the same number again. */
/* ---- buy again ----
   The owner, 13 Oct 2026: "that place push all the cards down it's not
   making that nice". A heading on its own line and a stacked card under it
   stood about 120px tall, before a single bundle had been seen.

   One line now: the words "Buy again" and the shortcuts beside them on the
   same row, scrolling sideways if there is more than one. Everything it
   said is still on it, read across instead of down. */
.again {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 12px;
  min-width: 0;
}
.again__lead {
  margin: 0;
  flex: 0 0 auto;
  font-size: 12.5px; font-weight: 700; letter-spacing: .01em;
  color: var(--muted, #64748b);
}
.again__row {
  display: flex; gap: 8px; min-width: 0; flex: 1 1 auto;
  overflow-x: auto; scrollbar-width: none;
}
.again__row::-webkit-scrollbar { height: 0; }
.again__pick {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 8px;
  text-align: left; cursor: pointer;
  padding: 7px 12px; border-radius: 999px;
  border: 1px solid var(--line, rgba(15, 23, 42, .12));
  background: #fff; color: inherit; font: inherit;
  white-space: nowrap;
}
.again__pick:hover { border-color: var(--brand, #0164ce); }
.again__what { font-weight: 700; font-size: 13.5px; }
/* The number it went to. Dropped on a narrow phone, where the bundle and
   the price are what the finger is after; it is still on the order itself. */
.again__to { font-size: 12px; color: var(--muted, #64748b); font-variant-numeric: tabular-nums; }
.again__price { font-size: 13px; font-weight: 700; color: var(--brand, #0164ce); }

@media (max-width: 420px) {
  .again__to { display: none; }
}

.sorter { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 13px; }
.sorter label { color: var(--muted, #64748b); }
.sorter select { padding: 7px 10px; border-radius: 10px; border: 1px solid var(--line, rgba(15, 23, 42, .12)); background: #fff; font: inherit; }

.bundle__band { position: relative; }
.bundle__tag {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  padding: 3px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 800;
  letter-spacing: .02em; background: rgba(255, 255, 255, .94); color: #0f172a;
  box-shadow: 0 2px 8px rgba(15, 23, 42, .18);
}
.bundle__tag--hot { color: #b45309; }
.bundle__tag--value { color: #047857; }

/* The way out of the form for somebody who will not pay online. */
.btn--wa { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 10px; background: #25d366; color: #05331a; border: 0; }
.btn--wa svg { width: 18px; height: 18px; }
.btn--wa:hover { filter: brightness(1.04); }

/* The page held still while a sheet or a menu is open. overflow: hidden
   alone is ignored by iOS Safari, which is why the shop behind an order
   sheet still scrolled under the thumb. See js/scroll-lock.js. */
body.scroll-locked { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }
body:not(.scroll-locked) { top: 0 !important; }

/* ============================================================
   Thumbs, 9 Oct 2026.

   Measured every button on a 390px phone: the Buy button on each bundle
   was 67x30 and the two hero controls were 36x36, against the 44px both
   Apple and Google ask for. Thirty four of them on one page, and the Buy
   button is the whole point of the page.

   The buttons are not redrawn. Each grows an invisible hit area to 44,
   so the shop looks exactly as the reseller set it up and the thumb
   stops missing. Only where the pointer is a finger.
   ============================================================ */
@media (pointer: coarse) {
  .bundle__buy,
  .hero__ctrl,
  .voucher__buy { position: relative; }

  .bundle__buy::after,
  .hero__ctrl::after,
  .voucher__buy::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    min-width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
    background: none;
    border: 0;
  }
}

/* ---- finding a shop from the bare address ----
   The domain on its own names no shop, so this is the one useful thing
   that page can offer. Nothing is listed until two letters are typed: it
   answers a name somebody already knows, it is not a directory. */
.findshop {
  display: flex;
  gap: 8px;
  width: min(420px, 100%);
  margin: 18px auto 0;
}

.findshop input {
  flex: 1 1 auto;
  min-width: 0;
  /* 16px, or iOS zooms the whole page the moment it is tapped. */
  font: inherit;
  font-size: 16px;
  padding: 13px 15px;
  border-radius: 14px;
  border: 1px solid var(--line-strong, #d6dbe3);
  background: var(--card, #fff);
  color: inherit;
}

.findshop input:focus {
  outline: none;
  border-color: var(--brand-ink, #0164ce);
}

/* .btn in this stylesheet is a full-width block with a top margin, which
   is right under a form and wrong beside an input: it took the row and
   left the box a stub. The same trap as the chat Send button. */
.findshop .btn {
  flex: 0 0 auto;
  width: auto;
  margin-top: 0;
  min-height: 48px;
  padding: 0 22px;
}

/* This stylesheet has no visually-hidden of its own, so the label for the
   search box was simply on screen. */
.findshop .visually-hidden,
.state .visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.findshop__note { margin: 10px 0 0; font-size: 13.5px; color: var(--soft, #6b7280); }

.findshop__list {
  list-style: none;
  margin: 12px auto 0;
  padding: 0;
  width: min(420px, 100%);
  display: grid;
  gap: 8px;
}

.findshop__hit {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: 14px;
  border: 1px solid var(--line, #e6e9ef);
  background: var(--card, #fff);
  text-decoration: none;
  color: inherit;
  font-weight: 600;
  text-align: left;
}

.findshop__hit:hover { border-color: var(--brand-ink, #0164ce); }

.findshop__hit img,
.findshop__letter {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  object-fit: cover;
  flex: 0 0 auto;
}

.findshop__letter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--brand-soft, #e8f1fd);
  color: var(--brand-ink, #0164ce);
  font-weight: 700;
}
