/* Shared layout primitives. Every pack loads this, then its own theme.css.
   Nothing here decides colour: that is the pack's job. */

/* ======================================================================
 * The four families the theme editor offers
 *
 * ⚠⚠ UNTIL 15 OCTOBER 2026 NO WEBFONT WAS EVER LOADED. `Theme.font_display`
 * and `font_body` have offered Plus Jakarta Sans, Manrope, Outfit and Inter
 * since they were written, and no page linked or @font-faced one of them, so
 * the picker was decoration: the named family resolved on a phone only by
 * luck and the FALLBACK was what a customer actually read.
 *
 * ⚠ SELF-HOSTED, NOT LINKED. Owner's decision, 15 Oct. A `<link>` to
 * fonts.googleapis.com puts a third party in front of text rendering on
 * every page of every tenant shop, which on a slow Nigerian mobile
 * connection is where the time goes — and a shop that cannot reach Google
 * is a shop whose text is still waiting. These are served from the tenant's
 * own domain.
 *
 * ⚠ VARIABLE FONTS, ONE FILE EACH. The whole 200-800 (or 100-900) weight
 * range is in the single file, so there is no per-weight request and no
 * synthesised bold. 132KB for all four on disk — and a browser downloads a
 * font only when a rule that uses it MATCHES, so a tenant pays for the one
 * family they chose and nothing for the other three.
 *
 * ⚠ `font-display: swap`. Text is readable in the fallback immediately and
 * reflows when the font lands. The alternative is a customer looking at a
 * blank balance while a font downloads, which is the worse failure on the
 * connection this is built for.
 *
 * ⚠ The `unicode-range` is Google's own latin subset and is NOT decoration:
 * only the latin subset is shipped, so a character outside it falls through
 * to the UI stack and renders, instead of becoming tofu in a font that does
 * not contain it.
 *
 * Replacing a file: refetch the src from the latin block of
 * `https://fonts.googleapis.com/css2?family=<Family>:wght@<range>&display=swap`
 * and send a browser user-agent — the API serves TTF to anything it does
 * not recognise, and a TTF here would quietly triple the download.
 * ==================================================================== */
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('../fonts/plus-jakarta-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('../fonts/manrope-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/outfit-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ⚠ STILL STATED ON EVERY RULE THAT SETS A FAMILY, even now the fonts are
   real. A `font-family` whose only entry is an absent family is not
   "ignored": it computes to the browser's default, which is a SERIF.
   Measured on tetragit.com on 15 October 2026 — the live landing headings
   rendered at exactly the `serif` width, Times New Roman on a fintech shop,
   because four Meridian rules said `var(--t-font-display)` and stopped
   there. A font file can fail to load; the stack is what is read then. */
:root {
  --ui-stack: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
              'Helvetica Neue', Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

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

/* ⚠⚠ A CONTROL THAT IS A REAL <button> ARRIVES WEARING THE BROWSER'S OWN
   BUTTON. Kobopay's `.icon-btn` sets a size, a radius and a hover background
   and never resets the resting one, so every icon button on that pack — the
   mobile burger, hide-balance on the dashboard and on the wallet — rendered
   as a grey `buttonface` rectangle inside a violet app bar. Seen only by
   opening the page; the markup, the CSS and every test were all correct.

   ⚠ Plain `.icon-btn`, NOT `button.icon-btn`. A type selector would raise
   the specificity above the packs' own rules and Techsub, which deliberately
   gives its icon buttons a border and a white face, would lose them. At this
   specificity a pack's `theme.css` loads later and wins, which is the order
   that lets this be a floor rather than an override. */
.icon-btn {
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; padding: 0;
  font: inherit; color: inherit; cursor: pointer;
}

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--t-font-body), var(--ui-stack);
  background: var(--surface);
  color: var(--ink);
  line-height: 1.55;
  min-height: 100vh;
}

h1, h2, h3, h4 {
  font-family: var(--t-font-display), var(--ui-stack);
  line-height: 1.2;
  margin: 0 0 .5rem;
  letter-spacing: -0.015em;
}

a { color: inherit; }

/* Both default to min-width:auto, so a child refuses to shrink and pushes the
   page wider than the viewport. This is the single most common cause of
   horizontal overflow on a phone. */
.row > *, .grid > *, .stack > * { min-width: 0; }

.wrap { width: 100%; max-width: 1180px; margin-inline: auto; padding-inline: 24px; }

/* Never a `padding` shorthand on an element that also carries .wrap: the
   shorthand's zero sides silently wipe the inline padding above. */
.section { padding-block: 48px; }

.row { display: flex; gap: 16px; align-items: center; }
.row--between { justify-content: space-between; }
.row--wrap { flex-wrap: wrap; }
.stack { display: flex; flex-direction: column; gap: 16px; }

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

.muted { color: var(--muted); }
.small { font-size: .875rem; }
.tiny  { font-size: .78rem; }
.strong { font-weight: 650; }
.right { text-align: right; }
.mono { font-variant-numeric: tabular-nums; }

.scroll-x { overflow-x: auto; }

/* A filter chip that is a LINK.
 *
 * Kobopay and Techsub already carry `.chip`, authored for the amount buttons
 * on the demo pages, and the buy page's narrowing chips reuse those rules so
 * each pack keeps its own pill shape and its own selected colour. They were
 * written for <button> though, and an <a> brings an underline and no pointer
 * with it, so without this the chips on both packs rendered as underlined
 * text in a pill. Meridian has no `.chip` of its own and states the whole
 * thing in its theme.
 *
 * Here rather than three times over because it is the same correction in all
 * three, and it is a correction to the ELEMENT rather than to any pack's
 * design. */
a.chip {
  text-decoration: none; cursor: pointer;
  /* An <a> is inline, so the pack's vertical padding lands on a box that does
     not centre its own text. `a.chip` outranks every pack's `.chip` on
     specificity whatever the load order, so this holds in all three. */
  display: inline-flex; align-items: center;
  /* A flex item does not shrink below its content, which is the overflow
     noted at the top of this file. The picker keeps long labels out of chips
     and puts them in a dropdown instead, so this should never bind - and it
     costs nothing to make sure one long word cannot widen the whole page on
     a phone. */
  max-width: 100%;
}

@media (max-width: 900px) {
  .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .wrap { padding-inline: 16px; }
  .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
}
