/* ============================================================================
   nik — design tokens. THE design system: every value the site is allowed to
   use lives here, once. Components (components.css) may reference tokens and
   nothing else — no raw hex, no magic numbers. See docs/components/WEB.md.

   The palette is "the violet hour", lifted from the landing page: a light
   lavender ground, deep plum ink, violet accents, white cards floating on
   soft violet shadow. Change a token here and the whole product re-skins —
   marketing page, dashboard, admin, all of it. The landing's Halloween night
   rebinds it below while the edition lasts.
   ============================================================================ */

:root {
  /* Light only, on purpose: nik is a family product that should feel like it
     ships with the phone, and the violet hour is a *light* palette — there is
     no dark counterpart to fall back to. */
  color-scheme: light only;

  /* -- ground ------------------------------------------------------------ */
  --bg: #f7f4fc;                        /* the page's lavender ground */
  --bg-nav: rgba(247, 244, 252, 0.85);  /* nav, over blurred content */
  --glow-hero: rgba(160, 140, 255, 0.22); /* the halo behind the blob */
  --glow-warm: rgba(244, 155, 192, 0.16); /* aurora, warm (pink) */
  --glow-cool: rgba(199, 169, 255, 0.18); /* aurora, cool (violet) */

  /* -- ink --------------------------------------------------------------- */
  /* One plum, three weights. --fg2 is secondary copy; --fg3 is decorative
     only (kickers, captions, disabled) — it does not clear AA for body-sized
     text, so never put anything a user must read in it. */
  --fg: #2b2140;
  --fg2: rgba(43, 33, 64, 0.62);
  --fg3: rgba(43, 33, 64, 0.42);

  /* -- surfaces ---------------------------------------------------------- */
  --card: #ffffff;
  --line: rgba(43, 33, 64, 0.08);       /* the hairline's ink */
  --line-strong: rgba(43, 33, 64, 0.14); /* a hairline that must carry weight */
  --hover: rgba(124, 92, 214, 0.06);    /* row/cell hover wash */
  --chosen: rgba(124, 92, 214, 0.07);   /* the entry of a list that is open */
  --selected: rgba(124, 92, 214, 0.09); /* the hit the arrow keys selected */
  --well: rgba(43, 33, 64, 0.05);       /* a block set into a message, such as its transcript */
  --skeleton: #ebe6f2;                  /* a word drawn as a bar while its page loads */

  /* A separator is 0.5px, the way iOS draws them: one physical pixel on a
     retina screen, so the line reads as a crease in the surface rather than as
     a drawn border. Never 1px — that is a frame, and it is only used to give a
     card its outer edge. Every `border-top`/`border-bottom` between rows,
     cells or steps uses this. */
  --hairline: 0.5px;
  --edge: 1px;                          /* a surface's own outer border */

  /* -- accent ------------------------------------------------------------ */
  --accent: #7c5cd6;                    /* the violet: links, marks, bars */
  --accent-2: #c77bd8;                  /* the orchid: gradient's far end */
  --accent-soft: rgba(124, 92, 214, 0.12); /* tinted fills behind accent ink */
  --accent-hover: rgba(124, 92, 214, 0.18); /* a tinted control under the pointer */
  --accent-ink: #ffffff;                /* ink on top of accent fills */
  --accent-grad: linear-gradient(180deg, #9578e5, #7c5cd6);

  /* -- semantic ---------------------------------------------------------- */
  /* Green means "linked, live, delivered"; red means "this removes something".
     Both are plum-shifted so they sit inside the palette instead of shouting
     over it. */
  --ok: #1d8a3e;
  --ok-soft: rgba(52, 199, 89, 0.14);
  --ok-glow: rgba(29, 138, 62, 0.35);   /* the ring a live dot breathes out */
  --danger: #d92d4a;
  --danger-soft: rgba(217, 45, 74, 0.10);
  --danger-grad: linear-gradient(180deg, #e8455f, #d92d4a);
  --neutral-soft: rgba(43, 33, 64, 0.08); /* the "nothing happened" pill */
  /* Amber is a person being asked for something: somebody waiting to be
     let in. The admin palette's three weights, because one amber cannot be
     both text that clears AA and a dot that keeps its saturation. */
  --warn: #8c5a12;                       /* amber ink */
  --warn-dot: #d98c21;                   /* a dot, where there is nothing to read */
  --warn-soft: rgba(217, 140, 33, 0.16); /* the fill --warn is set on */

  /* -- the blob ---------------------------------------------------------- */
  /* nik's face is a brand object, not a themed component: it keeps its own
     two tokens so it reads the same wherever it lands. */
  --blob: #43307a;
  --blob-glow: rgba(124, 92, 214, 0.30);
  --blob-shine: none;                    /* the light the face gives off */
  /* The moon the dreaming Nik sleeps on, and the plum his face is drawn
     in there. */
  --moon: radial-gradient(circle at 38% 36%, #fff8e6, #f6dfae 55%, #e2bd78);
  --moon-glow: 0 0 60px rgba(255, 214, 150, 0.45), 0 0 140px rgba(255, 190, 110, 0.25);
  --moon-ink: #4a2c6e;

  /* -- the landing's demo chats ------------------------------------------ */
  /* The family's bubbles, Nik's, its edge and words, and the senders' names.
     By day they are WhatsApp light's; the Halloween night rebinds them. */
  --chat-them: var(--wa-them-light);
  --chat-nik: var(--wa-mine-light);
  --chat-nik-edge: transparent;
  --chat-nik-ink: var(--wa-mine-light-ink);
  --chat-who: var(--accent);
  --chat-who-nik: var(--accent);

  /* -- code surfaces ----------------------------------------------------- */
  /* Terminal ground. Deep plum rather than black — the terminal heart of the
     product, still inside the palette. */
  --code-bg: #241b38;
  --code-fg: #e9dff7;
  --code-dim: rgba(233, 223, 247, 0.40);

  /* -- the one dark object ----------------------------------------------- */
  /* The nightly-reflection card is content, not theme: it is the drama on a
     light page, and stays dark through every re-skin. The dashboard's Your
     Nik card is the same night: Nik's face, speaking for itself. */
  --night: linear-gradient(155deg, #181128, #3a2158 55%, #120f22);
  --night-ink: #ffffff;
  --night-ink2: rgba(255, 255, 255, 0.72);
  --night-line: rgba(255, 255, 255, 0.10); /* a hairline across the night */
  --night-wash: rgba(255, 255, 255, 0.12); /* a control or a hover on it */
  --night-ink3: rgba(255, 255, 255, 0.45); /* a group's caption on the night */
  --night-hover: rgba(255, 255, 255, 0.08); /* a link hovered on the night */
  --night-accent: #c9b8ff;                 /* counts and actions on the night */
  --night-warm: #ffd1a8;                   /* a note that wants attention on the night */
  --night-field: rgba(24, 17, 40, 0.22);   /* a field set into the sky */
  --night-ring: rgba(255, 255, 255, 0.3);  /* a field's or a button's edge on the sky */
  --night-faint: rgba(255, 255, 255, 0.62); /* a quiet line of words on the sky */
  --night-glass: rgba(255, 255, 255, 0.22); /* a button of glass on the sky */
  --night-glass-edge: rgba(255, 255, 255, 0.4); /* that button's edge */
  /* The workspace sidebar is the same night, lit from its top corner. */
  --night-lit: radial-gradient(120% 55% at 0% 0%, rgba(160, 140, 255, 0.24), transparent 60%),
    linear-gradient(155deg, #181128, #3a2158 55%, #120f22);

  /* -- the sky's states -------------------------------------------------- */
  /* The Your Nik card multiplies its picture, and the moss standing in front
     of Nik, by these: dusk while the Nik is not answering, night before one
     is assigned. */
  --sky-h: clamp(260px, 23vw, 330px);  /* the dashboard's band of sky, which holds the art at its proportions */
  --sky-dusk: linear-gradient(180deg, rgba(40, 40, 120, .55), rgba(240, 150, 120, .35));
  --sky-night: linear-gradient(180deg, rgba(8, 12, 48, .8), rgba(25, 32, 90, .65));
  /* A new family's first Nik wakes on the sky over the whole window: the
     window's height, which the picture takes; where the picture starts, in
     the middle of a tall window and at the right edge of a wide one, so the
     island is always in view; Nik's own size there, which the picture's
     would make wider than a phone; and the band's place beside the sidebar,
     which the sky opens out of and closes back into. */
  --hatch-h: 100vh;
  --hatch-x: max(calc(50vw - 2.451 * var(--hatch-h)), calc(100vw - 3 * var(--hatch-h)));
  --hatch-nik: clamp(180px, min(64vw, 34vh), 300px);
  --hatch-band: var(--sky-h);
  --hatch-side: 232px;

  /* -- content colours, not theme ---------------------------------------- */
  /* The landing's product shot is a picture OF WhatsApp. Those greens and
     slates belong to WhatsApp, not to nik: they are the photograph, and they
     stay put when our palette changes. Never reach for them anywhere else. */
  --wa-bezel: #04080c;                  /* the phone's body */
  --wa-screen: #0b141a;                 /* WhatsApp dark chat ground */
  --wa-head: rgba(18, 27, 34, 0.97);    /* conversation header */
  --wa-line: rgba(255, 255, 255, 0.06);
  --wa-ink: #e9edef;
  --wa-ink2: #8696a0;
  --wa-them: #202c33;                   /* incoming bubble, dark */
  --wa-mine: #005c4b;                   /* outgoing bubble, dark */
  --wa-who: #9cb8f5;                    /* group sender name */
  --wa-play: rgba(233, 237, 239, 0.10); /* voice-note play button */
  --wa-them-light: #efebf7;             /* incoming bubble, light demo cards */
  --wa-mine-light: #d9fdd3;             /* outgoing bubble, light demo cards */
  --wa-mine-light-ink: #123b2a;
  --wa-photo: linear-gradient(145deg, #e3ecd8, #b9d1a6); /* a photo stand-in */
  /* The same chat as Messages draws it, where the environment carries
     iMessage: the picture's colours, as the --wa-* ones are WhatsApp's. Nik's
     bubble is a deeper blue than --service-imessage so white words on it
     clear AA. */
  --im-screen: #ffffff;                 /* the light thread */
  --im-head: rgba(246, 246, 248, 0.96); /* the group's header */
  --im-line: rgba(0, 0, 0, 0.12);
  --im-ink: #000000;
  --im-ink2: #6e6e73;                   /* a sender's name, the group's members */
  --im-them: #e9e9eb;                   /* incoming bubble */
  --im-mine: #006ee0;                   /* Nik's bubble */
  --im-mine-ink: #ffffff;
  --im-play: rgba(0, 0, 0, 0.08);       /* voice-note play button */
  /* Each service a conversation is on, by its own colour, on the badge
     that says which it is: the one place the workspace wears them. */
  --service-whatsapp: #25d366;
  --service-imessage: #0a84ff;
  /* The voice speakers have no brand of their own: they wear the voice
     skill's mark (data-kind teal), so the chat and the skill read as one. */
  --service-voice: #0d7a68;

  /* -- the grid ---------------------------------------------------------- */
  /* One column; the page owns the gutter; EVERY child spans the column
     exactly (no element brings its own horizontal margins, ever). Text
     aligns one --inset in — the same line cell text sits on, iOS-style.
     Vertical rhythm is the column's gap; section titles add headroom.
     Change these numbers and the whole site re-snaps. */
  --gutter: 16px;      /* page edge → column */
  --inset: 16px;       /* column edge → text baseline alignment */
  --gap: 12px;         /* default stack rhythm */
  --col: 680px;        /* reading column */
  --col-signin: 440px; /* provider handoff */
  --col-wide: 980px;   /* the docs article's column */

  /* -- the cockpit ------------------------------------------------------- */
  /* The operator's panel is the one surface that is not a reading column: it
     fills the window, because eleven destinations do not fit a segmented
     control and a table of families does not fit 980px. These three are the
     page grid's equivalent for it — the rail, the title bar over the
     workspace, and the pad the workspace keeps off the window's edges. */
  --rail: 264px;       /* the navigation rail */
  --bar: 64px;         /* the title bar over the workspace */
  --pad: 28px;         /* workspace edge → content */
  /* The signed-in workspace's flat surface: the page edge to its content,
     which every row and the sky over each page share, so a row's words sit
     on the heading's line. */
  --ws-x: 28px;
  /* An action the panel offers rather than a page you go to: the ground
     behind it, and the lift that puts it above everything. */
  --scrim: rgba(43, 33, 64, 0.30);
  --shadow-dialog: 0 40px 90px rgba(60, 40, 120, 0.38);

  /* -- radii ------------------------------------------------------------- */
  --radius-lg: 16px;   /* cards */
  --radius: 12px;      /* inner blocks, token boxes */
  --radius-sm: 10px;   /* inputs, snippets, segmented control */
  --radius-xs: 8px;    /* a control nested inside a --radius-sm control */
  --radius-pill: 999px; /* buttons, pills */

  /* -- elevation --------------------------------------------------------- */
  /* Shadows are violet, never grey: they read as light through lavender. */
  --shadow-card: 0 18px 40px rgba(84, 60, 150, 0.10);
  --shadow-btn: inset 0 1px 0 rgba(255, 255, 255, 0.28),
                0 10px 26px rgba(124, 92, 214, 0.35);
  --shadow-btn-danger: inset 0 1px 0 rgba(255, 255, 255, 0.28),
                       0 10px 26px rgba(217, 45, 74, 0.30);
  --shadow-raise: 0 1px 3px rgba(84, 60, 150, 0.18); /* the lifted seg pill */
  --shadow-device: 0 34px 90px rgba(60, 40, 120, 0.32),
                   0 6px 18px rgba(60, 40, 120, 0.18);

  /* -- type -------------------------------------------------------------- */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas,
    monospace;

  /* The scale, largest to smallest. Twelve steps sounds like a lot; it is the
     honest count of what the product actually uses, and naming them is what
     stops the thirteenth from being invented in a hurry. */
  --t-display: 40px;   /* hero headline */
  --t-title: 28px;     /* page h1, closing statement */
  --t-head: 26px;      /* feature claim */
  --t-subhead: 23px;   /* card headline */
  --t-lead: 19px;      /* standfirst, brand wordmark */
  --t-body: 17px;      /* body copy — the base */
  --t-callout: 15px;   /* buttons, cells, table rows, chat */
  --t-fine: 14px;      /* supporting lines under a callout */
  --t-micro: 13px;     /* labels, footnotes, monospaced values */
  --t-nano: 12px;      /* pills, timestamps */
  --t-eyebrow: 11px;   /* mono kicker over a claim */
  --t-kicker: 10px;    /* mono caption on a card */
  --t-nav: 16px;       /* nav links — the one step the scale keeps for chrome */

  --lh-tight: 1.06;    /* display headlines */
  --lh-head: 1.12;     /* headlines */
  --lh-body: 1.47;     /* body copy */
  --tracking-display: -0.03em;  /* the closing statement */
  --tracking-tight: -0.025em;   /* hero headline */
  --tracking-head: -0.02em;     /* headlines, h1, h2 */
  --tracking-caps: 0.04em;      /* uppercase sans labels */
  --tracking-mono: 0.22em;      /* mono captions, wide on purpose */
  --tracking-mono-lg: 0.24em;   /* mono eyebrows, wider still */

  /* -- motion ------------------------------------------------------------ */
  --ease: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-ish: quick out, soft in */
  --fast: 120ms;
  --slow: 240ms;

  /* -- component parameters ---------------------------------------------- */
  /* Defaults a component overrides on its own element: a memory kind's
     colour and wash, the margins of a list's entries either side (a list
     across the page or the search's hits set their own), a pane's
     inset (each pane sets its own words' inset), a spinner's size
     and ring, the workspace header's measured height (workspace.js), and a
     page's band icon (pageIcon). */
  --mark: var(--accent);
  --wash: var(--accent-soft);
  --pick-x: 20px;
  --pick-end: 16px;
  --pane-x: var(--ws-x);
  --spin: 14px;
  --ring: 2.5px;
  --ws-head-h: 0px;
  --ws-page-icon: none;
}

/* ---------------------------------------------------------------------------
   The Halloween night.

   While the landing's hero is on the page, the landing is night: Nik's sky
   over the hero and the closing band, a starry plum ground, and the light of
   his lantern on everything that acts. Scoped to the root with :has() so the
   page's own background takes the ground too, and fx swapping #page
   re-evaluates it; the workspace keeps its lavender day. The edition goes
   with this block, the landing's sky and spook rules in components.css, the
   hero's band and the spooks in landing.gsx and spooks.go, and the
   halloween-* art in site/.
   --------------------------------------------------------------------------- */

:root:has(.hero.big) {
  color-scheme: dark only;

  --bg: #120c1e;                        /* the night's plum ground */
  --bg-nav: rgba(18, 12, 30, 0.78);
  --fg: #f6effd;
  --fg2: #c9bcd9;
  --fg3: #a596b8;                       /* clears AA on the ground and cards */
  --card: #1b1430;
  --line: rgba(255, 214, 170, 0.10);
  --line-strong: rgba(255, 214, 170, 0.18);
  --well: rgba(255, 255, 255, 0.06);

  /* Pumpkin light: links and kickers in its pale flame, buttons in its
     deep orange with white words and marks, as the violet buttons have. */
  --accent: #ffb877;
  --accent-2: #ff6a1a;
  --accent-soft: rgba(255, 150, 60, 0.15);
  --accent-ink: #ffffff;
  --accent-grad: linear-gradient(180deg, #d4601a, #a8440b);

  --shadow-card: 0 24px 60px rgba(0, 0, 0, 0.35);
  --shadow-btn: inset 0 1px 0 rgba(255, 255, 255, 0.35),
                0 12px 32px rgba(255, 120, 30, 0.35);
  --shadow-raise: 0 1px 3px rgba(0, 0, 0, 0.40);
  --shadow-device: 0 0 0 1px rgba(255, 255, 255, 0.09),
                   0 40px 120px rgba(255, 130, 40, 0.22),
                   0 24px 60px rgba(0, 0, 0, 0.55);

  /* Nik's face is lit from inside, a candle in his bubble, so the day's
     halo behind a hero's face goes out. */
  --glow-hero: transparent;
  --blob: #ffd29a;
  --blob-shine: 0 0 0.14em rgba(255, 196, 120, 0.95), 0 0 0.5em rgba(255, 140, 40, 0.6),
                0 0 1.2em rgba(255, 120, 30, 0.35);

  --chat-them: #2a2040;
  --chat-nik: rgba(255, 150, 60, 0.15);
  --chat-nik-edge: rgba(255, 160, 80, 0.30);
  --chat-nik-ink: #ffeedd;
  --chat-who: #c4a8ff;
  --chat-who-nik: #ffb36b;

  /* The spooks: a ghost's pale glow, and the dusk violet of bats and
     spiders. */
  --ghost: #f1e9ff;
  --ghost-shine: 0 0 0.3em rgba(225, 205, 255, 0.75);
  --bat: #a58bdc;

  /* The Halloween sky's own plum, under the picture while it loads and
     where it ends, and the shade that keeps the hero's words readable on
     it, across a wide band and down a narrow one. */
  --hero-ground: #30203f;
  --hero-veil: linear-gradient(90deg, rgba(24, 14, 37, 0.72), rgba(24, 14, 37, 0.3) 45%, transparent 60%);
  --hero-veil-narrow: linear-gradient(180deg, rgba(24, 14, 37, 0.35), transparent 55%);
  /* The height of the picture behind the hero, at most its own 800 pixels,
     and the inset of the hero's words on a wide window. */
  --hero-h: clamp(540px, 54vw, 800px);
  --hero-x: max(var(--ws-x), calc((100vw - var(--col-wide)) / 2));
  /* Five stars, tiled. */
  --night-stars:
    radial-gradient(1.2px 1.2px at 30px 40px, rgba(255, 240, 220, 0.55) 50%, transparent 51%) 0 0 / 280px 220px,
    radial-gradient(1px 1px at 140px 96px, rgba(255, 255, 255, 0.38) 50%, transparent 51%) 0 0 / 280px 220px,
    radial-gradient(1.4px 1.4px at 214px 22px, rgba(255, 214, 170, 0.5) 50%, transparent 51%) 0 0 / 280px 220px,
    radial-gradient(1px 1px at 82px 182px, rgba(255, 255, 255, 0.32) 50%, transparent 51%) 0 0 / 280px 220px,
    radial-gradient(1.2px 1.2px at 252px 150px, rgba(255, 255, 255, 0.28) 50%, transparent 51%) 0 0 / 280px 220px;
  /* The closing band's veil: the ground at its edges, and a shade behind
     its words. */
  --fin-veil:
    linear-gradient(180deg, var(--bg) 0%, rgba(18, 12, 30, 0.3) 26%, rgba(18, 12, 30, 0.3) 74%, var(--bg) 100%),
    radial-gradient(ellipse 34% 46% at 50% 50%, rgba(18, 12, 30, 0.72), rgba(18, 12, 30, 0) 72%);
}

/* ---------------------------------------------------------------------------
   The display scale.

   The landing is a display context: bigger and quieter type. It rebinds
   exactly six sizes here, and the Halloween night above is the only palette
   it changes, so the marketing page cannot drift from the product by
   accident.

   Scoped with :has() rather than a body class because fx swaps the contents of
   #page on navigation: a server-rendered body class would go stale, a :has()
   test re-evaluates itself.
   --------------------------------------------------------------------------- */

/* -- display scale ------------------------------------------------------ */
body:has(.hero.big) {
  --d-hero: 60px;    /* the headline, on the sky */
  --d-tag: 19px;     /* the tagline under it */
  --d-claim: 26px;   /* a feature claim */
  --d-card: 23px;    /* a statement card's headline */
  --d-body: 14.5px;  /* a statement card's copy — off-scale, hand-set */
  --d-fin: 44px;     /* the closing invitation, on the sky */
}
@media (max-width: 760px) {
  :root { --ws-x: 16px; --hatch-band: min(60%, 420px); --hatch-side: 0px; }
}
/* Any value is a valid custom property, so a second declaration cannot fall
   back to the first: the window's height excludes a phone's bars only where
   the browser knows the unit. */
@supports (height: 100dvh) {
  :root { --hatch-h: 100dvh; }
}
@media (max-width: 480px) {
  body:has(.hero.big) {
    --d-hero: 38px;
    --d-tag: 17px;
    --d-claim: 22px;
    --d-card: 21px;
    --d-fin: 32px;
  }
}
