/* ── Kobopay ──────────────────────────────────────────────────────────
 * Ported from the kobo demo, 15 September 2026. The premium pack.
 *
 * Structurally distinct from the other two and it must stay that way: Kobopay
 * navigates from a horizontal nav inside the top bar, with the sidebar hidden
 * at desktop and demoted to a mobile drawer. Meridian navigates from a
 * sidebar, Techsub from a grouped one. If two packs converge on one layout,
 * one of them has stopped existing and the tier stops being real.
 *
 * The tenant's theme record emits the --t-* tokens. This block is the only
 * place that knows how Kobopay's palette maps onto them, and the ramp is
 * COMPUTED: an owner picking one violet cannot be asked to pick six.
 * ------------------------------------------------------------------ */
:root {
  --brand:     var(--t-brand);
  --brand-600: color-mix(in srgb, var(--t-brand) 86%, black);
  --brand-700: color-mix(in srgb, var(--t-brand) 72%, black);
  --brand-800: color-mix(in srgb, var(--t-brand) 60%, black);
  --brand-50:  color-mix(in srgb, var(--t-brand)  6%, white);
  --brand-100: color-mix(in srgb, var(--t-brand) 14%, white);
  --brand-200: color-mix(in srgb, var(--t-brand) 26%, white);

  --amber:     var(--t-accent);
  --amber-600: color-mix(in srgb, var(--t-accent) 88%, black);

  /* Computed server side against the real colour, never guessed. An owner
     picking a pale accent would otherwise get pale text on pale. */
  --amber-ink: var(--t-accent-ink);
  --brand-ink: var(--t-brand-ink);

  --r-sm: calc(10px * var(--t-radius-scale, 1));
  --r-md: calc(14px * var(--t-radius-scale, 1));
  --r-lg: calc(22px * var(--t-radius-scale, 1));
}

/* ── the locks, from docs/02-TEMPLATES-HANDOVER.md ──────────────────── */

/* An author display rule beats the UA stylesheet, so without this every
   hidden tab panel renders at once. */
[hidden] { display: none !important; }

/* Both flex and grid children default to min-width:auto, so a child refuses to
   shrink and pushes the page wider than the viewport. */
.shell > *, .row > *, .grid > *, .stack > *, .appnav > * { min-width: 0; }

/*
 * RIF-AFRICA-TEMPLATE-ID: rif-tpl-kob-1e93a6bd
 * Copyright 2026 RIF Africa. This template is licensed to the purchaser of the RIF VTU Platform and to nobody else. Reproduction, resale or redistribution without a licence is prohibited. https://rif.africa
 */
/*
 * Kobo - VTU template, base tier, variety 3
 *
 * Positioning: bold consumer. Meridian is the restrained one and Techsub is the
 * dense navy one, so this is the loud one, aimed at a buyer whose customers are
 * everyday retail: market traders, students, agents topping up on a phone.
 *
 * Benchmarked on OPay, PalmPay and Kuda, because a saturated single brand colour
 * carried across the whole page is what this audience already recognises as a
 * money app. Two failure modes are avoided deliberately:
 *
 *   - the brand colour is a FLAT solid, never a gradient. A violet-to-cyan mesh
 *     reads as machine-generated and it is the reason the Obsidian variety was
 *     thrown out.
 *   - the structure stays conventional. Differentiation goes into density,
 *     radius, colour confidence and motion, not into a new visual language.
 *
 * Structurally different from the other two on purpose, so this is a third
 * template rather than a recolour:
 *   - the signed-in app navigates from a TOP bar on desktop, not a sidebar. The
 *     sidebar only exists as the mobile drawer.
 *   - the landing hero is a full-bleed brand-colour panel with a stats card
 *     overlapping its bottom edge.
 *   - service icons are solid-filled circles, not pastel tinted squares.
 *
 * Self-contained. Only the reset and the demo ribbon come from shared/base.css.
 */

/* ======================================================================
 * Tokens
 * ==================================================================== */
:root {
  --rif-template-id: "rif-tpl-kob-1e93a6bd";
  /* Flat violet carries the brand. Amber carries the action, because a
     yellow button on violet is the highest-contrast pairing available and
     it is what the benchmark apps do with their own accent. */
  --ink:        #1B1033;
  --ink-2:      #453B5C;
  --muted:      #6E6685;
  --faint:      #9A93AD;
  --line:       #E7E3F0;
  --line-soft:  #F1EEF8;

  --bg:         #FFFFFF;
  --surface:    #F8F6FD;
  --surface-2:  #F1EDFB;

  --green:      #12A150;
  --green-tint: #E6F6EC;
  --amber-tint: #FEF4E0;
  --danger:     #DC2626;
  --danger-tint:#FCEAEA;

  /* Service colour coding. Solid fills, one hue per service, so the tile grid
     is scannable at a glance the way an app home screen is. */
  --s-airtime:  #7C3AED;
  --s-data:     #2563EB;
  --s-cable:    #DB2777;
  --s-power:    #EA580C;
  --s-exam:     #0891B2;
  --s-print:    #9333EA;
  --s-wallet:   #059669;
  --s-transfer: #4F46E5;
  --s-refer:    #CA8A04;
  --s-more:     #64748B;

  --r-xs:  10px;
  --r-sm:  14px;
  --r:     18px;
  --r-lg:  24px;
  --r-xl:  32px;
  --r-pill: 999px;

  --shadow-xs: 0 1px 2px rgba(27, 16, 51, .06);
  --shadow-sm: 0 2px 6px rgba(27, 16, 51, .06);
  --shadow:    0 6px 24px rgba(27, 16, 51, .08);
  --shadow-lg: 0 24px 60px -18px rgba(27, 16, 51, .28);
  --shadow-brand: 0 12px 28px -10px rgba(109, 40, 217, .55);
  --shadow-amber: 0 12px 26px -10px rgba(245, 166, 35, .6);

  --wrap: 1200px;
  --topbar: 72px;
  --drawer: 288px;

  /* ⚠⚠ THE OWNER'S CHOICE FIRST, THIS PACK'S OWN FONT AS THE FALLBACK.
     This said `'Outfit'` and nothing else, so `Theme.font_body` moved the
     headings (set from `--t-font-display` in `shared/base.css`) and left
     every paragraph on Outfit. A picker that changes half the page reads as
     a rendering fault, not as a setting. Outfit stays second so the pack
     still reads as itself when no preference has been expressed. */
  --font: var(--t-font-body), 'Outfit', -apple-system, BlinkMacSystemFont,
          'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

/* ======================================================================
 * Dark
 *
 * ⚠⚠ PART OF WHAT THE PREMIUM TIER IS. Kobopay costs N10,000 more than the
 * two included packs and that has to buy something a customer can point at.
 * This and the charts below are it. Do not port either down — there is a
 * test (`test_only_the_premium_pack_carries_the_receipt_actions`) that
 * fails if the paid pack stops being distinguishable.
 *
 * ⚠ The LIGHT rendering must be byte-identical to what shipped. Nothing
 * above was restyled; the 29 literal `background: #fff` declarations became
 * `var(--bg)`, and `--bg` is `#FFFFFF`, so every existing visitor sees
 * exactly the page they saw yesterday. Only this block is new behaviour,
 * and only for a reader who asked for it.
 *
 * ⚠ ATTRIBUTE, NOT `prefers-color-scheme`. The media query alone would
 * override a reader who deliberately chose light on a phone set to dark,
 * with no way back. `shared/_theme_boot.html` reads the system preference
 * ONCE as the initial default and then the stored choice wins forever.
 *
 * ⚠⚠ THE OWNER'S BRAND COLOUR IS NOT TOUCHED. `--t-brand` is what they
 * chose and what their customers recognise; a pack that recolours it after
 * dark has stopped being their shop. Only the surfaces, the ink and the
 * tints move, and the tints are re-mixed against the dark page rather than
 * against white — mixing a brand with white on a dark surface produces the
 * pale wash that makes most retrofitted dark modes look broken.
 * ==================================================================== */
:root[data-theme="dark"] {
  --ink:        #F3F0FB;
  --ink-2:      #CDC5E0;
  --muted:      #9C93B3;
  --faint:      #78708F;
  --line:       #332A4A;
  --line-soft:  #271E3C;

  --bg:         #1A1230;
  --surface:    #120B22;
  --surface-2:  #241A3C;

  /* Re-mixed against the page, not against white. */
  --brand-50:  color-mix(in srgb, var(--t-brand)  9%, #120B22);
  --brand-100: color-mix(in srgb, var(--t-brand) 17%, #120B22);
  --brand-200: color-mix(in srgb, var(--t-brand) 30%, #120B22);

  /* ⚠ Lightened, not reused. #12A150 and #DC2626 are tuned to carry on
     white; on #120B22 they fall below 4.5:1 and a failed order stops
     reading as a failed order. */
  --green:      #35D07F;
  --green-tint: color-mix(in srgb, #35D07F 16%, #120B22);
  --amber-tint: color-mix(in srgb, var(--t-accent) 18%, #120B22);
  --danger:     #FF6B6B;
  --danger-tint:color-mix(in srgb, #FF6B6B 16%, #120B22);

  /* A shadow on a dark surface is not a lighter shadow — it is a darker one
     with a hairline of light on top, which is what gives a dark card an
     edge at all. */
  --shadow-xs: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, .55);
  --shadow:    0 8px 28px rgba(0, 0, 0, .6);
  --shadow-lg: 0 28px 64px -18px rgba(0, 0, 0, .8);
}

/* The handful of places a token swap is not enough. Each one is here
   because it paints ON the brand panel, where the surface does not change
   between themes but the thing sitting on it does. */
:root[data-theme="dark"] .appnav a.is-active {
  /* Was a white pill carrying dark violet text. The pill follows the page
     into the dark; the text cannot follow it, or it is dark on dark. */
  background: var(--bg);
  color: var(--ink);
}
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .tbl tbody tr:hover {
  /* The hairline that separates one dark surface from the next. Borders do
     the work that shadows do in the light theme. */
  border-color: var(--line);
}
:root[data-theme="dark"] img:not([src$=".svg"]) {
  /* A logo uploaded as a white-background PNG is a bright rectangle in a
     dark sidebar. Nothing can be done about the pixels, but it does not
     have to glow. */
  filter: brightness(.94);
}

body {
  font-family: var(--font);
  color: var(--ink);
  background: var(--bg);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Every flex and grid child defaults to min-width:auto, refuses to shrink below
   its content, and pushes the page wider than the viewport. This is the single
   most common cause of horizontal overflow on phones, so it is turned off once,
   globally, rather than patched per component. */
.wrap > *, .grid > *, .row > *, .stack > *, .metrics > *, .filters > *,
[class*="__in"] > * { min-width: 0; }

/* An author `display` rule beats the UA stylesheet's `[hidden] { display: none }`,
   so .rates and .card panels stayed visible when the tab switcher hid them.
   Assert it once here rather than per component. */
[hidden] { display: none !important; }

h1, h2, h3, h4 { line-height: 1.15; letter-spacing: -0.02em; font-weight: 700; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: 20px;
}

/* ======================================================================
 * Buttons
 * ==================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 13px 24px;
  border-radius: var(--r-pill);
  font-weight: 600;
  font-size: 15px;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  transition: transform .16s ease, box-shadow .2s ease, background-color .2s ease,
              color .2s ease, border-color .2s ease;
}

.btn svg { width: 18px; height: 18px; flex: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .6; cursor: not-allowed; transform: none; }

.btn--lg { padding: 16px 30px; font-size: 16.5px; }
.btn--sm { padding: 9px 16px; font-size: 14px; gap: 7px; }
.btn--block { display: flex; width: 100%; }

.btn--brand { background: var(--brand); color: #fff; box-shadow: var(--shadow-brand); }
.btn--brand:hover { background: var(--brand-600); }

.btn--amber { background: var(--amber); color: var(--amber-ink); box-shadow: var(--shadow-amber); }
.btn--amber:hover { background: var(--amber-600); }

.btn--ink { background: var(--ink); color: #fff; }
.btn--ink:hover { background: #2B1B4D; }

.btn--line {
  background: var(--bg);
  color: var(--ink);
  border: 1.5px solid var(--line);
}
.btn--line:hover { border-color: var(--brand-200); background: var(--brand-50); }

.btn--onbrand {
  background: rgba(255, 255, 255, .14);
  color: #fff;
  border: 1.5px solid rgba(255, 255, 255, .38);
  backdrop-filter: none;
}
.btn--onbrand:hover { background: rgba(255, 255, 255, .24); }

.btn--ghost { background: transparent; color: var(--brand); }
.btn--ghost:hover { background: var(--brand-50); }

/* Spinner for the simulated submits. */
.btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn.is-loading svg { opacity: 0; }
.btn.is-loading::after {
  content: '';
  position: absolute;
  width: 19px; height: 19px;
  border: 2.5px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin .7s linear infinite;
  color: #fff;
}
.btn--amber.is-loading::after, .btn--line.is-loading::after { color: var(--ink); }
@keyframes spin { to { transform: rotate(360deg); } }

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 42px; height: 42px;
  border-radius: var(--r-pill);
  color: var(--ink-2);
  position: relative;
  transition: background-color .18s ease, color .18s ease;
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.icon-btn svg { width: 20px; height: 20px; }
.icon-btn .dot {
  position: absolute; top: 9px; right: 10px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--danger); border: 2px solid #fff;
}

/* ======================================================================
 * Logo
 * ==================================================================== */
.logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  font-weight: 800;
  font-size: 21px;
  letter-spacing: -0.03em;
  color: inherit;
}

.logo__mark {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 12px;
  background: var(--amber);
  color: var(--amber-ink);
  font-size: 19px;
  font-weight: 800;
  flex: none;
  transform: rotate(-6deg);
}

.logo--ink .logo__mark { background: var(--brand); color: #fff; }

/* ======================================================================
 * Tags and pills
 * ==================================================================== */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}
.tag svg { width: 13px; height: 13px; }
.tag--green  { background: var(--green-tint); color: #0B7A3C; }
.tag--amber  { background: var(--amber-tint); color: #92610A; }
.tag--grey   { background: var(--surface-2); color: var(--muted); }
.tag--red    { background: var(--danger-tint); color: var(--danger); }
.tag--brand  { background: var(--brand-100); color: var(--brand-700); }

.pill-note {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 16px 7px 8px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .26);
  color: #fff;
  font-size: 13.5px;
  font-weight: 500;
}
.pill-note b {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  background: var(--amber);
  color: var(--amber-ink);
  font-size: 12px;
  font-weight: 700;
}

/* ======================================================================
 * Landing header
 * ==================================================================== */
.site-head {
  position: sticky;
  top: 0;
  z-index: 200;
  background: var(--brand);
  color: #fff;
  transition: box-shadow .25s ease, background-color .25s ease;
}
.site-head.is-stuck { box-shadow: 0 6px 24px rgba(27, 16, 51, .22); background: var(--brand-600); }

.site-head__in {
  display: flex;
  align-items: center;
  gap: 20px;
  height: var(--topbar);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-inline-start: 16px;
  flex: 1 1 auto;
}

.site-nav a {
  padding: 9px 15px;
  border-radius: var(--r-pill);
  font-size: 15px;
  font-weight: 500;
  color: rgba(255, 255, 255, .88);
  text-decoration: none;
  transition: background-color .18s ease, color .18s ease;
}
.site-nav a:hover { background: rgba(255, 255, 255, .16); color: #fff; }

.site-head__acts { display: flex; align-items: center; gap: 10px; margin-inline-start: auto; }
.site-head__acts .btn--onbrand { padding: 10px 20px; }
.site-head__acts .btn--amber { padding: 11px 22px; }

.burger {
  display: none;
  width: 44px; height: 44px;
  border-radius: 12px;
  place-items: center;
  color: #fff;
}
.burger:hover { background: rgba(255, 255, 255, .16); }
.burger svg { width: 24px; height: 24px; }

@media (max-width: 1000px) {
  .burger { display: grid; }
  .site-head__acts .btn--onbrand { display: none; }
  .site-nav {
    position: absolute;
    inset: var(--topbar) 0 auto 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    margin: 0;
    padding: 12px 16px 20px;
    background: var(--brand-600);
    box-shadow: 0 20px 40px rgba(27, 16, 51, .3);
  }
  .site-nav.is-open { display: flex; }
  .site-nav a { padding: 13px 16px; border-radius: 14px; font-size: 16px; }
}

@media (max-width: 560px) {
  .site-head__acts .btn { padding: 10px 18px; font-size: 14.5px; }
  .logo { font-size: 19px; }
  .logo__mark { width: 34px; height: 34px; font-size: 17px; }
}

/* ======================================================================
 * Hero
 * ==================================================================== */
.hero {
  position: relative;
  background: var(--brand);
  color: #fff;
  padding: 72px 0 150px;
  overflow: hidden;
}

/* Flat brand colour, with depth coming from a soft radial wash and a ring
   motif rather than a gradient ramp across the whole panel. */
.hero::before {
  content: '';
  position: absolute;
  right: -180px; top: -220px;
  width: 720px; height: 720px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .07);
  pointer-events: none;
}
.hero::after {
  content: '';
  position: absolute;
  left: -140px; bottom: -260px;
  width: 520px; height: 520px;
  border-radius: 50%;
  border: 60px solid rgba(255, 255, 255, .05);
  pointer-events: none;
}

.hero__in {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
  gap: 48px;
  align-items: center;
}

.hero h1 {
  font-size: clamp(34px, 5.2vw, 60px);
  font-weight: 800;
  margin: 22px 0 18px;
  letter-spacing: -0.035em;
}
.hero h1 em { font-style: normal; color: var(--amber); }

.hero__deck {
  font-size: clamp(16px, 1.3vw, 19px);
  color: rgba(255, 255, 255, .84);
  max-width: 34em;
}

.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }

.hero__trust {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 32px;
  flex-wrap: wrap;
}

.faces { display: flex; }
.faces span {
  width: 38px; height: 38px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 700;
  color: var(--brand-700);
  background: var(--brand-100);
  border: 2.5px solid var(--brand);
  margin-inline-start: -12px;
}
.faces span:first-child { margin-inline-start: 0; }

.hero__trust p { font-size: 14px; color: rgba(255, 255, 255, .82); }
.hero__trust b { color: #fff; }

/* --- Phone mock ------------------------------------------------------- */
.phone {
  width: 100%;
  max-width: 320px;
  margin-inline: auto;
  background: #120A24;
  border-radius: 42px;
  padding: 11px;
  box-shadow: 0 40px 80px -24px rgba(0, 0, 0, .55), 0 0 0 1.5px rgba(255, 255, 255, .16);
  animation: float 7s ease-in-out infinite;
}
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-14px); }
}

.phone__screen {
  background: var(--surface);
  border-radius: 32px;
  overflow: hidden;
  color: var(--ink);
}

.phone__top {
  background: var(--brand-700);
  color: #fff;
  padding: 16px 18px 26px;
}
.phone__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.phone__hi { font-size: 13px; color: rgba(255, 255, 255, .78); }
.phone__nm { font-size: 15px; font-weight: 700; }
.phone__av {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--amber); color: var(--amber-ink);
  display: grid; place-items: center;
  font-size: 12.5px; font-weight: 700; flex: none;
}

.phone__bal { margin-top: 18px; }
.phone__bal span { font-size: 12px; color: rgba(255, 255, 255, .7); display: block; }
.phone__bal b { font-size: 27px; font-weight: 800; letter-spacing: -0.02em; }

.phone__body { padding: 0 14px 16px; margin-top: -14px; }

.phone__card {
  background: var(--bg);
  border-radius: 18px;
  padding: 12px;
  box-shadow: var(--shadow-sm);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px 4px;
}

.phone__ic {
  display: grid;
  justify-items: center;
  gap: 5px;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--ink-2);
  text-align: center;
}
.phone__ic i {
  width: 38px; height: 38px;
  border-radius: 13px;
  display: grid; place-items: center;
  color: #fff;
}
.phone__ic svg { width: 18px; height: 18px; }

.phone__list { margin-top: 12px; display: grid; gap: 8px; }
.phone__tx {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg);
  border-radius: 14px;
  padding: 10px 12px;
  box-shadow: var(--shadow-xs);
}
.phone__tx i {
  width: 32px; height: 32px; border-radius: 10px;
  display: grid; place-items: center; color: #fff; flex: none;
}
.phone__tx svg { width: 16px; height: 16px; }
.phone__tx span { flex: 1 1 auto; min-width: 0; }
.phone__tx b { display: block; font-size: 12.5px; font-weight: 600; }
.phone__tx small { display: block; font-size: 11px; color: var(--muted); }
.phone__tx em { font-style: normal; font-size: 12.5px; font-weight: 700; }
.phone__tx em.in { color: var(--green); }

@media (max-width: 900px) {
  .hero { padding: 48px 0 132px; }
  .hero__in { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .hero__visual { order: -1; }
  .phone { max-width: 268px; animation: none; }
}

/* ======================================================================
 * Stats strip, overlapping the hero edge
 * ==================================================================== */
.stats-wrap { position: relative; z-index: 5; margin-top: -104px; }

.stats {
  background: var(--bg);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  padding: 30px 20px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.stat { text-align: center; padding: 6px 14px; border-inline-end: 1px solid var(--line-soft); }
.stat:last-child { border-inline-end: 0; }
.stat b {
  display: block;
  font-size: clamp(24px, 2.6vw, 34px);
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--brand);
}
.stat span { font-size: 13.5px; color: var(--muted); font-weight: 500; }

@media (max-width: 760px) {
  .stats-wrap { margin-top: -96px; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 0; padding: 24px 12px; }
  .stat:nth-child(2) { border-inline-end: 0; }
  .stat:nth-child(-n+2) { border-bottom: 1px solid var(--line-soft); padding-bottom: 18px; }
}

/* ======================================================================
 * Sections
 * ==================================================================== */
.sec { padding: 84px 0; }
.sec--surface { background: var(--surface); }
.sec--tight { padding: 60px 0; }

.sec-head { max-width: 760px; margin-bottom: 44px; }
.sec-head--center { margin-inline: auto; text-align: center; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px;
  border-radius: var(--r-pill);
  background: var(--brand-100);
  color: var(--brand-700);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .01em;
  margin-bottom: 16px;
}
.eyebrow svg { width: 15px; height: 15px; }
.eyebrow--onbrand { background: rgba(255, 255, 255, .16); color: #fff; }

.sec-head h2 { font-size: clamp(26px, 3.4vw, 40px); font-weight: 800; }
.sec-head p { margin-top: 14px; font-size: 17px; color: var(--muted); }

@media (max-width: 760px) {
  .sec { padding: 60px 0; }
  .sec-head { margin-bottom: 32px; }
  .sec-head p { font-size: 16px; }
}

/* ======================================================================
 * Service tiles
 * ==================================================================== */
.tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
}

.tile {
  display: block;
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r-lg);
  padding: 26px 22px;
  text-decoration: none;
  transition: transform .2s ease, box-shadow .25s ease, border-color .2s ease;
}
.tile:hover {
  transform: translateY(-4px);
  border-color: transparent;
  box-shadow: var(--shadow);
}

.tile__i {
  width: 56px; height: 56px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
  margin-bottom: 18px;
}
.tile__i svg { width: 25px; height: 25px; }

.tile h3 { font-size: 18px; font-weight: 700; }
.tile p { margin-top: 8px; font-size: 14.5px; color: var(--muted); line-height: 1.5; }

.tile__more {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 14px;
  font-size: 14px; font-weight: 600; color: var(--brand);
}
.tile__more svg { width: 15px; height: 15px; transition: transform .2s ease; }
.tile:hover .tile__more svg { transform: translateX(3px); }

@media (max-width: 1000px) { .tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; } }
@media (max-width: 420px) {
  .tile { padding: 18px 16px; }
  .tile__i { width: 46px; height: 46px; margin-bottom: 13px; }
  .tile__i svg { width: 21px; height: 21px; }
  .tile h3 { font-size: 16px; }
  .tile p { font-size: 13.5px; }
}

/* ======================================================================
 * Steps
 * ==================================================================== */
.steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px; }

.step {
  background: var(--bg);
  border-radius: var(--r-lg);
  border: 1.5px solid var(--line);
  padding: 28px 24px;
  position: relative;
}
.step__n {
  width: 48px; height: 48px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  display: grid; place-items: center;
  font-size: 19px; font-weight: 800;
  margin-bottom: 18px;
}
.step h3 { font-size: 18.5px; }
.step p { margin-top: 9px; font-size: 15px; color: var(--muted); }

@media (max-width: 860px) { .steps { grid-template-columns: minmax(0, 1fr); } }

/* ======================================================================
 * Feature cards
 * ==================================================================== */
.feats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }

.feat {
  background: var(--bg);
  border-radius: var(--r-lg);
  border: 1.5px solid var(--line);
  padding: 26px 24px;
}
.feat__i {
  width: 46px; height: 46px;
  border-radius: 14px;
  background: var(--brand-50);
  color: var(--brand);
  display: grid; place-items: center;
  margin-bottom: 16px;
}
.feat__i svg { width: 22px; height: 22px; }
.feat h3 { font-size: 17.5px; }
.feat p { margin-top: 8px; font-size: 15px; color: var(--muted); }

@media (max-width: 980px) { .feats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .feats { grid-template-columns: minmax(0, 1fr); } }

/* ======================================================================
 * Rate teaser and tabs
 * ==================================================================== */
.tabs {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}
.tab {
  padding: 10px 20px;
  border-radius: var(--r-pill);
  border: 1.5px solid var(--line);
  background: var(--bg);
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink-2);
  transition: all .18s ease;
}
.tab:hover { border-color: var(--brand-200); }
.tab.is-selected { background: var(--brand); border-color: var(--brand); color: #fff; }

.rates { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }

.rate {
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  padding: 20px 18px;
  text-align: center;
  transition: border-color .2s ease, transform .2s ease;
}
.rate:hover { border-color: var(--brand-200); transform: translateY(-3px); }
.rate b { display: block; font-size: 21px; font-weight: 800; letter-spacing: -0.02em; }
.rate span { display: block; font-size: 13px; color: var(--muted); margin-top: 2px; }
.rate em {
  display: block; font-style: normal;
  margin-top: 12px; font-size: 17px; font-weight: 700; color: var(--brand);
}

@media (max-width: 900px) { .rates { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 620px) { .rates { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } }

/* ======================================================================
 * Plan slider  —  PREMIUM ONLY
 *
 * Nothing in any demo or any live pack had a carousel of any kind before
 * this. It is deliberately Kobopay's alone: the premium tier has to be
 * visibly and functionally more than the two included packs or the tier is
 * fiction, and a swipeable rail is the clearest "more" on a phone.
 *
 * ⚠ THE SCROLLING IS CSS, NOT SCRIPT. The rail is a scroll-snap container,
 * so drag, swipe and a trackpad all work with no JavaScript at all. The
 * script in `shared/storefront.js` only drives the two buttons and their
 * disabled state. A visitor whose script never arrives, which on a Nigerian
 * mobile connection is a normal case rather than an edge case, still gets
 * every card.
 * ==================================================================== */
.slider { position: relative; }

.slider__rail {
  display: flex; gap: 14px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Room for the cards' hover lift, which would otherwise be clipped by the
     scroll container. */
  padding: 4px 4px 12px;
  margin: -4px -4px -12px;
  /* The rail is swiped, and a scrollbar under a row of cards reads as a
     rendering fault rather than an affordance. The buttons and the partially
     visible next card are what say "there is more". */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.slider__rail::-webkit-scrollbar { display: none; }

/* ⚠ `flex: 0 0 <width>`, never a bare width. A flex child defaults to
   shrinking, so cards in a nowrap row would compress to fit instead of
   overflowing, and the rail would have nothing to scroll. */
.slider__item {
  flex: 0 0 clamp(216px, 25vw, 268px);
  scroll-snap-align: start;
  margin: 0;
}

.slider__nav { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; }
.slider__btn {
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1.5px solid var(--line); background: var(--bg); color: var(--ink);
  cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.slider__btn:hover:not([disabled]) { border-color: var(--brand); color: var(--brand); }
.slider__btn svg { width: 20px; height: 20px; }
/* One chevron symbol, turned around, rather than a second sprite entry. */
.slider__btn[data-slider-prev] svg { transform: rotate(180deg); }
/* ⚠ Disabled, not hidden. A control that vanishes at the end of the rail
   makes the row jump; one that dims stays where the thumb already is. */
.slider__btn[disabled] { opacity: .38; cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .slider__rail { scroll-behavior: auto; }
}

/* ======================================================================
 * Testimonials
 * ==================================================================== */
.says { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }

.say {
  background: var(--bg);
  border-radius: var(--r-lg);
  border: 1.5px solid var(--line);
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.say__stars { display: flex; gap: 3px; color: var(--amber-600); }
.say__stars svg { width: 17px; height: 17px; fill: currentColor; }
.say p { font-size: 15.5px; color: var(--ink-2); flex: 1 1 auto; }
.say__who { display: flex; align-items: center; gap: 12px; }
.say__av {
  width: 42px; height: 42px; border-radius: 50%;
  background: var(--brand-100); color: var(--brand-700);
  display: grid; place-items: center;
  font-size: 14px; font-weight: 700; flex: none;
}
.say__who b { display: block; font-size: 15px; }
.say__who span { display: block; font-size: 13px; color: var(--muted); }

@media (max-width: 980px) { .says { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .says { grid-template-columns: minmax(0, 1fr); } }

/* ======================================================================
 * Brand band
 * ==================================================================== */
.band {
  background: var(--brand);
  color: #fff;
  border-radius: var(--r-xl);
  padding: 52px 48px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, .8fr);
  gap: 40px;
  align-items: center;
  position: relative;
  overflow: hidden;
}
.band::before {
  content: '';
  position: absolute;
  right: -120px; top: -160px;
  width: 460px; height: 460px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .07);
}
.band > * { position: relative; z-index: 1; }
.band h2 { font-size: clamp(25px, 3vw, 36px); font-weight: 800; }
.band p { margin-top: 14px; font-size: 16.5px; color: rgba(255, 255, 255, .85); }
.band__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }

.band__pts { display: grid; gap: 12px; }
.band__pts li { display: flex; align-items: flex-start; gap: 11px; font-size: 15.5px; }
.band__pts svg { width: 21px; height: 21px; flex: none; margin-top: 2px; color: var(--amber); }

@media (max-width: 880px) {
  .band { grid-template-columns: minmax(0, 1fr); padding: 36px 26px; gap: 28px; }
}
@media (max-width: 420px) { .band { padding: 28px 20px; border-radius: var(--r-lg); } }

/* ======================================================================
 * FAQ, native disclosure so it needs no script
 * ==================================================================== */
.faq { max-width: 820px; margin-inline: auto; display: grid; gap: 12px; }

.faq details {
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  transition: border-color .2s ease;
}
.faq details[open] { border-color: var(--brand-200); }

.faq summary {
  list-style: none;
  cursor: pointer;
  padding: 19px 22px;
  font-size: 16.5px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '';
  width: 11px; height: 11px;
  border-right: 2.5px solid var(--brand);
  border-bottom: 2.5px solid var(--brand);
  transform: rotate(45deg);
  margin-top: -5px;
  flex: none;
  transition: transform .22s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg); margin-top: 3px; }
.faq p { padding: 0 22px 20px; font-size: 15.5px; color: var(--muted); }

/* ======================================================================
 * Footer
 * ==================================================================== */
.foot { background: #150C29; color: #fff; padding: 64px 0 28px; }

.foot__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: 36px;
}
.foot__about p { margin-top: 16px; font-size: 15px; color: rgba(255, 255, 255, .62); max-width: 34ch; }
.foot__soc { display: flex; gap: 10px; margin-top: 20px; }
.foot__soc a {
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .09);
  color: #fff;
  transition: background-color .2s ease;
}
.foot__soc a:hover { background: var(--brand); }
.foot__soc svg { width: 18px; height: 18px; }

.foot h4 { font-size: 14px; text-transform: uppercase; letter-spacing: .08em; color: rgba(255, 255, 255, .5); margin-bottom: 16px; }
.foot li + li { margin-top: 10px; }
.foot li a { font-size: 15px; color: rgba(255, 255, 255, .74); text-decoration: none; }
.foot li a:hover { color: #fff; }

/* Single-column footer on the pricing page: the credit goes underneath. */
.foot__rif { margin-top: 10px; opacity: .75; }

.foot__bar {
  margin-top: 44px;
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, .12);
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  justify-content: space-between;
  font-size: 14px;
  color: rgba(255, 255, 255, .55);
}

@media (max-width: 900px) { .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px; } }
@media (max-width: 520px) { .foot__grid { grid-template-columns: minmax(0, 1fr); } }

/* ======================================================================
 * Reveal on scroll
 * ==================================================================== */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .55s ease, transform .55s ease;
}
[data-reveal].is-revealed { opacity: 1; transform: none; }

/* ======================================================================
 * Auth pages
 *
 * A centred card on a solid brand field. The other two varieties both use a
 * split panel, so this deliberately does not.
 * ==================================================================== */
.auth {
  min-height: 100vh;
  background: var(--brand);
  background-image: radial-gradient(rgba(255, 255, 255, .16) 1.5px, transparent 1.6px);
  background-size: 26px 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 32px 20px calc(var(--ribbon-h, 52px) + 32px);
}

.auth__head {
  width: 100%;
  max-width: 480px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  color: #fff;
  margin-bottom: 26px;
}
.auth__back {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 14.5px; font-weight: 600;
  color: rgba(255, 255, 255, .9);
  text-decoration: none;
}
.auth__back svg { width: 17px; height: 17px; transform: rotate(180deg); }

.auth__card {
  width: 100%;
  max-width: 480px;
  background: var(--bg);
  border-radius: var(--r-xl);
  padding: 36px 34px;
  box-shadow: var(--shadow-lg);
}
.auth__card h1 { font-size: 27px; font-weight: 800; }
.auth__card > p { margin-top: 8px; font-size: 15.5px; color: var(--muted); }
.auth__card form { margin-top: 26px; }

.auth__switch {
  margin-top: 22px;
  text-align: center;
  font-size: 15px;
  color: var(--muted);
}
.auth__switch a { color: var(--brand); font-weight: 600; text-decoration: none; }

.auth__pts {
  width: 100%;
  max-width: 480px;
  margin-top: 26px;
  display: grid;
  gap: 10px;
  color: rgba(255, 255, 255, .9);
}
.auth__pts li { display: flex; align-items: flex-start; gap: 10px; font-size: 14.5px; }
.auth__pts svg { width: 19px; height: 19px; flex: none; margin-top: 2px; color: var(--amber); }

@media (max-width: 520px) {
  .auth { padding-inline: 14px; }
  .auth__card { padding: 26px 20px; border-radius: var(--r-lg); }
  .auth__card h1 { font-size: 23px; }
}

/* --- Stepper (register) ----------------------------------------------- */
.stepper { display: flex; align-items: center; gap: 6px; margin-bottom: 26px; }
.stepper__s { display: flex; align-items: center; gap: 8px; flex: 1 1 0; min-width: 0; }
.stepper__d {
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  font-size: 13px; font-weight: 700;
  background: var(--surface-2); color: var(--muted);
}
.stepper__s.is-now .stepper__d { background: var(--brand); color: #fff; }
.stepper__s.is-done .stepper__d { background: var(--green-tint); color: var(--green); }
.stepper__l { font-size: 13px; font-weight: 600; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stepper__s.is-now .stepper__l { color: var(--ink); }

@media (max-width: 460px) { .stepper__l { display: none; } .stepper__s { flex: none; } }

/* ======================================================================
 * Forms
 * ==================================================================== */
.field { margin-bottom: 18px; }
.field:last-child { margin-bottom: 0; }

.label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 8px;
  color: var(--ink-2);
}

.input {
  width: 100%;
  padding: 14px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  font-size: 15.5px;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.input::placeholder { color: var(--faint); }
.input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--brand-50);
}

/* appearance:none removes the native arrow, and without a replacement the field
   stops reading as a dropdown. The caret is a data URI because the CSP allows
   data: images but no external asset host. */
select.input {
  appearance: none;
  cursor: pointer;
  padding-inline-end: 44px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none' stroke='%236E6685' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='1 1.5 7 7.5 13 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
}

.input-group { position: relative; }
.input-group .input { padding-inline-end: 50px; }
.input-group__btn {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  width: 38px; height: 38px;
  border-radius: 10px;
  display: grid; place-items: center;
  color: var(--muted);
}
.input-group__btn:hover { background: var(--surface-2); color: var(--ink); }
.input-group__btn svg { width: 19px; height: 19px; }

.hint { display: block; margin-top: 7px; font-size: 13.5px; color: var(--muted); }

.form-aside {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: 6px 0 22px;
  font-size: 14.5px;
}
.form-aside a { color: var(--brand); font-weight: 600; text-decoration: none; }

.check { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; }
.check input { width: 18px; height: 18px; accent-color: var(--brand); flex: none; }
.check span { font-size: 14.5px; color: var(--ink-2); }

/* The row owns the spacing. Leaving it to the children makes the gap between
   the pair 32px and the gap after the pair 0 once the row stacks on a phone. */
.two {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 14px;
  margin-bottom: 18px;
}
.two .field { margin-bottom: 0; }
@media (max-width: 480px) { .two { grid-template-columns: minmax(0, 1fr); } }

/* OTP boxes */
.otp { display: flex; gap: 10px; justify-content: center; }
.otp input {
  width: 56px; height: 62px;
  text-align: center;
  font-size: 24px;
  font-weight: 700;
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
}
.otp input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-50); }
@media (max-width: 400px) { .otp input { width: 46px; height: 54px; font-size: 21px; } .otp { gap: 7px; } }

/* ======================================================================
 * App shell
 *
 * Desktop navigates from the top bar. The aside exists only as the mobile
 * drawer, which is why it is display:none above 1024 rather than the other
 * way round.
 * ==================================================================== */
.shell { min-height: 100vh; background: var(--surface); }

.appbar {
  position: sticky;
  top: 0;
  z-index: 150;
  background: var(--brand);
  color: #fff;
}

.appbar__in {
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--topbar);
}

.appnav { display: flex; align-items: center; gap: 2px; margin-inline-start: 18px; flex: 1 1 auto; min-width: 0; }
.appnav a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  border-radius: var(--r-pill);
  font-size: 15px;
  font-weight: 500;
  color: rgba(255, 255, 255, .85);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .18s ease, color .18s ease;
}
.appnav a svg { width: 18px; height: 18px; }
.appnav a:hover { background: rgba(255, 255, 255, .16); color: #fff; }
.appnav a.is-active { background: var(--bg); color: var(--brand-700); font-weight: 600; }

.appbar__acts { display: flex; align-items: center; gap: 8px; margin-inline-start: auto; }
.appbar .icon-btn { color: rgba(255, 255, 255, .88); }
.appbar .icon-btn:hover { background: rgba(255, 255, 255, .16); color: #fff; }
.appbar .icon-btn .dot { border-color: var(--brand); }

.who {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 14px 5px 5px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .13);
  min-width: 0;
}
.who__av {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--amber); color: var(--amber-ink);
  display: grid; place-items: center;
  font-size: 13px; font-weight: 700; flex: none;
}
.who__t { display: grid; min-width: 0; }
.who__n { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.who__m { font-size: 11.5px; color: rgba(255, 255, 255, .72); }

.appbar__burger { display: none; }

@media (max-width: 1100px) {
  .appnav a { padding: 9px 12px; font-size: 14.5px; }
  .appnav a span { display: none; }
  .appnav a.is-active span { display: inline; }
}
@media (max-width: 1023px) {
  .appnav { display: none; }
  .appbar__burger { display: inline-grid; }
  .who__t { display: none; }
  .who { padding: 5px; background: transparent; }
}

/* --- Mobile drawer ----------------------------------------------------- */
.side {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 300;
  width: var(--drawer);
  max-width: 86vw;
  background: var(--bg);
  transform: translateX(-102%);
  transition: transform .28s cubic-bezier(.32, .72, 0, 1);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.shell.is-nav-open .side { transform: none; }

.side__top { padding: 20px; border-bottom: 1px solid var(--line-soft); }
.side__nav { padding: 14px 12px; flex: 1 1 auto; }
.side__grp {
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--faint);
  padding: 16px 12px 8px;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: 14px;
  font-size: 15px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
}
.nav-item svg { width: 20px; height: 20px; flex: none; color: var(--muted); }
.nav-item:hover { background: var(--surface); color: var(--ink); }
.nav-item.is-active { background: var(--brand); color: #fff; font-weight: 600; }
.nav-item.is-active svg { color: #fff; }
.side__foot { padding: 14px 12px; border-top: 1px solid var(--line-soft); }

.shell__scrim {
  position: fixed;
  inset: 0;
  z-index: 290;
  background: rgba(21, 12, 41, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity .25s ease, visibility .25s ease;
}
.shell.is-nav-open .shell__scrim { opacity: 1; visibility: visible; }

@media (min-width: 1024px) {
  .side, .shell__scrim { display: none; }
}

/* --- Page body --------------------------------------------------------- */
.page { padding: 30px 0 60px; }
.page__h { margin-bottom: 26px; }
.page__h h1 { font-size: clamp(23px, 2.6vw, 31px); font-weight: 800; }
.page__h p { margin-top: 6px; font-size: 15.5px; color: var(--muted); }

.crumbs { display: flex; align-items: center; gap: 7px; font-size: 13.5px; color: var(--muted); margin-bottom: 12px; flex-wrap: wrap; }
.crumbs a { color: var(--brand); text-decoration: none; font-weight: 600; }
.crumbs svg { width: 14px; height: 14px; }

/* ⚠ `align-content: start`. A grid's rows stretch to fill their container by
   default, and this stack is usually the short column beside a tall form in
   a `.grid--2`. Without it the balance panel and the fee card on the buy and
   fund pages were each stretched to the full height of the form opposite —
   a 570px tall card holding one line of text. Techsub avoids it with
   `align-items: start` on its own two-column grids; this is the same fix for
   the one pack that uses the shared grid. */
.stack { display: grid; gap: 22px; align-content: start; }

.sec-t { font-size: 19px; font-weight: 700; margin-bottom: 16px; }

/* ======================================================================
 * Cards
 * ==================================================================== */
.card {
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line-soft);
}
.card__head h2 { font-size: 17.5px; font-weight: 700; }
.card__head a {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 14px; font-weight: 600; color: var(--brand); text-decoration: none;
}
.card__head a svg { width: 15px; height: 15px; }
.card__body { padding: 24px; }
.card__body--flush { padding: 0; }

/* ⚠⚠ A CARD WITH ONE REGION AND NO HEAD. `.card` here is a flush container —
   the padding lives on `.card__head` and `.card__body` — and six templates
   put content straight into it: the buy form, the fund form and the account
   number page, on both flush packs. Every field ran edge to edge, the
   heading sat outside the card's top border and the submit button overflowed
   it. On the two pages money moves through.

   This is the same padding `.card__body` carries, as a modifier, so a card
   with nothing to separate does not need a wrapper div round its only child.
   Meridian's `.card` has always behaved this way, so it narrows the gap
   between the packs rather than adding a third convention.

   ⚠ NEVER on a card holding a `.tbl`. That table is `min-width: 640px`, so
   inside a padded card it overflows horizontally, and its cells already
   carry 24px of their own — use `.card__head` plus the bare table, which is
   what the dashboard and the wallet already do. */
.card--pad { padding: 24px; }
@media (max-width: 520px) { .card--pad { padding: 18px; } }

@media (max-width: 520px) {
  .card__head { padding: 16px 18px; }
  .card__body { padding: 18px; }
}

/* ======================================================================
 * Wallet panel
 * ==================================================================== */
.wallet {
  background: var(--brand);
  color: #fff;
  border-radius: var(--r-lg);
  padding: 26px 28px;
  position: relative;
  overflow: hidden;
}
.wallet::before {
  content: '';
  position: absolute;
  right: -90px; top: -130px;
  width: 320px; height: 320px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .08);
}
.wallet > * { position: relative; z-index: 1; }

.wallet__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.wallet__l {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13.5px; color: rgba(255, 255, 255, .78); font-weight: 500;
}
.wallet__v { font-size: clamp(28px, 4vw, 40px); font-weight: 800; letter-spacing: -0.03em; margin-top: 6px; }
.wallet__eye {
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .16);
  color: #fff; flex: none;
}
.wallet__eye:hover { background: rgba(255, 255, 255, .26); }
.wallet__eye svg { width: 19px; height: 19px; }

.wallet__acts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }

.wallet__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 30px;
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.wallet__s b { display: block; font-size: 16px; font-weight: 700; }
.wallet__s span { display: block; font-size: 12.5px; color: rgba(255, 255, 255, .72); }

@media (max-width: 480px) {
  .wallet { padding: 22px 20px; }
  .wallet__acts .btn { flex: 1 1 auto; }
}

/* ======================================================================
 * Metrics
 * ==================================================================== */
.metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }

.metric {
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  padding: 20px;
}
.metric__h { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.metric__l { font-size: 13.5px; color: var(--muted); font-weight: 500; }
.metric__i {
  width: 38px; height: 38px; border-radius: 12px;
  display: grid; place-items: center; flex: none;
}
.metric__i svg { width: 18px; height: 18px; }
.metric__v { font-size: 25px; font-weight: 800; letter-spacing: -0.02em; margin-top: 12px; }
.metric__d { display: inline-flex; align-items: center; gap: 5px; margin-top: 6px; font-size: 13px; color: var(--muted); }
.metric__d svg { width: 14px; height: 14px; color: var(--green); }

.metrics--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 980px) {
  .metrics, .metrics--three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 400px) {
  .metrics, .metrics--three { grid-template-columns: minmax(0, 1fr); }
}

/* ======================================================================
 * Transaction history
 * ==================================================================== */
.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
}
.filters .chips { gap: 8px; }
.filters .chip { padding: 9px 16px; font-size: 14px; }

.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 16px 24px;
  border-top: 1px solid var(--line-soft);
}
.card__foot .hint { margin: 0; }
.card__foot b { color: var(--ink); font-weight: 700; }

.pager { display: flex; gap: 6px; }
.pager__b {
  min-width: 36px;
  height: 36px;
  padding: 0 10px;
  border-radius: 10px;
  border: 1.5px solid var(--line);
  background: var(--bg);
  display: grid;
  place-items: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-2);
  transition: all .18s ease;
}
.pager__b:hover:not(:disabled) { border-color: var(--brand-200); background: var(--brand-50); }
.pager__b.is-current { background: var(--brand); border-color: var(--brand); color: #fff; }
.pager__b:disabled { opacity: .45; cursor: not-allowed; }
.pager__b svg { width: 16px; height: 16px; }
/* The two arrow buttons reuse one chevron symbol; the first is mirrored. */
.pager__b:first-child svg { transform: rotate(180deg); }

.empty {
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 48px 24px;
  color: var(--muted);
  font-size: 15px;
}
.empty svg { width: 30px; height: 30px; color: var(--faint); }

@media (max-width: 520px) {
  .card__foot { padding: 14px 18px; justify-content: center; }
  /* A no-wrap row of five chips has a min-content width of about 400px, and as a
     flex item that width propagates all the way up and widens the page. The
     min-width:0 is what lets it become the scroller it is meant to be. */
  .filters .chips {
    width: 100%;
    min-width: 0;
    overflow-x: auto;
    flex-wrap: nowrap;
    padding-bottom: 4px;
    scrollbar-width: none;
  }
  .filters .chips::-webkit-scrollbar { display: none; }
  .filters .chip { flex: none; }
}

/* ======================================================================
 * Quick tiles, the app home grid
 * ==================================================================== */
.qtiles {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
}

.qtile {
  position: relative;
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r);
  padding: 18px 10px 14px;
  display: grid;
  justify-items: center;
  gap: 10px;
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
  text-align: center;
  transition: transform .18s ease, box-shadow .22s ease, border-color .18s ease;
}
.qtile:hover { transform: translateY(-3px); border-color: transparent; box-shadow: var(--shadow-sm); }
.qtile__i {
  width: 48px; height: 48px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
}
.qtile__i svg { width: 22px; height: 22px; }
.qtile .tag { position: absolute; top: 7px; right: 7px; padding: 2px 7px; font-size: 10px; }

@media (max-width: 1000px) { .qtiles { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@media (max-width: 820px)  { .qtiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 400px)  {
  .qtiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
  .qtile { padding: 14px 6px 11px; font-size: 12.5px; }
  .qtile__i { width: 42px; height: 42px; }
}

/* ======================================================================
 * Tables, which become cards on a phone
 * ==================================================================== */
.tbl-wrap { overflow-x: auto; }

/* ⚠ `width: 100%` AND `border-collapse`, both of which this pack alone was
   missing — Techsub's `.tbl` has carried them since it was written. Without
   the width a table only takes its content width, so the order history and
   the wallet ledger bunched into the left two thirds of their card and left
   a third of it blank, on the premium pack. Seen by opening the page; a
   table that renders at the wrong width is still a table that renders. */
.tbl { width: 100%; border-collapse: collapse; min-width: 640px; font-size: 14.5px; }
.tbl th {
  text-align: start;
  padding: 13px 24px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--faint);
  background: var(--surface);
  white-space: nowrap;
}
.tbl td { padding: 15px 24px; border-top: 1px solid var(--line-soft); vertical-align: middle; }
.tbl tbody tr:hover { background: var(--brand-50); }

.cell { display: flex; align-items: center; gap: 12px; min-width: 0; }
.cell__i {
  width: 38px; height: 38px; border-radius: 12px;
  display: grid; place-items: center; color: #fff; flex: none;
}
.cell__i svg { width: 18px; height: 18px; }
.cell__t { display: block; font-weight: 600; }
.cell__s { font-size: 13px; color: var(--muted); }

.amt-out { font-weight: 700; }
.amt-in { font-weight: 700; color: var(--green); }

@media (max-width: 720px) {
  .tbl-wrap { overflow-x: visible; }
  .tbl { min-width: 0; }
  .tbl thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .tbl tr {
    display: block;
    padding: 16px 18px;
    border-top: 1px solid var(--line-soft);
  }
  .tbl tbody tr:hover { background: transparent; }
  .tbl td { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 0; border: 0; }
  .tbl td + td { margin-top: 9px; }
  .tbl td::before {
    content: attr(data-label);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--faint);
    flex: none;
  }
  .tbl td:first-child { margin-bottom: 12px; }
  .tbl td:first-child::before { content: none; }
  .tbl td:first-child .cell { flex: 1 1 auto; }
}

/* ======================================================================
 * Buy pages
 * ==================================================================== */
.buy {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}
@media (max-width: 940px) { .buy { grid-template-columns: minmax(0, 1fr); } }

.summary { position: sticky; top: calc(var(--topbar) + 20px); }
@media (max-width: 940px) { .summary { position: static; } }

/* Network picker */
.nets { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.net {
  display: grid;
  justify-items: center;
  gap: 9px;
  padding: 16px 8px;
  border: 2px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
  transition: all .18s ease;
}
.net:hover { border-color: var(--brand-200); }
.net.is-selected { border-color: var(--brand); background: var(--brand-50); color: var(--brand-700); }

.net__l {
  width: 46px; height: 46px;
  border-radius: 14px;
  display: grid; place-items: center;
  font-size: 12px;
  font-weight: 800;
  color: #fff;
  letter-spacing: -0.02em;
}
.net__l--mtn      { background: #FFCB05; color: #10100F; }
.net__l--glo      { background: #59B04A; }
.net__l--airtel   { background: #E40000; }
.net__l--9mobile  { background: #006F51; }

@media (max-width: 420px) { .nets { gap: 8px; } .net { padding: 12px 4px; font-size: 12.5px; } .net__l { width: 40px; height: 40px; } }

/* Amount chips */
.chips { display: flex; flex-wrap: wrap; gap: 10px; }
.chip {
  padding: 11px 20px;
  border-radius: var(--r-pill);
  border: 1.5px solid var(--line);
  background: var(--bg);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-2);
  transition: all .18s ease;
}
.chip:hover { border-color: var(--brand-200); }
.chip.is-selected { background: var(--brand); border-color: var(--brand); color: #fff; }

/* Tip presets. Kobopay says "chip" and states figures in mono on brand
   violet, so these do both. Selection is `aria-pressed` rather than a class,
   because that is what announces the chosen amount to a screen reader. */
.tip-opts { display: flex; flex-wrap: wrap; gap: 10px; }
.tip-opt { padding: 11px 20px; cursor: pointer;
           border-radius: var(--r-pill); border: 1.5px solid var(--line);
           background: var(--bg); font-weight: 700;
           font-family: var(--font-mono, ui-monospace, monospace); }
.tip-opt:hover { border-color: var(--brand-200); }
.tip-opt[aria-pressed="true"] { background: var(--brand); border-color: var(--brand);
                                color: #fff; }
/* Both axes plus `text-align`, because the packs lay `.field` out differently
   (meridian flex, techsub grid) and a button's text centres by default
   whatever its width. Without these it stretches and reads as a centred link. */
.tip-custom { align-self: flex-start; justify-self: start; text-align: left;
              margin-top: 10px; padding: 0; border: 0;
              background: none; cursor: pointer; font-size: 13px;
              font-weight: 700; text-decoration: underline; }
.tip-custom-box { margin-top: 10px; }
.tip-custom-box[hidden] { display: none; }

/* Plan list */
.plans { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.plan {
  border: 2px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  padding: 16px;
  text-align: start;
  transition: all .18s ease;
}
.plan:hover { border-color: var(--brand-200); }
.plan.is-selected { border-color: var(--brand); background: var(--brand-50); }
.plan b { display: block; font-size: 18px; font-weight: 800; letter-spacing: -0.02em; }
.plan span { display: block; font-size: 12.5px; color: var(--muted); margin-top: 1px; }
.plan em { display: block; font-style: normal; margin-top: 10px; font-size: 16px; font-weight: 700; color: var(--brand); }

@media (max-width: 620px) { .plans { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.notice {
  display: flex;
  gap: 11px;
  padding: 14px 16px;
  border-radius: var(--r-sm);
  background: var(--brand-50);
  color: var(--brand-700);
  font-size: 14px;
  line-height: 1.5;
  margin-top: 4px;
}
.notice svg { width: 19px; height: 19px; flex: none; margin-top: 1px; }

/* Summary rows */
.sum-r, .sum-tot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 15px;
}
.sum-r dt, .sum-tot dt { color: var(--muted); }
.sum-r dd { font-weight: 600; text-align: end; }
.sum-tot { border-bottom: 0; padding-top: 16px; }
.sum-tot dt { font-weight: 600; color: var(--ink); }
.sum-tot dd { font-size: 24px; font-weight: 800; color: var(--brand); letter-spacing: -0.02em; }

/* ======================================================================
 * Modal
 * ==================================================================== */
.modal {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: none;
  place-items: center;
  padding: 20px;
  background: rgba(21, 12, 41, .6);
}
.modal.is-open { display: grid; }

.modal__panel {
  width: 100%;
  max-width: 440px;
  background: var(--bg);
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  animation: pop .25s cubic-bezier(.32, .72, 0, 1);
}
@keyframes pop { from { opacity: 0; transform: translateY(18px) scale(.97); } }

.modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line-soft);
}
.modal__head h3 { font-size: 18px; font-weight: 700; }
.modal__body { padding: 24px; }
.modal__foot { padding: 0 24px 24px; display: grid; gap: 10px; }

.rcpt { margin-top: 18px; }
.rcpt > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px dashed var(--line);
  font-size: 15px;
}
.rcpt > div:last-child { border-bottom: 0; }
.rcpt dt { color: var(--muted); }
.rcpt dd { font-weight: 600; text-align: end; }

.pin {
  width: 100%;
  padding: 16px;
  text-align: center;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: .5em;
  text-indent: .5em;
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
}
.pin:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-50); }

.done {
  display: grid;
  place-items: center;
  margin-bottom: 16px;
}
.done svg { width: 66px; height: 66px; color: var(--green); }

@media (max-width: 560px) {
  .modal { place-items: end center; padding: 0 0 var(--ribbon-h, 0px); }
  .modal__panel { max-width: none; border-radius: var(--r-xl) var(--r-xl) 0 0; }
}

/* ======================================================================
 * Toasts
 * ==================================================================== */
.toast-stack {
  position: fixed;
  right: 20px;
  bottom: calc(var(--ribbon-h, 52px) + 20px);
  z-index: 500;
  display: grid;
  gap: 10px;
  justify-items: end;
}
.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 18px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: #fff;
  font-size: 14.5px;
  font-weight: 500;
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .22s ease, transform .22s ease;
  max-width: min(92vw, 420px);
}
.toast.is-in { opacity: 1; transform: none; }
.toast__icon { width: 19px; height: 19px; flex: none; color: #6EE7A8; }
.toast--error .toast__icon { color: #FCA5A5; }

@media (max-width: 560px) {
  .toast-stack { left: 14px; right: 14px; bottom: calc(var(--ribbon-h, 52px) + 78px); justify-items: stretch; }
  .toast { max-width: none; }
}

/* ======================================================================
 * Bottom tab bar
 * ==================================================================== */
.tabbar { display: none; }

@media (max-width: 1023px) {
  .tabbar {
    position: fixed;
    inset: auto 0 var(--ribbon-h, 0px) 0;
    z-index: 140;
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    background: var(--bg);
    border-top: 1px solid var(--line);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
  }
  .tabbar a, .tabbar button {
    display: grid;
    justify-items: center;
    gap: 3px;
    padding: 7px 2px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
  }
  .tabbar svg { width: 21px; height: 21px; }
  .tabbar .is-active { color: var(--brand); }
  .shell .page { padding-bottom: calc(var(--ribbon-h, 0px) + 92px); }
}

/* ======================================================================
 * Pricing page
 * ==================================================================== */
.price-head { background: var(--brand); color: #fff; padding: 56px 0 64px; }
.price-head h1 { font-size: clamp(28px, 4vw, 44px); font-weight: 800; }
.price-head p { margin-top: 14px; font-size: 17px; color: rgba(255, 255, 255, .84); max-width: 56ch; }

.price-tbl { min-width: 560px; font-size: 15px; }
.price-tbl th {
  text-align: start;
  padding: 14px 22px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--faint);
  background: var(--surface);
  white-space: nowrap;
}
.price-tbl td { padding: 15px 22px; border-top: 1px solid var(--line-soft); }
.price-tbl td:last-child, .price-tbl th:last-child { text-align: end; }
.price-tbl .p { font-weight: 700; color: var(--brand); }
.price-tbl .was { color: var(--faint); text-decoration: line-through; font-size: 13.5px; margin-inline-end: 8px; }

@media (max-width: 720px) {
  .price-tbl { min-width: 0; }
  .price-tbl thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .price-tbl tr { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 14px 18px; border-top: 1px solid var(--line-soft); }
  .price-tbl td { display: block; padding: 0; border: 0; }
  .price-tbl td:first-child { flex: 1 1 100%; font-weight: 600; }
  .price-tbl td:last-child { margin-inline-start: auto; text-align: end; }
}

.tiers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.tier {
  background: var(--bg);
  border: 1.5px solid var(--line);
  border-radius: var(--r-lg);
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
}
.tier.is-featured { border-color: var(--brand); box-shadow: var(--shadow); position: relative; }
.tier.is-featured::before {
  content: 'Most popular';
  position: absolute;
  top: -12px; left: 24px;
  padding: 4px 14px;
  border-radius: var(--r-pill);
  background: var(--amber);
  color: var(--amber-ink);
  font-size: 12px;
  font-weight: 700;
}
.tier h3 { font-size: 19px; }
.tier__p { margin: 14px 0 4px; font-size: 34px; font-weight: 800; letter-spacing: -0.03em; }
.tier__p small { font-size: 15px; font-weight: 500; color: var(--muted); letter-spacing: 0; }
.tier > p { font-size: 14.5px; color: var(--muted); }
.tier ul { margin: 20px 0 24px; display: grid; gap: 11px; flex: 1 1 auto; }
.tier li { display: flex; align-items: flex-start; gap: 10px; font-size: 14.5px; }
.tier li svg { width: 18px; height: 18px; flex: none; margin-top: 2px; color: var(--green); }

@media (max-width: 900px) { .tiers { grid-template-columns: minmax(0, 1fr); } }


/* ── service colour coding ────────────────────────────────────────────
 * Fixed in the pack, never exposed to the owner. Kobopay fills the tile
 * with the solid service colour rather than tinting it.
 * ------------------------------------------------------------------ */
.qtile--data .qtile__i { background: var(--s-data, var(--brand)); color: #fff; }
.qtile--airtime .qtile__i { background: var(--s-airtime, var(--brand)); color: #fff; }
.qtile--cable .qtile__i { background: var(--s-cable, var(--brand)); color: #fff; }
.qtile--electricity .qtile__i { background: var(--s-power, var(--brand)); color: #fff; }
.qtile--result_checker .qtile__i { background: var(--s-exam, var(--brand)); color: #fff; }
.qtile--recharge_pin .qtile__i { background: var(--s-print, var(--brand)); color: #fff; }
.qtile--betting .qtile__i { background: var(--s-more, var(--brand)); color: #fff; }
.qtile--marketing .qtile__i { background: var(--s-refer, var(--brand)); color: #fff; }

/* ======================================================================
 * The states the demo never had
 *
 * Added 16 September 2026, when the templates were brought back onto this
 * pack's own vocabulary. Everything above came from the kobo demo, which was a
 * brochure: it had no form errors, no uploaded logo, no flash messages and no
 * meter token, because it never submitted anything or signed anyone in.
 *
 * Written in Kobopay's idiom. Violet carries the brand, amber carries action,
 * the radius ramp is this pack's and so is the shadow. The same states exist in
 * Techsub under the same names and look nothing alike, which is the whole
 * point: a buyer picks one pack at onboarding and it has to feel like its own
 * product, not a recolour of the other two.
 * ==================================================================== */

/* An owner's uploaded logo, in place of the lettermark. Bounded so a tall or
   very wide file cannot push the header or the auth card out of shape. */
.logo__img { height: 36px; width: auto; max-width: 180px; object-fit: contain; flex: none; }

/* The wordmark sits on `.logo` itself in this pack, so there is no `logo__txt`
   to carry the wrap rule that Techsub's has. Without it a two-word site name
   breaks across two lines in the top bar. */
.logo > span { white-space: nowrap; }

/* The auth card stacks its logo above the heading rather than sitting inline. */
.logo--stacked { display: grid; justify-items: center; gap: 10px; margin-bottom: 20px; }

/* The balance as a strip above a form, rather than the full panel. The customer
   needs to see what they can spend while choosing what to buy. */
.wallet--slim {
  padding: 16px 20px;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}
.wallet--slim .wallet__v { font-size: 1.5rem; margin-top: 0; }

/* The line under an auth heading. */
.auth__lead { color: var(--muted); font-size: 15px; margin-bottom: 22px; }

/* Field help and field errors. `.field` is a grid, so both sit under the input
   without any positioning. */
.help  { font-size: 13px; color: var(--muted); }
.error { font-size: 13px; font-weight: 600; color: var(--danger); }

/* Flash messages. `.notice` alone is this pack's brand-tinted information tone;
   the other two states needed saying. Colour is never the only signal, because
   a red and a green box read the same to a colourblind customer: the copy
   always says what happened. */
.notice--info    { background: var(--brand-50); border-color: var(--brand-100); color: var(--brand-700); }
.notice--success { background: var(--green-tint); border-color: rgba(18,183,106,.26); color: #0B7A3C; }
.notice--error   { background: var(--danger-tint); border-color: rgba(217,45,32,.26); color: var(--danger); }

/* The amount at the top of a receipt. Large enough to be the first thing read
   in a screenshot, which is what this page is for. */
.rcpt__amt {
  font-size: clamp(1.5rem, 1.2rem + 1vw, 1.9rem);
  font-weight: 800; letter-spacing: -.03em; color: var(--ink);
}

/* A prepaid meter token. It is the product: if the customer loses it they paid
   and got nothing. Given its own block, never a table row, and nothing is
   allowed to float over it. Kobopay states it on brand violet where Techsub
   uses navy. */
.token {
  margin-top: 20px; padding: 18px 20px;
  border-radius: var(--r-md);
  background: var(--brand); color: #fff;
  display: grid; gap: 6px; justify-items: start;
}
.token span { font-size: 12px; font-weight: 700; letter-spacing: .05em;
              text-transform: uppercase; color: rgba(255,255,255,.72); }
.token b { font-size: clamp(1.25rem, 1rem + 1.2vw, 1.6rem); font-weight: 800;
           letter-spacing: .04em; word-break: break-all; }
.token .btn { margin-top: 8px; }

/* Django renders a widget as a bare <input>, <select> or <textarea> with no
   class on it, and `_form.html` renders a bare <label>. This pack styles
   `.input` and `.label`, which the demo could write by hand and a form cannot,
   so every field on every form here rendered as an unmodified browser control:
   login, register, PIN, buy, fund. Meridian had element selectors from the
   start and did not have the problem.

   Aliased onto the existing components rather than restated, so the two can
   never drift apart. Scoped to `.field` so nothing else on the page is
   caught. */
.field label { display: block; font-size: 14px; font-weight: 600; }
.field input, .field select, .field textarea {
  width: 100%;
  padding: 14px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg);
  font: inherit;
  font-size: 15.5px;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.field input::placeholder, .field textarea::placeholder { color: var(--faint); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--brand-50);
}

/* ======================================================================
 * Spending charts  —  PREMIUM ONLY
 *
 * ⚠⚠ Part of what the extra N10,000 buys, with dark mode and the receipt
 * actions. Do not port these rules into Meridian or Techsub.
 *
 * ⚠ Both charts are DRAWN ON THE SERVER and arrive in the HTML. `initCharts`
 * only measures the line's real length into `--len` so the dash animation is
 * exact, and lays the donut segments end to end. With JavaScript off the line
 * is still there, fully drawn, and the donut degrades to its track — which on
 * a cheap Android on a bad connection is the difference between a dashboard
 * and a hole.
 *
 * ⚠ No figure here is invented. An empty window renders an empty state in the
 * template instead of a flat line at zero, because a flat line is a claim
 * that nothing was spent.
 * ==================================================================== */
.insights { margin-bottom: 20px; }

.chart {
  display: block; width: 100%; height: 140px;
  /* ⚠ Not `height: auto`. The viewBox is 560x140 with
     `preserveAspectRatio="none"`, so the path stretches to whatever box it
     is given — which is the point: the shape has to stay legible from 360px
     to 1200px without re-rendering it per breakpoint. */
  overflow: visible;
}
.chart__line {
  fill: none;
  stroke: var(--brand);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* `--len` is set by initCharts from getTotalLength(). The fallback draws
     the line instantly rather than leaving it invisible if the script never
     runs — an animation that depends on JS must fail to FINISHED, never to
     its first frame. */
  stroke-dasharray: var(--len, none);
  stroke-dashoffset: 0;
  animation: chart-draw 1.1s cubic-bezier(.22, .61, .36, 1) both;
}
@keyframes chart-draw {
  from { stroke-dashoffset: var(--len, 0); }
  to   { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* A line that draws itself is decoration. Somebody who has asked their
     phone to stop moving things gets the finished chart. */
  .chart__line { animation: none; stroke-dashoffset: 0; }
}
.chart__foot {
  display: flex; justify-content: space-between;
  margin-top: 8px;
}

.donut-row {
  display: flex; align-items: center; gap: 22px; flex-wrap: wrap;
}
.donut {
  width: 132px; height: 132px; flex: none;
  /* The ring starts at twelve o'clock. Without this the first segment starts
     at three and every reader reads the chart as rotated. */
  transform: rotate(-90deg);
}
.donut__track {
  fill: none; stroke: var(--surface-2); stroke-width: 12;
}
.donut__seg {
  fill: none; stroke: var(--s, var(--brand)); stroke-width: 12;
  stroke-linecap: butt;
  transition: stroke-dasharray .5s ease;
}
.legend {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 9px;
  flex: 1 1 180px;
}
.legend li { display: flex; align-items: center; gap: 9px; font-size: .9rem; }
.legend__dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--s, var(--brand)); flex: none;
}
.legend__l { flex: 1 1 auto; color: var(--ink-2); }
.legend__v { color: var(--muted); font-size: .84rem; }

/* One mapping for both the ring and its key, written once. The hues are the
   pack's existing service coding — the same ones the tile grid uses — so a
   service is the same colour everywhere on the page. */
.donut__seg--data,           .legend__dot--data           { --s: var(--s-data); }
.donut__seg--airtime,        .legend__dot--airtime        { --s: var(--s-airtime); }
.donut__seg--cable,          .legend__dot--cable          { --s: var(--s-cable); }
.donut__seg--electricity,    .legend__dot--electricity    { --s: var(--s-power); }
.donut__seg--result_checker, .legend__dot--result_checker { --s: var(--s-exam); }
.donut__seg--recharge_pin,   .legend__dot--recharge_pin   { --s: var(--s-print); }
.donut__seg--betting,        .legend__dot--betting        { --s: var(--s-more); }
.donut__seg--marketing,      .legend__dot--marketing      { --s: var(--s-refer); }

@media (max-width: 560px) {
  .donut-row { gap: 16px; }
  .donut { width: 108px; height: 108px; }
}

/* ======================================================================
 * The receipt, and printing it  —  PREMIUM ONLY
 *
 * ⚠⚠ `@media print` DID NOT EXIST ANYWHERE IN THIS PROJECT. Not in any of the
 * five demos, not in any of the three live packs. Printing a receipt printed
 * the sidebar, the top bar, the bottom tab bar and the "Back to orders"
 * button along with it, across two or three pages. Meanwhile Kobopay's own
 * landing page has promised "receipts you can download or forward to a
 * customer" since it was written, with nothing behind it.
 *
 * A VTU receipt is the product for half of what this platform sells: a prepaid
 * token, an exam pin and a printed recharge pin ARE the thing bought. Being
 * able to hand one over is not a nicety.
 * ==================================================================== */
.rcpt-acts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.rcpt-acts .btn { flex: 1 1 auto; justify-content: center; }
@media (max-width: 420px) { .rcpt-acts .btn { flex: 1 1 100%; } }

/* The brand strip that only exists on paper and in the saved image. On screen
   the shop's name is already in the sidebar; on a printed sheet handed to a
   customer there would otherwise be nothing saying who issued it. */
.rcpt-head { display: none; }

@media print {
  /* ⚠ Hide by role, not by one selector. Every piece of app chrome has to go
     or the sheet is mostly navigation. */
  .side, .appbar, .tabbar, .appnav, .shell__scrim,
  .rcpt-acts, .toast-stack, .btn, .pager, .crumbs { display: none !important; }

  /* The shell is a grid with a sidebar column. On paper there is one column
     and no viewport height to fill. */
  .shell { display: block !important; }
  .page { padding: 0 !important; margin: 0 !important; }
  .grid--2 { display: block !important; }

  /* The progress card is the order's internal timeline. Useful on screen,
     noise on a receipt somebody is handed. */
  .stack { display: none !important; }

  .rcpt-head { display: block; margin-bottom: 14px; }

  html, body {
    background: #fff !important; color: #000 !important;
    font-size: 12pt;
  }
  /* Shadows and tints cost toner and print as grey mud on a mono printer. */
  .card {
    border: 1px solid #000 !important; box-shadow: none !important;
    break-inside: avoid; background: #fff !important;
  }
  .token {
    border: 1.5px solid #000 !important; background: #fff !important;
    break-inside: avoid;
  }
  /* ⚠ The token is the product. It must survive a bad printer, so it is set
     larger on paper than on screen, never lighter. */
  .token b { font-size: 15pt !important; color: #000 !important;
             letter-spacing: .04em; }
  .rcpt__amt { color: #000 !important; }
  .muted, .tiny { color: #333 !important; }

  @page { margin: 14mm; }
}

/* ⚠ A tick box is not a text box. `_form.html` renders every other control
   label-above-field; a checkbox is one sentence you tick, so the box sits
   inline with its label and the whole line is the hit target. Before this
   the label floated above a lonely square with nothing under it — seen on
   "Save this number for next time" on the buy page of all three packs. */
.field--check > label {
  display: flex; align-items: center; gap: 10px;
  margin: 0; font-weight: 500; cursor: pointer;
}
.field--check input[type="checkbox"] {
  width: 18px; height: 18px; flex: none; margin: 0;
  accent-color: var(--brand); cursor: pointer;
}

/* ⚠ `.tbl` carries a 640px minimum so a five-column order history stays
   readable and scrolls rather than crushing. A two-column list does not
   need it, and in the narrow column of a two-up grid that minimum is wider
   than the card: the fee row was clipped mid-word by the card's
   `overflow: hidden`, then grew a permanent scrollbar once it was wrapped.
   The fee stated before a customer pays is the one line that must not be
   cut, so a short table opts out of the minimum instead. */
.tbl--narrow { min-width: 0; }
