/* ══════════════════════════════════════════════════════════════
   Meetback — design tokens as CSS custom properties
   Generated from design-system/tokens/tokens.json.
   Every screen/component reads these vars; do not hard-code values
   that live here. --profile is set per-card at render time.
   ══════════════════════════════════════════════════════════════ */

:root {
  /* brand */
  --profile: #3f7bff;              /* per-card accent, overridden at runtime */
  --ink: #0f1220;
  --surface: #ffffff;
  --surface-muted: #f5f5f7;

  /* dark surfaces (Share hub, present mode) */
  --dk-bg: #111318;
  --dk-surface: #1e222b;
  --dk-line: #2a2f3a;
  --dk-text: #f3f4f7;
  --dk-muted: #9aa0ac;
  --dk-faint: #6b7180;

  /* state dots */
  --ok: #3fb950;
  --busy: #d29922;
  --err: #f85149;

  /* type */
  --font-display: 'Fraunces', serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: ui-monospace, 'SF Mono', Menlo, monospace;

  --t-xs: clamp(11px, 3vw, 12px);
  --t-sm: clamp(13px, 3.6vw, 14px);
  --t-base: clamp(15px, 4vw, 16px);
  --t-lg: clamp(17px, 4.6vw, 19px);
  /* ⚠ Q43 — A RUNG THE LADDER DID NOT HAVE. The editor hardcoded 19px on every
     field, and the item's own complaint is that "the type ladder still has no
     rungs at 19-20px throughout". Moving those fields onto --t-lg would have
     SHRUNK them: at a 375pt width --t-lg resolves to 17.25px, not 19. The
     ruling anticipated exactly this — "if Inter at 19px reads worse than the
     system font did, the answer is the type scale, not a quiet return to the
     exemption" — so the scale gains the rung instead of the form losing size.
     19.1px at 375, 21px at 430: never below what shipped. */
  --t-form: clamp(19px, 5.1vw, 21px);
  --t-xl: clamp(20px, 5.5vw, 24px);
  --t-display: clamp(26px, 7.6vw, 33px);

  /* radius */
  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 22px;
  --r-pill: 100px;

  /* spacing */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s8: 32px; --s10: 40px;

  /* shadows */
  /* ══ Q32 — the app's colours, in ONE place ══════════════════
     Before 2026-08-06 there were SIX palettes: this file plus one declared
     privately by cards-home, public-card, share-hub, editor and editor-item.
     252 literal colours lived in the ten stylesheets and only 20 of them were
     here, so most of the interface was not reading the palette at all — and
     nothing could be themed, which is what Q28 walked into.

     EVERY VALUE BELOW IS THE ONE THAT WAS ALREADY BEING PAINTED. Nothing was
     reconciled, corrected or merged on the way in: this item moves colours, it
     does not change them, and the harness's 24 screenshots are compared before
     and after to prove it.

     ⚠ WHICH MEANS THE DRIFT IS NOW VISIBLE HERE, AND IT SHOULD BE. Four page
     greys sit within 4/255 of each other (#f4f4f6 #f4f4f5 #f2f2f3 #f0f0f2) and
     three mid-greys within 8 (#c2c2c6 #c0c0c5 #bfbfc1). Those are almost
     certainly one colour each that drifted across five files, not six
     deliberate choices. Merging them is a VISIBLE change and therefore not this
     item's business — but it is now a five-line diff in one file instead of an
     archaeology exercise, which is the whole point of having done this. */

  /* page + surfaces, light → dark */
  --page: #f4f4f6;
  --surface-1: #fbfbfd;
  --surface-2: #f4f4f5;
  --surface-3: #f2f2f3;
  --surface-4: #f0f0f2;
  --surface-5: #e3e3e6;

  /* the monogram gradient, drawn on cards-home, editor and public-card */
  --mono-1: #eef0ff;
  --mono-2: #e3e6ff;

  /* neutral ramp — named by lightness, because that is all these are */
  --n-300: #c9c9ce;
  --n-320: #c2c2c6;
  --n-330: #c0c0c5;
  --n-340: #bfbfc1;
  --n-380: #b7b7bb;
  --n-450: #a6a6ab;
  --n-600: #7c7c81;
  --n-650: #6c6c74;
  --n-800: #3c3c43;

  /* inks */
  --ink-900: #101014;
  --ink-950: #111111;
  --ink-975: #0a0a0c;

  /* the blue-greys: cards-home's stock gradients, share-hub's dark chrome */
  --slate-300: #cbcbd2;
  --slate-400: #9aa1b2;
  --slate-500: #7c8497;
  --slate-600: #6c7488;
  --slate-700: #3a3f4c;
  --slate-800: #232833;
  --slate-850: #23232b;
  --slate-900: #14151a;

  /* ── ON-CARD, and these do NOT follow the theme (Q28) ──
     The card face is the OWNER'S card: it is the same object in either mode,
     drawn on their own accent, and the chips that sit on it keep white
     backgrounds regardless. So their ink has to stay dark. Using --ink-950
     here is what turned the ⋯ chip into a blank white circle in the first dark
     build — the background stayed white and the glyph inverted with the theme.
     Anything painted ON the card reads from these, and nothing overrides
     them below. */
  --on-card-ink: #111111;
  --on-card-ink-inv: #ffffff;

  /* ── the camera's two absolutes (Q44) ──
     ⚠ THESE ARE NOT PALETTE COLOURS AND THEY ARE HERE ANYWAY. The viewfinder's
     surround is TRUE black and the shutter is TRUE white — not the app's dark
     surface and not its paper, because a camera's chrome is the same on every
     phone and reading the app's palette there would make the viewfinder change
     colour with the theme, which no camera does. They live in tokens.css
     because one_palette is right that a literal outside this file is a second
     palette waiting to happen; naming them here is the difference between a
     decision and a stray hex. */
  --cam-surround: #000000;
  --cam-shutter: #ffffff;

  /* semantic */
  --accent-dk: #4338ca;
  --err-soft: #ffd9d6;
  --err-strong: #d93025;
  --warn-1: #f4c76b;
  --warn-2: #e8a15f;
  --olive: #8f8f7a;
  /* Q6 — CAUTION, which is not the same thing as ERROR and must not look like
     it. The scan-confirm surface marks fields the reader is unsure about, and
     an unsure field is not a failure: it is a value that is probably right and
     wants a glance. --err is the colour of the one irreversible action in this
     app, and spending it on "check this" would flatten the difference between
     "you may lose something" and "I may have misread something".
     Values are the adopted reference's own (assets/scan-confirm.html). */
  --caution: #a15c00;
  --caution-bg: #fff8ec;
  --caution-line: #e8b569;

  /* ── the five per-screen palettes, moved here ──
     cards-home, public-card, share-hub, editor and editor-item each declared
     their own colours. The NAMES stay where they are — renaming would touch
     hundreds of call sites for nothing — but every VALUE now lives here, and
     tests/gates/one_palette.mjs fails any per-screen variable that is not an
     alias of something on this list. A private name is a shorthand; it is not
     a licence to invent a colour.

     ⚠ THREE NAMES MEAN DIFFERENT THINGS ON DIFFERENT SCREENS, and moving the
     values here is what makes that visible. `--ink` is #0f1220 at :root and
     #0a0a0a in the editor. `--muted` is #6b7280 on cards-home and #8a8a8f in
     the editor. `--profile` — the CARD'S OWN ACCENT, set at runtime by
     theme.js — falls back to #3f7bff here and #2f6bff on three screens.
     Reconciling those is a VISIBLE change and therefore not Q32's business;
     Q32 moved colours without altering one. They are named below so the
     decision is a short diff rather than an excavation. */

  /* cards-home */
  --ch-surface2: #f0f0f3;
  --ch-border: #e4e4e9;
  --ch-text: #111114;
  --ch-muted: #6b7280;
  --ch-faint: #9ca3af;
  --ch-accent: #4f46e5;
  --ch-accent2: #6366f1;
  --ch-seal: #8b93a5;
  --ch-stock1: #f5f6f8;
  --ch-stock2: #e9eaef;

  /* editor + editor item */
  --ed-ink: #0a0a0a;
  --ed-muted: #8a8a8f;
  --ed-field: #e7e7ea;
  --ed-fieldtx: #141416;
  --ed-red: #e5484d;

  /* public card */
  --pc-line: #eaeaee;

  /* the card accent's fallback, before theme.js sets the real one */
  --profile-fallback: #2f6bff;
  --profile-fallback-dk: #1f52d6;
  --profile-fallback-lt: #5b8cff;

  /* ── Q30: the destructive palette ──
     ONE red, and it is the only one in the app. It exists so that a control
     which destroys an account cannot be mistaken for the neutral chrome around
     it — but Q24's rule still binds: colour is the SUPPORTING signal and the
     words carry the meaning. --on-del is the ink that goes ON the red, and it
     is declared rather than assumed white, because the dark blocks below move
     the red and an assumed white would silently stop contrasting. */
  --del: #b3261e;
  --del-line: #f3c7c3;
  --on-del: #ffffff;
  --scrim: rgba(20, 20, 28, .42);
  --sheet-shadow: rgba(0, 0, 0, .15);
  /* Q55 — the central button's shadow and the halo bleeding into the bar
     around its notch. Both are tinted with the accent rather than neutral:
     the reference's glow is coloured, and a neutral shadow under a coloured
     circle reads as a sticker rather than as something set into the bar.

     ⚠ BOTH CAME DOWN 2026-08-08, and WHICH thing came down is the whole point.
     Owner, third report: "too intense color". The DISC cannot be washed — a
     white plane sits on it and legible_accent_text governs that pair; a paler
     fill under a white glyph is the contrast failure this project has already
     paid for twice, and the item says so in capitals. What CAN come down is
     everything around it, and that was four accent-coloured things stacked on
     one 56px button: the fill, a 132px halo, a shadow tinted the same hue, and
     the label. The halo goes .14 → .09 and the shadow drops to a neutral ink at
     low alpha, so the button keeps its colour and stops staining the bar it
     sits in. The measured pixel 30px beside the button was rgb(219,218,250) —
     a visible violet cast across a third of the bar's width. */
  --fab-shadow: rgba(15, 18, 32, .28);
  --fab-halo: rgba(79, 70, 229, .09);

  --sh-card: 0 8px 22px rgba(10,20,60,.35);
  --sh-float: 0 20px 50px -22px rgba(0,0,0,.7);
  --sh-seal: 0 0 0 4px #fff, 0 0 0 6px rgba(139,147,165,.55), 0 8px 20px rgba(20,16,60,.32);
}

/* iOS-first base — adaptive invariants */
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
/* Double-tap-to-zoom off, app-wide. This is the zoom people trigger by accident
   — a second tap on a card or a toggle — and unlike `user-scalable=no` it is
   honoured by iOS Safari, because it removes a gesture rather than an
   accessibility guarantee. Deliberate pinch still works there; see the note on
   the viewport meta in index.html.

   `manipulation` still permits pan and pinch, so it does not fight the
   carousel's own `touch-action: pan-y` — that value is narrower and applies to
   the lane itself. touch-action is not an inherited property; it is set here
   because an ancestor's value constrains the gestures available to what is
   under the finger. */
html { touch-action: manipulation; }

/* THE CANVAS, and why it is set here rather than left to propagate.
   Measured for Q7 (owner report: a band of page background below the tab bar,
   running to the bottom edge of the screen). Inside the layout viewport the bar
   is correct — probed at 375×667 and 430×932, with and without a 34px bottom
   inset injected at --sab: the bar's padding grows to 42px, its bottom equals
   window.innerHeight exactly, the gap below it is 0px, and elementFromPoint at
   the bottom-centre returns .tabbar painted #ffffff. The band is therefore
   OUTSIDE the layout viewport, and nothing in the document can paint there —
   only the canvas can.
   With no background on <html>, body's propagates to the canvas, so that strip
   was drawn in the page colour while the bar above it is white. Giving <html>
   its own background stops the propagation: body still paints --surface-muted
   across its own box (the whole viewport, unchanged), and the strip beyond it
   now matches the bar. The page does not scroll — the shell is a fixed-height
   flex column — so the only canvas anyone ever sees is that bottom strip. */
/* ── THE SCREEN OWNS ITS EDGES, AND THE DOM IS THE DECLARATION (Q7) ──

   Two regions of the screen sit OUTSIDE the layout viewport and nothing in the
   document can lay out into them: the band behind the system chrome at the
   top, and — in Safari, and in a home-screen app, which reserves room for
   Safari's controls whether or not it draws them — a band at the bottom. The
   owner's ask is that the app has no visible boundaries; the landing page
   already has none, and its whole trick is one line: its colour is on BODY,
   and a body background propagates to the CSS canvas, which covers the entire
   screen.

   iOS gives a page exactly two knobs, one per band:
     · the CANVAS shows through the bottom  → body's background, below
     · theme-color tints the top            → carried from --edge-top by the
                                              observer in index.html, since a
                                              meta tag is the one thing CSS
                                              cannot write

   Both are declared PER SCREEN, IN THAT SCREEN'S OWN STYLESHEET, keyed off the
   rendered DOM — `body:has([data-screen="…"])` and `--edge-top` on the screen
   root. Not a JS table: one lived in router.js for a single day and went stale
   the same day, because the auth listener repaints the shell without passing
   through the router, and the owner photographed welcome's indigo on the Cards
   screen. Keying off the DOM means painting a screen IS declaring its edges,
   whichever code path painted it.

   ⚠ <html> MUST NOT carry a background. v29 gave it one to fix a single
   screen's band and thereby froze every other screen's — a background on
   <html> blocks the body→canvas propagation this whole design rides on.
   Level 1 asserts this stays true.

   The defaults below are the four tab destinations: white canvas under the
   white tab bar, page grey above. Screens that differ override in their own
   files: welcome.css, share-hub.css, editor.css, public-card.css. */
body {
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--surface);
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
}
[data-screen] { --edge-top: var(--bg, #f4f4f6); }

/* ══════════════════════════════════════════════════════════════
   Q28 — DARK. One block, because Q32 made one palette.
   ══════════════════════════════════════════════════════════════

   The owner asked for a light/dark override on 2026-08-06. What they were
   told the app did — "follows the system" — it did not: `prefers-color-scheme`
   appeared ZERO times in ten stylesheets. This is the first dark mode the app
   has ever had, and it is thirty-odd lines rather than ten files because Q32
   consolidated six palettes into one first. Every screen follows from here
   without being touched.

   ── THE THREE STATES, AND WHY IT IS WRITTEN THIS WAY ──

     no data-theme     follow the system
     data-theme=light  stay light even where the system is dark
     data-theme=dark   go dark even where the system is light

   The media query is scoped `:not([data-theme="light"])` so an explicit LIGHT
   choice wins over a dark system, and the explicit dark block sits after it so
   an explicit DARK choice wins over a light system. Both directions have to be
   overridable or "system" is the only setting that works.

   ⚠ AND THE EDGES MOVE WITH IT — BOTH OF THEM. body's background propagates
   to the canvas (the strip below the layout viewport) and --edge-top is read
   off the mounted screen by the observer in index.html and written to the
   theme-color meta. Those are two knobs on one band, and Q7 spent three
   attempts learning that moving one without the other leaves a dark page under
   a light bar. Nothing here special-cases them: both read tokens, so both move
   when the tokens do. What DID need attention is that a theme change is not a
   DOM mutation — see the observer, which now watches <html>'s data-theme too.

   ⚠ THE STRUCTURAL DARKS DO NOT INVERT. --slate-800/850/900 are already dark
   and are already used as dark chrome (the share hub's sheet, the scanner's
   frame). Flipping them would turn the one dark surface in the app light.
   Same for --dk-*, which IS the dark ramp. They are absent below on purpose.

   ⚠ NEITHER DOES THE CARD'S ACCENT. --profile is the owner's own colour, set
   per card at runtime by theme.js; a card is the same card in either mode.
   Only its FALLBACK is nudged, and only enough to sit on a dark sheet. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}
:root[data-theme="dark"] { color-scheme: dark; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page: #0e1014;
    --surface: #17191f;
    --surface-muted: #1b1e25;
    --surface-1: #1b1e25;
    --surface-2: #1c1f26;
    --surface-3: #1e222b;
    --surface-4: #20242d;
    --surface-5: #272c36;
    --ink: #f3f4f7;
    --ink-900: #f3f4f7;
    --ink-950: #f3f4f7;
    --ink-975: #ffffff;
    --mono-1: #232a3d;
    --mono-2: #2b3350;
    --n-300: #4a4f5a;
    --n-320: #4e535e;
    --n-330: #525761;
    --n-340: #565b65;
    --n-380: #5f646e;
    --n-450: #6b7180;
    --n-600: #9aa0ac;
    --n-650: #a6acb8;
    --n-800: #d5d8de;
    --slate-300: #4a4f5a;
    --slate-400: #6b7180;
    --slate-500: #7c8497;
    --slate-600: #8f96a8;
    --slate-700: #c3c7d0;
    --ch-surface2: #1c1f26;
    --ch-border: #2a2f3a;
    --ch-text: #f3f4f7;
    --ch-muted: #9aa0ac;
    --ch-faint: #6b7180;
    --ch-stock1: #1b1e25;
    --ch-stock2: #232833;
    --ch-seal: #6b7180;
    --ed-ink: #f3f4f7;
    --ed-muted: #9aa0ac;
    --ed-field: #262b34;
    --ed-fieldtx: #f3f4f7;
    --pc-line: #2a2f3a;
    --err-soft: #3a1f1e;
    /* Q6 — caution on a dark surface. The ink lifts to stay legible and the
       fill drops, rather than inverting the pair, which would make an unsure
       field the brightest thing on the screen. */
    --caution: #e8b569;
    --caution-bg: #2b2214;
    --caution-line: #6b5326;
    --profile-fallback: #5b8cff;
    --profile-fallback-dk: #3f6fe0;
    --profile-fallback-lt: #7ea6ff;
    /* Q30 — lifted off the deep red, which is unreadable against a dark
       surface, and the line lowered so a red border does not glow. */
    --del: #ff6b60;
    --del-line: #5c2b28;
    --on-del: #1a0f0e;
    --scrim: rgba(0, 0, 0, .58);
    --sheet-shadow: rgba(0, 0, 0, .5);
    /* Deeper shadow, fainter halo: on a dark bar a light glow is a smear. */
    --fab-shadow: rgba(0, 0, 0, .55);
    --fab-halo: rgba(99, 102, 241, .20);
  }
}

/* The same values under an explicit choice. Duplicated rather than shared via
   a class, because the media query must keep working for someone who never
   opens Settings — that is the default and it cannot depend on a stored
   preference existing. */
:root[data-theme="dark"] {
    --page: #0e1014;
    --surface: #17191f;
    --surface-muted: #1b1e25;
    --surface-1: #1b1e25;
    --surface-2: #1c1f26;
    --surface-3: #1e222b;
    --surface-4: #20242d;
    --surface-5: #272c36;
    --ink: #f3f4f7;
    --ink-900: #f3f4f7;
    --ink-950: #f3f4f7;
    --ink-975: #ffffff;
    --mono-1: #232a3d;
    --mono-2: #2b3350;
    --n-300: #4a4f5a;
    --n-320: #4e535e;
    --n-330: #525761;
    --n-340: #565b65;
    --n-380: #5f646e;
    --n-450: #6b7180;
    --n-600: #9aa0ac;
    --n-650: #a6acb8;
    --n-800: #d5d8de;
    --slate-300: #4a4f5a;
    --slate-400: #6b7180;
    --slate-500: #7c8497;
    --slate-600: #8f96a8;
    --slate-700: #c3c7d0;
    --ch-surface2: #1c1f26;
    --ch-border: #2a2f3a;
    --ch-text: #f3f4f7;
    --ch-muted: #9aa0ac;
    --ch-faint: #6b7180;
    --ch-stock1: #1b1e25;
    --ch-stock2: #232833;
    --ch-seal: #6b7180;
    --ed-ink: #f3f4f7;
    --ed-muted: #9aa0ac;
    --ed-field: #262b34;
    --ed-fieldtx: #f3f4f7;
    --pc-line: #2a2f3a;
    --err-soft: #3a1f1e;
    /* Q6 — caution on a dark surface. The ink lifts to stay legible and the
       fill drops, rather than inverting the pair, which would make an unsure
       field the brightest thing on the screen. */
    --caution: #e8b569;
    --caution-bg: #2b2214;
    --caution-line: #6b5326;
    --profile-fallback: #5b8cff;
    --profile-fallback-dk: #3f6fe0;
    --profile-fallback-lt: #7ea6ff;
    /* Q30 — lifted off the deep red, which is unreadable against a dark
       surface, and the line lowered so a red border does not glow. */
    --del: #ff6b60;
    --del-line: #5c2b28;
    --on-del: #1a0f0e;
    --scrim: rgba(0, 0, 0, .58);
    --sheet-shadow: rgba(0, 0, 0, .5);
    /* Deeper shadow, fainter halo: on a dark bar a light glow is a smear. */
    --fab-shadow: rgba(0, 0, 0, .55);
    --fab-halo: rgba(99, 102, 241, .20);
}
