/* Gaia Rangers — the design system, as tokens.
 *
 * Extracted from the app rather than invented for it: every value below was already being used,
 * often in several spellings. Loaded before styles.css, which may use these and nothing else.
 *
 * Three layers, and the rule is that a component never reaches past the one above it:
 *
 *   primitive   the raw value — a hex, a number of pixels. Named after what it IS.
 *   semantic    what the value is FOR — a surface, a line, a warning. Components use these.
 *   component   the few places a component needs its own knob.
 *
 * Why it matters here: the app is one screen deep in a phone, and the same card is drawn on a
 * shelf, on a map, in a feed, in a sheet and on a public web page. When a card's radius is 22px
 * in five files it is five decisions; when it is --radius-card it is one.
 */

:root {
  /* ---------------------------------------------------------------- 1. Primitive
   * The palette is one hue — the sage green of the app's ground — walked from near-black to the
   * page itself, plus two colours that are allowed to interrupt it. Nothing else is a brand
   * colour; the photographs are the colour in this app.
   */
  --sage-900: #0F2A20;                  /* text, and the darkest thing on screen */
  --sage-800: #173F31;                  /* dark cards, the bottom of gradients */
  --sage-700: #1D5540;                  /* the one strong green: buttons, chips that are on */
  --sage-600: #2A4E40;                  /* secondary text */
  --sage-500: #2B6D53;                  /* the top of the guardian-card gradient */
  --sage-glow: #7FDCB4;                 /* the lit ring around a circular action — the one sage
                                           brighter than the page, and never a text colour */
  --sage-300: #6FA98A;                  /* common rarity, quiet marks */
  --sage-200: #C3DDCB;                  /* page, bottom */
  --sage-100: #D7E9DC;                  /* page, top */
  --sage-050: #E3EAE6;                  /* an empty photo well */
  --white: #FFFFFF;

  /* Paper. The one surface in the app that is not glass over sage: an envelope, a seal, a card
   * dressed as a field note. Four values because an envelope is two gradients, not one colour. */
  --paper-100: #F1E9D6;
  --paper-200: #E8DCC1;
  --paper-300: #DDCDAA;
  --paper-400: #D2BF98;
  --paper-edge: rgba(120, 100, 60, .25);

  --gold-500: #E9B949;                  /* emblematic, legendary, the one celebratory colour */
  --gold-700: #D4AF37;                  /* the same gold in metal: foil, a seal, an envelope */
  --clay-600: #9E3B2A;                  /* the alert: a failure, a refusal, a last chance */

  /* Rarity is data, not decoration: these are the colours the game assigns, not a ramp. */
  --gem-common: #6FA98A;
  --gem-special: #2E8B61;
  --gem-rare: #2F7FA8;
  --gem-epic: #8A5BB5;
  --gem-legendary: #E9B949;
  --gem-unranked: rgba(15, 42, 32, .35);

  /* Glass is how this app makes depth: white at a handful of strengths over the sage page.
   * These five replace the twelve alpha values that had grown across the stylesheet. */
  --white-a35: rgba(255, 255, 255, .35);
  --white-a45: rgba(255, 255, 255, .45);
  --white-a60: rgba(255, 255, 255, .60);
  --white-a70: rgba(255, 255, 255, .70);
  --white-a85: rgba(255, 255, 255, .85);

  /* And ink at a few strengths, for hairlines, scrims and shadows. */
  --accent-a12: rgba(29, 85, 64, .12);  /* the accent as a wash, behind a chip that is chosen */
  --ink-a08: rgba(15, 42, 32, .08);
  --ink-a18: rgba(15, 42, 32, .18);
  --ink-a35: rgba(15, 42, 32, .35);
  --ink-a45: rgba(15, 42, 32, .45);
  --ink-a75: rgba(15, 42, 32, .75);    /* the shadow under small type with no ground of its own */

  /* ---------------------------------------------------------------- 2. Semantic */

  /* Surfaces — what a thing is made of. `raised` is the glass a card or panel is cut from;
   * `float` is the stronger glass used by anything that sits over the map or over content. */
  --surface-page: linear-gradient(170deg, var(--sage-100) 0%, var(--sage-200) 100%);

  /* The scene. The ground the mini app stands on: one photograph, fixed, behind every screen
   * except Explore — where the map IS the ground and the scene would only be a second picture
   * nobody can see.
   *
   * No veil. Martin's call, and it is the right one for how the app looks: a white wash over a
   * photograph makes it a texture instead of a place. The sage gradient stays as the last layer,
   * because it is what shows if the photograph fails to load — a screen that cannot fetch an
   * image should still be the app's own colour rather than white.
   *
   * What the veil was doing has to be done somewhere, though, and it is now done by the text
   * instead: see --page-halo below and `.screen > .h1` in styles.css. This was measured before
   * it was decided — brightening the photograph instead cannot work, because a picture of dark
   * green leaves contains pixels at exactly the luminance of dark green type, at any exposure.
   * Either the picture is dimmed or the letters carry their own ground. The letters do. */
  --scene: url("/brand/scene.jpg");
  --surface-scene: var(--scene) center top / cover no-repeat,
    linear-gradient(170deg, var(--sage-100) 0%, var(--sage-200) 100%);

  /* The ground a letter carries with it, for the four things the app writes straight onto the
   * scene with nothing behind them.
   *
   * The four offset copies are an outline, not a glow, and they are the part that works: a blur
   * alone was fine under a 40px heading and useless under a 13px note, because a soft edge on
   * small type reads as fuzz rather than as separation. The outline gives every letter a hard
   * pale edge at any size; the two blurs behind it lift the whole word off busy ground. It costs
   * the picture nothing, which a wash over the whole page does. */
  /* The ground the opened card's name stands on. The hero photograph used to be masked away to
   * nothing across its bottom half, which was right while the page behind it was a flat pale
   * gradient — the picture dissolved into the page, and the name sat on the page. The page is a
   * photograph now, so that fade dissolved a picture of an animal into a picture of a beach, and
   * dropped the name and the latin onto open water where, measured, the latin could not be read
   * at all. The picture keeps its own ground to the bottom now and finishes on this plate. */
  --hero-plate: rgba(240, 248, 243, .97);
  --hero-plate-soft: rgba(240, 248, 243, .62);

  --page-halo:
    -1px -1px 0 rgba(240, 248, 243, .96), 1px -1px 0 rgba(240, 248, 243, .96),
    -1px  1px 0 rgba(240, 248, 243, .96), 1px  1px 0 rgba(240, 248, 243, .96),
    0 0 6px rgba(240, 248, 243, .92),
    0 0 16px rgba(240, 248, 243, .75);

  --surface-raised: var(--white-a45);
  --surface-float: var(--white-a85);
  --surface-solid: var(--white);
  --surface-dark: var(--sage-800);
  --surface-well: var(--sage-050);
  --surface-paper: linear-gradient(165deg, var(--paper-100) 0%, var(--paper-300) 100%);

  /* Ink — text and marks. Three weights is all the app has ever needed. */
  --ink-strong: var(--sage-900);
  --ink-muted: var(--sage-600);
  --ink-on-dark: var(--white);

  /* Lines and edges. A card's edge is a light hairline, never a border colour. */
  --line: var(--white-a60);
  --line-strong: var(--white-a70);
  --line-ink: var(--ink-a08);

  /* The species' category colour, as a card's ground, its spine and its type label. Every card
   * sets this for itself from the catalogue; this is the value for anything that does not, so
   * nothing has to carry a hex of its own. */
  --tint: var(--sage-600);

  /* Accents. One green that means "this is chosen or this is the action", one gold that means
   * "this is rare", one clay that means "this went wrong". */
  --accent: var(--sage-700);
  --accent-ink: var(--white);
  /* Gold is a FILL, never an ink on anything light. Measured: gold as ink on a glass row is
   * 1.56:1, and --gold-700 is 1.79 — neither gold in this palette can carry text there. As a
   * ground under --ink-strong it is 8.38:1. First place wears the gold; it is not written in it. */
  --prize: var(--gold-500);
  --prize-ink: var(--ink-strong);
  --danger: var(--clay-600);

  /* Type. The ladder the app already climbs: three reading sizes, three labels, three headlines.
   * Poppins for everything; the serif appears only where a card imitates a field note. */
  --font: Poppins, -apple-system, system-ui, sans-serif;
  --text-tiny: 9px;                     /* the uppercase tag over a photograph, and only that */
  --text-micro: 10px;                   /* uppercase labels */
  --text-caption: 11px;
  --text-small: 12px;                   /* the app's most common size: notes, hints, meta */
  --text-note: 13px;                    /* the second most common: a line under a heading */
  --text-body: 14px;
  --text-lead: 15px;
  --text-card: 17px;                    /* a species name on a card */
  --text-title: 22px;                   /* a sheet's title */
  --text-stat: 24px;                    /* a number worth reading across a room */
  --text-heading: 32px;
  --text-display: 44px;                 /* a screen's name, once per screen */

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --tracking-label: .1em;               /* uppercase micro labels only */
  --tracking-tight: -.02em;             /* headings, where Poppins is otherwise airy */

  /* Space. A two-pixel grid, and six of the steps do almost all the work. */
  --space-1: 2px;
  --space-2: 4px;
  --space-3: 6px;
  --space-4: 8px;
  --space-5: 10px;
  --space-6: 12px;
  --space-8: 16px;
  --space-10: 20px;
  --space-12: 24px;
  --space-16: 32px;

  /* Radius. Round in this app means soft, not circular — a card is rounder than a chip, and a
   * pill is fully round. The two odd ones (9px, 14px) are the small tags and the sheet rows. */
  --radius-tight: 4px;
  --radius-tag: 9px;
  --radius-chip: 12px;
  --radius-row: 14px;
  --radius-panel: 16px;
  --radius-photo: 22px;
  --radius-card: 28px;
  --radius-sheet: 32px;
  --radius-pill: 999px;
  --radius-round: 50%;

  /* How much of the top and bottom of the screen belongs to somebody else. Normally the phone's
   * own notch and home bar. In Telegram's fullscreen mode the app also runs underneath Telegram's
   * floating close and menu buttons, which env() knows nothing about, so app.js raises these two
   * when Telegram says so. Every fixed edge in styles.css measures from here rather than from
   * env() directly, which is what makes that possible. */
  --inset-top: env(safe-area-inset-top, 0px);
  --inset-bottom: env(safe-area-inset-bottom, 0px);
  /* The sides matter for one reason only: in fullscreen Telegram's close and menu buttons float
   * at the top RIGHT, over the app. Correct top padding does not help if the collision is
   * sideways. Nothing is reported on any client today, so these are zero and nothing moves. */
  --inset-left: env(safe-area-inset-left, 0px);
  --inset-right: env(safe-area-inset-right, 0px);

  /* Elevation. Four, and they mean different heights rather than different blurs. */
  --lift-flat: 0 2px 10px var(--ink-a08);
  --lift-float: 0 6px 18px var(--ink-a18);
  --lift-card: 0 12px 28px rgba(15, 42, 32, .22);
  --lift-pop: 0 18px 26px rgba(15, 42, 32, .5);       /* a subject standing off its card */
  --lift-scene: 0 24px 60px var(--ink-a45);           /* an envelope opening, a card being dealt */

  /* The same four heights in black, for anything casting onto a photograph or the map. Green
   * shadows over imagery read as a colour cast rather than as a shadow. */
  --shade-float: 0 6px 18px rgba(0, 0, 0, .18);
  --shade-card: 0 8px 22px rgba(0, 0, 0, .2);
  --shade-scene: 0 30px 60px rgba(0, 0, 0, .45);

  /* Three that are not heights, and would be lost if they were read as one. */
  --glow-accent: 0 8px 18px rgba(29, 85, 64, .4);     /* the camera button, glowing its own green */

  /* A circular action is lit, not merely filled. One bright ring hard against the edge, one soft
   * bloom outside it, and the app's own drop beneath — so the button reads as the lamp rather than
   * as a green disc with a border drawn on it. Both rings are the same hue; only the alpha and the
   * spread differ, which is what keeps it a glow and not an outline. */
  --glow-ring:
    0 0 0 3px color-mix(in srgb, var(--sage-glow) 88%, transparent),
    0 0 18px 5px color-mix(in srgb, var(--sage-glow) 42%, transparent),
    0 10px 22px var(--ink-a35);
  /* The same light at the size a button in a row can carry. */
  --glow-ring-sm:
    0 0 0 2px color-mix(in srgb, var(--sage-glow) 88%, transparent),
    0 0 12px 3px color-mix(in srgb, var(--sage-glow) 40%, transparent),
    0 6px 14px var(--ink-a35);
  /* The face of a lit action: lighter at the shoulder, darker at the foot, so the disc reads round. */
  --glow-face: radial-gradient(120% 120% at 50% 12%, var(--sage-500) 0%, var(--sage-800) 100%);
  --lift-seam: 0 1px 2px rgba(15, 42, 32, .04);       /* a seam between two pale surfaces */
  --shade-strong: 0 6px 14px var(--ink-a35);          /* a mark that must hold against any terrain */

  --blur-glass: blur(14px);

  /* Motion. Two curves: one that settles, one that overshoots. Anything that only spins is
   * linear. A duration longer than --dur-slow belongs to a celebration, not to an interface. */
  --dur-fast: .2s;
  --dur-base: .3s;
  --dur-slow: .5s;
  --ease-settle: cubic-bezier(.2, .9, .3, 1);
  --ease-overshoot: cubic-bezier(.2, 1.4, .4, 1);

  /* Layers. Written down because they were being guessed at. */
  --z-card: 1;
  --z-cutout: 2;
  --z-card-label: 3;
  --z-map-chrome: 3;
  --z-sheet-back: 30;
  --z-sheet: 31;

  /* ---------------------------------------------------------------- 3. Component */

  --card-bg: var(--surface-raised);
  --card-radius: var(--radius-card);
  --card-edge: 1px solid var(--line);
  --card-photo-radius: var(--radius-photo);

  /* A card is lit, not outlined. A uniform rim reads as print; a bright edge along the top and
   * left with shade along the bottom is what the eye reads as glass. --card-bleed is the
   * category's colour leaking out from under the card onto the page — the shelf stays one
   * surface, which a hard-edged tinted well behind each card does not. All three take --tint
   * from the card, so they resolve per category at the point of use. */
  --card-edge-lit: inset 0 1px 0 var(--white-a85), inset 1px 0 0 var(--white-a45),
                   inset 0 -1px 0 color-mix(in srgb, var(--tint) 30%, transparent);
  --card-lift: 0 10px 20px color-mix(in srgb, var(--tint) 26%, var(--ink-a08));
  --card-bleed: 0 0 34px 8px color-mix(in srgb, var(--tint) 18%, transparent);
  /* The photograph dissolves into the card rather than sitting in a frame inside it. */
  --card-photo-fade: linear-gradient(to bottom, #000 0%, #000 52%, rgba(0, 0, 0, .55) 78%,
                                     transparent 100%);

  --chip-height: 44px;                  /* also the minimum a thumb can hit comfortably */
  --chip-radius: var(--radius-pill);
  /* A chip is on the page, so it needs what the page needs. .35 measured 2.7:1. */
  --chip-bg: var(--white-a85);
  --chip-bg-on: var(--accent);
  --chip-ink-on: var(--accent-ink);

  --sheet-bg: var(--surface-float);
  --sheet-radius: var(--radius-sheet);
  --sheet-pad: 18px;

  --button-height: 52px;
  --nav-height: 86px;                   /* the bar, taller than it was: each destination says its
                                           name now, and a name needs a line of its own */
  --nav-action: 62px;                   /* the lit circle at the middle of it */
  --button-radius: var(--radius-pill);

  --tap-min: 44px;                      /* nothing tappable is smaller than this */
  /* How far a control may grow its hit area upward when it is shorter than --tap-min and has a
   * neighbour above it. Two controls in a column with a 6px gap and -5px of growth each overlap
   * by 4px, and the one that paints later wins — which is how a tap just below Accept came to
   * decline a swap. Growing up by less than half the smallest gap makes that impossible. */
  --tap-grow-up: 2px;
  --tap-grow-down: 8px;

  /* A control that is working. `opacity` composites a control's ink together with its ground and
   * halves the separation between them — measured 3.15:1 on a "Sending…" button and 3.46:1 on a
   * busy Accept, at the one moment a ranger wants to read what they just pressed. A busy control
   * is not an inactive one: it dims its ground and keeps its ink. */
  /* A working control sits on the page like anything else, so it needs what the page needs. At
   * white-a35 its ink measured 1.61:1 over the worst ground the scene can put behind it — and this
   * is the state a ranger most wants to read, because the word on it is the only evidence the app
   * is doing anything. The dimming is carried by the ink and the edge, not by letting the page
   * through. */
  --busy-bg: var(--white-a85);
  --busy-ink: var(--ink-muted);
  --busy-edge: var(--ink-a08);

  /* Difficulty has NO colour family, and this is the note saying why rather than an omission.
   *
   * It was --gem-common/rare/epic/legendary written out as hex, which is the rarity ramp used as
   * decoration and is forbidden twice over. Replacing it with four strengths of one hue looked
   * like the obvious answer and is arithmetically impossible: four steps that can be told apart
   * need 3:1 between adjacent ones, which from pure black reaches luminance 1.30 by the fourth,
   * while a fill carrying white ink is capped at 0.1833. Even two steps do not both fit. Measured,
   * the ramp that shipped had 1.31-1.41:1 between its steps — four indistinguishable dark greens,
   * and on the camera's dark ground a FILLED progress pip came out darker than an empty one, so
   * the row appeared to lose marks as the ranger made progress.
   *
   * The old ramp worked because it varied HUE, which a contrast ratio does not measure and which
   * is why nobody noticed: green, blue, purple and gold are trivially distinct and measure 1.11:1
   * between the middle two. So the only honest answers are a second hue family — which the app
   * already spends on rarity — or not colouring difficulty at all.
   *
   * Difficulty is a WORD. It is written on the mission's tag, and one accent means one accent. */

  /* The ground a word takes when it sits on a photograph it does not choose. Full strength flat
   * across every letter, feathering away at each end and nowhere else: a radial centred on a mark
   * puts its strength where the mark is and its weakness where the word is, which is how the
   * rarity word came to measure 1.00-3.93:1 over a light photograph. A line of text wants a
   * ground shaped like a line of text. Pair with generous side padding so the ramps fall outside
   * the longest word; --ink-a75 is 6.81:1 under white. */
  --band-on-photo: linear-gradient(90deg, transparent 0%, var(--ink-a75) 11%,
                                   var(--ink-a75) 89%, transparent 100%);

  /* The edge a bar fill carries when its colour is data. No single track can hold both gold and
   * green: Legendary measured 1.09:1 against its own track and was a bar that was not visibly
   * there. The fill takes its own outline instead, which holds whatever hue it is given. */
  /* A bar whose fill is coloured by the game needs an edge, because no single track can carry
   * both gold and green — but no single edge works either, and that is the part the first version
   * got wrong. An inset hairline sits ON the fill, so it has to separate from the fill, and the
   * palette runs from gold to epic violet. Measured against every rarity: ink at any alpha fails
   * on epic (2.97:1 even at .95), white at any alpha fails on gold (1.77:1 at .95). The two
   * polarities fail at opposite ends of the same palette.
   *
   * So it is two rings, not one: ink outside, white inside. Each only has to separate on the side
   * it touches, so whichever the fill is, one of them is doing the work — gold is held by the ink
   * ring at 4.85:1, epic by the white ring at 4.07:1, and the worst rarity in the set (special) is
   * 3.51:1 against the 3:1 a meaningful graphic needs.
   *
   * It is 2px of edge rather than 1px. That is the price of a fill whose colour is data. */
  --fill-edge: inset 0 0 0 1px var(--ink-a75), inset 0 0 0 2px var(--white-a85);

  /* ---------------------------------------------------------------- Compatibility
   * The names the stylesheet has used until now. They are aliases, not a second palette: each
   * points at the token above that replaced it, so nothing moves while the sheet is converted.
   */
  --ink: var(--ink-strong);
  --ink2: var(--ink-muted);
  --deep: var(--accent);
  --gold: var(--prize);
  --alert: var(--danger);
  --bg1: var(--sage-100);
  --bg2: var(--sage-200);
  /* Glass had two strengths while the page was a flat sage gradient. It has one now, because the
   * page is a photograph and the weaker one does not work on it.
   *
   * Measured against brand/scene.jpg rather than judged: the darkest pixel in the picture is
   * luminance .0004, and a 14px backdrop blur — which every glass surface carries — lifts the
   * darkest ground any of them can actually sit on to .0067. Composite white over that and
   * --ink-muted, the palest ink the app puts on a panel, needs alpha .681 to reach 4.5:1. The old
   * --glass was .45, which gives 2.3:1: not dim, unreadable. --white-a70 clears the floor by .23
   * and that is too thin a margin to hold a whole app up, so both land on --white-a85, which
   * measures 11.1:1 for --ink-strong and 6.8:1 for --ink-muted over the worst ground in the
   * picture, and cannot fail on a lighter one.
   *
   * The cost is that a panel is now nearly opaque, and the two-step scale collapses. That scale
   * was a luxury of a page with nothing behind it. --glass-strong is kept as a name because nine
   * rules say it, and it also stops being a hand-written rgba that happened to sit .03 from a
   * token that already existed. */
  --glass: var(--white-a85);
  --glass-strong: var(--white-a85);
}
