/* Niadirect Space: the foundation.
 *
 * Reset, scale, typography, layout. Everything here is structural; nothing here
 * decorates. VII-1: a visual decision that cannot say what operational purpose
 * it serves is removed.
 *
 * Mobile-first by construction (V-3). The narrow layout is the default and the
 * wide one is the enhancement, because the promoter this platform is built for
 * is holding a phone on a 3G connection, not sitting at a desk.
 */

/* --------------------------------------------------------------------------
 * The typeface
 *
 * Inter, one variable file, self hosted, carrying the whole weight ramp.
 *
 * CHOSEN AGAINST THE ALTERNATIVES RATHER THAN BY HABIT. Three families were
 * measured at the latin subset a Kenyan platform actually needs: IBM Plex Sans
 * cost 134KB for three static weights, Inter cost 141KB for the same three, and
 * Inter as a single variable file costs 47KB for every weight between 400 and
 * 700. That is the entire ramp for a third of the weight, on connections where
 * a kilobyte is a real delay. Source Sans and Public Sans came in lighter still
 * and were not taken: one is deliberately neutral and the other is a government
 * face, and neither is drawn for the twelve and fourteen pixel sizes where this
 * interface does its work.
 *
 * Two of its features are here for reasons this product has and most do not.
 * Referral codes are uppercase alphanumeric, so a zero read as an O is a
 * promoter who cannot be paid: the code style below asks for the slashed zero
 * and the disambiguated l and I. And every figure on the platform is money, so
 * money is set in genuine tabular figures rather than in whatever the face
 * happens to default to.
 *
 * font-display: swap on purpose. On a slow connection the interface renders
 * immediately in the fallback and swaps when the face arrives. Somebody waiting
 * on a blank screen for a font has been failed by a decoration.
 * ---------------------------------------------------------------------- */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/inter-variable.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
 * Scale
 *
 * One spacing rhythm, one radius set, one shadow. A second shadow would be
 * decoration; VII-4 allows exactly one.
 * ---------------------------------------------------------------------- */

:root {
  --s1: 0.25rem;   /*  4px */
  --s2: 0.5rem;    /*  8px */
  --s3: 0.75rem;   /* 12px */
  --s4: 1rem;      /* 16px */
  --s5: 1.5rem;    /* 24px */
  --s6: 2rem;      /* 32px */
  --s7: 3rem;      /* 48px */
  --s8: 4rem;      /* 64px */

  --r-sm: 3px;
  --r-md: 5px;
  --r-lg: 7px;
  --r-pill: 999px;

  /* VII-3 bans oversized rounding, gradients and glow. This is the whole of the
   * platform's depth vocabulary, and it is used sparingly.
   *
   * --emboss is a one pixel highlight along the top inside edge of a surface,
   * which is where light would catch it. It is a border drawn with a shadow
   * because a border cannot be drawn on one edge inside a rounded corner. It is
   * not a gradient: it is one line, one pixel, at one opacity. */
  --shadow: 0 10px 28px -14px rgba(0, 0, 0, 0.68);
  --emboss: inset 0 1px 0 rgba(255, 255, 255, 0.055);

  --edge: rgba(255, 255, 255, 0.13);
  --edge-strong: rgba(255, 255, 255, 0.22);

  --rail-w: 13.5rem;
  /* Wide enough that a very large display does not run a line of text across a
   * metre of glass, far wider than the column the app used to sit in. */
  --content-max: 120rem;
  --gutter: 1.25rem;

  --font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;

  --step: 140ms cubic-bezier(0.2, 0, 0.2, 1);
}

/* --------------------------------------------------------------------------
 * Reset
 * ---------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  min-height: 100vh;
  background: var(--surface-canvas);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 1rem;           /* 16px. Read at arm's length, outdoors, on a phone. */
  line-height: 1.55;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
table { border-collapse: collapse; width: 100%; }

a { color: var(--accent); text-decoration: none; }

/* Inline links in running prose: the privacy policy, the terms, the footer.
 * These sit inside sentences and have to be findable without colour alone, so
 * they carry a rule set below the descenders rather than through them. */
.doc a,
.public-foot a,
p > a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  text-decoration-color: rgba(255, 255, 255, 0.32);
}
.doc a:hover,
.public-foot a:hover,
p > a:hover { text-decoration-color: currentColor; }

/* The focus ring is the accent at its full strength with a gap around it, so it
 * reads on every surface in the ramp without being drawn twice. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}
.list-row:focus-visible { outline-offset: -2px; }

/* VII-4. Motion is state-transition logic only. Someone who has asked their
 * system for less of it gets none. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
 * Typography
 *
 * Six sizes, no more. Hierarchy comes from size, weight and colour together,
 * never from one of them working alone.
 * ---------------------------------------------------------------------- */

.t-display {
  font-size: clamp(1.875rem, 1.2rem + 2.6vw, 2.75rem);
  line-height: 1.08;
  font-weight: 700;
  letter-spacing: -0.028em;
}

.t-title {
  font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.5rem);
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.012em;
}

.t-head {
  font-size: 1.125rem;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.008em;
}

.t-body { font-size: 1rem; line-height: 1.55; }

.t-small {
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* The field caption. Uppercase, tracked, quiet: it names the number below it
 * and then gets out of the way. */
.t-label {
  font-size: 0.6875rem;
  line-height: 1.2;
  font-weight: 600;
  letter-spacing: 0.075em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  /* Uppercase sits high in its line box; this restores the optical spacing
   * under it so a caption and its figure read as one unit. */
  display: block;
  margin-bottom: 0.45rem;
}

.t-secondary { color: var(--text-secondary); }
.t-tertiary  { color: var(--text-tertiary); }

/* Money is set in tabular figures everywhere, so a column of shillings aligns
 * on the digit rather than wandering with the glyph widths. */
.money {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  font-weight: 600;
  letter-spacing: -0.014em;
  white-space: nowrap;
}

/* The currency mark: smaller, a step lighter, and held off the figure by a
 * measured amount rather than by a space character. Aligned to the top of the
 * figures because these are lining numerals and the mark reads as a superior
 * when it sits with their cap line. */
.money-mark {
  font-size: 0.88em;
  margin-right: 0.22em;
}

.money-figure { font-variant-numeric: tabular-nums; }

/* At display size the mark steps down further, because four digits set at
 * three rem next to a three letter mark of the same size is a mark that shouts.
 * It stays on the same baseline and in the same colour. */
.money-lg .money-mark { font-size: 0.5em; margin-right: 0.24em; }

.money-lg {
  font-size: clamp(2.25rem, 1.5rem + 3.2vw, 3.25rem);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -0.035em;
}

.money-pos { color: var(--money-positive); }
.money-neg { color: var(--money-negative); }
.money-nil { color: var(--money-neutral); }

/* A resting balance takes the colour of the text around it.
 *
 * The money tones say which way money moved: in, out, or nowhere. A balance has
 * not moved, so it was being drawn in the neutral tone, and a mid grey figure
 * under a bright label reads as unavailable rather than as at rest. Colour is
 * kept for movement, and the figure somebody is looking at is simply legible.
 * The zero case still greys, because there a grey figure is the truth. */
.money-plain { color: inherit; }

.num { font-variant-numeric: tabular-nums; }

.code {
  font-variant-numeric: slashed-zero tabular-nums;
  font-feature-settings: "zero" 1, "cv05" 1, "cv08" 1;
  font-weight: 500;
  letter-spacing: 0.08em;
}

.wrap-anywhere { overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
 * Layout
 * ---------------------------------------------------------------------- */

.stack > * + * { margin-top: var(--s4); }
.stack-sm > * + * { margin-top: var(--s2); }
.stack-lg > * + * { margin-top: var(--s6); }

/* Between one section of a screen and the next. Deliberately much larger than
 * the space inside either of them: proximity is what tells somebody scanning
 * the page where one idea ends. */
.sections > * + * { margin-top: var(--s7); }

/* A horizontal row, built on grid rather than flex. Flexbox did not carry gap
 * until Chrome 84 and the platform's floor is Chrome 80 (VII-7); grid has
 * carried it since Chrome 66, so the row that holds a screen's actions is built
 * on the property that works at the floor rather than four versions above it. */
.row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  justify-content: start;
  align-items: center;
  gap: var(--s3);
}
.row-between { grid-auto-columns: auto; justify-content: space-between; width: 100%; }
.row-baseline { align-items: baseline; }
.row-wrap { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; }
.grow { min-width: 0; }

.page {
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--gutter) var(--gutter) var(--s8);
}

@media (min-width: 40rem) {
  .page { --gutter: 1.5rem; padding: var(--s5) var(--gutter) var(--s8); }
}

@media (min-width: 90rem) {
  .page { --gutter: 2rem; }
}

/* A grid that never needs a media query per screen: cards choose their own
 * width and the row reflows. At 320px every card is full width.
 *
 * auto-fill rather than auto-fit, deliberately. auto-fit collapses the tracks
 * nothing occupies, so a single campaign stretched one card across a desk
 * monitor and looked like a mistake. auto-fill keeps the track, so one card is
 * the same size as one of four. */
.grid {
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fill, minmax(min(19rem, 100%), 1fr));
}

.grid-2 {
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
}

.hide { display: none !important; }

@media (min-width: 56rem) {
  .only-narrow { display: none !important; }
}
@media (max-width: 55.999rem) {
  .only-wide { display: none !important; }
}

/* Present to a screen reader, absent from the page. Used for the labels that a
 * sighted reader gets from position and a blind reader does not. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--s3);
  top: -4rem;
  z-index: 60;
  padding: var(--s2) var(--s4);
  background: var(--accent);
  color: var(--text-inverse);
  border-radius: var(--r-sm);
  font-weight: 600;
  transition: top var(--step);
}
.skip-link:focus { top: var(--s3); text-decoration: none; }
