@motir/design-system 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -5
- package/dist/components/theme/AppearancePickers.js +30 -2
- package/dist/components/theme/AppearancePickers.js.map +1 -1
- package/dist/components/ui/DatePicker.d.ts +31 -0
- package/dist/components/ui/DatePicker.js +442 -0
- package/dist/components/ui/DatePicker.js.map +1 -0
- package/dist/components/ui/DatePickerFormatContext.d.ts +26 -0
- package/dist/components/ui/DatePickerFormatContext.js +58 -0
- package/dist/components/ui/DatePickerFormatContext.js.map +1 -0
- package/dist/components/ui/datePickerFormat.d.ts +40 -0
- package/dist/components/ui/datePickerFormat.js +30 -0
- package/dist/components/ui/datePickerFormat.js.map +1 -0
- package/dist/contexts/theme-context.js +30 -2
- package/dist/contexts/theme-context.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/mock/index.js +30 -2
- package/dist/mock/index.js.map +1 -1
- package/dist/mock/renderMock.js +30 -2
- package/dist/mock/renderMock.js.map +1 -1
- package/dist/specimen/TokensSpecimen.js +2 -2
- package/dist/specimen/TokensSpecimen.js.map +1 -1
- package/dist/theme/appearance-resolution.js +30 -2
- package/dist/theme/appearance-resolution.js.map +1 -1
- package/dist/theme/init-script.js +30 -2
- package/dist/theme/init-script.js.map +1 -1
- package/dist/theme/styles.d.ts +1 -1
- package/dist/theme/styles.js +2 -2
- package/dist/theme/styles.js.map +1 -1
- package/dist/theme/types.js +2 -2
- package/dist/theme/types.js.map +1 -1
- package/dist/theme/typography.d.ts +46 -3
- package/dist/theme/typography.js +28 -0
- package/dist/theme/typography.js.map +1 -1
- package/package.json +1 -1
- package/theme.css +105 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/theme/typography.ts","../../src/theme/styles.ts","../../src/theme/palettes.ts","../../src/theme/types.ts","../../src/theme/fontSets.ts","../../src/contexts/theme-context.tsx"],"names":["value"],"mappings":"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","file":"theme-context.js","sourcesContent":["/**\n * The Motir TYPOGRAPHY LIBRARY — the named-pairing registry (Subtask 7.3.53).\n *\n * A \"type pairing\" is a named TYPOGRAPHY scheme the whole UI can swap to at\n * runtime — the THIRD design axis, completing the token decomposition\n * (Colour `data-palette` · Type `data-type` · Shape/feel `data-style`). It is\n * deliberately INDEPENDENT of the other two: a pairing decides which TYPEFACES\n * the product wears (the headline / body / mono role mapping); it never touches\n * a hue (palette axis) or a radius (style axis).\n *\n * ── The three-axis runtime contract ─────────────────────────────────────\n * data-type=\"<type-id>\" — THIS axis. A `[data-type='<id>']` block in\n * app/globals.css re-points the `--font-*`\n * role tokens (sans / serif / mono) — and ONLY\n * font tokens, never colour or shape.\n * data-palette=\"<palette-id>\" — the INDEPENDENT colour axis (`--el-*`).\n * data-style=\"<style-id>\" — the INDEPENDENT shape/feel axis.\n *\n * The shared `--font-size-*` SCALE stays common across pairings — sizes are\n * layout, not brand; a pairing remaps the FACES, not the type scale.\n *\n * ── A style declares a DEFAULT pairing (overridable) ────────────────────\n * Typography used to be BUNDLED into the style axis: `swiss-minimal-flat`,\n * `neo-brutalism` and `cybercore-y2k` each re-pointed `--font-serif` inside\n * their own `[data-style]` block. That is now a disjointness violation (two\n * axes setting `--font-*`). The fix: each style declares a `defaultTypeId`\n * (see ./styles.ts) — the pairing applied when the user has NOT pinned an\n * explicit type — and the per-style font overrides move here, as pairings.\n * So out-of-the-box looks are UNCHANGED; the new freedom is that type is now\n * independently overridable (e.g. Swiss shape + a serif pairing).\n *\n * ── v1 — the base faces only (no new payload) ───────────────────────────\n * v1 registered three pairings built from the THREE already-loaded next/font\n * faces (Inter · Source Serif 4 · JetBrains Mono) — so they add ZERO font\n * payload and carry NO new-typeface design decision:\n * - `motir` — the base: editorial serif headlines over a sans body.\n * - `motir-sans` — all-sans (Inter headlines); reproduces the Swiss look.\n * - `motir-mono` — mono THROUGHOUT (headlines + body/UI); a code-native UI.\n * `motir` is the base — like the `motir` palette / `warm-editorial` style it\n * needs NO `[data-type]` override block; the base `--font-*` tokens already are\n * it.\n *\n * ── New-typeface pairings (Grotesk / Editorial / Mono-Technical, 7.3.54–56) ──\n * Each adds a real type-design decision and loads its own next/font face(s),\n * re-pointing a role in its own `[data-type]` block:\n * - `grotesk` (7.3.54) — Space Grotesk THROUGHOUT (headlines + body/UI).\n * - `editorial` (7.3.55) — Fraunces display-serif headlines over the Inter\n * body; a magazine, considered pairing (one new face — Inter + JetBrains reused).\n * Body STAYS Inter — a display serif is unreadable as UI, so this pairing\n * re-types headlines only (the legibility exception to whole-UI re-typing).\n * - `mono-technical` (7.3.56) — IBM Plex Mono THROUGHOUT (headlines + body/UI +\n * meta); a precise, developer-grade UI (one new face — Inter reused for nothing\n * now, kept as the ultimate fallback).\n *\n * ── Whole-UI re-typing (Yue, 2026-06-19) ────────────────────────────────\n * A type pairing re-types the WHOLE app, chrome included (nav, header, buttons),\n * not just headlines: a pairing whose signature face reads as a UI sans/mono\n * (`grotesk`, `motir-mono`, `mono-technical`) ALSO re-points the body/UI role\n * (`--font-sans`) in its `[data-type]` block. The display-serif `editorial` and\n * the all-Inter `motir` / `motir-sans` keep the Inter body for legibility.\n * Each later \"Type: …\" subtask ADDS its entry here + a `[data-type]` block (its\n * face loaded in `app/layout.tsx`) + its `docs/typography/<id>.md`.\n */\n\n/** A registered, runtime-selectable type pairing. */\nexport interface TypographyDefinition {\n /** The `data-type` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the typographic feel. */\n tagline: string;\n /** The headline / body / mono face mapping (credit / what it loads). */\n faces: string;\n /**\n * Repo-relative path to this pairing's doc — the type-role reference a later\n * getdesign-style swap or the `/tokens` composer reads. Every registered\n * pairing maps to exactly one.\n */\n designDoc: string;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each non-base `id`\n * matches a `[data-type='<id>']` block in app/globals.css (`motir` is the base,\n * so its block is the Tier-0 `--font-*` defaults — no override block needed).\n */\nexport const TYPE_REGISTRY = {\n motir: {\n id: 'motir',\n name: 'Motir',\n tagline: 'Editorial serif headlines over a clean sans body — the house pairing.',\n faces: 'Source Serif 4 headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir.md',\n },\n 'motir-sans': {\n id: 'motir-sans',\n name: 'Motir Sans',\n tagline: 'All-sans — Inter for headlines and body; structural, no serif.',\n faces: 'Inter headlines + body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir-sans.md',\n },\n 'motir-mono': {\n id: 'motir-mono',\n name: 'Motir Mono',\n tagline: 'Monospace throughout — a code-native UI, headlines, body and all.',\n faces: 'JetBrains Mono headlines + body/UI + meta.',\n designDoc: 'docs/typography/motir-mono.md',\n },\n grotesk: {\n id: 'grotesk',\n name: 'Grotesk',\n tagline: 'Geometric neo-grotesque across the whole UI — tight, confident, product-y.',\n faces: 'Space Grotesk headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/grotesk.md',\n },\n editorial: {\n id: 'editorial',\n name: 'Editorial',\n tagline: 'A characterful display serif over a humanist sans — magazine, considered.',\n faces: 'Fraunces display headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/editorial.md',\n },\n 'mono-technical': {\n id: 'mono-technical',\n name: 'Mono-Technical',\n tagline: 'IBM Plex Mono throughout — a precise, developer-grade UI.',\n faces: 'IBM Plex Mono headlines + body/UI + meta/code.',\n designDoc: 'docs/typography/mono-technical.md',\n },\n} satisfies Record<string, TypographyDefinition>;\n\n/** The id of every registered pairing — `data-type` value space. */\nexport type TypeId = keyof typeof TYPE_REGISTRY;\n\n/** All registered pairing ids, in gallery order. */\nexport const TYPE_IDS = Object.keys(TYPE_REGISTRY) as TypeId[];\n\n/**\n * The default pairing a fresh install / unset preference resolves to when the\n * active style declares none. Motir is the house pairing and the Tier-0 base.\n */\nexport const DEFAULT_TYPE_ID: TypeId = 'motir';\n\n/** Narrowing guard — is an arbitrary string a registered pairing id? */\nexport function isTypeId(value: unknown): value is TypeId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(TYPE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid pairing definition. */\nexport function resolveType(value: unknown): TypographyDefinition {\n return TYPE_REGISTRY[isTypeId(value) ? value : DEFAULT_TYPE_ID];\n}\n","/**\n * The Motir STYLE LIBRARY — the named-aesthetic registry (Subtask 7.3.32).\n *\n * A \"style\" is a named, end-to-end AESTHETIC the whole UI can swap to at\n * runtime — the foundation the onboarding design wizard (7.3.27) and the\n * `/tokens` composer (7.3.30) build on. It is the SHAPE/feel axis, and it is\n * deliberately INDEPENDENT of the COLOR axis (`data-palette` — see below): a\n * style decides how the product FEELS (silhouette, stroke, elevation, surface\n * treatment, density, motion, typography, component silhouettes); a palette\n * decides what colours it WEARS. Picking a style never changes a hue, and\n * picking a palette never changes a radius.\n *\n * ── The two-axis runtime contract ───────────────────────────────────────\n * data-style=\"<style-id>\" — THIS axis. Layers per-style shape/feel\n * tokens (+ component-variant overrides) over\n * the Tier-0 base scale in app/globals.css.\n * data-palette=\"<palette-id>\" — the INDEPENDENT colour axis (re-skins the\n * `--el-*` layer). Orthogonal to data-style.\n * data-theme=\"light|dark\" — the light/dark base within a palette.\n *\n * A `[data-style='…']` TOKEN block in globals.css MUST override ONLY shape/feel\n * tokens (radius / spacing / shadow / sizing / transition / type) — NEVER a\n * `--color-*` or `--el-*` colour token. That is the palette axis's job, and\n * keeping the two disjoint is what makes \"style × palette\" a true product of\n * two independent choices rather than 2×N hand-tuned combinations.\n *\n * A SURFACE-MATERIAL style (glassmorphism, 7.3.35; later cybercore, aurora,\n * neumorphism …) owns a richer surface — translucency, a gradient canvas,\n * frosted backdrop-blur — that the token block cannot express. It adds a\n * palette-DERIVED MATERIAL LAYER: style-scoped component rules\n * (`[data-style='id'] [data-surface='…'] { … }`) whose colour comes ONLY from\n * `color-mix()`/`var(--color-*|--el-*)` over the ACTIVE palette, never a raw\n * hue — so the two axes stay disjoint (a palette swap re-tints the material; a\n * style swap leaves hues untouched). Surfaces opt in via the `data-surface`\n * hook the shared primitives emit. Both rules are enforced by\n * tests/theme/styleRegistry.test.ts.\n *\n * ── Why a style is MORE than a token swap ───────────────────────────────\n * The feel-bearing DIMENSIONS below are the axes a pure token swap ignores:\n * two styles can share the Tier-0 scale yet feel utterly different because\n * their silhouette, elevation philosophy, surface treatment, and component\n * silhouettes diverge. The registry names those dimensions so each style (and\n * its DESIGN.md) is authored against the same rubric.\n *\n * This module is the schema + the registration of the styles. Each \"Style: …\"\n * subtask (7.3.33–7.3.42) ADDS its entry here, ships a `[data-style='<id>']`\n * token block in globals.css (plus, for a surface-material style, the\n * palette-derived material layer above), and authors its `docs/styles/<id>.md`\n * DESIGN.md.\n */\n\nimport { DEFAULT_TYPE_ID, type TypeId } from './typography';\n\n/**\n * The feel-bearing axes a named style controls. A token-only swap reaches the\n * first few; the rest (surface treatment, motion, typography, component\n * silhouettes) are what give a style its identity and must be authored, not\n * derived. The order here is the canonical presentation order (the `/tokens`\n * page + each style's DESIGN.md walk them in this sequence).\n */\nexport const STYLE_DIMENSIONS = [\n {\n key: 'silhouette',\n label: 'Shape / silhouette',\n description:\n 'Corner-radius personality — sober rectangles vs. soft pills vs. hard right angles.',\n },\n {\n key: 'stroke',\n label: 'Border / stroke',\n description: 'Hairline vs. heavy outline; whether structure is drawn with borders at all.',\n },\n {\n key: 'elevation',\n label: 'Elevation philosophy',\n description: 'How depth is expressed — soft diffuse shadows, hard offset shadows, or flat.',\n },\n {\n key: 'surface',\n label: 'Surface / background treatment',\n description:\n 'Opaque cards vs. translucent glass vs. tinted washes — the material of a surface.',\n },\n {\n key: 'density',\n label: 'Density rhythm',\n description: 'Padding/sizing cadence — compact and efficient vs. roomy and relaxed.',\n },\n {\n key: 'motion',\n label: 'Motion',\n description: 'Transition speed and easing — snappy, gentle, or springy.',\n },\n {\n key: 'typography',\n label: 'Typography',\n description:\n \"The style's DEFAULT type pairing — type is the independent `data-type` axis now (see ./typography.ts + `defaultTypeId`); this describes the pairing the style ships with: editorial serif, all-sans, or mono headlines.\",\n },\n {\n key: 'components',\n label: 'Component silhouettes',\n description:\n 'Per-component shape overrides beyond tokens (e.g. a pill button vs. a square one).',\n },\n] as const;\n\nexport type StyleDimensionKey = (typeof STYLE_DIMENSIONS)[number]['key'];\n\n/** A registered, runtime-selectable style. */\nexport interface StyleDefinition {\n /** The `data-style` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the feel. */\n tagline: string;\n /** Where the look is drawn from (credit / mood anchor). */\n inspiration: string;\n /** One line per feel-bearing dimension — the authored rubric for this style. */\n dimensions: Record<StyleDimensionKey, string>;\n /**\n * Repo-relative path to this style's DESIGN.md — the doc the `/tokens` page\n * composes (and a later getdesign-style swap reads). Every registered style\n * maps to exactly one.\n */\n designDoc: string;\n /**\n * The type pairing (`data-type`, see ./typography.ts) this style applies when\n * the user has NOT pinned an explicit type — the style's curated default,\n * overridable. Type is its own axis now; this is how a style keeps its\n * out-of-the-box typographic feel (e.g. swiss → `motir-sans`, neo-brutalism /\n * cybercore-y2k → `motir-mono`) without owning `--font-*` in its own block.\n */\n defaultTypeId: TypeId;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each `id` matches a\n * `[data-style='<id>']` block in app/globals.css (Warm Editorial is the base,\n * so its block is the Tier-0 defaults — no override block needed).\n */\nexport const STYLE_REGISTRY = {\n 'warm-editorial': {\n id: 'warm-editorial',\n name: 'Warm Editorial',\n tagline: 'Thoughtful, warm, technical-but-not-cold, slightly editorial.',\n inspiration: \"Notion's warm palette + Source Serif headlines over Inter body.\",\n designDoc: 'docs/styles/warm-editorial.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette: 'Sober rectangles — 8px buttons, 12px cards. Restrained, document-like.',\n stroke: 'Hairline borders (1px warm-grey); structure drawn quietly, never heavy.',\n elevation: 'Modest, low-spread shadows; surfaces sit close to the page.',\n surface: 'Opaque cream surfaces over a white canvas; pastel tints for feature cards only.',\n density: 'Comfortable default rhythm — 18×10 button padding, 24px card padding.',\n motion: 'Brisk 150ms ease; understated, gets out of the way.',\n typography: 'Source Serif 4 editorial headlines + Inter body + JetBrains Mono meta.',\n components:\n 'Rectangular buttons/inputs, hairline-bordered cards, badge pills for status only.',\n },\n },\n 'soft-playful': {\n id: 'soft-playful',\n name: 'Soft / Playful',\n tagline: 'More energy — rounded, generous, gently animated.',\n inspiration: \"Figma's pill-shape language (50px pills, roomy spacing).\",\n designDoc: 'docs/styles/soft-playful.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette: 'Pill buttons (fully rounded) and large 24px card/input radii. Friendly, bubbly.',\n stroke: 'Same hairline borders as the base; identity comes from radius, not stroke weight.',\n elevation: 'Softer, more diffused shadows — surfaces float a touch more.',\n surface: 'Opaque, like the base — colour stays the palette axis; only the shape softens.',\n density: 'Roomier — 20×12 button padding, 28px card padding, taller controls.',\n motion: 'Gentler 200ms ease and a slightly deeper press scale for a springy feel.',\n typography: 'Inherits the base type pairing; the personality is in shape, not type.',\n components: 'Pill buttons, heavily-rounded inputs/cards/modals, rounder small affordances.',\n },\n },\n 'swiss-minimal-flat': {\n id: 'swiss-minimal-flat',\n name: 'Swiss / Minimal-Flat',\n tagline: 'International-typographic, structural, calm — flat, sharp, gridded.',\n inspiration: 'Swiss International Typographic Style — Müller-Brockmann grids, flat surfaces.',\n designDoc: 'docs/styles/swiss-minimal-flat.md',\n defaultTypeId: 'motir-sans',\n dimensions: {\n silhouette:\n 'Sharp near-square corners (2px) on every surface — hard right angles, structural.',\n stroke:\n 'Hairline borders do ALL the structural work; with elevation removed, the 1px rule is the only divider.',\n elevation:\n 'Flat — every shadow is removed (none). Depth comes from borders + whitespace, not lift.',\n surface:\n 'Opaque, untinted panels delineated by hairline rules; no floating, no glass, no wash.',\n density:\n 'Tight, gridded controls (crisp 16×9 buttons, 34px control height) with generous 28px card whitespace.',\n motion:\n 'Minimal — fast 100ms transitions and NO press-scale (calm, mechanical, never springy).',\n typography:\n 'Restrained neo-grotesque sans throughout — headlines drop the editorial serif for Inter, tight and strong.',\n components:\n 'Square buttons/inputs/cards/modals, rectangular (non-pill) status chips, flat hairline-ruled surfaces.',\n },\n },\n 'neo-brutalism': {\n id: 'neo-brutalism',\n name: 'Neo-Brutalism',\n tagline: 'Raw, punchy, utilitarian — 0px corners, thick borders, hard-offset shadows.',\n inspiration:\n 'Neo-brutalist web design — Gumroad / Figma-community brutalism: blocky, unpolished, loud.',\n designDoc: 'docs/styles/neo-brutalism.md',\n defaultTypeId: 'motir-mono',\n dimensions: {\n silhouette:\n 'Zero radius — hard 0px corners on EVERY surface (buttons, cards, inputs, modals, status chips). Blocky and uncompromising.',\n stroke:\n 'Heavy solid 2px outlines do the structural work — borders are LOUD, not hairline (the defining neo-brutalist move, applied via a style-scoped component-variant block).',\n elevation:\n 'Hard-offset drop shadows with ZERO blur (e.g. 4px 4px 0 0) — a solid block of shadow, never a soft lift; the chunky frame, not diffusion.',\n surface:\n 'Opaque, flat, untinted panels framed by the thick border + the hard shadow; no glass, no wash, no gradient.',\n density:\n 'Tight, utilitarian controls (16×10 buttons, 36px control height) with compact 20px card padding — no wasted space.',\n motion:\n 'Snappy and mechanical — near-instant 60ms transitions and NO press-scale; the UI reacts, it does not animate.',\n typography:\n 'Raw monospace headlines — the editorial serif is re-pointed at the JetBrains Mono stack, against the Inter grotesk body.',\n components:\n 'Square buttons/inputs/cards/modals, rectangular (non-pill) status chips, thick-bordered surfaces with hard-offset shadows.',\n },\n },\n glassmorphism: {\n id: 'glassmorphism',\n name: 'Glassmorphism',\n tagline: 'Translucent frosted glass floating over a soft, vibrant gradient.',\n inspiration:\n \"Apple's visionOS / macOS Big Sur 'frosted glass' material — backdrop-blur over depth.\",\n designDoc: 'docs/styles/glassmorphism.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Soft, rounded glass tiles — 12px buttons/inputs, 18px cards, 22px modals. Friendly, never sharp.',\n stroke:\n 'Light 1px hairlines at reduced opacity — a glass edge catching light, not a structural rule.',\n elevation:\n 'Layered, diffuse, low-opacity shadows — panels float as hovering frosted sheets above the canvas.',\n surface:\n 'The identity axis: translucent frosted panels (backdrop-blur) over a soft palette-derived gradient canvas — material, not opaque.',\n density:\n 'Comfortable, a touch roomy — 20×11 buttons, 26px card padding, 38px controls; glass tiles want air.',\n motion:\n 'Gentle, smooth — 220ms ease and a light press-scale; glass slides into place, it never snaps.',\n typography:\n 'Inherits the base editorial type pairing; the personality is in the material, not the type.',\n components:\n 'Rounded frosted cards / popovers / modals / sidebar / inputs (the data-surface material layer), pill status chips.',\n },\n },\n 'cybercore-y2k': {\n id: 'cybercore-y2k',\n name: 'Cybercore / Y2K',\n tagline: 'Neon-on-dark retro-tech — glowing HUD panels, a tech grid, mono headlines.',\n inspiration:\n 'Y2K / cyberpunk HUDs — neon-on-dark terminals, Tron grids, glowing edges, monospace displays.',\n designDoc: 'docs/styles/cybercore-y2k.md',\n defaultTypeId: 'motir-mono',\n dimensions: {\n silhouette:\n 'Hard terminal-frame corners (sharp 2–4px) — chiseled HUD panels, not soft cards.',\n stroke:\n 'Structure drawn by a lit edge — a 1px glow-ring (currentColor halo) on every surface, not a heavier border.',\n elevation:\n 'Neon GLOW instead of drop-shadow — surfaces emit an outward currentColor halo, lit rather than lifted.',\n surface:\n 'A faint tech grid (scanline/grid texture) washes the canvas behind opaque HUD panels.',\n density:\n 'Tight, technical HUD rhythm — compact controls (18×9 buttons, 34px control height) over snug 20px card padding.',\n motion: 'Snappy and electric — very fast 90ms transitions with a crisp press-scale.',\n typography:\n 'Mono/display headlines — the serif headline face is swapped to JetBrains Mono for a terminal read; body stays sans.',\n components:\n 'Sharp glow-ringed panels, mono headings, rectangular chips; the lit edge replaces the drop shadow everywhere.',\n },\n },\n aurora: {\n id: 'aurora',\n name: 'Aurora',\n tagline: 'Soft, drifting aurora light behind fluid, gently-glowing surfaces.',\n inspiration:\n 'The northern lights — slow ribbons of colour over a night sky — and the soft animated-gradient \"aurora\" hero look (Stripe / Vercel ambient gradients).',\n designDoc: 'docs/styles/aurora.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Fluid, soft corners — 14px buttons/inputs, 20px cards, 26px modals. Organic and rounded, a touch softer than glass.',\n stroke:\n 'Quiet 1px hairlines; structure is implied by the gentle glow and elevation, never drawn by a heavy rule.',\n elevation:\n 'A soft neutral lift PLUS a gentle palette-derived COLOUR glow halo — surfaces feel lit from within, never hard-edged.',\n surface:\n 'The identity axis: opaque fluid surfaces floating over a slowly-DRIFTING, animated aurora gradient canvas (palette-derived ribbons).',\n density:\n 'Comfortable and roomy — 20×11 buttons, 26px card padding, 38px controls; the light wants space to breathe.',\n motion:\n 'Smooth and slow — 260ms eased transitions, a faint press-scale, and a 28s ambient drift of the aurora canvas (stilled under prefers-reduced-motion).',\n typography:\n 'Inherits the base editorial pairing (Source Serif headlines + Inter body); the personality is in the moving light, not the type.',\n components:\n 'Fluid, gently-glowing cards / popovers / modals / sidebar (the data-surface material layer); pill status chips.',\n },\n },\n '3d-immersive': {\n id: '3d-immersive',\n name: '3D / Immersive',\n tagline: 'Depth and perspective — dimensional cards floating over the page.',\n inspiration:\n 'Spatial / depth UI — visionOS spatial layers, Stripe-era layered cards, soft real-world light.',\n designDoc: 'docs/styles/3d-immersive.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Generously rounded dimensional tiles — 14px buttons/inputs, 20px cards, 28px modals. Soft, tactile, never sharp.',\n stroke:\n 'Borders nearly vanish (a faint hairline) — structure is read from depth + shadow, not an outline.',\n elevation:\n 'The identity axis: deep multi-layer shadows float every surface off the canvas, and on tilt a card becomes a TRUE 3D object — perspective + preserve-3d + per-slot translateZ planes (header pops forward, footer behind) parallax under a cursor-tracked glare. Not a flat-plane tilt.',\n surface:\n 'A palette-derived immersive background atmosphere on every page, with opaque dimensional panels (cards, tables, board columns, widgets) floating above it on deep resting shadows — depth (not tint or glass) is the material; nothing sits flat.',\n density:\n 'Roomy and immersive — 22×12 buttons, 28px card padding, 40px controls; depth wants air around each tile.',\n motion:\n 'The standard \"3D card\" pointer-parallax — tiles tip toward the cursor (rotateX/rotateY over a perspective) via the ImmersiveTilt engine, lifting as they turn; reduced-motion-gated, settling flat on leave.',\n typography:\n 'Inherits the base editorial type pairing; the personality is in depth + light, not the type.',\n components:\n 'Rounded cards / board cards / modals that tilt toward the pointer with layered parallax; PHYSICAL buttons with real thickness (a solid base edge) that press DOWN on click; each board column floats as its own card. Nothing is flat.',\n },\n },\n neumorphism: {\n id: 'neumorphism',\n name: 'Neumorphism',\n tagline: 'Soft extruded surfaces that swell out of a single monochrome field.',\n inspiration:\n 'Neumorphism / soft-UI — Dribbble-era \"soft shadow\" interfaces where panels are pressed OUT of (or INTO) one continuous surface by a paired light + dark shadow.',\n designDoc: 'docs/styles/neumorphism.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Soft, generously rounded pebbles — 12px buttons/inputs, 18px cards, 22px modals. Tactile and friendly, the rounding that lets a shadow wrap a surface.',\n stroke:\n 'Borders nearly vanish — a faint hairline kept ONLY as the accessibility fallback (see below); structure is read from the paired soft shadow, not an outline.',\n elevation:\n 'The identity axis: a DUAL soft shadow — a light highlight top-left + a palette-derived dark shadow bottom-right — extrudes each surface OUT of the canvas (raised), while inputs and pressed controls invert it to an INSET well (pressed in). Depth is a soft swell, never a hard drop.',\n surface:\n 'Monochrome and continuous: the page canvas and every panel share ONE opaque palette surface, so a card looks moulded out of the background rather than placed on top of it — the soft-UI \"single material\" look. Opaque (text AA preserved), with the depth carried entirely by the shadows.',\n density:\n 'Comfortable and roomy — 20×11 buttons, 26px card padding, 38px controls; the soft shadows need air around each surface to read as extruded.',\n motion:\n 'Gentle — 200ms ease and a soft press-scale; nothing snaps, matching the moulded surfaces (form fields rest as concave inset wells, panels swell out raised).',\n typography:\n 'Inherits the base editorial type pairing; the personality is in the moulded surfaces, not the type.',\n components:\n 'Raised, soft-shadowed cards / popovers / modals / sidebar (the data-surface material layer); concave inset inputs; pill status chips. Every surface swells from or sinks into the one field.',\n },\n },\n 'hand-drawn-indie': {\n id: 'hand-drawn-indie',\n name: 'Hand-Drawn / Indie',\n tagline:\n 'Sketchy and friendly — rough hand-inked edges, wonky corners, soft offset shadows, a playful bounce.',\n inspiration:\n 'Indie / zine + hand-drawn web design — Excalidraw’s rough hand-sketched strokes and doodled notebook UIs: warm, imperfect, human.',\n designDoc: 'docs/styles/hand-drawn-indie.md',\n defaultTypeId: 'editorial',\n dimensions: {\n silhouette:\n 'Wonky hand-drawn corners — every surface gets a strongly asymmetric multi-value radius (one corner near-sharp, the opposite generously round, no two alike) so cards/buttons/inputs read as DRAWN boxes, never machined rectangles.',\n stroke:\n 'Genuinely ROUGH, wavy ink edges — a turbulence-displacement filter (Excalidraw / rough.js style) warps each outline into a hand-inked wobble, drawn on a content-safe pseudo-border so the lines are sketchy but the text stays crisp.',\n elevation:\n 'Soft, hand-placed offset shadows — a gentle blurred drop nudged down-right (a doodled shadow), friendly and low; never a hard brutalist block nor a flat removal.',\n surface:\n 'Opaque, warm, paper-like panels — colour stays the palette axis; the personality is in the rough wavy outline + the soft shadow, not a tint or a wash.',\n density:\n 'Roomy and relaxed — generous 20×12 buttons, 26px card padding, taller 38px controls; the hand-drawn feel wants air, like margins round a sketch.',\n motion:\n 'Springy and playful — gentle 200ms transitions with a bouncy hover lift (1.02) and a deeper press squish (0.97); the UI has a friendly wobble.',\n typography:\n 'Ships the Editorial pairing (Fraunces display headlines + Inter body) — the warmest, most characterful registered face, its soft optical wonk echoing the hand-drawn shapes; an actual marker/script face is a future data-type addition.',\n components:\n 'Wonky-cornered buttons/inputs/cards/modals, irregular (non-pill) status chips, rough wavy-ink-bordered surfaces with soft hand-placed shadows.',\n },\n },\n retrofuturism: {\n id: 'retrofuturism',\n name: 'Retrofuturism',\n tagline: 'Chrome, gradient sweeps and a synthwave horizon — 70s/80s sci-fi.',\n inspiration:\n 'Retrofuturism / Outrun — 70s–80s sci-fi: chrome lettering, synthwave sunset gradients, airbrushed sci-fi paperbacks, and wide geometric display type.',\n designDoc: 'docs/styles/retrofuturism.md',\n defaultTypeId: 'grotesk',\n dimensions: {\n silhouette:\n 'Streamlined chrome capsules — fully-rounded pill buttons + chips, 16px cards, 22px modals. Aerodynamic 70s/80s curves, never sharp.',\n stroke:\n 'A lit chrome edge — a bright inset highlight + a dark base line over a faint hairline, reading as a polished metal rim rather than a drawn border.',\n elevation:\n 'Chrome bevels + a diagonal SPECULAR streak + a palette-derived glow — a bright top highlight fading to a dark base with a swept light-bar across each panel, so surfaces read as polished reflective metal, not flat cards.',\n surface:\n 'The identity axis: a synthwave SKY + a bright HORIZON GLOW band + a receding neon perspective GRID floor (all palette-derived) behind chrome-bevelled, specular-streaked surfaces. Dark-first — the neon horizon + grid read strongest on a dark palette.',\n density:\n 'Confident and roomy — 22×11 chrome-capsule buttons, 24px card padding, 38px controls; the chrome wants room to catch the light.',\n motion:\n 'Smooth and gliding — 200ms eased transitions and a confident press-scale; the synthwave horizon + grid are a STATIC backdrop (no motion to distract the work surface).',\n typography:\n 'Wide geometric grotesque display (the `grotesk` type pairing) — the retro-futurist sci-fi headline read; body stays a legible sans.',\n components:\n 'Chrome-capsule buttons with a metallic glint, specular-streaked bevelled cards / popovers / modals / sidebar / inputs (the data-surface material layer), pill status chips — a synthwave horizon + neon grid behind it all.',\n },\n },\n} satisfies Record<string, StyleDefinition>;\n\n/** The id of every registered style — `data-style` value space. */\nexport type StyleId = keyof typeof STYLE_REGISTRY;\n\n/** All registered style ids, in gallery order. */\nexport const STYLE_IDS = Object.keys(STYLE_REGISTRY) as StyleId[];\n\n/**\n * The default style a fresh install / unset preference resolves to. Warm\n * Editorial is Motir's house style and the Tier-0 base.\n */\nexport const DEFAULT_STYLE_ID: StyleId = 'warm-editorial';\n\n/** Narrowing guard — is an arbitrary string a registered style id? */\nexport function isStyleId(value: unknown): value is StyleId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(STYLE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid style definition. */\nexport function resolveStyle(value: unknown): StyleDefinition {\n return STYLE_REGISTRY[isStyleId(value) ? value : DEFAULT_STYLE_ID];\n}\n\n/**\n * styleId → its `defaultTypeId` — the type pairing applied when the user has\n * not pinned an explicit `data-type`. Baked into the FOUC init script (so the\n * pre-hydration pass can resolve the right type for the active style) and used\n * by the theme context's effective-type derivation. `resolveType` (the\n * registry) supplies the ultimate fallback, so every value here is a real id.\n */\nexport const STYLE_DEFAULT_TYPE: Record<StyleId, TypeId> = Object.fromEntries(\n STYLE_IDS.map((id) => [id, STYLE_REGISTRY[id].defaultTypeId]),\n) as Record<StyleId, TypeId>;\n\n/** The default type for a (possibly unknown) style value — the fallback chain. */\nexport function defaultTypeForStyle(styleValue: unknown): TypeId {\n return isStyleId(styleValue) ? STYLE_REGISTRY[styleValue].defaultTypeId : DEFAULT_TYPE_ID;\n}\n","/**\n * The Motir PALETTE LIBRARY — the named-colour registry (Subtask 7.3.48).\n *\n * A \"palette\" is a named COLOUR scheme the whole UI can swap to at runtime —\n * the second half of the two-axis contract the style schema (7.3.32) wrote but\n * did not implement. It is the COLOUR/hue axis, and it is deliberately\n * INDEPENDENT of the SHAPE axis (`data-style` — see ./styles.ts): a palette\n * decides what colours the product WEARS (surfaces, ink, accent, tints,\n * semantic hues); a style decides how it FEELS (silhouette, elevation,\n * density, motion). Picking a palette never changes a radius, and picking a\n * style never changes a hue.\n *\n * ── The two-axis runtime contract ───────────────────────────────────────\n * data-palette=\"<palette-id>\" — THIS axis. Layers per-palette `--el-*`\n * element-token overrides over the Tier-3\n * base in app/globals.css.\n * data-style=\"<style-id>\" — the INDEPENDENT shape/feel axis.\n * data-theme=\"light|dark\" — the light/dark base WITHIN a palette\n * (`--color-*`); orthogonal to the palette.\n *\n * A `[data-palette='…']` block in globals.css MUST override ONLY `--el-*`\n * element colour tokens — NEVER a shape/feel token (radius / spacing / shadow\n * / sizing / motion / type). That is the style axis's job, and keeping the two\n * disjoint is what makes \"style × palette\" a true product of two independent\n * choices rather than N×M hand-tuned combinations.\n *\n * ── Motir's own palette, and the one that used to be ──────────────────────\n * **Motir** is the palette Motir wears when a person has chosen none: the stark\n * monochrome scheme (cool greyscale surfaces + ink, an ink CTA, a single\n * restrained cool-blue accent). It shipped as \"Graphite\" (7.3.51) and took the\n * product's name in MOTIR-6471, so its colours arrive through its\n * `[data-palette='motir']` block.\n *\n * **Amethyst** is the warm scheme the product wore until then (cream surfaces,\n * charcoal ink, purple primary, pastel feature tints), which carried the name\n * \"Motir\" from v1 (Per Yue, 2026-06-17) until that rename. It is still the\n * Tier-0/Tier-3 BASE in theme.css, so — exactly like the `warm-editorial` base\n * style — the base `--el-*` tokens already are it; its `[data-palette='amethyst']`\n * blocks only re-assert that base inside a scoped subtree. `<html>` always\n * carries a `data-palette`, so the base is a fallback, never what a person sees\n * by default. Each later \"Palette: …\" subtask ADDS its entry here, ships a\n * `[data-palette='<id>']` block overriding the `--el-*` layer, and authors its\n * `docs/palettes/<id>.md` doc — exactly the shape the per-style subtasks\n * (7.3.33+) follow on the other axis.\n *\n * ⚠️ A STORED palette id written before the rename means the OTHER palette:\n * `motir` meant today's `amethyst`, and `graphite` meant today's `motir`. Every\n * store is migrated once (`PALETTE_ID_MIGRATION` in ./types.ts, and the\n * `user_appearance_preference` migration) — never alias the old spellings.\n */\n\n/** A registered, runtime-selectable colour palette. */\nexport interface PaletteDefinition {\n /** The `data-palette` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the colour mood. */\n tagline: string;\n /** Where the colours are drawn from (credit / mood anchor). */\n inspiration: string;\n /**\n * Repo-relative path to this palette's doc — the colour-role reference a\n * later getdesign-style swap or the `/tokens` composer reads. Every\n * registered palette maps to exactly one.\n */\n designDoc: string;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each `id` matches a\n * `[data-palette='<id>']` block in theme.css (Amethyst is the Tier-3 base, so its\n * blocks only re-assert the defaults inside a scoped subtree).\n */\nexport const PALETTE_REGISTRY = {\n motir: {\n id: 'motir',\n name: 'Motir',\n tagline:\n 'Stark and editorial — cool greyscale surfaces + ink, an ink CTA, a restrained cool-blue accent and a few warm touches.',\n inspiration:\n \"Vercel's black-and-white precision + Linear's ultra-minimal, on Radix Slate/Blue scales.\",\n designDoc: 'docs/palettes/motir.md',\n },\n amethyst: {\n id: 'amethyst',\n name: 'Amethyst',\n tagline: 'Warm and editorial — cream surfaces, charcoal ink, a purple primary, pastel tints.',\n inspiration:\n \"Notion's warm marketing palette — Motir's house colours until the monochrome rename.\",\n designDoc: 'docs/palettes/amethyst.md',\n },\n cobalt: {\n id: 'cobalt',\n name: 'Cobalt',\n tagline:\n 'Cool and institutional — slate-cool surfaces, a confident cobalt primary, cooled tints.',\n inspiration:\n \"Coinbase's clean institutional blue + IBM's structured blue, on Radix Blue/Indigo/Slate scales.\",\n designDoc: 'docs/palettes/cobalt.md',\n },\n evergreen: {\n id: 'evergreen',\n name: 'Evergreen',\n tagline:\n 'Fresh and technical — an emerald primary over cooled forest neutrals, a green-leaning UI.',\n inspiration:\n 'getdesign.md — Supabase (dark emerald) / Spotify (vibrant green) / MongoDB (spring green), on Radix green/jade ramps.',\n designDoc: 'docs/palettes/evergreen.md',\n },\n spectrum: {\n id: 'spectrum',\n name: 'Spectrum',\n tagline:\n 'Vibrant and playful — crisp cool surfaces, a bright violet primary, a candy-bright multi-hue accent & tint set.',\n inspiration:\n \"Figma's vibrant multi-colour brand + Airtable's colourful, friendly palette, on Radix Violet/Iris/Pink/Blue scales.\",\n designDoc: 'docs/palettes/spectrum.md',\n },\n amber: {\n id: 'amber',\n name: 'Amber',\n tagline:\n 'Warm and electric — Binance trading-floor gold over the exchange’s graphite near-blacks; dark labels on the gold.',\n inspiration:\n 'Binance — its whole documented palette: the bold gold #F0B90B/#FCD535 over cool graphite trading-floor blacks (#0B0E11/#1E2329) + greys, with the buy-green/sell-red semantics.',\n designDoc: 'docs/palettes/amber.md',\n },\n sienna: {\n id: 'sienna',\n name: 'Sienna',\n tagline:\n 'Warm and vivid — Mistral flame-orange over the Mediterranean warm-cream surfaces; dark labels on the flame.',\n inspiration:\n 'Mistral AI — its whole documented palette (getdesign.md/mistral.ai): the flame #fa520f + Block-gradient + Sunshine ambers over Warm-Ivory cream surfaces (#fffaeb) and Mistral-Black ink.',\n designDoc: 'docs/palettes/sienna.md',\n },\n garnet: {\n id: 'garnet',\n name: 'Garnet',\n tagline:\n 'Rich and bold — Pinterest Pushpin red over the brand’s neutral greys; white labels on the red.',\n inspiration:\n 'Pinterest — its whole documented palette: Pushpin red #E60023/#BD081C over Cod-Gray ink (#111111) and neutral greys (#767676/#EFEFEF), with Gestalt “Skycicle” blue links.',\n designDoc: 'docs/palettes/garnet.md',\n },\n citrine: {\n id: 'citrine',\n name: 'Citrine',\n tagline:\n 'Bright and collaborative — Miro’s Sunglow yellow over the whiteboard greys, with the Miro action-blue accent.',\n inspiration:\n 'Miro — its whole Mirotone palette (every value a documented Mirotone step): Sunglow yellow #FFD02F (primary) + the action-blue #3859FF over the gray ramp + Stratos ink #050038, with the green/red semantic ramps.',\n designDoc: 'docs/palettes/citrine.md',\n },\n candy: {\n id: 'candy',\n name: 'Candy',\n tagline:\n 'Sweet and playful — a light bubblegum-pink primary over candy-paper pinks, with a full pastel-candy rainbow.',\n inspiration:\n 'A candy concept grounded entirely in documented Radix Colors scales (every value a Radix step): Pink primary + Mauve neutrals + a Sky/Jade/Violet/Amber/Crimson candy rainbow; Glossier Pink #F5DADF as the pastel-pink mood reference.',\n designDoc: 'docs/palettes/candy.md',\n },\n} satisfies Record<string, PaletteDefinition>;\n\n/** The id of every registered palette — `data-palette` value space. */\nexport type PaletteId = keyof typeof PALETTE_REGISTRY;\n\n/** All registered palette ids, in gallery order. */\nexport const PALETTE_IDS = Object.keys(PALETTE_REGISTRY) as PaletteId[];\n\n/**\n * The palette Motir wears when a person has chosen none — a fresh install, a\n * signed-out visitor, an unset (null) server preference. Its VALUE has been\n * `'motir'` since v1; what it NAMES changed in MOTIR-6471, from the warm scheme\n * (now `amethyst`) to the monochrome one (formerly `graphite`).\n */\nexport const DEFAULT_PALETTE_ID: PaletteId = 'motir';\n\n/**\n * The palette theme.css's Tier-0/Tier-3 BASE carries — the one with no root\n * `[data-palette]` override block, only a scoped re-assertion. It is the\n * reference every other palette's override is measured AGAINST. Until\n * MOTIR-6471 it was also {@link DEFAULT_PALETTE_ID}; since the rename the two\n * differ, so a reader asking \"which palette is the base?\" names this and never\n * the default.\n */\nexport const BASE_PALETTE_ID: PaletteId = 'amethyst';\n\n/**\n * The default look the onboarding Design step offers for a user's OWN project —\n * a different question from the one {@link DEFAULT_PALETTE_ID} answers. It stays\n * on the warm scheme the step has always opened on (MOTIR-6470's scope boundary,\n * confirmed by the requester).\n */\nexport const DEFAULT_PROJECT_PALETTE_ID: PaletteId = 'amethyst';\n\n/** Narrowing guard — is an arbitrary string a registered palette id? */\nexport function isPaletteId(value: unknown): value is PaletteId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(PALETTE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid palette definition. */\nexport function resolvePalette(value: unknown): PaletteDefinition {\n return PALETTE_REGISTRY[isPaletteId(value) ? value : DEFAULT_PALETTE_ID];\n}\n","/**\n * Theme types for Motir's THREE-axis design system.\n *\n * Axis 1 — COLOR (`data-theme` light/dark base · `data-palette` full\n * `--el-*` palette swap — the colour axis)\n * Axis 2 — STYLE (`data-style` — a named aesthetic controlling shape /\n * silhouette / elevation / surface / density / motion /\n * component silhouettes)\n * Axis 3 — TYPE (`data-type` — a named pairing controlling the `--font-*`\n * role mapping; a style declares a default pairing, overridable)\n *\n * The three axes are INDEPENDENT: a style never touches a hue or a face, a\n * palette never touches a radius, a type pairing never touches colour or shape.\n * The registries live in `./styles.ts`, `./palettes.ts`, `./typography.ts`.\n *\n * Architecture mirrors dooooWeb. See docs/DESIGN.md for the rationale.\n */\n\nimport { DEFAULT_STYLE_ID, type StyleId } from './styles';\nimport { DEFAULT_PALETTE_ID, type PaletteId } from './palettes';\nimport { DEFAULT_TYPE_ID, type TypeId } from './typography';\n\n/** Tier 1 — light/dark base. `system` follows OS preference at runtime. */\nexport type ThemePattern = 'system' | 'light' | 'dark';\n\n/** Resolved pattern (what data-theme is set to after `system` resolves). */\nexport type ResolvedThemePattern = 'light' | 'dark';\n\n/**\n * Axis 2 — the active named style (`data-style`). The value space + the\n * registry of every style live in `./styles.ts`; this re-export keeps\n * `lib/theme/*` the single import surface for theme consumers.\n */\nexport type { StyleId } from './styles';\n\n/**\n * Axis 1 (colour) — the active named palette (`data-palette`). The value space\n * + the registry of every palette live in `./palettes.ts`; re-exported here so\n * `lib/theme/*` stays the single import surface for theme consumers.\n */\nexport type { PaletteId } from './palettes';\n\n/**\n * Axis 3 — the active named type pairing (`data-type`). The value space + the\n * registry live in `./typography.ts`; re-exported here so `lib/theme/*` stays\n * the single import surface for theme consumers.\n */\nexport type { TypeId } from './typography';\n\n/** Storage keys for persisting user preferences. */\nexport const THEME_STORAGE_KEYS = {\n pattern: 'motir.theme.pattern',\n style: 'motir.theme.style',\n palette: 'motir.theme.palette',\n type: 'motir.theme.type',\n /**\n * The version marker of the STORED palette id's meaning. Absent (or anything\n * but {@link PALETTE_IDS_VERSION}) means the stored `palette` was written\n * before MOTIR-6471 swapped the palette identities, so it is read through\n * {@link PALETTE_ID_MIGRATION} once and the marker is then written.\n */\n paletteIds: 'motir.theme.paletteIds',\n /**\n * The signed-in person's per-language font picks as `<html>` attributes,\n * `{ [locale]: { 'data-font-set-<role>': member } }` (MOTIR-7896). Written by\n * every signed-in layout render and cleared by every signed-out one, so only\n * the error page — which cannot read the database — ever reads it back.\n */\n fontPicks: 'motir.theme.fontPicks',\n} as const;\n\n/** The current meaning of a stored palette id — see `THEME_STORAGE_KEYS.paletteIds`. */\nexport const PALETTE_IDS_VERSION = '2';\n\n/**\n * What a palette id stored BEFORE MOTIR-6471 means today. The two palettes\n * swapped identities: the warm scheme called `motir` is now `amethyst`, and the\n * monochrome scheme called `graphite` is now `motir`. A value absent from this\n * map meant the same palette before and after. Applied ONCE per store — a\n * second pass would map a migrated `motir` on to `amethyst`, so it is keyed on\n * a version marker, never on the value.\n */\nexport const PALETTE_ID_MIGRATION: Readonly<Partial<Record<string, string>>> = {\n motir: 'amethyst',\n graphite: 'motir',\n};\n\n/**\n * Read a stored palette id through the rename. `marker` is the stored\n * `THEME_STORAGE_KEYS.paletteIds` value: at {@link PALETTE_IDS_VERSION} the\n * value is already current and is returned untouched; otherwise a value the\n * rename changed is mapped. `null` (nothing stored) stays `null`.\n */\nexport function migrateStoredPaletteId(value: string | null, marker: string | null): string | null {\n if (value === null || marker === PALETTE_IDS_VERSION) return value;\n // Own properties only — a stored `constructor` must not reach Object.prototype.\n if (!Object.prototype.hasOwnProperty.call(PALETTE_ID_MIGRATION, value)) return value;\n return PALETTE_ID_MIGRATION[value] ?? value;\n}\n\n/**\n * Sensible defaults if localStorage is empty. Note `type` is the GLOBAL fallback\n * only — when no explicit type is pinned, the active STYLE's `defaultTypeId`\n * wins (see `STYLE_DEFAULT_TYPE` / the theme context); this is the floor under\n * an unknown style.\n */\nexport const THEME_DEFAULTS = {\n pattern: 'system' as ThemePattern,\n style: DEFAULT_STYLE_ID as StyleId,\n palette: DEFAULT_PALETTE_ID as PaletteId,\n type: DEFAULT_TYPE_ID as TypeId,\n} as const;\n\n/**\n * Narrowing guard — is an arbitrary value a valid `pattern` axis id? The\n * colour / style / type axes each ship `isXId` in their registry file; the\n * pattern axis is a fixed three-value union, so its guard lives here next to\n * the type. Mirrors `isStyleId` / `isPaletteId` / `isTypeId`.\n */\nexport function isThemePattern(value: unknown): value is ThemePattern {\n return value === 'system' || value === 'light' || value === 'dark';\n}\n\n/**\n * Resolve a (possibly stale / unknown / null) value to a valid `pattern`,\n * falling back to `THEME_DEFAULTS.pattern`. The pattern-axis analogue of\n * `resolveStyle` / `resolvePalette` / `resolveType`.\n */\nexport function resolvePattern(value: unknown): ThemePattern {\n return isThemePattern(value) ? value : THEME_DEFAULTS.pattern;\n}\n","/**\n * The Motir FONT-SET REGISTRY: which faces each locale's script is drawn in\n * (Story MOTIR-7733 · Subtask MOTIR-7843).\n *\n * A Type pairing (`./typography.ts`) decides the LATIN faces of the three roles\n * (sans / serif / mono). A font set decides the SCRIPT face that sits BEHIND\n * them, so a ja page draws its Latin in the pairing's face and its kana and\n * kanji in the ja set's face, under every pairing. The two axes are\n * independent: a set never changes which pairing is active, and a pairing never\n * changes which set a locale uses.\n *\n * ── Where the rows come from ────────────────────────────────────────────\n * Every set, member and default below is taken from\n * `docs/typography/font-sets.md` (MOTIR-7841), which read dooooWeb's mapping at\n * `225af77` and filled the roles dooooWeb leaves empty from cited references.\n * `inDooooWeb` carries that document's `in dooooWeb` / `added (not in dooooWeb)`\n * tag. Change a row here only together with that document; the package test\n * holds the two equal.\n *\n * ── What this module is NOT ─────────────────────────────────────────────\n * Data and pure resolvers only. It names CSS custom properties (the contract\n * between the app's `next/font` loader and `theme.css`) but sets none, loads no\n * face, and writes no CSS. Loading stays in the consuming app.\n */\n\n/** The eleven locales app.motir.co ships in. A closed product decision. */\nexport const FONT_SET_LOCALES = [\n 'en',\n 'zh',\n 'ja',\n 'ko',\n 'de',\n 'fr',\n 'es',\n 'it',\n 'nl',\n 'pl',\n 'pt',\n] as const;\n\n/** One of the eleven app locales. */\nexport type FontSetLocale = (typeof FONT_SET_LOCALES)[number];\n\n/**\n * Languages with a font set that only motir.co ships (MOTIR-8937 · MOTIR-9239).\n * Kept apart from `FONT_SET_LOCALES`, which is app.motir.co's own locale set and\n * is closed over its stored font picks (one `fontPick<Locale>` column each).\n */\nexport const FONT_SET_EXTRA_LOCALES = ['zh-tw'] as const;\n\n/** A language the font-set registry knows: an app locale or an extra one. */\nexport type FontSetLanguage = FontSetLocale | (typeof FONT_SET_EXTRA_LOCALES)[number];\n\n/** The three type roles every set covers: the same roles `--font-*` carries. */\nexport const FONT_SET_ROLES = ['sans', 'serif', 'mono'] as const;\n\n/** One of the three type roles. */\nexport type FontSetRole = (typeof FONT_SET_ROLES)[number];\n\n/**\n * Where a member's face comes from.\n *\n * - `type-pairing`: the active Type pairing's own face for the role. The Latin\n * set's only member; it loads nothing and has no CSS variable.\n * - `next/font/google`: a Google Fonts face the app loads with `next/font`;\n * `googleFamily` is the loader's export name (`Noto_Sans_JP`).\n */\nexport type FontSetMemberSource =\n | { readonly kind: 'type-pairing' }\n | { readonly kind: 'next/font/google'; readonly googleFamily: string };\n\n/** One selectable face in one role of one set. */\nexport interface FontSetMember {\n /** Stable kebab-case slug, unique within its role. The value a pick stores. */\n readonly id: string;\n /** The display name, or `null` for the `type-pairing` member. */\n readonly family: string | null;\n readonly source: FontSetMemberSource;\n /** `docs/typography/font-sets.md`'s tag: `in dooooWeb` (true) or `added` (false). */\n readonly inDooooWeb: boolean;\n /**\n * The role whose member of the SAME id this member re-uses the face of. A\n * CJK set's mono role draws its Han glyphs from the set's sans face (the Noto\n * Sans Mono CJK practice, see the doc), so the face is loaded once and both\n * roles read one variable.\n */\n readonly sameFaceAs?: FontSetRole;\n}\n\n/** One role of one set: its members, and which one a page uses by default. */\nexport interface FontSetRoleMembers {\n /** The id of the member used when nobody has picked one. */\n readonly default: string;\n readonly members: readonly FontSetMember[];\n}\n\n/** A locale group's font set. */\nexport interface FontSet {\n readonly id: string;\n /** Display name for `/tokens` and the picker. */\n readonly label: string;\n /**\n * The BCP 47 tag the set's `:lang()` rule matches. It is the tag the app\n * renders on `<html lang>` (`zh`, not `zh-Hans`), because `:lang(zh)`\n * matches `zh` and `zh-Hans` while `:lang(zh-Hans)` does not match `zh`.\n * `null` for the Latin set, which is matched by its locales instead.\n */\n readonly lang: string | null;\n /** True for a CJK set: its faces are unicode-range sliced and never preloaded. */\n readonly cjk: boolean;\n readonly roles: Readonly<Record<FontSetRole, FontSetRoleMembers>>;\n}\n\nconst TYPE_PAIRING: FontSetMember = {\n id: 'type-pairing',\n family: null,\n source: { kind: 'type-pairing' },\n inDooooWeb: false,\n};\n\nfunction google(\n id: string,\n family: string,\n inDooooWeb: boolean,\n sameFaceAs?: FontSetRole,\n): FontSetMember {\n return {\n id,\n family,\n source: { kind: 'next/font/google', googleFamily: family.replace(/ /g, '_') },\n inDooooWeb,\n ...(sameFaceAs ? { sameFaceAs } : {}),\n };\n}\n\n/**\n * The registry, one entry per set. Ids are the doc's registry ids; dooooWeb's\n * own group names are noted beside each.\n */\nexport const FONT_SET_REGISTRY = {\n // dooooWeb: `latin`. Its Latin picker faces are deliberately not members:\n // behind the pairing's face they would never draw a glyph (font-sets.md § latin).\n latin: {\n id: 'latin',\n label: 'Latin',\n lang: null,\n cjk: false,\n roles: {\n sans: { default: 'type-pairing', members: [TYPE_PAIRING] },\n serif: { default: 'type-pairing', members: [TYPE_PAIRING] },\n mono: { default: 'type-pairing', members: [TYPE_PAIRING] },\n },\n },\n // dooooWeb: `chinese-simplified`.\n 'zh-Hans': {\n id: 'zh-Hans',\n label: 'Simplified Chinese',\n lang: 'zh',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-sc', members: [google('noto-sans-sc', 'Noto Sans SC', false)] },\n serif: {\n default: 'noto-serif-sc',\n members: [\n google('noto-serif-sc', 'Noto Serif SC', true),\n // Draws Traditional / inherited forms, so never the default (font-sets.md § Region forms).\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', true),\n ],\n },\n mono: {\n default: 'noto-sans-sc',\n members: [google('noto-sans-sc', 'Noto Sans SC', false, 'sans')],\n },\n },\n },\n // Not in dooooWeb: Traditional Chinese (Taiwan) for motir.co's `/zh-tw`\n // (MOTIR-8937). The zh-Hans set with its Traditional-form faces.\n 'zh-Hant': {\n id: 'zh-Hant',\n label: 'Traditional Chinese',\n lang: 'zh-TW',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-tc', members: [google('noto-sans-tc', 'Noto Sans TC', false)] },\n serif: {\n default: 'noto-serif-tc',\n members: [\n google('noto-serif-tc', 'Noto Serif TC', false),\n // A handwritten-style face, so a pick rather than the default.\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', false),\n ],\n },\n mono: {\n default: 'noto-sans-tc',\n members: [google('noto-sans-tc', 'Noto Sans TC', false, 'sans')],\n },\n },\n },\n // dooooWeb: `japanese`.\n ja: {\n id: 'ja',\n label: 'Japanese',\n lang: 'ja',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-jp',\n members: [\n google('noto-sans-jp', 'Noto Sans JP', true),\n google('m-plus-rounded-1c', 'M PLUS Rounded 1c', true),\n ],\n },\n serif: {\n default: 'noto-serif-jp',\n members: [google('noto-serif-jp', 'Noto Serif JP', false)],\n },\n mono: {\n default: 'noto-sans-jp',\n members: [google('noto-sans-jp', 'Noto Sans JP', false, 'sans')],\n },\n },\n },\n // dooooWeb: `korean`.\n ko: {\n id: 'ko',\n label: 'Korean',\n lang: 'ko',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-kr',\n members: [\n google('noto-sans-kr', 'Noto Sans KR', true),\n google('nanum-gothic', 'Nanum Gothic', true),\n ],\n },\n serif: {\n default: 'noto-serif-kr',\n members: [google('noto-serif-kr', 'Noto Serif KR', false)],\n },\n mono: {\n default: 'noto-sans-kr',\n members: [google('noto-sans-kr', 'Noto Sans KR', false, 'sans')],\n },\n },\n },\n} as const satisfies Record<string, FontSet>;\n\n/** The id of every registered set. */\nexport type FontSetId = keyof typeof FONT_SET_REGISTRY;\n\n/** All set ids, in registry order. */\nexport const FONT_SET_IDS = Object.keys(FONT_SET_REGISTRY) as FontSetId[];\n\n/** The set an unknown or empty page language falls back to: the app renders it in English. */\nexport const DEFAULT_FONT_SET_ID: FontSetId = 'latin';\n\n/** Which set each locale is drawn in (font-sets.md § Locale → set). */\nexport const LOCALE_FONT_SET = {\n en: 'latin',\n zh: 'zh-Hans',\n ja: 'ja',\n ko: 'ko',\n de: 'latin',\n fr: 'latin',\n es: 'latin',\n it: 'latin',\n nl: 'latin',\n pl: 'latin',\n pt: 'latin',\n 'zh-tw': 'zh-Hant',\n} as const satisfies Record<FontSetLanguage, FontSetId>;\n\n/** Narrowing guard: is an arbitrary value one of the eleven locales? */\nexport function isFontSetLocale(value: unknown): value is FontSetLocale {\n return typeof value === 'string' && (FONT_SET_LOCALES as readonly string[]).includes(value);\n}\n\n/** Narrowing guard: is an arbitrary value an app locale or an extra language? */\nexport function isFontSetLanguage(value: unknown): value is FontSetLanguage {\n return (\n isFontSetLocale(value) ||\n (typeof value === 'string' && (FONT_SET_EXTRA_LOCALES as readonly string[]).includes(value))\n );\n}\n\n/**\n * The set for an `<html lang>` value. Tries the whole tag first, case-insensitively\n * and reading `_` as `-` (`zh-TW` / `zh_tw` → zh-Hant), then the primary subtag\n * (`ja-JP` → ja, `PT-br` → pt, `zh-Hans` → zh). No script or region heuristics:\n * `zh-Hant` and `zh-HK` reach zh-Hant only through a page whose `lang` is `zh-TW`.\n *\n * The fallback is explicit: an empty or unknown tag returns the Latin set,\n * because the app renders an unknown language in English. It never falls back\n * to \"the nearest CJK set\". Use `isFontSetLocale` on the primary subtag to tell\n * a fallback from a match.\n */\nexport function resolveFontSet(lang: string): FontSet {\n const tag = lang.trim().toLowerCase().replace(/_/g, '-');\n if (isFontSetLanguage(tag)) return FONT_SET_REGISTRY[LOCALE_FONT_SET[tag]];\n const primary = tag.split('-')[0] ?? '';\n const id = isFontSetLocale(primary) ? LOCALE_FONT_SET[primary] : DEFAULT_FONT_SET_ID;\n return FONT_SET_REGISTRY[id];\n}\n\n/**\n * A member of one role of one set, by id. A missing or unknown id (a stored\n * pick naming a member that has since been removed) returns that role's\n * default, never another set's face.\n */\nexport function resolveFontSetMember(\n setId: FontSetId,\n role: FontSetRole,\n memberId?: string,\n): FontSetMember {\n const r: FontSetRoleMembers = FONT_SET_REGISTRY[setId].roles[role];\n const named = memberId === undefined ? undefined : r.members.find((m) => m.id === memberId);\n return named ?? r.members.find((m) => m.id === r.default)!;\n}\n\n/**\n * The CSS custom property the app's `next/font` loader assigns to a member's\n * face and `theme.css` reads: `--font-set-<set>-<role>-<member>`. This name is\n * the contract between the registry, the loader and the stylesheet, so it is\n * built here and nowhere else.\n *\n * A member that re-uses another role's face (`sameFaceAs`) returns THAT role's\n * variable, so the face is declared once. The `type-pairing` member, and an id\n * the role does not have, return `null`.\n */\nexport function fontSetMemberVar(\n setId: FontSetId,\n role: FontSetRole,\n memberId: string,\n): string | null {\n const member = FONT_SET_REGISTRY[setId].roles[role].members.find(\n (m: FontSetMember) => m.id === memberId,\n );\n if (!member || member.source.kind === 'type-pairing') return null;\n const faceRole = member.sameFaceAs ?? role;\n return `--font-set-${setId}-${faceRole}-${member.id}`;\n}\n\n/** The `<html>` attribute that names a picked member for one role. */\nexport type FontSetPickAttribute = `data-font-set-${FontSetRole}`;\n\n/**\n * The `data-font-set-<role>` attributes a person's pick for `locale` puts on\n * `<html>` (Story MOTIR-7736 · MOTIR-7897). One attribute per role of the\n * locale's set that has a member with this id, unless that member is the\n * `type-pairing` placeholder or the role's default: `theme.css` carries a rule\n * only for a non-default member, so a default needs no attribute.\n *\n * `null`, an unknown id, a default id and every Latin locale return `{}`. This\n * is the ONE derivation the client (`setFontPick`) and the server-rendered\n * first byte share, so a pick sets identical attributes on both sides — which\n * is why it lives here, free of any `'use client'` boundary.\n */\nexport function fontSetPickAttributes(\n locale: FontSetLanguage,\n memberId: string | null,\n): Partial<Record<FontSetPickAttribute, string>> {\n if (memberId === null) return {};\n const set: FontSet = FONT_SET_REGISTRY[LOCALE_FONT_SET[locale]];\n const attrs: Partial<Record<FontSetPickAttribute, string>> = {};\n for (const role of FONT_SET_ROLES) {\n const r: FontSetRoleMembers = set.roles[role];\n if (memberId === r.default) continue;\n const member = r.members.find((m: FontSetMember) => m.id === memberId);\n if (member && member.source.kind !== 'type-pairing') attrs[`data-font-set-${role}`] = memberId;\n }\n return attrs;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from 'react';\nimport {\n migrateStoredPaletteId,\n PALETTE_IDS_VERSION,\n THEME_DEFAULTS,\n THEME_STORAGE_KEYS,\n type ResolvedThemePattern,\n type ThemePattern,\n} from '../theme/types';\nimport { DEFAULT_STYLE_ID, isStyleId, resolveStyle, type StyleId } from '../theme/styles';\nimport { DEFAULT_PALETTE_ID, isPaletteId, type PaletteId } from '../theme/palettes';\nimport { isTypeId, type TypeId } from '../theme/typography';\nimport {\n FONT_SET_ROLES,\n fontSetPickAttributes,\n isFontSetLocale,\n type FontSetLocale,\n} from '../theme/fontSets';\nimport type { AppearancePreferenceDto, AppliedAppearanceDto } from '../appearance';\n\n/**\n * Theme context for Motir's three-axis design system (style · palette · type),\n * plus the light/dark pattern.\n *\n * `pattern` is what the user picked (system | light | dark).\n * `resolvedPattern` is what's currently applied (light | dark) — these\n * differ when pattern='system' and the OS preference resolves to one or\n * the other.\n *\n * State is persisted to localStorage and re-applied to <html>'s\n * data-attributes on every change. The init script in app/layout.tsx\n * does the same work BEFORE React hydrates to avoid FOUC; this provider\n * keeps the attributes in sync after hydration.\n *\n * Implementation note: this uses lazy `useState` initializers and\n * `useSyncExternalStore` rather than `useEffect`+`setState`, per the\n * React 19 / react-hooks/set-state-in-effect rule. Setting state inside\n * effects causes cascading renders; subscribing to external systems\n * (localStorage, matchMedia) via the right primitives avoids that.\n *\n * The per-language font pick (Story MOTIR-7736 · MOTIR-7897) is a fifth,\n * signed-in-only axis with a different shape. `setFontPick(locale, member)`\n * rewrites `<html>`'s `data-font-set-*` attributes in the event handler, and\n * only when `locale` is the page's own language, so a pick for another language\n * touches no DOM. It keeps no localStorage copy and runs no state effect: the\n * server renders the stored pick on the first byte, and an effect seeded from\n * state would duplicate or clear that on mount. `fontPicks` holds only this\n * session's picks; the stored baseline is the pane's to read. The pick rides\n * the same debounced, seq-guarded PATCH and quiet `syncState` as the other axes.\n */\ninterface ThemeContextValue {\n pattern: ThemePattern;\n resolvedPattern: ResolvedThemePattern;\n /** The active named style (`data-style`) — Axis 2, independent of palette. */\n styleId: StyleId;\n /** The active named palette (`data-palette`) — Axis 1 (colour), independent of style. */\n palette: PaletteId;\n /**\n * The EFFECTIVE type pairing (`data-type`) — Axis 3. Either the user's pinned\n * choice, or (when unpinned) the active style's `defaultTypeId`. Independent\n * of palette + style.\n */\n type: TypeId;\n setPattern: (pattern: ThemePattern) => void;\n setStyleId: (styleId: StyleId) => void;\n setPalette: (palette: PaletteId) => void;\n /** Pin an explicit type pairing (overrides the style's default until cleared). */\n setType: (type: TypeId) => void;\n /**\n * The per-language font picks made in THIS session (MOTIR-7897): a member id,\n * or `null` for a pick cleared back to Automatic. A locale absent from it is\n * unchanged from what the server rendered.\n */\n fontPicks: Partial<Record<FontSetLocale, string | null>>;\n /**\n * Pick a font-set member for one language, or `null` for Automatic. A no-op\n * for an anonymous visitor. Re-types the page at once only when `locale` is\n * the page's own language.\n */\n setFontPick: (locale: FontSetLocale, memberId: string | null) => void;\n /**\n * Cross-device sync status (Subtask 7.3.62). `'idle'` while nothing has\n * failed; `'error'` when the LAST attempt to persist a change to the account\n * failed (a 401 / write failure) — the local switch still applied (it's in\n * localStorage), so this only drives a quiet \"couldn't sync\" affordance. Always\n * `'idle'` for an anonymous visitor, who never syncs. Clears back to `'idle'`\n * on the next successful sync.\n */\n syncState: AppearanceSyncState;\n}\n\n/** The cross-device sync status surfaced to the Appearance pane (7.3.62). */\nexport type AppearanceSyncState = 'idle' | 'error';\n\n/** The axes a single PATCH carries — the setters only ever send valid ids. */\ntype AppearanceSyncPatch = Partial<\n Record<'pattern' | 'styleId' | 'paletteId' | 'typeId', string>\n> & {\n /** Per-locale font picks; `null` clears one. Merged per locale, never replaced. */\n fontPicks?: Partial<Record<FontSetLocale, string | null>>;\n};\n\n/** The page's language as a font-set locale, parsed as `resolveFontSet` does. */\nfunction pageFontSetLocale(): FontSetLocale | null {\n if (typeof document === 'undefined') return null;\n const primary = document.documentElement.lang.trim().split(/[-_]/)[0]?.toLowerCase() ?? '';\n return isFontSetLocale(primary) ? primary : null;\n}\n\n/** Rewrite `<html>`'s `data-font-set-*` attributes for a pick on the page's language. */\nfunction applyPageFontPick(locale: FontSetLocale, memberId: string | null): void {\n const root = document.documentElement;\n for (const role of FONT_SET_ROLES) root.removeAttribute(`data-font-set-${role}`);\n for (const [attr, value] of Object.entries(fontSetPickAttributes(locale, memberId))) {\n root.setAttribute(attr, value);\n }\n}\n\n/** How long rapid toggles of the SAME axis coalesce before one write fires. */\nconst SYNC_DEBOUNCE_MS = 250;\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\n/** Lazily read a localStorage key. Returns null on SSR or if the key isn't set. */\nfunction readStorage<T extends string>(key: string, fallback: T): T {\n if (typeof window === 'undefined') return fallback;\n try {\n return (window.localStorage.getItem(key) as T | null) ?? fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * `useSyncExternalStore` subscription to the prefers-color-scheme media\n * query. Returns 'dark' or 'light' based on the OS preference.\n *\n * Server snapshot returns 'light' (a stable default — the FOUC init\n * script will apply the real value before hydration completes).\n */\nfunction subscribeColorScheme(callback: () => void): () => void {\n if (typeof window === 'undefined') return () => {};\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n}\n\nfunction getColorSchemeSnapshot(): ResolvedThemePattern {\n if (typeof window === 'undefined') return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nfunction getColorSchemeServerSnapshot(): ResolvedThemePattern {\n return 'light';\n}\n\nexport function ThemeProvider({\n children,\n initialPreference = null,\n signedIn = false,\n}: {\n children: ReactNode;\n /**\n * The signed-in user's APPLIED appearance, resolved server-side (Subtask\n * 7.3.61). When present it SEEDS this island's state — the page-state\n * contract: a client island must be GIVEN the server value, not left to\n * re-read localStorage (which `useState` would do only at mount, ignoring the\n * authoritative server pref and risking a flash from a stale value synced on\n * another device). `null` (anonymous) keeps the original localStorage path.\n */\n initialPreference?: AppliedAppearanceDto | null;\n /**\n * Whether a user is signed in (Subtask 7.3.62). Only then does an axis change\n * also PERSIST to the account via `/api/appearance-preference` (cross-device\n * sync). Anonymous visitors keep the localStorage-only path and never fire a\n * write — so they never hit a 401 or see a \"couldn't sync\" affordance. This is\n * a SEPARATE signal from `initialPreference`: a signed-in user who has pinned\n * nothing yet has `initialPreference === null` but must still sync their first\n * pick (a row is created on the first PATCH).\n */\n signedIn?: boolean;\n}) {\n // Lazy initializers — run once on first render. Seed from the server\n // preference when signed in (authoritative, cross-device); otherwise read\n // localStorage synchronously without an effect.\n const [pattern, setPatternState] = useState<ThemePattern>(() =>\n initialPreference\n ? initialPreference.pattern\n : readStorage<ThemePattern>(THEME_STORAGE_KEYS.pattern, THEME_DEFAULTS.pattern),\n );\n const [styleId, setStyleIdState] = useState<StyleId>(() => {\n if (initialPreference) {\n return isStyleId(initialPreference.styleId) ? initialPreference.styleId : DEFAULT_STYLE_ID;\n }\n // A stale / unknown stored value (e.g. a pre-7.3.32 `default`/`soft`\n // display-style leftover) resolves to the default, never a dead id.\n const stored = readStorage<string>(THEME_STORAGE_KEYS.style, THEME_DEFAULTS.style);\n return isStyleId(stored) ? stored : DEFAULT_STYLE_ID;\n });\n const [palette, setPaletteState] = useState<PaletteId>(() => {\n if (initialPreference) {\n return isPaletteId(initialPreference.paletteId)\n ? initialPreference.paletteId\n : DEFAULT_PALETTE_ID;\n }\n // A value stored before MOTIR-6471's rename is read through the migration\n // (a no-op once the init script has run and written the marker), and a\n // stale / unknown one resolves to the default, never a dead id.\n const stored = migrateStoredPaletteId(\n readStorage<string>(THEME_STORAGE_KEYS.palette, '') || null,\n readStorage<string>(THEME_STORAGE_KEYS.paletteIds, '') || null,\n );\n return isPaletteId(stored) ? stored : DEFAULT_PALETTE_ID;\n });\n // The user's PINNED type choice, or `null` = \"follow the active style's\n // default pairing\". Only a valid stored id pins; anything else (unset/stale)\n // falls through to the style default below.\n const [typeChoice, setTypeChoiceState] = useState<TypeId | null>(() => {\n if (initialPreference) {\n // The server pref carries the EFFECTIVE type + whether it was pinned: an\n // unpinned user must stay `null` so they keep following the style default.\n return initialPreference.typePinned && isTypeId(initialPreference.typeId)\n ? initialPreference.typeId\n : null;\n }\n const stored = readStorage<string>(THEME_STORAGE_KEYS.type, '');\n return isTypeId(stored) ? stored : null;\n });\n\n // Subscribe to OS color-scheme changes. Only consulted when pattern='system'.\n const osColorScheme = useSyncExternalStore(\n subscribeColorScheme,\n getColorSchemeSnapshot,\n getColorSchemeServerSnapshot,\n );\n\n const resolvedPattern: ResolvedThemePattern = pattern === 'system' ? osColorScheme : pattern;\n\n // Effective type (Axis 3): a pinned choice wins; otherwise FOLLOW the active\n // style's default pairing — so switching style (while unpinned) re-points type\n // to that style's curated default, and an explicit pick sticks across styles.\n const type: TypeId = typeChoice ?? resolveStyle(styleId).defaultTypeId;\n\n // Sync data-theme to <html>. This IS an effect (synchronizing with an\n // external system — the DOM), but the effect body only writes to the DOM,\n // not back to React state. That's the correct effect shape.\n useEffect(() => {\n document.documentElement.setAttribute('data-theme', resolvedPattern);\n }, [resolvedPattern]);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-style', styleId);\n }, [styleId]);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-palette', palette);\n }, [palette]);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-type', type);\n }, [type]);\n\n // ── Cross-device sync (Subtask 7.3.62) ────────────────────────────────────\n // A signed-in user's axis change is OPTIMISTIC: the setter flips the live UI +\n // localStorage instantly (below), then queues a debounced PATCH to\n // `/api/appearance-preference`. The write is reconciled from its 200 body, and\n // a failure (401 / network / server) degrades quietly — the local switch is\n // never lost (localStorage holds it) — surfacing `syncState: 'error'` for the\n // pane's affordance. Anonymous visitors never sync (the localStorage-only path).\n const [syncState, setSyncState] = useState<AppearanceSyncState>('idle');\n // Monotonic per-flush counter — the seq-guard that stops an older response from\n // clobbering a newer choice (the WatchControl `fetchSeq` idiom; the\n // E2E-authoritative-signal contract). Only the latest flush may reconcile / set\n // the affordance.\n const syncSeqRef = useRef(0);\n // Axes changed since the last flush, coalesced so rapid toggles send one write.\n const pendingPatchRef = useRef<AppearanceSyncPatch>({});\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The per-language picks made this session (MOTIR-7897), and the page-locale\n // member this provider last wrote onto <html> (`undefined` = never wrote), so\n // a reconcile re-applies only a value the server actually changed.\n const [fontPicks, setFontPicksState] = useState<Partial<Record<FontSetLocale, string | null>>>(\n {},\n );\n const appliedPageFontPickRef = useRef<string | null | undefined>(undefined);\n\n // Reconcile ONLY the axes this flush actually sent, from the resolved 200 body\n // — never the untouched axes (the per-axis DTO resolves an unpinned `typeId` to\n // the GLOBAL default, which would wrongly pin a follow-the-style user if applied\n // off a different axis's write). The raw state setters skip the localStorage /\n // re-PATCH side effects; for the valid ids the setters send, this is a no-op\n // confirmation, but it adopts any value the server normalised.\n const reconcile = useCallback((sent: AppearanceSyncPatch, resolved: AppearancePreferenceDto) => {\n if ('pattern' in sent) setPatternState(resolved.pattern);\n if ('styleId' in sent && isStyleId(resolved.styleId)) setStyleIdState(resolved.styleId);\n if ('paletteId' in sent && isPaletteId(resolved.paletteId)) setPaletteState(resolved.paletteId);\n if ('typeId' in sent && isTypeId(resolved.typeId)) setTypeChoiceState(resolved.typeId);\n if (sent.fontPicks) {\n const pageLocale = pageFontSetLocale();\n const adopted: Partial<Record<FontSetLocale, string | null>> = {};\n for (const locale of Object.keys(sent.fontPicks) as FontSetLocale[]) {\n const value = resolved.fontPicks?.[locale] ?? null;\n adopted[locale] = value;\n if (locale === pageLocale && value !== appliedPageFontPickRef.current) {\n applyPageFontPick(locale, value);\n appliedPageFontPickRef.current = value;\n }\n }\n setFontPicksState((prev) => ({ ...prev, ...adopted }));\n }\n }, []);\n\n const flushSync = useCallback(() => {\n debounceRef.current = null;\n const patch = pendingPatchRef.current;\n pendingPatchRef.current = {};\n if (Object.keys(patch).length === 0) return;\n\n const seq = ++syncSeqRef.current;\n fetch('/api/appearance-preference', {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(patch),\n })\n .then(async (res) => {\n if (!res.ok) throw new Error(`appearance sync failed: ${res.status}`);\n const body = (await res.json()) as { preference: AppearancePreferenceDto };\n // Drop a response superseded by a newer flush — it must not clobber the\n // newer choice nor flip the affordance.\n if (seq !== syncSeqRef.current) return;\n reconcile(patch, body.preference);\n setSyncState('idle');\n })\n .catch(() => {\n if (seq !== syncSeqRef.current) return;\n // Keep the optimistic local value (localStorage holds it); surface the\n // quiet \"couldn't sync\" affordance without blocking the live switch.\n setSyncState('error');\n });\n }, [reconcile]);\n\n // Merge an axis change into the pending patch and (re)arm the debounce. A\n // no-op for anonymous visitors — they never write to the account.\n const queueSync = useCallback(\n (patch: AppearanceSyncPatch) => {\n if (!signedIn) return;\n const prev = pendingPatchRef.current;\n const next: AppearanceSyncPatch = { ...prev, ...patch };\n // Font picks merge PER LOCALE: a ja pick and a ko pick inside one window\n // must both reach the one PATCH, where a shallow spread keeps only the last.\n if (prev.fontPicks || patch.fontPicks) {\n next.fontPicks = { ...prev.fontPicks, ...patch.fontPicks };\n }\n pendingPatchRef.current = next;\n if (debounceRef.current) clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(flushSync, SYNC_DEBOUNCE_MS);\n },\n [signedIn, flushSync],\n );\n\n // Flush any pending write on unmount so a just-made choice isn't dropped.\n useEffect(() => {\n return () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n flushSync();\n }\n };\n }, [flushSync]);\n\n const setPattern = useCallback(\n (next: ThemePattern) => {\n setPatternState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.pattern, next);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ pattern: next });\n },\n [queueSync],\n );\n\n const setStyleId = useCallback(\n (next: StyleId) => {\n setStyleIdState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.style, next);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ styleId: next });\n },\n [queueSync],\n );\n\n const setPalette = useCallback(\n (next: PaletteId) => {\n setPaletteState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.palette, next);\n // A value written now is already in today's id space — stamp it, or a\n // load that never ran the init script would re-migrate a fresh `motir`.\n window.localStorage.setItem(THEME_STORAGE_KEYS.paletteIds, PALETTE_IDS_VERSION);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ paletteId: next });\n },\n [queueSync],\n );\n\n const setType = useCallback(\n (next: TypeId) => {\n setTypeChoiceState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.type, next);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ typeId: next });\n },\n [queueSync],\n );\n\n const setFontPick = useCallback(\n (locale: FontSetLocale, memberId: string | null) => {\n // Signed-out pages keep the defaults: no device-local copy, no write.\n if (!signedIn) return;\n setFontPicksState((prev) => ({ ...prev, [locale]: memberId }));\n if (locale === pageFontSetLocale()) {\n applyPageFontPick(locale, memberId);\n appliedPageFontPickRef.current = memberId;\n }\n queueSync({ fontPicks: { [locale]: memberId } });\n },\n [signedIn, queueSync],\n );\n\n const value = useMemo<ThemeContextValue>(\n () => ({\n pattern,\n resolvedPattern,\n styleId,\n palette,\n type,\n setPattern,\n setStyleId,\n setPalette,\n setType,\n fontPicks,\n setFontPick,\n syncState,\n }),\n [\n pattern,\n resolvedPattern,\n styleId,\n palette,\n type,\n setPattern,\n setStyleId,\n setPalette,\n setType,\n fontPicks,\n setFontPick,\n syncState,\n ],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) {\n throw new Error('useTheme must be used inside <ThemeProvider>');\n }\n return ctx;\n}\n\n/**\n * Non-throwing variant of {@link useTheme}. Returns `null` when no\n * `<ThemeProvider>` is mounted instead of throwing.\n *\n * Use this in low-level primitives that must also render outside the authed\n * shell — e.g. the `MarkdownEditor` (Subtask 2.3.5) reads `resolvedPattern`\n * to drive the underlying editor's `data-color-mode`, but it's also exercised\n * on the public `/tokens` specimen and in component tests where wrapping every\n * render in a provider would be noise. Callers fall back to a sensible default\n * (`'light'`).\n */\nexport function useOptionalTheme(): ThemeContextValue | null {\n return useContext(ThemeContext);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/theme/typography.ts","../../src/theme/styles.ts","../../src/theme/palettes.ts","../../src/theme/types.ts","../../src/theme/fontSets.ts","../../src/contexts/theme-context.tsx"],"names":["value"],"mappings":";;;;AAsGO,IAAM,aAAA,GAAgB;AAAA,EAC3B,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EAAS,4EAAA;AAAA,IACT,KAAA,EAAO,oEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,qEAAA;AAAA,IACT,KAAA,EAAO,kDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,wEAAA;AAAA,IACT,KAAA,EAAO,4CAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,OAAA,EAAS;AAAA,IACP,EAAA,EAAI,SAAA;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EAAS,iFAAA;AAAA,IACT,KAAA,EAAO,6DAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,WAAA;AAAA,IACJ,IAAA,EAAM,WAAA;AAAA,IACN,OAAA,EAAS,gFAAA;AAAA,IACT,KAAA,EAAO,sEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,gBAAA,EAAkB;AAAA,IAChB,EAAA,EAAI,gBAAA;AAAA,IACJ,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,gEAAA;AAAA,IACT,KAAA,EAAO,gDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EAAS,wFAAA;AAAA,IACT,KAAA,EAAO,mEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,OAAA,EAAS;AAAA,IACP,EAAA,EAAI,SAAA;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EAAS,2EAAA;AAAA,IACT,KAAA,EAAO,sDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,EAAA,EAAI,MAAA;AAAA,IACJ,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,kFAAA;AAAA,IACT,KAAA,EAAO,iEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,EAAA,EAAI,UAAA;AAAA,IACJ,IAAA,EAAM,UAAA;AAAA,IACN,OAAA,EAAS,oEAAA;AAAA,IACT,KAAA,EAAO,yDAAA;AAAA,IACP,SAAA,EAAW;AAAA;AAEf,CAAA;AAeO,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,eAAe,KAAK,CAAA;AAC/F;;;AChDO,IAAM,cAAA,GAAiB;AAAA,EAC5B,gBAAA,EAAkB;AAAA,IAChB,EAAA,EAAI,gBAAA;AAAA,IACJ,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,+DAAA;AAAA,IACT,WAAA,EAAa,iEAAA;AAAA,IACb,SAAA,EAAW,+BAAA;AAAA,IACX,aAAA,EAAe,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EAAY,6EAAA;AAAA,MACZ,MAAA,EAAQ,yEAAA;AAAA,MACR,SAAA,EAAW,6DAAA;AAAA,MACX,OAAA,EAAS,iFAAA;AAAA,MACT,OAAA,EAAS,+EAAA;AAAA,MACT,MAAA,EAAQ,qDAAA;AAAA,MACR,UAAA,EAAY,wEAAA;AAAA,MACZ,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,cAAA,EAAgB;AAAA,IACd,EAAA,EAAI,cAAA;AAAA,IACJ,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,wDAAA;AAAA,IACT,WAAA,EAAa,0DAAA;AAAA,IACb,SAAA,EAAW,6BAAA;AAAA,IACX,aAAA,EAAe,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EAAY,iFAAA;AAAA,MACZ,MAAA,EAAQ,mFAAA;AAAA,MACR,SAAA,EAAW,mEAAA;AAAA,MACX,OAAA,EAAS,qFAAA;AAAA,MACT,OAAA,EAAS,6EAAA;AAAA,MACT,MAAA,EAAQ,0EAAA;AAAA,MACR,UAAA,EAAY,wEAAA;AAAA,MACZ,UAAA,EAAY;AAAA;AACd,GACF;AAAA,EACA,oBAAA,EAAsB;AAAA,IACpB,EAAA,EAAI,oBAAA;AAAA,IACJ,IAAA,EAAM,sBAAA;AAAA,IACN,OAAA,EAAS,0EAAA;AAAA,IACT,WAAA,EAAa,wFAAA;AAAA,IACb,SAAA,EAAW,mCAAA;AAAA,IACX,aAAA,EAAe,YAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,wFAAA;AAAA,MACF,MAAA,EACE,wGAAA;AAAA,MACF,SAAA,EACE,8FAAA;AAAA,MACF,OAAA,EACE,uFAAA;AAAA,MACF,OAAA,EACE,0GAAA;AAAA,MACF,MAAA,EACE,6FAAA;AAAA,MACF,UAAA,EACE,iHAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,EAAA,EAAI,eAAA;AAAA,IACJ,IAAA,EAAM,eAAA;AAAA,IACN,OAAA,EAAS,kFAAA;AAAA,IACT,WAAA,EACE,gGAAA;AAAA,IACF,SAAA,EAAW,8BAAA;AAAA,IACX,aAAA,EAAe,YAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,iIAAA;AAAA,MACF,MAAA,EACE,8KAAA;AAAA,MACF,SAAA,EACE,gJAAA;AAAA,MACF,OAAA,EACE,6GAAA;AAAA,MACF,OAAA,EACE,4HAAA;AAAA,MACF,MAAA,EACE,oHAAA;AAAA,MACF,UAAA,EACE,+HAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,aAAA,EAAe;AAAA,IACb,EAAA,EAAI,eAAA;AAAA,IACJ,IAAA,EAAM,eAAA;AAAA,IACN,OAAA,EAAS,mEAAA;AAAA,IACT,WAAA,EACE,4FAAA;AAAA,IACF,SAAA,EAAW,8BAAA;AAAA,IACX,aAAA,EAAe,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,uGAAA;AAAA,MACF,MAAA,EACE,mGAAA;AAAA,MACF,SAAA,EACE,wGAAA;AAAA,MACF,OAAA,EACE,wIAAA;AAAA,MACF,OAAA,EACE,6GAAA;AAAA,MACF,MAAA,EACE,oGAAA;AAAA,MACF,UAAA,EACE,6FAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,EAAA,EAAI,eAAA;AAAA,IACJ,IAAA,EAAM,iBAAA;AAAA,IACN,OAAA,EAAS,iFAAA;AAAA,IACT,WAAA,EACE,oGAAA;AAAA,IACF,SAAA,EAAW,8BAAA;AAAA,IACX,aAAA,EAAe,YAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,4FAAA;AAAA,MACF,MAAA,EACE,kHAAA;AAAA,MACF,SAAA,EACE,6GAAA;AAAA,MACF,OAAA,EACE,uFAAA;AAAA,MACF,OAAA,EACE,yHAAA;AAAA,MACF,MAAA,EAAQ,iFAAA;AAAA,MACR,UAAA,EACE,0HAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EAAS,oEAAA;AAAA,IACT,WAAA,EACE,kKAAA;AAAA,IACF,SAAA,EAAW,uBAAA;AAAA,IACX,aAAA,EAAe,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,0HAAA;AAAA,MACF,MAAA,EACE,0GAAA;AAAA,MACF,SAAA,EACE,4HAAA;AAAA,MACF,OAAA,EACE,sIAAA;AAAA,MACF,OAAA,EACE,oHAAA;AAAA,MACF,MAAA,EACE,2JAAA;AAAA,MACF,UAAA,EACE,kIAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,cAAA,EAAgB;AAAA,IACd,EAAA,EAAI,cAAA;AAAA,IACJ,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,wEAAA;AAAA,IACT,WAAA,EACE,qGAAA;AAAA,IACF,SAAA,EAAW,6BAAA;AAAA,IACX,aAAA,EAAe,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,uHAAA;AAAA,MACF,MAAA,EACE,wGAAA;AAAA,MACF,SAAA,EACE,8RAAA;AAAA,MACF,OAAA,EACE,wPAAA;AAAA,MACF,OAAA,EACE,kHAAA;AAAA,MACF,MAAA,EACE,mNAAA;AAAA,MACF,UAAA,EACE,8FAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,WAAA,EAAa;AAAA,IACX,EAAA,EAAI,aAAA;AAAA,IACJ,IAAA,EAAM,aAAA;AAAA,IACN,OAAA,EAAS,qEAAA;AAAA,IACT,WAAA,EACE,sKAAA;AAAA,IACF,SAAA,EAAW,4BAAA;AAAA,IACX,aAAA,EAAe,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,6JAAA;AAAA,MACF,MAAA,EACE,mKAAA;AAAA,MACF,SAAA,EACE,oSAAA;AAAA,MACF,OAAA,EACE,mSAAA;AAAA,MACF,OAAA,EACE,qJAAA;AAAA,MACF,MAAA,EACE,mKAAA;AAAA,MACF,UAAA,EACE,qGAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,kBAAA,EAAoB;AAAA,IAClB,EAAA,EAAI,kBAAA;AAAA,IACJ,IAAA,EAAM,oBAAA;AAAA,IACN,OAAA,EACE,2GAAA;AAAA,IACF,WAAA,EACE,6IAAA;AAAA,IACF,SAAA,EAAW,iCAAA;AAAA,IACX,aAAA,EAAe,QAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,0OAAA;AAAA,MACF,MAAA,EACE,6OAAA;AAAA,MACF,SAAA,EACE,wKAAA;AAAA,MACF,OAAA,EACE,6JAAA;AAAA,MACF,OAAA,EACE,0JAAA;AAAA,MACF,MAAA,EACE,qJAAA;AAAA,MACF,UAAA,EACE,iNAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ,GACF;AAAA,EACA,aAAA,EAAe;AAAA,IACb,EAAA,EAAI,eAAA;AAAA,IACJ,IAAA,EAAM,eAAA;AAAA,IACN,OAAA,EAAS,wEAAA;AAAA,IACT,WAAA,EACE,iKAAA;AAAA,IACF,SAAA,EAAW,8BAAA;AAAA,IACX,aAAA,EAAe,SAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,UAAA,EACE,0IAAA;AAAA,MACF,MAAA,EACE,yJAAA;AAAA,MACF,SAAA,EACE,kOAAA;AAAA,MACF,OAAA,EACE,gQAAA;AAAA,MACF,OAAA,EACE,yIAAA;AAAA,MACF,MAAA,EACE,6KAAA;AAAA,MACF,UAAA,EACE,0IAAA;AAAA,MACF,UAAA,EACE;AAAA;AACJ;AAEJ,CAAA;AAMO,IAAM,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,cAAc,CAAA;AAM5C,IAAM,gBAAA,GAA4B,gBAAA;AAGlC,SAAS,UAAU,KAAA,EAAkC;AAC1D,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAChG;AAGO,SAAS,aAAa,KAAA,EAAiC;AAC5D,EAAA,OAAO,cAAA,CAAe,SAAA,CAAU,KAAK,CAAA,GAAI,QAAQ,gBAAgB,CAAA;AACnE;AAS2D,MAAA,CAAO,WAAA;AAAA,EAChE,SAAA,CAAU,GAAA,CAAI,CAAC,EAAA,KAAO,CAAC,IAAI,cAAA,CAAe,EAAE,CAAA,CAAE,aAAa,CAAC;AAC9D;;;AC7XO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EACE,6HAAA;AAAA,IACF,WAAA,EACE,0FAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,EAAA,EAAI,UAAA;AAAA,IACJ,IAAA,EAAM,UAAA;AAAA,IACN,OAAA,EAAS,yFAAA;AAAA,IACT,WAAA,EACE,2FAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EACE,8FAAA;AAAA,IACF,WAAA,EACE,iGAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,WAAA;AAAA,IACJ,IAAA,EAAM,WAAA;AAAA,IACN,OAAA,EACE,gGAAA;AAAA,IACF,WAAA,EACE,4HAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,EAAA,EAAI,UAAA;AAAA,IACJ,IAAA,EAAM,UAAA;AAAA,IACN,OAAA,EACE,sHAAA;AAAA,IACF,WAAA,EACE,qHAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EACE,6HAAA;AAAA,IACF,WAAA,EACE,sLAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EACE,kHAAA;AAAA,IACF,WAAA,EACE,gMAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EACE,0GAAA;AAAA,IACF,WAAA,EACE,2LAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,OAAA,EAAS;AAAA,IACP,EAAA,EAAI,SAAA;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EACE,yHAAA;AAAA,IACF,WAAA,EACE,0NAAA;AAAA,IACF,SAAA,EAAW;AAAA,GACb;AAAA,EACA,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EACE,mHAAA;AAAA,IACF,WAAA,EACE,yOAAA;AAAA,IACF,SAAA,EAAW;AAAA;AAEf,CAAA;AAcO,IAAM,kBAAA,GAAgC,OAAA;AAqBtC,SAAS,YAAY,KAAA,EAAoC;AAC9D,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,kBAAkB,KAAK,CAAA;AAClG;;;ACvJO,IAAM,kBAAA,GAAqB;AAAA,EAChC,OAAA,EAAS,qBAAA;AAAA,EACT,KAAA,EAAO,mBAAA;AAAA,EACP,OAAA,EAAS,qBAAA;AAAA,EACT,IAAA,EAAM,kBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAON,UAAA,EAAY,wBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOZ,SAAA,EAAW;AACb,CAAA;AAGO,IAAM,mBAAA,GAAsB,GAAA;AAU5B,IAAM,oBAAA,GAAkE;AAAA,EAC7E,KAAA,EAAO,UAAA;AAAA,EACP,QAAA,EAAU;AACZ,CAAA;AAQO,SAAS,sBAAA,CAAuB,OAAsB,MAAA,EAAsC;AACjG,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,MAAA,KAAW,mBAAA,EAAqB,OAAO,KAAA;AAE7D,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,cAAA,CAAe,KAAK,oBAAA,EAAsB,KAAK,GAAG,OAAO,KAAA;AAC/E,EAAA,OAAO,oBAAA,CAAqB,KAAK,CAAA,IAAK,KAAA;AACxC;AAQO,IAAM,cAAA,GAAiB;AAAA,EAC5B,OAAA,EAAS,QAAA;AAAA,EACT,KAAA,EAAO,gBAGT,CAAA;;;ACrFO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA;AACF,CAAA;AAgBO,IAAM,cAAA,GAAiB,CAAC,MAAA,EAAQ,OAAA,EAAS,MAAM,CAAA;AA2DtD,IAAM,YAAA,GAA8B;AAAA,EAClC,EAAA,EAAI,cAAA;AAAA,EACJ,MAAA,EAAQ,IAAA;AAAA,EACR,MAAA,EAAQ,EAAE,IAAA,EAAM,cAAA,EAAe;AAAA,EAC/B,UAAA,EAAY;AACd,CAAA;AAEA,SAAS,MAAA,CACP,EAAA,EACA,MAAA,EACA,UAAA,EACA,UAAA,EACe;AACf,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA,EAAQ,EAAE,IAAA,EAAM,kBAAA,EAAoB,cAAc,MAAA,CAAO,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,EAAE;AAAA,IAC5E,UAAA;AAAA,IACA,GAAI,UAAA,GAAa,EAAE,UAAA,KAAe;AAAC,GACrC;AACF;AAMO,IAAM,iBAAA,GAAoB;AAAA;AAAA;AAAA,EAG/B,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,KAAA,EAAO,OAAA;AAAA,IACP,IAAA,EAAM,IAAA;AAAA,IACN,GAAA,EAAK,KAAA;AAAA,IACL,KAAA,EAAO;AAAA,MACL,MAAM,EAAE,OAAA,EAAS,gBAAgB,OAAA,EAAS,CAAC,YAAY,CAAA,EAAE;AAAA,MACzD,OAAO,EAAE,OAAA,EAAS,gBAAgB,OAAA,EAAS,CAAC,YAAY,CAAA,EAAE;AAAA,MAC1D,MAAM,EAAE,OAAA,EAAS,gBAAgB,OAAA,EAAS,CAAC,YAAY,CAAA;AAAE;AAC3D,GACF;AAAA;AAAA,EAEA,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,SAAA;AAAA,IACJ,KAAA,EAAO,oBAAA;AAAA,IACP,IAAA,EAAM,IAAA;AAAA,IACN,GAAA,EAAK,IAAA;AAAA,IACL,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,EAAE,OAAA,EAAS,cAAA,EAAgB,OAAA,EAAS,CAAC,MAAA,CAAO,cAAA,EAAgB,cAAA,EAAgB,KAAK,CAAC,CAAA,EAAE;AAAA,MAC1F,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,eAAA;AAAA,QACT,OAAA,EAAS;AAAA,UACP,MAAA,CAAO,eAAA,EAAiB,eAAA,EAAiB,IAAI,CAAA;AAAA;AAAA,UAE7C,MAAA,CAAO,gBAAA,EAAkB,gBAAA,EAAkB,IAAI;AAAA;AACjD,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,cAAA;AAAA,QACT,SAAS,CAAC,MAAA,CAAO,gBAAgB,cAAA,EAAgB,KAAA,EAAO,MAAM,CAAC;AAAA;AACjE;AACF,GACF;AAAA;AAAA;AAAA,EAGA,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,SAAA;AAAA,IACJ,KAAA,EAAO,qBAAA;AAAA,IACP,IAAA,EAAM,OAAA;AAAA,IACN,GAAA,EAAK,IAAA;AAAA,IACL,KAAA,EAAO;AAAA,MACL,IAAA,EAAM,EAAE,OAAA,EAAS,cAAA,EAAgB,OAAA,EAAS,CAAC,MAAA,CAAO,cAAA,EAAgB,cAAA,EAAgB,KAAK,CAAC,CAAA,EAAE;AAAA,MAC1F,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,eAAA;AAAA,QACT,OAAA,EAAS;AAAA,UACP,MAAA,CAAO,eAAA,EAAiB,eAAA,EAAiB,KAAK,CAAA;AAAA;AAAA,UAE9C,MAAA,CAAO,gBAAA,EAAkB,gBAAA,EAAkB,KAAK;AAAA;AAClD,OACF;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,cAAA;AAAA,QACT,SAAS,CAAC,MAAA,CAAO,gBAAgB,cAAA,EAAgB,KAAA,EAAO,MAAM,CAAC;AAAA;AACjE;AACF,GACF;AAAA;AAAA,EAEA,EAAA,EAAI;AAAA,IACF,EAAA,EAAI,IAAA;AAAA,IACJ,KAAA,EAAO,UAAA;AAAA,IACP,IAAA,EAAM,IAAA;AAAA,IACN,GAAA,EAAK,IAAA;AAAA,IACL,KAAA,EAAO;AAAA,MACL,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,cAAA;AAAA,QACT,OAAA,EAAS;AAAA,UACP,MAAA,CAAO,cAAA,EAAgB,cAAA,EAAgB,IAAI,CAAA;AAAA,UAC3C,MAAA,CAAO,mBAAA,EAAqB,mBAAA,EAAqB,IAAI;AAAA;AACvD,OACF;AAAA,MACA,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,eAAA;AAAA,QACT,SAAS,CAAC,MAAA,CAAO,eAAA,EAAiB,eAAA,EAAiB,KAAK,CAAC;AAAA,OAC3D;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,cAAA;AAAA,QACT,SAAS,CAAC,MAAA,CAAO,gBAAgB,cAAA,EAAgB,KAAA,EAAO,MAAM,CAAC;AAAA;AACjE;AACF,GACF;AAAA;AAAA,EAEA,EAAA,EAAI;AAAA,IACF,EAAA,EAAI,IAAA;AAAA,IACJ,KAAA,EAAO,QAAA;AAAA,IACP,IAAA,EAAM,IAAA;AAAA,IACN,GAAA,EAAK,IAAA;AAAA,IACL,KAAA,EAAO;AAAA,MACL,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,cAAA;AAAA,QACT,OAAA,EAAS;AAAA,UACP,MAAA,CAAO,cAAA,EAAgB,cAAA,EAAgB,IAAI,CAAA;AAAA,UAC3C,MAAA,CAAO,cAAA,EAAgB,cAAA,EAAgB,IAAI;AAAA;AAC7C,OACF;AAAA,MACA,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,eAAA;AAAA,QACT,SAAS,CAAC,MAAA,CAAO,eAAA,EAAiB,eAAA,EAAiB,KAAK,CAAC;AAAA,OAC3D;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EAAS,cAAA;AAAA,QACT,SAAS,CAAC,MAAA,CAAO,gBAAgB,cAAA,EAAgB,KAAA,EAAO,MAAM,CAAC;AAAA;AACjE;AACF;AAEJ,CAAA;AAYO,IAAM,eAAA,GAAkB;AAAA,EAC7B,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,SAAA;AAAA,EACJ,EAAA,EAAI,IAAA;AAAA,EACJ,EAAA,EAAI,IAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,OAAA,EAAS;AACX,CAAA;AAGO,SAAS,gBAAgB,KAAA,EAAwC;AACtE,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAa,gBAAA,CAAuC,SAAS,KAAK,CAAA;AAC5F;AAkFO,SAAS,qBAAA,CACd,QACA,QAAA,EAC+C;AAC/C,EAAA,IAAI,QAAA,KAAa,IAAA,EAAM,OAAO,EAAC;AAC/B,EAAA,MAAM,GAAA,GAAe,iBAAA,CAAkB,eAAA,CAAgB,MAAM,CAAC,CAAA;AAC9D,EAAA,MAAM,QAAuD,EAAC;AAC9D,EAAA,KAAA,MAAW,QAAQ,cAAA,EAAgB;AACjC,IAAA,MAAM,CAAA,GAAwB,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA;AAC5C,IAAA,IAAI,QAAA,KAAa,EAAE,OAAA,EAAS;AAC5B,IAAA,MAAM,MAAA,GAAS,EAAE,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAqB,CAAA,CAAE,OAAO,QAAQ,CAAA;AACrE,IAAA,IAAI,MAAA,IAAU,OAAO,MAAA,CAAO,IAAA,KAAS,gBAAgB,KAAA,CAAM,CAAA,cAAA,EAAiB,IAAI,CAAA,CAAE,CAAA,GAAI,QAAA;AAAA,EACxF;AACA,EAAA,OAAO,KAAA;AACT;ACjQA,SAAS,iBAAA,GAA0C;AACjD,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,IAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,eAAA,CAAgB,IAAA,CAAK,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM,CAAA,CAAE,CAAC,CAAA,EAAG,WAAA,EAAY,IAAK,EAAA;AACxF,EAAA,OAAO,eAAA,CAAgB,OAAO,CAAA,GAAI,OAAA,GAAU,IAAA;AAC9C;AAGA,SAAS,iBAAA,CAAkB,QAAuB,QAAA,EAA+B;AAC/E,EAAA,MAAM,OAAO,QAAA,CAAS,eAAA;AACtB,EAAA,KAAA,MAAW,QAAQ,cAAA,EAAgB,IAAA,CAAK,eAAA,CAAgB,CAAA,cAAA,EAAiB,IAAI,CAAA,CAAE,CAAA;AAC/E,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,CAAA,IAAK,MAAA,CAAO,QAAQ,qBAAA,CAAsB,MAAA,EAAQ,QAAQ,CAAC,CAAA,EAAG;AACnF,IAAA,IAAA,CAAK,YAAA,CAAa,MAAM,KAAK,CAAA;AAAA,EAC/B;AACF;AAGA,IAAM,gBAAA,GAAmB,GAAA;AAEzB,IAAM,YAAA,GAAe,cAAwC,IAAI,CAAA;AAGjE,SAAS,WAAA,CAA8B,KAAa,QAAA,EAAgB;AAClE,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA;AAC1C,EAAA,IAAI;AACF,IAAA,OAAQ,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,GAAG,CAAA,IAAkB,QAAA;AAAA,EAC3D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,QAAA;AAAA,EACT;AACF;AASA,SAAS,qBAAqB,QAAA,EAAkC;AAC9D,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,MAAM;AAAA,EAAC,CAAA;AACjD,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA;AAC5D,EAAA,GAAA,CAAI,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACvC,EAAA,OAAO,MAAM,GAAA,CAAI,mBAAA,CAAoB,QAAA,EAAU,QAAQ,CAAA;AACzD;AAEA,SAAS,sBAAA,GAA+C;AACtD,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,OAAA;AAC1C,EAAA,OAAO,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,CAAE,UAAU,MAAA,GAAS,OAAA;AAC9E;AAEA,SAAS,4BAAA,GAAqD;AAC5D,EAAA,OAAO,OAAA;AACT;AAEO,SAAS,aAAA,CAAc;AAAA,EAC5B,QAAA;AAAA,EACA,iBAAA,GAAoB,IAAA;AAAA,EACpB,QAAA,GAAW;AACb,CAAA,EAqBG;AAID,EAAA,MAAM,CAAC,OAAA,EAAS,eAAe,CAAA,GAAI,QAAA;AAAA,IAAuB,MACxD,oBACI,iBAAA,CAAkB,OAAA,GAClB,YAA0B,kBAAA,CAAmB,OAAA,EAAS,eAAe,OAAO;AAAA,GAClF;AACA,EAAA,MAAM,CAAC,OAAA,EAAS,eAAe,CAAA,GAAI,SAAkB,MAAM;AACzD,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,OAAO,SAAA,CAAU,iBAAA,CAAkB,OAAO,CAAA,GAAI,kBAAkB,OAAA,GAAU,gBAAA;AAAA,IAC5E;AAGA,IAAA,MAAM,MAAA,GAAS,WAAA,CAAoB,kBAAA,CAAmB,KAAA,EAAO,eAAe,KAAK,CAAA;AACjF,IAAA,OAAO,SAAA,CAAU,MAAM,CAAA,GAAI,MAAA,GAAS,gBAAA;AAAA,EACtC,CAAC,CAAA;AACD,EAAA,MAAM,CAAC,OAAA,EAAS,eAAe,CAAA,GAAI,SAAoB,MAAM;AAC3D,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,OAAO,WAAA,CAAY,iBAAA,CAAkB,SAAS,CAAA,GAC1C,kBAAkB,SAAA,GAClB,kBAAA;AAAA,IACN;AAIA,IAAA,MAAM,MAAA,GAAS,sBAAA;AAAA,MACb,WAAA,CAAoB,kBAAA,CAAmB,OAAA,EAAS,EAAE,CAAA,IAAK,IAAA;AAAA,MACvD,WAAA,CAAoB,kBAAA,CAAmB,UAAA,EAAY,EAAE,CAAA,IAAK;AAAA,KAC5D;AACA,IAAA,OAAO,WAAA,CAAY,MAAM,CAAA,GAAI,MAAA,GAAS,kBAAA;AAAA,EACxC,CAAC,CAAA;AAID,EAAA,MAAM,CAAC,UAAA,EAAY,kBAAkB,CAAA,GAAI,SAAwB,MAAM;AACrE,IAAA,IAAI,iBAAA,EAAmB;AAGrB,MAAA,OAAO,kBAAkB,UAAA,IAAc,QAAA,CAAS,kBAAkB,MAAM,CAAA,GACpE,kBAAkB,MAAA,GAClB,IAAA;AAAA,IACN;AACA,IAAA,MAAM,MAAA,GAAS,WAAA,CAAoB,kBAAA,CAAmB,IAAA,EAAM,EAAE,CAAA;AAC9D,IAAA,OAAO,QAAA,CAAS,MAAM,CAAA,GAAI,MAAA,GAAS,IAAA;AAAA,EACrC,CAAC,CAAA;AAGD,EAAA,MAAM,aAAA,GAAgB,oBAAA;AAAA,IACpB,oBAAA;AAAA,IACA,sBAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,eAAA,GAAwC,OAAA,KAAY,QAAA,GAAW,aAAA,GAAgB,OAAA;AAKrF,EAAA,MAAM,IAAA,GAAe,UAAA,IAAc,YAAA,CAAa,OAAO,CAAA,CAAE,aAAA;AAKzD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,YAAA,CAAa,YAAA,EAAc,eAAe,CAAA;AAAA,EACrE,CAAA,EAAG,CAAC,eAAe,CAAC,CAAA;AAEpB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,YAAA,CAAa,YAAA,EAAc,OAAO,CAAA;AAAA,EAC7D,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,YAAA,CAAa,cAAA,EAAgB,OAAO,CAAA;AAAA,EAC/D,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,YAAA,CAAa,WAAA,EAAa,IAAI,CAAA;AAAA,EACzD,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAST,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAA8B,MAAM,CAAA;AAKtE,EAAA,MAAM,UAAA,GAAa,OAAO,CAAC,CAAA;AAE3B,EAAA,MAAM,eAAA,GAAkB,MAAA,CAA4B,EAAE,CAAA;AACtD,EAAA,MAAM,WAAA,GAAc,OAA6C,IAAI,CAAA;AAIrE,EAAA,MAAM,CAAC,SAAA,EAAW,iBAAiB,CAAA,GAAI,QAAA;AAAA,IACrC;AAAC,GACH;AACA,EAAA,MAAM,sBAAA,GAAyB,OAAkC,MAAS,CAAA;AAQ1E,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,CAAC,IAAA,EAA2B,QAAA,KAAsC;AAC9F,IAAA,IAAI,SAAA,IAAa,IAAA,EAAM,eAAA,CAAgB,QAAA,CAAS,OAAO,CAAA;AACvD,IAAA,IAAI,SAAA,IAAa,QAAQ,SAAA,CAAU,QAAA,CAAS,OAAO,CAAA,EAAG,eAAA,CAAgB,SAAS,OAAO,CAAA;AACtF,IAAA,IAAI,WAAA,IAAe,QAAQ,WAAA,CAAY,QAAA,CAAS,SAAS,CAAA,EAAG,eAAA,CAAgB,SAAS,SAAS,CAAA;AAC9F,IAAA,IAAI,QAAA,IAAY,QAAQ,QAAA,CAAS,QAAA,CAAS,MAAM,CAAA,EAAG,kBAAA,CAAmB,SAAS,MAAM,CAAA;AACrF,IAAA,IAAI,KAAK,SAAA,EAAW;AAClB,MAAA,MAAM,aAAa,iBAAA,EAAkB;AACrC,MAAA,MAAM,UAAyD,EAAC;AAChE,MAAA,KAAA,MAAW,MAAA,IAAU,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,SAAS,CAAA,EAAsB;AACnE,QAAA,MAAMA,MAAAA,GAAQ,QAAA,CAAS,SAAA,GAAY,MAAM,CAAA,IAAK,IAAA;AAC9C,QAAA,OAAA,CAAQ,MAAM,CAAA,GAAIA,MAAAA;AAClB,QAAA,IAAI,MAAA,KAAW,UAAA,IAAcA,MAAAA,KAAU,sBAAA,CAAuB,OAAA,EAAS;AACrE,UAAA,iBAAA,CAAkB,QAAQA,MAAK,CAAA;AAC/B,UAAA,sBAAA,CAAuB,OAAA,GAAUA,MAAAA;AAAA,QACnC;AAAA,MACF;AACA,MAAA,iBAAA,CAAkB,CAAC,IAAA,MAAU,EAAE,GAAG,IAAA,EAAM,GAAG,SAAQ,CAAE,CAAA;AAAA,IACvD;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,SAAA,GAAY,YAAY,MAAM;AAClC,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,MAAM,QAAQ,eAAA,CAAgB,OAAA;AAC9B,IAAA,eAAA,CAAgB,UAAU,EAAC;AAC3B,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,WAAW,CAAA,EAAG;AAErC,IAAA,MAAM,GAAA,GAAM,EAAE,UAAA,CAAW,OAAA;AACzB,IAAA,KAAA,CAAM,4BAAA,EAA8B;AAAA,MAClC,MAAA,EAAQ,OAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,KAAK;AAAA,KAC3B,CAAA,CACE,IAAA,CAAK,OAAO,GAAA,KAAQ;AACnB,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AACpE,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAG7B,MAAA,IAAI,GAAA,KAAQ,WAAW,OAAA,EAAS;AAChC,MAAA,SAAA,CAAU,KAAA,EAAO,KAAK,UAAU,CAAA;AAChC,MAAA,YAAA,CAAa,MAAM,CAAA;AAAA,IACrB,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,MAAA,IAAI,GAAA,KAAQ,WAAW,OAAA,EAAS;AAGhC,MAAA,YAAA,CAAa,OAAO,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAId,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,KAAA,KAA+B;AAC9B,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,MAAM,OAAO,eAAA,CAAgB,OAAA;AAC7B,MAAA,MAAM,IAAA,GAA4B,EAAE,GAAG,IAAA,EAAM,GAAG,KAAA,EAAM;AAGtD,MAAA,IAAI,IAAA,CAAK,SAAA,IAAa,KAAA,CAAM,SAAA,EAAW;AACrC,QAAA,IAAA,CAAK,YAAY,EAAE,GAAG,KAAK,SAAA,EAAW,GAAG,MAAM,SAAA,EAAU;AAAA,MAC3D;AACA,MAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,MAAA,IAAI,WAAA,CAAY,OAAA,EAAS,YAAA,CAAa,WAAA,CAAY,OAAO,CAAA;AACzD,MAAA,WAAA,CAAY,OAAA,GAAU,UAAA,CAAW,SAAA,EAAW,gBAAgB,CAAA;AAAA,IAC9D,CAAA;AAAA,IACA,CAAC,UAAU,SAAS;AAAA,GACtB;AAGA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,YAAY,OAAA,EAAS;AACvB,QAAA,YAAA,CAAa,YAAY,OAAO,CAAA;AAChC,QAAA,SAAA,EAAU;AAAA,MACZ;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,UAAA,GAAa,WAAA;AAAA,IACjB,CAAC,IAAA,KAAuB;AACtB,MAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,MAAA,IAAI;AACF,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,kBAAA,CAAmB,OAAA,EAAS,IAAI,CAAA;AAAA,MAC9D,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,SAAA,CAAU,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,UAAA,GAAa,WAAA;AAAA,IACjB,CAAC,IAAA,KAAkB;AACjB,MAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,MAAA,IAAI;AACF,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,kBAAA,CAAmB,KAAA,EAAO,IAAI,CAAA;AAAA,MAC5D,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,SAAA,CAAU,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,UAAA,GAAa,WAAA;AAAA,IACjB,CAAC,IAAA,KAAoB;AACnB,MAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,MAAA,IAAI;AACF,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,kBAAA,CAAmB,OAAA,EAAS,IAAI,CAAA;AAG5D,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,kBAAA,CAAmB,UAAA,EAAY,mBAAmB,CAAA;AAAA,MAChF,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,SAAA,CAAU,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,IAC/B,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,IAAA,KAAiB;AAChB,MAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,MAAA,IAAI;AACF,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,kBAAA,CAAmB,IAAA,EAAM,IAAI,CAAA;AAAA,MAC3D,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,SAAA,CAAU,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,IAC5B,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,QAAuB,QAAA,KAA4B;AAElD,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,iBAAA,CAAkB,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,QAAA,EAAS,CAAE,CAAA;AAC7D,MAAA,IAAI,MAAA,KAAW,mBAAkB,EAAG;AAClC,QAAA,iBAAA,CAAkB,QAAQ,QAAQ,CAAA;AAClC,QAAA,sBAAA,CAAuB,OAAA,GAAU,QAAA;AAAA,MACnC;AACA,MAAA,SAAA,CAAU,EAAE,WAAW,EAAE,CAAC,MAAM,GAAG,QAAA,IAAY,CAAA;AAAA,IACjD,CAAA;AAAA,IACA,CAAC,UAAU,SAAS;AAAA,GACtB;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,OAAO;AAAA,MACL,OAAA;AAAA,MACA,eAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA;AAAA,MACE,OAAA;AAAA,MACA,eAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA;AACF,GACF;AAEA,EAAA,uBAAO,GAAA,CAAC,YAAA,CAAa,QAAA,EAAb,EAAsB,OAAe,QAAA,EAAS,CAAA;AACxD;AAEO,SAAS,QAAA,GAA8B;AAC5C,EAAA,MAAM,GAAA,GAAM,WAAW,YAAY,CAAA;AACnC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,EAChE;AACA,EAAA,OAAO,GAAA;AACT;AAaO,SAAS,gBAAA,GAA6C;AAC3D,EAAA,OAAO,WAAW,YAAY,CAAA;AAChC","file":"theme-context.js","sourcesContent":["/**\n * The Motir TYPOGRAPHY LIBRARY — the named-pairing registry (Subtask 7.3.53).\n *\n * A \"type pairing\" is a named TYPOGRAPHY scheme the whole UI can swap to at\n * runtime — the THIRD design axis, completing the token decomposition\n * (Colour `data-palette` · Type `data-type` · Shape/feel `data-style`). It is\n * deliberately INDEPENDENT of the other two: a pairing decides which TYPEFACES\n * the product wears (the headline / body / mono role mapping); it never touches\n * a hue (palette axis) or a radius (style axis).\n *\n * ── The three-axis runtime contract ─────────────────────────────────────\n * data-type=\"<type-id>\" — THIS axis. A `[data-type='<id>']` block in\n * app/globals.css re-points the `--font-*`\n * role tokens (sans / serif / mono) — and ONLY\n * font tokens, never colour or shape.\n * data-palette=\"<palette-id>\" — the INDEPENDENT colour axis (`--el-*`).\n * data-style=\"<style-id>\" — the INDEPENDENT shape/feel axis.\n *\n * The shared `--font-size-*` SCALE stays common across pairings — sizes are\n * layout, not brand; a pairing remaps the FACES, not the type scale.\n *\n * ── A style declares a DEFAULT pairing (overridable) ────────────────────\n * Typography used to be BUNDLED into the style axis: `swiss-minimal-flat`,\n * `neo-brutalism` and `cybercore-y2k` each re-pointed `--font-serif` inside\n * their own `[data-style]` block. That is now a disjointness violation (two\n * axes setting `--font-*`). The fix: each style declares a `defaultTypeId`\n * (see ./styles.ts) — the pairing applied when the user has NOT pinned an\n * explicit type — and the per-style font overrides move here, as pairings.\n * So out-of-the-box looks are UNCHANGED; the new freedom is that type is now\n * independently overridable (e.g. Swiss shape + a serif pairing).\n *\n * ── v1 — the base faces only (no new payload) ───────────────────────────\n * v1 registered three pairings built from the THREE already-loaded next/font\n * faces (Inter · Source Serif 4 · JetBrains Mono) — so they add ZERO font\n * payload and carry NO new-typeface design decision:\n * - `motir` — the base: editorial serif headlines over a sans body.\n * - `motir-sans` — all-sans (Inter headlines); reproduces the Swiss look.\n * - `motir-mono` — mono THROUGHOUT (headlines + body/UI); a code-native UI.\n * `motir` is the base — like the `motir` palette / `warm-editorial` style it\n * needs NO `[data-type]` override block; the base `--font-*` tokens already are\n * it.\n *\n * ── New-typeface pairings (Grotesk / Editorial / Mono-Technical, 7.3.54–56) ──\n * Each adds a real type-design decision and loads its own next/font face(s),\n * re-pointing a role in its own `[data-type]` block:\n * - `grotesk` (7.3.54) — Space Grotesk THROUGHOUT (headlines + body/UI).\n * - `editorial` (7.3.55) — Fraunces display-serif headlines over the Inter\n * body; a magazine, considered pairing (one new face — Inter + JetBrains reused).\n * Body STAYS Inter — a display serif is unreadable as UI, so this pairing\n * re-types headlines only (the legibility exception to whole-UI re-typing).\n * - `mono-technical` (7.3.56) — IBM Plex Mono THROUGHOUT (headlines + body/UI +\n * meta); a precise, developer-grade UI (one new face — Inter reused for nothing\n * now, kept as the ultimate fallback).\n *\n * ── New-typeface pairings (Story MOTIR-10548) ─────────────────────────────\n * - `marker` (MOTIR-10553) — Caveat hand-lettered marker headlines over the\n * Inter body; Hand-Drawn / Indie's default. Display-only, like `editorial`: a\n * marker hand is unreadable as UI text, so only the headline role is re-typed.\n * - `rounded` (MOTIR-10554) — Nunito THROUGHOUT (headlines + body/UI); a\n * soft, friendly rounded sans that reads cleanly as UI text, so it re-types\n * the whole UI like `grotesk`.\n * - `slab` (MOTIR-10555) — Roboto Slab headlines over the Inter body;\n * sturdy and print-like. Display-only, like `editorial`: heavy slab serifs\n * crowd UI text, so only the headline role is re-typed.\n * - `humanist` (MOTIR-10556) — Fira Sans THROUGHOUT (headlines + body/UI); a\n * warm humanist sans drawn as a screen UI face, so it re-types the whole UI\n * like `grotesk`.\n *\n * ── Whole-UI re-typing (Yue, 2026-06-19) ────────────────────────────────\n * A type pairing re-types the WHOLE app, chrome included (nav, header, buttons),\n * not just headlines: a pairing whose signature face reads as a UI sans/mono\n * (`grotesk`, `motir-mono`, `mono-technical`, `rounded`, `humanist`) ALSO\n * re-points the body/UI role (`--font-sans`) in its `[data-type]` block. The\n * display-serif `editorial` and `slab`, the display-only `marker` and the\n * all-Inter `motir` / `motir-sans` keep the Inter body for legibility.\n * Each later \"Type: …\" subtask ADDS its entry here + a `[data-type]` block (its\n * face loaded in `app/layout.tsx`) + its `docs/typography/<id>.md`.\n */\n\n/** A registered, runtime-selectable type pairing. */\nexport interface TypographyDefinition {\n /** The `data-type` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the typographic feel. */\n tagline: string;\n /** The headline / body / mono face mapping (credit / what it loads). */\n faces: string;\n /**\n * Repo-relative path to this pairing's doc — the type-role reference a later\n * getdesign-style swap or the `/tokens` composer reads. Every registered\n * pairing maps to exactly one.\n */\n designDoc: string;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each non-base `id`\n * matches a `[data-type='<id>']` block in app/globals.css (`motir` is the base,\n * so its block is the Tier-0 `--font-*` defaults — no override block needed).\n */\nexport const TYPE_REGISTRY = {\n motir: {\n id: 'motir',\n name: 'Motir',\n tagline: 'Editorial serif headlines over a clean sans body — the house pairing.',\n faces: 'Source Serif 4 headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir.md',\n },\n 'motir-sans': {\n id: 'motir-sans',\n name: 'Motir Sans',\n tagline: 'All-sans — Inter for headlines and body; structural, no serif.',\n faces: 'Inter headlines + body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir-sans.md',\n },\n 'motir-mono': {\n id: 'motir-mono',\n name: 'Motir Mono',\n tagline: 'Monospace throughout — a code-native UI, headlines, body and all.',\n faces: 'JetBrains Mono headlines + body/UI + meta.',\n designDoc: 'docs/typography/motir-mono.md',\n },\n grotesk: {\n id: 'grotesk',\n name: 'Grotesk',\n tagline: 'Geometric neo-grotesque across the whole UI — tight, confident, product-y.',\n faces: 'Space Grotesk headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/grotesk.md',\n },\n editorial: {\n id: 'editorial',\n name: 'Editorial',\n tagline: 'A characterful display serif over a humanist sans — magazine, considered.',\n faces: 'Fraunces display headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/editorial.md',\n },\n 'mono-technical': {\n id: 'mono-technical',\n name: 'Mono-Technical',\n tagline: 'IBM Plex Mono throughout — a precise, developer-grade UI.',\n faces: 'IBM Plex Mono headlines + body/UI + meta/code.',\n designDoc: 'docs/typography/mono-technical.md',\n },\n marker: {\n id: 'marker',\n name: 'Marker',\n tagline: 'Hand-lettered marker headlines over a clean sans body — sketchy, friendly, human.',\n faces: 'Caveat marker headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/marker.md',\n },\n rounded: {\n id: 'rounded',\n name: 'Rounded',\n tagline: 'A soft rounded sans across the whole UI — friendly and approachable.',\n faces: 'Nunito headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/rounded.md',\n },\n slab: {\n id: 'slab',\n name: 'Slab',\n tagline: 'Sturdy slab-serif headlines over a clean sans body — confident, print-like.',\n faces: 'Roboto Slab headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/slab.md',\n },\n humanist: {\n id: 'humanist',\n name: 'Humanist',\n tagline: 'A warm humanist sans across the whole UI — open and readable.',\n faces: 'Fira Sans headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/humanist.md',\n },\n} satisfies Record<string, TypographyDefinition>;\n\n/** The id of every registered pairing — `data-type` value space. */\nexport type TypeId = keyof typeof TYPE_REGISTRY;\n\n/** All registered pairing ids, in gallery order. */\nexport const TYPE_IDS = Object.keys(TYPE_REGISTRY) as TypeId[];\n\n/**\n * The default pairing a fresh install / unset preference resolves to when the\n * active style declares none. Motir is the house pairing and the Tier-0 base.\n */\nexport const DEFAULT_TYPE_ID: TypeId = 'motir';\n\n/** Narrowing guard — is an arbitrary string a registered pairing id? */\nexport function isTypeId(value: unknown): value is TypeId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(TYPE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid pairing definition. */\nexport function resolveType(value: unknown): TypographyDefinition {\n return TYPE_REGISTRY[isTypeId(value) ? value : DEFAULT_TYPE_ID];\n}\n","/**\n * The Motir STYLE LIBRARY — the named-aesthetic registry (Subtask 7.3.32).\n *\n * A \"style\" is a named, end-to-end AESTHETIC the whole UI can swap to at\n * runtime — the foundation the onboarding design wizard (7.3.27) and the\n * `/tokens` composer (7.3.30) build on. It is the SHAPE/feel axis, and it is\n * deliberately INDEPENDENT of the COLOR axis (`data-palette` — see below): a\n * style decides how the product FEELS (silhouette, stroke, elevation, surface\n * treatment, density, motion, typography, component silhouettes); a palette\n * decides what colours it WEARS. Picking a style never changes a hue, and\n * picking a palette never changes a radius.\n *\n * ── The two-axis runtime contract ───────────────────────────────────────\n * data-style=\"<style-id>\" — THIS axis. Layers per-style shape/feel\n * tokens (+ component-variant overrides) over\n * the Tier-0 base scale in app/globals.css.\n * data-palette=\"<palette-id>\" — the INDEPENDENT colour axis (re-skins the\n * `--el-*` layer). Orthogonal to data-style.\n * data-theme=\"light|dark\" — the light/dark base within a palette.\n *\n * A `[data-style='…']` TOKEN block in globals.css MUST override ONLY shape/feel\n * tokens (radius / spacing / shadow / sizing / transition / type) — NEVER a\n * `--color-*` or `--el-*` colour token. That is the palette axis's job, and\n * keeping the two disjoint is what makes \"style × palette\" a true product of\n * two independent choices rather than 2×N hand-tuned combinations.\n *\n * A SURFACE-MATERIAL style (glassmorphism, 7.3.35; later cybercore, aurora,\n * neumorphism …) owns a richer surface — translucency, a gradient canvas,\n * frosted backdrop-blur — that the token block cannot express. It adds a\n * palette-DERIVED MATERIAL LAYER: style-scoped component rules\n * (`[data-style='id'] [data-surface='…'] { … }`) whose colour comes ONLY from\n * `color-mix()`/`var(--color-*|--el-*)` over the ACTIVE palette, never a raw\n * hue — so the two axes stay disjoint (a palette swap re-tints the material; a\n * style swap leaves hues untouched). Surfaces opt in via the `data-surface`\n * hook the shared primitives emit. Both rules are enforced by\n * tests/theme/styleRegistry.test.ts.\n *\n * ── Why a style is MORE than a token swap ───────────────────────────────\n * The feel-bearing DIMENSIONS below are the axes a pure token swap ignores:\n * two styles can share the Tier-0 scale yet feel utterly different because\n * their silhouette, elevation philosophy, surface treatment, and component\n * silhouettes diverge. The registry names those dimensions so each style (and\n * its DESIGN.md) is authored against the same rubric.\n *\n * This module is the schema + the registration of the styles. Each \"Style: …\"\n * subtask (7.3.33–7.3.42) ADDS its entry here, ships a `[data-style='<id>']`\n * token block in globals.css (plus, for a surface-material style, the\n * palette-derived material layer above), and authors its `docs/styles/<id>.md`\n * DESIGN.md.\n */\n\nimport { DEFAULT_TYPE_ID, type TypeId } from './typography';\n\n/**\n * The feel-bearing axes a named style controls. A token-only swap reaches the\n * first few; the rest (surface treatment, motion, typography, component\n * silhouettes) are what give a style its identity and must be authored, not\n * derived. The order here is the canonical presentation order (the `/tokens`\n * page + each style's DESIGN.md walk them in this sequence).\n */\nexport const STYLE_DIMENSIONS = [\n {\n key: 'silhouette',\n label: 'Shape / silhouette',\n description:\n 'Corner-radius personality — sober rectangles vs. soft pills vs. hard right angles.',\n },\n {\n key: 'stroke',\n label: 'Border / stroke',\n description: 'Hairline vs. heavy outline; whether structure is drawn with borders at all.',\n },\n {\n key: 'elevation',\n label: 'Elevation philosophy',\n description: 'How depth is expressed — soft diffuse shadows, hard offset shadows, or flat.',\n },\n {\n key: 'surface',\n label: 'Surface / background treatment',\n description:\n 'Opaque cards vs. translucent glass vs. tinted washes — the material of a surface.',\n },\n {\n key: 'density',\n label: 'Density rhythm',\n description: 'Padding/sizing cadence — compact and efficient vs. roomy and relaxed.',\n },\n {\n key: 'motion',\n label: 'Motion',\n description: 'Transition speed and easing — snappy, gentle, or springy.',\n },\n {\n key: 'typography',\n label: 'Typography',\n description:\n \"The style's DEFAULT type pairing — type is the independent `data-type` axis now (see ./typography.ts + `defaultTypeId`); this describes the pairing the style ships with: editorial serif, all-sans, or mono headlines.\",\n },\n {\n key: 'components',\n label: 'Component silhouettes',\n description:\n 'Per-component shape overrides beyond tokens (e.g. a pill button vs. a square one).',\n },\n] as const;\n\nexport type StyleDimensionKey = (typeof STYLE_DIMENSIONS)[number]['key'];\n\n/** A registered, runtime-selectable style. */\nexport interface StyleDefinition {\n /** The `data-style` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the feel. */\n tagline: string;\n /** Where the look is drawn from (credit / mood anchor). */\n inspiration: string;\n /** One line per feel-bearing dimension — the authored rubric for this style. */\n dimensions: Record<StyleDimensionKey, string>;\n /**\n * Repo-relative path to this style's DESIGN.md — the doc the `/tokens` page\n * composes (and a later getdesign-style swap reads). Every registered style\n * maps to exactly one.\n */\n designDoc: string;\n /**\n * The type pairing (`data-type`, see ./typography.ts) this style applies when\n * the user has NOT pinned an explicit type — the style's curated default,\n * overridable. Type is its own axis now; this is how a style keeps its\n * out-of-the-box typographic feel (e.g. swiss → `motir-sans`, neo-brutalism /\n * cybercore-y2k → `motir-mono`) without owning `--font-*` in its own block.\n */\n defaultTypeId: TypeId;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each `id` matches a\n * `[data-style='<id>']` block in app/globals.css (Warm Editorial is the base,\n * so its block is the Tier-0 defaults — no override block needed).\n */\nexport const STYLE_REGISTRY = {\n 'warm-editorial': {\n id: 'warm-editorial',\n name: 'Warm Editorial',\n tagline: 'Thoughtful, warm, technical-but-not-cold, slightly editorial.',\n inspiration: \"Notion's warm palette + Source Serif headlines over Inter body.\",\n designDoc: 'docs/styles/warm-editorial.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette: 'Sober rectangles — 8px buttons, 12px cards. Restrained, document-like.',\n stroke: 'Hairline borders (1px warm-grey); structure drawn quietly, never heavy.',\n elevation: 'Modest, low-spread shadows; surfaces sit close to the page.',\n surface: 'Opaque cream surfaces over a white canvas; pastel tints for feature cards only.',\n density: 'Comfortable default rhythm — 18×10 button padding, 24px card padding.',\n motion: 'Brisk 150ms ease; understated, gets out of the way.',\n typography: 'Source Serif 4 editorial headlines + Inter body + JetBrains Mono meta.',\n components:\n 'Rectangular buttons/inputs, hairline-bordered cards, badge pills for status only.',\n },\n },\n 'soft-playful': {\n id: 'soft-playful',\n name: 'Soft / Playful',\n tagline: 'More energy — rounded, generous, gently animated.',\n inspiration: \"Figma's pill-shape language (50px pills, roomy spacing).\",\n designDoc: 'docs/styles/soft-playful.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette: 'Pill buttons (fully rounded) and large 24px card/input radii. Friendly, bubbly.',\n stroke: 'Same hairline borders as the base; identity comes from radius, not stroke weight.',\n elevation: 'Softer, more diffused shadows — surfaces float a touch more.',\n surface: 'Opaque, like the base — colour stays the palette axis; only the shape softens.',\n density: 'Roomier — 20×12 button padding, 28px card padding, taller controls.',\n motion: 'Gentler 200ms ease and a slightly deeper press scale for a springy feel.',\n typography: 'Inherits the base type pairing; the personality is in shape, not type.',\n components: 'Pill buttons, heavily-rounded inputs/cards/modals, rounder small affordances.',\n },\n },\n 'swiss-minimal-flat': {\n id: 'swiss-minimal-flat',\n name: 'Swiss / Minimal-Flat',\n tagline: 'International-typographic, structural, calm — flat, sharp, gridded.',\n inspiration: 'Swiss International Typographic Style — Müller-Brockmann grids, flat surfaces.',\n designDoc: 'docs/styles/swiss-minimal-flat.md',\n defaultTypeId: 'motir-sans',\n dimensions: {\n silhouette:\n 'Sharp near-square corners (2px) on every surface — hard right angles, structural.',\n stroke:\n 'Hairline borders do ALL the structural work; with elevation removed, the 1px rule is the only divider.',\n elevation:\n 'Flat — every shadow is removed (none). Depth comes from borders + whitespace, not lift.',\n surface:\n 'Opaque, untinted panels delineated by hairline rules; no floating, no glass, no wash.',\n density:\n 'Tight, gridded controls (crisp 16×9 buttons, 34px control height) with generous 28px card whitespace.',\n motion:\n 'Minimal — fast 100ms transitions and NO press-scale (calm, mechanical, never springy).',\n typography:\n 'Restrained neo-grotesque sans throughout — headlines drop the editorial serif for Inter, tight and strong.',\n components:\n 'Square buttons/inputs/cards/modals, rectangular (non-pill) status chips, flat hairline-ruled surfaces.',\n },\n },\n 'neo-brutalism': {\n id: 'neo-brutalism',\n name: 'Neo-Brutalism',\n tagline: 'Raw, punchy, utilitarian — 0px corners, thick borders, hard-offset shadows.',\n inspiration:\n 'Neo-brutalist web design — Gumroad / Figma-community brutalism: blocky, unpolished, loud.',\n designDoc: 'docs/styles/neo-brutalism.md',\n defaultTypeId: 'motir-mono',\n dimensions: {\n silhouette:\n 'Zero radius — hard 0px corners on EVERY surface (buttons, cards, inputs, modals, status chips). Blocky and uncompromising.',\n stroke:\n 'Heavy solid 2px outlines do the structural work — borders are LOUD, not hairline (the defining neo-brutalist move, applied via a style-scoped component-variant block).',\n elevation:\n 'Hard-offset drop shadows with ZERO blur (e.g. 4px 4px 0 0) — a solid block of shadow, never a soft lift; the chunky frame, not diffusion.',\n surface:\n 'Opaque, flat, untinted panels framed by the thick border + the hard shadow; no glass, no wash, no gradient.',\n density:\n 'Tight, utilitarian controls (16×10 buttons, 36px control height) with compact 20px card padding — no wasted space.',\n motion:\n 'Snappy and mechanical — near-instant 60ms transitions and NO press-scale; the UI reacts, it does not animate.',\n typography:\n 'Raw monospace headlines — the editorial serif is re-pointed at the JetBrains Mono stack, against the Inter grotesk body.',\n components:\n 'Square buttons/inputs/cards/modals, rectangular (non-pill) status chips, thick-bordered surfaces with hard-offset shadows.',\n },\n },\n glassmorphism: {\n id: 'glassmorphism',\n name: 'Glassmorphism',\n tagline: 'Translucent frosted glass floating over a soft, vibrant gradient.',\n inspiration:\n \"Apple's visionOS / macOS Big Sur 'frosted glass' material — backdrop-blur over depth.\",\n designDoc: 'docs/styles/glassmorphism.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Soft, rounded glass tiles — 12px buttons/inputs, 18px cards, 22px modals. Friendly, never sharp.',\n stroke:\n 'Light 1px hairlines at reduced opacity — a glass edge catching light, not a structural rule.',\n elevation:\n 'Layered, diffuse, low-opacity shadows — panels float as hovering frosted sheets above the canvas.',\n surface:\n 'The identity axis: translucent frosted panels (backdrop-blur) over a soft palette-derived gradient canvas — material, not opaque.',\n density:\n 'Comfortable, a touch roomy — 20×11 buttons, 26px card padding, 38px controls; glass tiles want air.',\n motion:\n 'Gentle, smooth — 220ms ease and a light press-scale; glass slides into place, it never snaps.',\n typography:\n 'Inherits the base editorial type pairing; the personality is in the material, not the type.',\n components:\n 'Rounded frosted cards / popovers / modals / sidebar / inputs (the data-surface material layer), pill status chips.',\n },\n },\n 'cybercore-y2k': {\n id: 'cybercore-y2k',\n name: 'Cybercore / Y2K',\n tagline: 'Neon-on-dark retro-tech — glowing HUD panels, a tech grid, mono headlines.',\n inspiration:\n 'Y2K / cyberpunk HUDs — neon-on-dark terminals, Tron grids, glowing edges, monospace displays.',\n designDoc: 'docs/styles/cybercore-y2k.md',\n defaultTypeId: 'motir-mono',\n dimensions: {\n silhouette:\n 'Hard terminal-frame corners (sharp 2–4px) — chiseled HUD panels, not soft cards.',\n stroke:\n 'Structure drawn by a lit edge — a 1px glow-ring (currentColor halo) on every surface, not a heavier border.',\n elevation:\n 'Neon GLOW instead of drop-shadow — surfaces emit an outward currentColor halo, lit rather than lifted.',\n surface:\n 'A faint tech grid (scanline/grid texture) washes the canvas behind opaque HUD panels.',\n density:\n 'Tight, technical HUD rhythm — compact controls (18×9 buttons, 34px control height) over snug 20px card padding.',\n motion: 'Snappy and electric — very fast 90ms transitions with a crisp press-scale.',\n typography:\n 'Mono/display headlines — the serif headline face is swapped to JetBrains Mono for a terminal read; body stays sans.',\n components:\n 'Sharp glow-ringed panels, mono headings, rectangular chips; the lit edge replaces the drop shadow everywhere.',\n },\n },\n aurora: {\n id: 'aurora',\n name: 'Aurora',\n tagline: 'Soft, drifting aurora light behind fluid, gently-glowing surfaces.',\n inspiration:\n 'The northern lights — slow ribbons of colour over a night sky — and the soft animated-gradient \"aurora\" hero look (Stripe / Vercel ambient gradients).',\n designDoc: 'docs/styles/aurora.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Fluid, soft corners — 14px buttons/inputs, 20px cards, 26px modals. Organic and rounded, a touch softer than glass.',\n stroke:\n 'Quiet 1px hairlines; structure is implied by the gentle glow and elevation, never drawn by a heavy rule.',\n elevation:\n 'A soft neutral lift PLUS a gentle palette-derived COLOUR glow halo — surfaces feel lit from within, never hard-edged.',\n surface:\n 'The identity axis: opaque fluid surfaces floating over a slowly-DRIFTING, animated aurora gradient canvas (palette-derived ribbons).',\n density:\n 'Comfortable and roomy — 20×11 buttons, 26px card padding, 38px controls; the light wants space to breathe.',\n motion:\n 'Smooth and slow — 260ms eased transitions, a faint press-scale, and a 28s ambient drift of the aurora canvas (stilled under prefers-reduced-motion).',\n typography:\n 'Inherits the base editorial pairing (Source Serif headlines + Inter body); the personality is in the moving light, not the type.',\n components:\n 'Fluid, gently-glowing cards / popovers / modals / sidebar (the data-surface material layer); pill status chips.',\n },\n },\n '3d-immersive': {\n id: '3d-immersive',\n name: '3D / Immersive',\n tagline: 'Depth and perspective — dimensional cards floating over the page.',\n inspiration:\n 'Spatial / depth UI — visionOS spatial layers, Stripe-era layered cards, soft real-world light.',\n designDoc: 'docs/styles/3d-immersive.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Generously rounded dimensional tiles — 14px buttons/inputs, 20px cards, 28px modals. Soft, tactile, never sharp.',\n stroke:\n 'Borders nearly vanish (a faint hairline) — structure is read from depth + shadow, not an outline.',\n elevation:\n 'The identity axis: deep multi-layer shadows float every surface off the canvas, and on tilt a card becomes a TRUE 3D object — perspective + preserve-3d + per-slot translateZ planes (header pops forward, footer behind) parallax under a cursor-tracked glare. Not a flat-plane tilt.',\n surface:\n 'A palette-derived immersive background atmosphere on every page, with opaque dimensional panels (cards, tables, board columns, widgets) floating above it on deep resting shadows — depth (not tint or glass) is the material; nothing sits flat.',\n density:\n 'Roomy and immersive — 22×12 buttons, 28px card padding, 40px controls; depth wants air around each tile.',\n motion:\n 'The standard \"3D card\" pointer-parallax — tiles tip toward the cursor (rotateX/rotateY over a perspective) via the ImmersiveTilt engine, lifting as they turn; reduced-motion-gated, settling flat on leave.',\n typography:\n 'Inherits the base editorial type pairing; the personality is in depth + light, not the type.',\n components:\n 'Rounded cards / board cards / modals that tilt toward the pointer with layered parallax; PHYSICAL buttons with real thickness (a solid base edge) that press DOWN on click; each board column floats as its own card. Nothing is flat.',\n },\n },\n neumorphism: {\n id: 'neumorphism',\n name: 'Neumorphism',\n tagline: 'Soft extruded surfaces that swell out of a single monochrome field.',\n inspiration:\n 'Neumorphism / soft-UI — Dribbble-era \"soft shadow\" interfaces where panels are pressed OUT of (or INTO) one continuous surface by a paired light + dark shadow.',\n designDoc: 'docs/styles/neumorphism.md',\n defaultTypeId: 'motir',\n dimensions: {\n silhouette:\n 'Soft, generously rounded pebbles — 12px buttons/inputs, 18px cards, 22px modals. Tactile and friendly, the rounding that lets a shadow wrap a surface.',\n stroke:\n 'Borders nearly vanish — a faint hairline kept ONLY as the accessibility fallback (see below); structure is read from the paired soft shadow, not an outline.',\n elevation:\n 'The identity axis: a DUAL soft shadow — a light highlight top-left + a palette-derived dark shadow bottom-right — extrudes each surface OUT of the canvas (raised), while inputs and pressed controls invert it to an INSET well (pressed in). Depth is a soft swell, never a hard drop.',\n surface:\n 'Monochrome and continuous: the page canvas and every panel share ONE opaque palette surface, so a card looks moulded out of the background rather than placed on top of it — the soft-UI \"single material\" look. Opaque (text AA preserved), with the depth carried entirely by the shadows.',\n density:\n 'Comfortable and roomy — 20×11 buttons, 26px card padding, 38px controls; the soft shadows need air around each surface to read as extruded.',\n motion:\n 'Gentle — 200ms ease and a soft press-scale; nothing snaps, matching the moulded surfaces (form fields rest as concave inset wells, panels swell out raised).',\n typography:\n 'Inherits the base editorial type pairing; the personality is in the moulded surfaces, not the type.',\n components:\n 'Raised, soft-shadowed cards / popovers / modals / sidebar (the data-surface material layer); concave inset inputs; pill status chips. Every surface swells from or sinks into the one field.',\n },\n },\n 'hand-drawn-indie': {\n id: 'hand-drawn-indie',\n name: 'Hand-Drawn / Indie',\n tagline:\n 'Sketchy and friendly — rough hand-inked edges, wonky corners, soft offset shadows, a playful bounce.',\n inspiration:\n 'Indie / zine + hand-drawn web design — Excalidraw’s rough hand-sketched strokes and doodled notebook UIs: warm, imperfect, human.',\n designDoc: 'docs/styles/hand-drawn-indie.md',\n defaultTypeId: 'marker',\n dimensions: {\n silhouette:\n 'Wonky hand-drawn corners — every surface gets a strongly asymmetric multi-value radius (one corner near-sharp, the opposite generously round, no two alike) so cards/buttons/inputs read as DRAWN boxes, never machined rectangles.',\n stroke:\n 'Genuinely ROUGH, wavy ink edges — a turbulence-displacement filter (Excalidraw / rough.js style) warps each outline into a hand-inked wobble, drawn on a content-safe pseudo-border so the lines are sketchy but the text stays crisp.',\n elevation:\n 'Soft, hand-placed offset shadows — a gentle blurred drop nudged down-right (a doodled shadow), friendly and low; never a hard brutalist block nor a flat removal.',\n surface:\n 'Opaque, warm, paper-like panels — colour stays the palette axis; the personality is in the rough wavy outline + the soft shadow, not a tint or a wash.',\n density:\n 'Roomy and relaxed — generous 20×12 buttons, 26px card padding, taller 38px controls; the hand-drawn feel wants air, like margins round a sketch.',\n motion:\n 'Springy and playful — gentle 200ms transitions with a bouncy hover lift (1.02) and a deeper press squish (0.97); the UI has a friendly wobble.',\n typography:\n 'Ships the Marker pairing (Caveat hand-lettered marker headlines + Inter body) — the headlines read as if written with the same pen that inked the rough strokes, while the Inter body stays fully legible.',\n components:\n 'Wonky-cornered buttons/inputs/cards/modals, irregular (non-pill) status chips, rough wavy-ink-bordered surfaces with soft hand-placed shadows.',\n },\n },\n retrofuturism: {\n id: 'retrofuturism',\n name: 'Retrofuturism',\n tagline: 'Chrome, gradient sweeps and a synthwave horizon — 70s/80s sci-fi.',\n inspiration:\n 'Retrofuturism / Outrun — 70s–80s sci-fi: chrome lettering, synthwave sunset gradients, airbrushed sci-fi paperbacks, and wide geometric display type.',\n designDoc: 'docs/styles/retrofuturism.md',\n defaultTypeId: 'grotesk',\n dimensions: {\n silhouette:\n 'Streamlined chrome capsules — fully-rounded pill buttons + chips, 16px cards, 22px modals. Aerodynamic 70s/80s curves, never sharp.',\n stroke:\n 'A lit chrome edge — a bright inset highlight + a dark base line over a faint hairline, reading as a polished metal rim rather than a drawn border.',\n elevation:\n 'Chrome bevels + a diagonal SPECULAR streak + a palette-derived glow — a bright top highlight fading to a dark base with a swept light-bar across each panel, so surfaces read as polished reflective metal, not flat cards.',\n surface:\n 'The identity axis: a synthwave SKY + a bright HORIZON GLOW band + a receding neon perspective GRID floor (all palette-derived) behind chrome-bevelled, specular-streaked surfaces. Dark-first — the neon horizon + grid read strongest on a dark palette.',\n density:\n 'Confident and roomy — 22×11 chrome-capsule buttons, 24px card padding, 38px controls; the chrome wants room to catch the light.',\n motion:\n 'Smooth and gliding — 200ms eased transitions and a confident press-scale; the synthwave horizon + grid are a STATIC backdrop (no motion to distract the work surface).',\n typography:\n 'Wide geometric grotesque display (the `grotesk` type pairing) — the retro-futurist sci-fi headline read; body stays a legible sans.',\n components:\n 'Chrome-capsule buttons with a metallic glint, specular-streaked bevelled cards / popovers / modals / sidebar / inputs (the data-surface material layer), pill status chips — a synthwave horizon + neon grid behind it all.',\n },\n },\n} satisfies Record<string, StyleDefinition>;\n\n/** The id of every registered style — `data-style` value space. */\nexport type StyleId = keyof typeof STYLE_REGISTRY;\n\n/** All registered style ids, in gallery order. */\nexport const STYLE_IDS = Object.keys(STYLE_REGISTRY) as StyleId[];\n\n/**\n * The default style a fresh install / unset preference resolves to. Warm\n * Editorial is Motir's house style and the Tier-0 base.\n */\nexport const DEFAULT_STYLE_ID: StyleId = 'warm-editorial';\n\n/** Narrowing guard — is an arbitrary string a registered style id? */\nexport function isStyleId(value: unknown): value is StyleId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(STYLE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid style definition. */\nexport function resolveStyle(value: unknown): StyleDefinition {\n return STYLE_REGISTRY[isStyleId(value) ? value : DEFAULT_STYLE_ID];\n}\n\n/**\n * styleId → its `defaultTypeId` — the type pairing applied when the user has\n * not pinned an explicit `data-type`. Baked into the FOUC init script (so the\n * pre-hydration pass can resolve the right type for the active style) and used\n * by the theme context's effective-type derivation. `resolveType` (the\n * registry) supplies the ultimate fallback, so every value here is a real id.\n */\nexport const STYLE_DEFAULT_TYPE: Record<StyleId, TypeId> = Object.fromEntries(\n STYLE_IDS.map((id) => [id, STYLE_REGISTRY[id].defaultTypeId]),\n) as Record<StyleId, TypeId>;\n\n/** The default type for a (possibly unknown) style value — the fallback chain. */\nexport function defaultTypeForStyle(styleValue: unknown): TypeId {\n return isStyleId(styleValue) ? STYLE_REGISTRY[styleValue].defaultTypeId : DEFAULT_TYPE_ID;\n}\n","/**\n * The Motir PALETTE LIBRARY — the named-colour registry (Subtask 7.3.48).\n *\n * A \"palette\" is a named COLOUR scheme the whole UI can swap to at runtime —\n * the second half of the two-axis contract the style schema (7.3.32) wrote but\n * did not implement. It is the COLOUR/hue axis, and it is deliberately\n * INDEPENDENT of the SHAPE axis (`data-style` — see ./styles.ts): a palette\n * decides what colours the product WEARS (surfaces, ink, accent, tints,\n * semantic hues); a style decides how it FEELS (silhouette, elevation,\n * density, motion). Picking a palette never changes a radius, and picking a\n * style never changes a hue.\n *\n * ── The two-axis runtime contract ───────────────────────────────────────\n * data-palette=\"<palette-id>\" — THIS axis. Layers per-palette `--el-*`\n * element-token overrides over the Tier-3\n * base in app/globals.css.\n * data-style=\"<style-id>\" — the INDEPENDENT shape/feel axis.\n * data-theme=\"light|dark\" — the light/dark base WITHIN a palette\n * (`--color-*`); orthogonal to the palette.\n *\n * A `[data-palette='…']` block in globals.css MUST override ONLY `--el-*`\n * element colour tokens — NEVER a shape/feel token (radius / spacing / shadow\n * / sizing / motion / type). That is the style axis's job, and keeping the two\n * disjoint is what makes \"style × palette\" a true product of two independent\n * choices rather than N×M hand-tuned combinations.\n *\n * ── Motir's own palette, and the one that used to be ──────────────────────\n * **Motir** is the palette Motir wears when a person has chosen none: the stark\n * monochrome scheme (cool greyscale surfaces + ink, an ink CTA, a single\n * restrained cool-blue accent). It shipped as \"Graphite\" (7.3.51) and took the\n * product's name in MOTIR-6471, so its colours arrive through its\n * `[data-palette='motir']` block.\n *\n * **Amethyst** is the warm scheme the product wore until then (cream surfaces,\n * charcoal ink, purple primary, pastel feature tints), which carried the name\n * \"Motir\" from v1 (Per Yue, 2026-06-17) until that rename. It is still the\n * Tier-0/Tier-3 BASE in theme.css, so — exactly like the `warm-editorial` base\n * style — the base `--el-*` tokens already are it; its `[data-palette='amethyst']`\n * blocks only re-assert that base inside a scoped subtree. `<html>` always\n * carries a `data-palette`, so the base is a fallback, never what a person sees\n * by default. Each later \"Palette: …\" subtask ADDS its entry here, ships a\n * `[data-palette='<id>']` block overriding the `--el-*` layer, and authors its\n * `docs/palettes/<id>.md` doc — exactly the shape the per-style subtasks\n * (7.3.33+) follow on the other axis.\n *\n * ⚠️ A STORED palette id written before the rename means the OTHER palette:\n * `motir` meant today's `amethyst`, and `graphite` meant today's `motir`. Every\n * store is migrated once (`PALETTE_ID_MIGRATION` in ./types.ts, and the\n * `user_appearance_preference` migration) — never alias the old spellings.\n */\n\n/** A registered, runtime-selectable colour palette. */\nexport interface PaletteDefinition {\n /** The `data-palette` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the colour mood. */\n tagline: string;\n /** Where the colours are drawn from (credit / mood anchor). */\n inspiration: string;\n /**\n * Repo-relative path to this palette's doc — the colour-role reference a\n * later getdesign-style swap or the `/tokens` composer reads. Every\n * registered palette maps to exactly one.\n */\n designDoc: string;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each `id` matches a\n * `[data-palette='<id>']` block in theme.css (Amethyst is the Tier-3 base, so its\n * blocks only re-assert the defaults inside a scoped subtree).\n */\nexport const PALETTE_REGISTRY = {\n motir: {\n id: 'motir',\n name: 'Motir',\n tagline:\n 'Stark and editorial — cool greyscale surfaces + ink, an ink CTA, a restrained cool-blue accent and a few warm touches.',\n inspiration:\n \"Vercel's black-and-white precision + Linear's ultra-minimal, on Radix Slate/Blue scales.\",\n designDoc: 'docs/palettes/motir.md',\n },\n amethyst: {\n id: 'amethyst',\n name: 'Amethyst',\n tagline: 'Warm and editorial — cream surfaces, charcoal ink, a purple primary, pastel tints.',\n inspiration:\n \"Notion's warm marketing palette — Motir's house colours until the monochrome rename.\",\n designDoc: 'docs/palettes/amethyst.md',\n },\n cobalt: {\n id: 'cobalt',\n name: 'Cobalt',\n tagline:\n 'Cool and institutional — slate-cool surfaces, a confident cobalt primary, cooled tints.',\n inspiration:\n \"Coinbase's clean institutional blue + IBM's structured blue, on Radix Blue/Indigo/Slate scales.\",\n designDoc: 'docs/palettes/cobalt.md',\n },\n evergreen: {\n id: 'evergreen',\n name: 'Evergreen',\n tagline:\n 'Fresh and technical — an emerald primary over cooled forest neutrals, a green-leaning UI.',\n inspiration:\n 'getdesign.md — Supabase (dark emerald) / Spotify (vibrant green) / MongoDB (spring green), on Radix green/jade ramps.',\n designDoc: 'docs/palettes/evergreen.md',\n },\n spectrum: {\n id: 'spectrum',\n name: 'Spectrum',\n tagline:\n 'Vibrant and playful — crisp cool surfaces, a bright violet primary, a candy-bright multi-hue accent & tint set.',\n inspiration:\n \"Figma's vibrant multi-colour brand + Airtable's colourful, friendly palette, on Radix Violet/Iris/Pink/Blue scales.\",\n designDoc: 'docs/palettes/spectrum.md',\n },\n amber: {\n id: 'amber',\n name: 'Amber',\n tagline:\n 'Warm and electric — Binance trading-floor gold over the exchange’s graphite near-blacks; dark labels on the gold.',\n inspiration:\n 'Binance — its whole documented palette: the bold gold #F0B90B/#FCD535 over cool graphite trading-floor blacks (#0B0E11/#1E2329) + greys, with the buy-green/sell-red semantics.',\n designDoc: 'docs/palettes/amber.md',\n },\n sienna: {\n id: 'sienna',\n name: 'Sienna',\n tagline:\n 'Warm and vivid — Mistral flame-orange over the Mediterranean warm-cream surfaces; dark labels on the flame.',\n inspiration:\n 'Mistral AI — its whole documented palette (getdesign.md/mistral.ai): the flame #fa520f + Block-gradient + Sunshine ambers over Warm-Ivory cream surfaces (#fffaeb) and Mistral-Black ink.',\n designDoc: 'docs/palettes/sienna.md',\n },\n garnet: {\n id: 'garnet',\n name: 'Garnet',\n tagline:\n 'Rich and bold — Pinterest Pushpin red over the brand’s neutral greys; white labels on the red.',\n inspiration:\n 'Pinterest — its whole documented palette: Pushpin red #E60023/#BD081C over Cod-Gray ink (#111111) and neutral greys (#767676/#EFEFEF), with Gestalt “Skycicle” blue links.',\n designDoc: 'docs/palettes/garnet.md',\n },\n citrine: {\n id: 'citrine',\n name: 'Citrine',\n tagline:\n 'Bright and collaborative — Miro’s Sunglow yellow over the whiteboard greys, with the Miro action-blue accent.',\n inspiration:\n 'Miro — its whole Mirotone palette (every value a documented Mirotone step): Sunglow yellow #FFD02F (primary) + the action-blue #3859FF over the gray ramp + Stratos ink #050038, with the green/red semantic ramps.',\n designDoc: 'docs/palettes/citrine.md',\n },\n candy: {\n id: 'candy',\n name: 'Candy',\n tagline:\n 'Sweet and playful — a light bubblegum-pink primary over candy-paper pinks, with a full pastel-candy rainbow.',\n inspiration:\n 'A candy concept grounded entirely in documented Radix Colors scales (every value a Radix step): Pink primary + Mauve neutrals + a Sky/Jade/Violet/Amber/Crimson candy rainbow; Glossier Pink #F5DADF as the pastel-pink mood reference.',\n designDoc: 'docs/palettes/candy.md',\n },\n} satisfies Record<string, PaletteDefinition>;\n\n/** The id of every registered palette — `data-palette` value space. */\nexport type PaletteId = keyof typeof PALETTE_REGISTRY;\n\n/** All registered palette ids, in gallery order. */\nexport const PALETTE_IDS = Object.keys(PALETTE_REGISTRY) as PaletteId[];\n\n/**\n * The palette Motir wears when a person has chosen none — a fresh install, a\n * signed-out visitor, an unset (null) server preference. Its VALUE has been\n * `'motir'` since v1; what it NAMES changed in MOTIR-6471, from the warm scheme\n * (now `amethyst`) to the monochrome one (formerly `graphite`).\n */\nexport const DEFAULT_PALETTE_ID: PaletteId = 'motir';\n\n/**\n * The palette theme.css's Tier-0/Tier-3 BASE carries — the one with no root\n * `[data-palette]` override block, only a scoped re-assertion. It is the\n * reference every other palette's override is measured AGAINST. Until\n * MOTIR-6471 it was also {@link DEFAULT_PALETTE_ID}; since the rename the two\n * differ, so a reader asking \"which palette is the base?\" names this and never\n * the default.\n */\nexport const BASE_PALETTE_ID: PaletteId = 'amethyst';\n\n/**\n * The default look the onboarding Design step offers for a user's OWN project —\n * a different question from the one {@link DEFAULT_PALETTE_ID} answers. It stays\n * on the warm scheme the step has always opened on (MOTIR-6470's scope boundary,\n * confirmed by the requester).\n */\nexport const DEFAULT_PROJECT_PALETTE_ID: PaletteId = 'amethyst';\n\n/** Narrowing guard — is an arbitrary string a registered palette id? */\nexport function isPaletteId(value: unknown): value is PaletteId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(PALETTE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid palette definition. */\nexport function resolvePalette(value: unknown): PaletteDefinition {\n return PALETTE_REGISTRY[isPaletteId(value) ? value : DEFAULT_PALETTE_ID];\n}\n","/**\n * Theme types for Motir's THREE-axis design system.\n *\n * Axis 1 — COLOR (`data-theme` light/dark base · `data-palette` full\n * `--el-*` palette swap — the colour axis)\n * Axis 2 — STYLE (`data-style` — a named aesthetic controlling shape /\n * silhouette / elevation / surface / density / motion /\n * component silhouettes)\n * Axis 3 — TYPE (`data-type` — a named pairing controlling the `--font-*`\n * role mapping; a style declares a default pairing, overridable)\n *\n * The three axes are INDEPENDENT: a style never touches a hue or a face, a\n * palette never touches a radius, a type pairing never touches colour or shape.\n * The registries live in `./styles.ts`, `./palettes.ts`, `./typography.ts`.\n *\n * Architecture mirrors dooooWeb. See docs/DESIGN.md for the rationale.\n */\n\nimport { DEFAULT_STYLE_ID, type StyleId } from './styles';\nimport { DEFAULT_PALETTE_ID, type PaletteId } from './palettes';\nimport { DEFAULT_TYPE_ID, type TypeId } from './typography';\n\n/** Tier 1 — light/dark base. `system` follows OS preference at runtime. */\nexport type ThemePattern = 'system' | 'light' | 'dark';\n\n/** Resolved pattern (what data-theme is set to after `system` resolves). */\nexport type ResolvedThemePattern = 'light' | 'dark';\n\n/**\n * Axis 2 — the active named style (`data-style`). The value space + the\n * registry of every style live in `./styles.ts`; this re-export keeps\n * `lib/theme/*` the single import surface for theme consumers.\n */\nexport type { StyleId } from './styles';\n\n/**\n * Axis 1 (colour) — the active named palette (`data-palette`). The value space\n * + the registry of every palette live in `./palettes.ts`; re-exported here so\n * `lib/theme/*` stays the single import surface for theme consumers.\n */\nexport type { PaletteId } from './palettes';\n\n/**\n * Axis 3 — the active named type pairing (`data-type`). The value space + the\n * registry live in `./typography.ts`; re-exported here so `lib/theme/*` stays\n * the single import surface for theme consumers.\n */\nexport type { TypeId } from './typography';\n\n/** Storage keys for persisting user preferences. */\nexport const THEME_STORAGE_KEYS = {\n pattern: 'motir.theme.pattern',\n style: 'motir.theme.style',\n palette: 'motir.theme.palette',\n type: 'motir.theme.type',\n /**\n * The version marker of the STORED palette id's meaning. Absent (or anything\n * but {@link PALETTE_IDS_VERSION}) means the stored `palette` was written\n * before MOTIR-6471 swapped the palette identities, so it is read through\n * {@link PALETTE_ID_MIGRATION} once and the marker is then written.\n */\n paletteIds: 'motir.theme.paletteIds',\n /**\n * The signed-in person's per-language font picks as `<html>` attributes,\n * `{ [locale]: { 'data-font-set-<role>': member } }` (MOTIR-7896). Written by\n * every signed-in layout render and cleared by every signed-out one, so only\n * the error page — which cannot read the database — ever reads it back.\n */\n fontPicks: 'motir.theme.fontPicks',\n} as const;\n\n/** The current meaning of a stored palette id — see `THEME_STORAGE_KEYS.paletteIds`. */\nexport const PALETTE_IDS_VERSION = '2';\n\n/**\n * What a palette id stored BEFORE MOTIR-6471 means today. The two palettes\n * swapped identities: the warm scheme called `motir` is now `amethyst`, and the\n * monochrome scheme called `graphite` is now `motir`. A value absent from this\n * map meant the same palette before and after. Applied ONCE per store — a\n * second pass would map a migrated `motir` on to `amethyst`, so it is keyed on\n * a version marker, never on the value.\n */\nexport const PALETTE_ID_MIGRATION: Readonly<Partial<Record<string, string>>> = {\n motir: 'amethyst',\n graphite: 'motir',\n};\n\n/**\n * Read a stored palette id through the rename. `marker` is the stored\n * `THEME_STORAGE_KEYS.paletteIds` value: at {@link PALETTE_IDS_VERSION} the\n * value is already current and is returned untouched; otherwise a value the\n * rename changed is mapped. `null` (nothing stored) stays `null`.\n */\nexport function migrateStoredPaletteId(value: string | null, marker: string | null): string | null {\n if (value === null || marker === PALETTE_IDS_VERSION) return value;\n // Own properties only — a stored `constructor` must not reach Object.prototype.\n if (!Object.prototype.hasOwnProperty.call(PALETTE_ID_MIGRATION, value)) return value;\n return PALETTE_ID_MIGRATION[value] ?? value;\n}\n\n/**\n * Sensible defaults if localStorage is empty. Note `type` is the GLOBAL fallback\n * only — when no explicit type is pinned, the active STYLE's `defaultTypeId`\n * wins (see `STYLE_DEFAULT_TYPE` / the theme context); this is the floor under\n * an unknown style.\n */\nexport const THEME_DEFAULTS = {\n pattern: 'system' as ThemePattern,\n style: DEFAULT_STYLE_ID as StyleId,\n palette: DEFAULT_PALETTE_ID as PaletteId,\n type: DEFAULT_TYPE_ID as TypeId,\n} as const;\n\n/**\n * Narrowing guard — is an arbitrary value a valid `pattern` axis id? The\n * colour / style / type axes each ship `isXId` in their registry file; the\n * pattern axis is a fixed three-value union, so its guard lives here next to\n * the type. Mirrors `isStyleId` / `isPaletteId` / `isTypeId`.\n */\nexport function isThemePattern(value: unknown): value is ThemePattern {\n return value === 'system' || value === 'light' || value === 'dark';\n}\n\n/**\n * Resolve a (possibly stale / unknown / null) value to a valid `pattern`,\n * falling back to `THEME_DEFAULTS.pattern`. The pattern-axis analogue of\n * `resolveStyle` / `resolvePalette` / `resolveType`.\n */\nexport function resolvePattern(value: unknown): ThemePattern {\n return isThemePattern(value) ? value : THEME_DEFAULTS.pattern;\n}\n","/**\n * The Motir FONT-SET REGISTRY: which faces each locale's script is drawn in\n * (Story MOTIR-7733 · Subtask MOTIR-7843).\n *\n * A Type pairing (`./typography.ts`) decides the LATIN faces of the three roles\n * (sans / serif / mono). A font set decides the SCRIPT face that sits BEHIND\n * them, so a ja page draws its Latin in the pairing's face and its kana and\n * kanji in the ja set's face, under every pairing. The two axes are\n * independent: a set never changes which pairing is active, and a pairing never\n * changes which set a locale uses.\n *\n * ── Where the rows come from ────────────────────────────────────────────\n * Every set, member and default below is taken from\n * `docs/typography/font-sets.md` (MOTIR-7841), which read dooooWeb's mapping at\n * `225af77` and filled the roles dooooWeb leaves empty from cited references.\n * `inDooooWeb` carries that document's `in dooooWeb` / `added (not in dooooWeb)`\n * tag. Change a row here only together with that document; the package test\n * holds the two equal.\n *\n * ── What this module is NOT ─────────────────────────────────────────────\n * Data and pure resolvers only. It names CSS custom properties (the contract\n * between the app's `next/font` loader and `theme.css`) but sets none, loads no\n * face, and writes no CSS. Loading stays in the consuming app.\n */\n\n/** The eleven locales app.motir.co ships in. A closed product decision. */\nexport const FONT_SET_LOCALES = [\n 'en',\n 'zh',\n 'ja',\n 'ko',\n 'de',\n 'fr',\n 'es',\n 'it',\n 'nl',\n 'pl',\n 'pt',\n] as const;\n\n/** One of the eleven app locales. */\nexport type FontSetLocale = (typeof FONT_SET_LOCALES)[number];\n\n/**\n * Languages with a font set that only motir.co ships (MOTIR-8937 · MOTIR-9239).\n * Kept apart from `FONT_SET_LOCALES`, which is app.motir.co's own locale set and\n * is closed over its stored font picks (one `fontPick<Locale>` column each).\n */\nexport const FONT_SET_EXTRA_LOCALES = ['zh-tw'] as const;\n\n/** A language the font-set registry knows: an app locale or an extra one. */\nexport type FontSetLanguage = FontSetLocale | (typeof FONT_SET_EXTRA_LOCALES)[number];\n\n/** The three type roles every set covers: the same roles `--font-*` carries. */\nexport const FONT_SET_ROLES = ['sans', 'serif', 'mono'] as const;\n\n/** One of the three type roles. */\nexport type FontSetRole = (typeof FONT_SET_ROLES)[number];\n\n/**\n * Where a member's face comes from.\n *\n * - `type-pairing`: the active Type pairing's own face for the role. The Latin\n * set's only member; it loads nothing and has no CSS variable.\n * - `next/font/google`: a Google Fonts face the app loads with `next/font`;\n * `googleFamily` is the loader's export name (`Noto_Sans_JP`).\n */\nexport type FontSetMemberSource =\n | { readonly kind: 'type-pairing' }\n | { readonly kind: 'next/font/google'; readonly googleFamily: string };\n\n/** One selectable face in one role of one set. */\nexport interface FontSetMember {\n /** Stable kebab-case slug, unique within its role. The value a pick stores. */\n readonly id: string;\n /** The display name, or `null` for the `type-pairing` member. */\n readonly family: string | null;\n readonly source: FontSetMemberSource;\n /** `docs/typography/font-sets.md`'s tag: `in dooooWeb` (true) or `added` (false). */\n readonly inDooooWeb: boolean;\n /**\n * The role whose member of the SAME id this member re-uses the face of. A\n * CJK set's mono role draws its Han glyphs from the set's sans face (the Noto\n * Sans Mono CJK practice, see the doc), so the face is loaded once and both\n * roles read one variable.\n */\n readonly sameFaceAs?: FontSetRole;\n}\n\n/** One role of one set: its members, and which one a page uses by default. */\nexport interface FontSetRoleMembers {\n /** The id of the member used when nobody has picked one. */\n readonly default: string;\n readonly members: readonly FontSetMember[];\n}\n\n/** A locale group's font set. */\nexport interface FontSet {\n readonly id: string;\n /** Display name for `/tokens` and the picker. */\n readonly label: string;\n /**\n * The BCP 47 tag the set's `:lang()` rule matches. It is the tag the app\n * renders on `<html lang>` (`zh`, not `zh-Hans`), because `:lang(zh)`\n * matches `zh` and `zh-Hans` while `:lang(zh-Hans)` does not match `zh`.\n * `null` for the Latin set, which is matched by its locales instead.\n */\n readonly lang: string | null;\n /** True for a CJK set: its faces are unicode-range sliced and never preloaded. */\n readonly cjk: boolean;\n readonly roles: Readonly<Record<FontSetRole, FontSetRoleMembers>>;\n}\n\nconst TYPE_PAIRING: FontSetMember = {\n id: 'type-pairing',\n family: null,\n source: { kind: 'type-pairing' },\n inDooooWeb: false,\n};\n\nfunction google(\n id: string,\n family: string,\n inDooooWeb: boolean,\n sameFaceAs?: FontSetRole,\n): FontSetMember {\n return {\n id,\n family,\n source: { kind: 'next/font/google', googleFamily: family.replace(/ /g, '_') },\n inDooooWeb,\n ...(sameFaceAs ? { sameFaceAs } : {}),\n };\n}\n\n/**\n * The registry, one entry per set. Ids are the doc's registry ids; dooooWeb's\n * own group names are noted beside each.\n */\nexport const FONT_SET_REGISTRY = {\n // dooooWeb: `latin`. Its Latin picker faces are deliberately not members:\n // behind the pairing's face they would never draw a glyph (font-sets.md § latin).\n latin: {\n id: 'latin',\n label: 'Latin',\n lang: null,\n cjk: false,\n roles: {\n sans: { default: 'type-pairing', members: [TYPE_PAIRING] },\n serif: { default: 'type-pairing', members: [TYPE_PAIRING] },\n mono: { default: 'type-pairing', members: [TYPE_PAIRING] },\n },\n },\n // dooooWeb: `chinese-simplified`.\n 'zh-Hans': {\n id: 'zh-Hans',\n label: 'Simplified Chinese',\n lang: 'zh',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-sc', members: [google('noto-sans-sc', 'Noto Sans SC', false)] },\n serif: {\n default: 'noto-serif-sc',\n members: [\n google('noto-serif-sc', 'Noto Serif SC', true),\n // Draws Traditional / inherited forms, so never the default (font-sets.md § Region forms).\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', true),\n ],\n },\n mono: {\n default: 'noto-sans-sc',\n members: [google('noto-sans-sc', 'Noto Sans SC', false, 'sans')],\n },\n },\n },\n // Not in dooooWeb: Traditional Chinese (Taiwan) for motir.co's `/zh-tw`\n // (MOTIR-8937). The zh-Hans set with its Traditional-form faces.\n 'zh-Hant': {\n id: 'zh-Hant',\n label: 'Traditional Chinese',\n lang: 'zh-TW',\n cjk: true,\n roles: {\n sans: { default: 'noto-sans-tc', members: [google('noto-sans-tc', 'Noto Sans TC', false)] },\n serif: {\n default: 'noto-serif-tc',\n members: [\n google('noto-serif-tc', 'Noto Serif TC', false),\n // A handwritten-style face, so a pick rather than the default.\n google('lxgw-wenkai-tc', 'LXGW WenKai TC', false),\n ],\n },\n mono: {\n default: 'noto-sans-tc',\n members: [google('noto-sans-tc', 'Noto Sans TC', false, 'sans')],\n },\n },\n },\n // dooooWeb: `japanese`.\n ja: {\n id: 'ja',\n label: 'Japanese',\n lang: 'ja',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-jp',\n members: [\n google('noto-sans-jp', 'Noto Sans JP', true),\n google('m-plus-rounded-1c', 'M PLUS Rounded 1c', true),\n ],\n },\n serif: {\n default: 'noto-serif-jp',\n members: [google('noto-serif-jp', 'Noto Serif JP', false)],\n },\n mono: {\n default: 'noto-sans-jp',\n members: [google('noto-sans-jp', 'Noto Sans JP', false, 'sans')],\n },\n },\n },\n // dooooWeb: `korean`.\n ko: {\n id: 'ko',\n label: 'Korean',\n lang: 'ko',\n cjk: true,\n roles: {\n sans: {\n default: 'noto-sans-kr',\n members: [\n google('noto-sans-kr', 'Noto Sans KR', true),\n google('nanum-gothic', 'Nanum Gothic', true),\n ],\n },\n serif: {\n default: 'noto-serif-kr',\n members: [google('noto-serif-kr', 'Noto Serif KR', false)],\n },\n mono: {\n default: 'noto-sans-kr',\n members: [google('noto-sans-kr', 'Noto Sans KR', false, 'sans')],\n },\n },\n },\n} as const satisfies Record<string, FontSet>;\n\n/** The id of every registered set. */\nexport type FontSetId = keyof typeof FONT_SET_REGISTRY;\n\n/** All set ids, in registry order. */\nexport const FONT_SET_IDS = Object.keys(FONT_SET_REGISTRY) as FontSetId[];\n\n/** The set an unknown or empty page language falls back to: the app renders it in English. */\nexport const DEFAULT_FONT_SET_ID: FontSetId = 'latin';\n\n/** Which set each locale is drawn in (font-sets.md § Locale → set). */\nexport const LOCALE_FONT_SET = {\n en: 'latin',\n zh: 'zh-Hans',\n ja: 'ja',\n ko: 'ko',\n de: 'latin',\n fr: 'latin',\n es: 'latin',\n it: 'latin',\n nl: 'latin',\n pl: 'latin',\n pt: 'latin',\n 'zh-tw': 'zh-Hant',\n} as const satisfies Record<FontSetLanguage, FontSetId>;\n\n/** Narrowing guard: is an arbitrary value one of the eleven locales? */\nexport function isFontSetLocale(value: unknown): value is FontSetLocale {\n return typeof value === 'string' && (FONT_SET_LOCALES as readonly string[]).includes(value);\n}\n\n/** Narrowing guard: is an arbitrary value an app locale or an extra language? */\nexport function isFontSetLanguage(value: unknown): value is FontSetLanguage {\n return (\n isFontSetLocale(value) ||\n (typeof value === 'string' && (FONT_SET_EXTRA_LOCALES as readonly string[]).includes(value))\n );\n}\n\n/**\n * The set for an `<html lang>` value. Tries the whole tag first, case-insensitively\n * and reading `_` as `-` (`zh-TW` / `zh_tw` → zh-Hant), then the primary subtag\n * (`ja-JP` → ja, `PT-br` → pt, `zh-Hans` → zh). No script or region heuristics:\n * `zh-Hant` and `zh-HK` reach zh-Hant only through a page whose `lang` is `zh-TW`.\n *\n * The fallback is explicit: an empty or unknown tag returns the Latin set,\n * because the app renders an unknown language in English. It never falls back\n * to \"the nearest CJK set\". Use `isFontSetLocale` on the primary subtag to tell\n * a fallback from a match.\n */\nexport function resolveFontSet(lang: string): FontSet {\n const tag = lang.trim().toLowerCase().replace(/_/g, '-');\n if (isFontSetLanguage(tag)) return FONT_SET_REGISTRY[LOCALE_FONT_SET[tag]];\n const primary = tag.split('-')[0] ?? '';\n const id = isFontSetLocale(primary) ? LOCALE_FONT_SET[primary] : DEFAULT_FONT_SET_ID;\n return FONT_SET_REGISTRY[id];\n}\n\n/**\n * A member of one role of one set, by id. A missing or unknown id (a stored\n * pick naming a member that has since been removed) returns that role's\n * default, never another set's face.\n */\nexport function resolveFontSetMember(\n setId: FontSetId,\n role: FontSetRole,\n memberId?: string,\n): FontSetMember {\n const r: FontSetRoleMembers = FONT_SET_REGISTRY[setId].roles[role];\n const named = memberId === undefined ? undefined : r.members.find((m) => m.id === memberId);\n return named ?? r.members.find((m) => m.id === r.default)!;\n}\n\n/**\n * The CSS custom property the app's `next/font` loader assigns to a member's\n * face and `theme.css` reads: `--font-set-<set>-<role>-<member>`. This name is\n * the contract between the registry, the loader and the stylesheet, so it is\n * built here and nowhere else.\n *\n * A member that re-uses another role's face (`sameFaceAs`) returns THAT role's\n * variable, so the face is declared once. The `type-pairing` member, and an id\n * the role does not have, return `null`.\n */\nexport function fontSetMemberVar(\n setId: FontSetId,\n role: FontSetRole,\n memberId: string,\n): string | null {\n const member = FONT_SET_REGISTRY[setId].roles[role].members.find(\n (m: FontSetMember) => m.id === memberId,\n );\n if (!member || member.source.kind === 'type-pairing') return null;\n const faceRole = member.sameFaceAs ?? role;\n return `--font-set-${setId}-${faceRole}-${member.id}`;\n}\n\n/** The `<html>` attribute that names a picked member for one role. */\nexport type FontSetPickAttribute = `data-font-set-${FontSetRole}`;\n\n/**\n * The `data-font-set-<role>` attributes a person's pick for `locale` puts on\n * `<html>` (Story MOTIR-7736 · MOTIR-7897). One attribute per role of the\n * locale's set that has a member with this id, unless that member is the\n * `type-pairing` placeholder or the role's default: `theme.css` carries a rule\n * only for a non-default member, so a default needs no attribute.\n *\n * `null`, an unknown id, a default id and every Latin locale return `{}`. This\n * is the ONE derivation the client (`setFontPick`) and the server-rendered\n * first byte share, so a pick sets identical attributes on both sides — which\n * is why it lives here, free of any `'use client'` boundary.\n */\nexport function fontSetPickAttributes(\n locale: FontSetLanguage,\n memberId: string | null,\n): Partial<Record<FontSetPickAttribute, string>> {\n if (memberId === null) return {};\n const set: FontSet = FONT_SET_REGISTRY[LOCALE_FONT_SET[locale]];\n const attrs: Partial<Record<FontSetPickAttribute, string>> = {};\n for (const role of FONT_SET_ROLES) {\n const r: FontSetRoleMembers = set.roles[role];\n if (memberId === r.default) continue;\n const member = r.members.find((m: FontSetMember) => m.id === memberId);\n if (member && member.source.kind !== 'type-pairing') attrs[`data-font-set-${role}`] = memberId;\n }\n return attrs;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from 'react';\nimport {\n migrateStoredPaletteId,\n PALETTE_IDS_VERSION,\n THEME_DEFAULTS,\n THEME_STORAGE_KEYS,\n type ResolvedThemePattern,\n type ThemePattern,\n} from '../theme/types';\nimport { DEFAULT_STYLE_ID, isStyleId, resolveStyle, type StyleId } from '../theme/styles';\nimport { DEFAULT_PALETTE_ID, isPaletteId, type PaletteId } from '../theme/palettes';\nimport { isTypeId, type TypeId } from '../theme/typography';\nimport {\n FONT_SET_ROLES,\n fontSetPickAttributes,\n isFontSetLocale,\n type FontSetLocale,\n} from '../theme/fontSets';\nimport type { AppearancePreferenceDto, AppliedAppearanceDto } from '../appearance';\n\n/**\n * Theme context for Motir's three-axis design system (style · palette · type),\n * plus the light/dark pattern.\n *\n * `pattern` is what the user picked (system | light | dark).\n * `resolvedPattern` is what's currently applied (light | dark) — these\n * differ when pattern='system' and the OS preference resolves to one or\n * the other.\n *\n * State is persisted to localStorage and re-applied to <html>'s\n * data-attributes on every change. The init script in app/layout.tsx\n * does the same work BEFORE React hydrates to avoid FOUC; this provider\n * keeps the attributes in sync after hydration.\n *\n * Implementation note: this uses lazy `useState` initializers and\n * `useSyncExternalStore` rather than `useEffect`+`setState`, per the\n * React 19 / react-hooks/set-state-in-effect rule. Setting state inside\n * effects causes cascading renders; subscribing to external systems\n * (localStorage, matchMedia) via the right primitives avoids that.\n *\n * The per-language font pick (Story MOTIR-7736 · MOTIR-7897) is a fifth,\n * signed-in-only axis with a different shape. `setFontPick(locale, member)`\n * rewrites `<html>`'s `data-font-set-*` attributes in the event handler, and\n * only when `locale` is the page's own language, so a pick for another language\n * touches no DOM. It keeps no localStorage copy and runs no state effect: the\n * server renders the stored pick on the first byte, and an effect seeded from\n * state would duplicate or clear that on mount. `fontPicks` holds only this\n * session's picks; the stored baseline is the pane's to read. The pick rides\n * the same debounced, seq-guarded PATCH and quiet `syncState` as the other axes.\n */\ninterface ThemeContextValue {\n pattern: ThemePattern;\n resolvedPattern: ResolvedThemePattern;\n /** The active named style (`data-style`) — Axis 2, independent of palette. */\n styleId: StyleId;\n /** The active named palette (`data-palette`) — Axis 1 (colour), independent of style. */\n palette: PaletteId;\n /**\n * The EFFECTIVE type pairing (`data-type`) — Axis 3. Either the user's pinned\n * choice, or (when unpinned) the active style's `defaultTypeId`. Independent\n * of palette + style.\n */\n type: TypeId;\n setPattern: (pattern: ThemePattern) => void;\n setStyleId: (styleId: StyleId) => void;\n setPalette: (palette: PaletteId) => void;\n /** Pin an explicit type pairing (overrides the style's default until cleared). */\n setType: (type: TypeId) => void;\n /**\n * The per-language font picks made in THIS session (MOTIR-7897): a member id,\n * or `null` for a pick cleared back to Automatic. A locale absent from it is\n * unchanged from what the server rendered.\n */\n fontPicks: Partial<Record<FontSetLocale, string | null>>;\n /**\n * Pick a font-set member for one language, or `null` for Automatic. A no-op\n * for an anonymous visitor. Re-types the page at once only when `locale` is\n * the page's own language.\n */\n setFontPick: (locale: FontSetLocale, memberId: string | null) => void;\n /**\n * Cross-device sync status (Subtask 7.3.62). `'idle'` while nothing has\n * failed; `'error'` when the LAST attempt to persist a change to the account\n * failed (a 401 / write failure) — the local switch still applied (it's in\n * localStorage), so this only drives a quiet \"couldn't sync\" affordance. Always\n * `'idle'` for an anonymous visitor, who never syncs. Clears back to `'idle'`\n * on the next successful sync.\n */\n syncState: AppearanceSyncState;\n}\n\n/** The cross-device sync status surfaced to the Appearance pane (7.3.62). */\nexport type AppearanceSyncState = 'idle' | 'error';\n\n/** The axes a single PATCH carries — the setters only ever send valid ids. */\ntype AppearanceSyncPatch = Partial<\n Record<'pattern' | 'styleId' | 'paletteId' | 'typeId', string>\n> & {\n /** Per-locale font picks; `null` clears one. Merged per locale, never replaced. */\n fontPicks?: Partial<Record<FontSetLocale, string | null>>;\n};\n\n/** The page's language as a font-set locale, parsed as `resolveFontSet` does. */\nfunction pageFontSetLocale(): FontSetLocale | null {\n if (typeof document === 'undefined') return null;\n const primary = document.documentElement.lang.trim().split(/[-_]/)[0]?.toLowerCase() ?? '';\n return isFontSetLocale(primary) ? primary : null;\n}\n\n/** Rewrite `<html>`'s `data-font-set-*` attributes for a pick on the page's language. */\nfunction applyPageFontPick(locale: FontSetLocale, memberId: string | null): void {\n const root = document.documentElement;\n for (const role of FONT_SET_ROLES) root.removeAttribute(`data-font-set-${role}`);\n for (const [attr, value] of Object.entries(fontSetPickAttributes(locale, memberId))) {\n root.setAttribute(attr, value);\n }\n}\n\n/** How long rapid toggles of the SAME axis coalesce before one write fires. */\nconst SYNC_DEBOUNCE_MS = 250;\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\n/** Lazily read a localStorage key. Returns null on SSR or if the key isn't set. */\nfunction readStorage<T extends string>(key: string, fallback: T): T {\n if (typeof window === 'undefined') return fallback;\n try {\n return (window.localStorage.getItem(key) as T | null) ?? fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * `useSyncExternalStore` subscription to the prefers-color-scheme media\n * query. Returns 'dark' or 'light' based on the OS preference.\n *\n * Server snapshot returns 'light' (a stable default — the FOUC init\n * script will apply the real value before hydration completes).\n */\nfunction subscribeColorScheme(callback: () => void): () => void {\n if (typeof window === 'undefined') return () => {};\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n}\n\nfunction getColorSchemeSnapshot(): ResolvedThemePattern {\n if (typeof window === 'undefined') return 'light';\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nfunction getColorSchemeServerSnapshot(): ResolvedThemePattern {\n return 'light';\n}\n\nexport function ThemeProvider({\n children,\n initialPreference = null,\n signedIn = false,\n}: {\n children: ReactNode;\n /**\n * The signed-in user's APPLIED appearance, resolved server-side (Subtask\n * 7.3.61). When present it SEEDS this island's state — the page-state\n * contract: a client island must be GIVEN the server value, not left to\n * re-read localStorage (which `useState` would do only at mount, ignoring the\n * authoritative server pref and risking a flash from a stale value synced on\n * another device). `null` (anonymous) keeps the original localStorage path.\n */\n initialPreference?: AppliedAppearanceDto | null;\n /**\n * Whether a user is signed in (Subtask 7.3.62). Only then does an axis change\n * also PERSIST to the account via `/api/appearance-preference` (cross-device\n * sync). Anonymous visitors keep the localStorage-only path and never fire a\n * write — so they never hit a 401 or see a \"couldn't sync\" affordance. This is\n * a SEPARATE signal from `initialPreference`: a signed-in user who has pinned\n * nothing yet has `initialPreference === null` but must still sync their first\n * pick (a row is created on the first PATCH).\n */\n signedIn?: boolean;\n}) {\n // Lazy initializers — run once on first render. Seed from the server\n // preference when signed in (authoritative, cross-device); otherwise read\n // localStorage synchronously without an effect.\n const [pattern, setPatternState] = useState<ThemePattern>(() =>\n initialPreference\n ? initialPreference.pattern\n : readStorage<ThemePattern>(THEME_STORAGE_KEYS.pattern, THEME_DEFAULTS.pattern),\n );\n const [styleId, setStyleIdState] = useState<StyleId>(() => {\n if (initialPreference) {\n return isStyleId(initialPreference.styleId) ? initialPreference.styleId : DEFAULT_STYLE_ID;\n }\n // A stale / unknown stored value (e.g. a pre-7.3.32 `default`/`soft`\n // display-style leftover) resolves to the default, never a dead id.\n const stored = readStorage<string>(THEME_STORAGE_KEYS.style, THEME_DEFAULTS.style);\n return isStyleId(stored) ? stored : DEFAULT_STYLE_ID;\n });\n const [palette, setPaletteState] = useState<PaletteId>(() => {\n if (initialPreference) {\n return isPaletteId(initialPreference.paletteId)\n ? initialPreference.paletteId\n : DEFAULT_PALETTE_ID;\n }\n // A value stored before MOTIR-6471's rename is read through the migration\n // (a no-op once the init script has run and written the marker), and a\n // stale / unknown one resolves to the default, never a dead id.\n const stored = migrateStoredPaletteId(\n readStorage<string>(THEME_STORAGE_KEYS.palette, '') || null,\n readStorage<string>(THEME_STORAGE_KEYS.paletteIds, '') || null,\n );\n return isPaletteId(stored) ? stored : DEFAULT_PALETTE_ID;\n });\n // The user's PINNED type choice, or `null` = \"follow the active style's\n // default pairing\". Only a valid stored id pins; anything else (unset/stale)\n // falls through to the style default below.\n const [typeChoice, setTypeChoiceState] = useState<TypeId | null>(() => {\n if (initialPreference) {\n // The server pref carries the EFFECTIVE type + whether it was pinned: an\n // unpinned user must stay `null` so they keep following the style default.\n return initialPreference.typePinned && isTypeId(initialPreference.typeId)\n ? initialPreference.typeId\n : null;\n }\n const stored = readStorage<string>(THEME_STORAGE_KEYS.type, '');\n return isTypeId(stored) ? stored : null;\n });\n\n // Subscribe to OS color-scheme changes. Only consulted when pattern='system'.\n const osColorScheme = useSyncExternalStore(\n subscribeColorScheme,\n getColorSchemeSnapshot,\n getColorSchemeServerSnapshot,\n );\n\n const resolvedPattern: ResolvedThemePattern = pattern === 'system' ? osColorScheme : pattern;\n\n // Effective type (Axis 3): a pinned choice wins; otherwise FOLLOW the active\n // style's default pairing — so switching style (while unpinned) re-points type\n // to that style's curated default, and an explicit pick sticks across styles.\n const type: TypeId = typeChoice ?? resolveStyle(styleId).defaultTypeId;\n\n // Sync data-theme to <html>. This IS an effect (synchronizing with an\n // external system — the DOM), but the effect body only writes to the DOM,\n // not back to React state. That's the correct effect shape.\n useEffect(() => {\n document.documentElement.setAttribute('data-theme', resolvedPattern);\n }, [resolvedPattern]);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-style', styleId);\n }, [styleId]);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-palette', palette);\n }, [palette]);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-type', type);\n }, [type]);\n\n // ── Cross-device sync (Subtask 7.3.62) ────────────────────────────────────\n // A signed-in user's axis change is OPTIMISTIC: the setter flips the live UI +\n // localStorage instantly (below), then queues a debounced PATCH to\n // `/api/appearance-preference`. The write is reconciled from its 200 body, and\n // a failure (401 / network / server) degrades quietly — the local switch is\n // never lost (localStorage holds it) — surfacing `syncState: 'error'` for the\n // pane's affordance. Anonymous visitors never sync (the localStorage-only path).\n const [syncState, setSyncState] = useState<AppearanceSyncState>('idle');\n // Monotonic per-flush counter — the seq-guard that stops an older response from\n // clobbering a newer choice (the WatchControl `fetchSeq` idiom; the\n // E2E-authoritative-signal contract). Only the latest flush may reconcile / set\n // the affordance.\n const syncSeqRef = useRef(0);\n // Axes changed since the last flush, coalesced so rapid toggles send one write.\n const pendingPatchRef = useRef<AppearanceSyncPatch>({});\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The per-language picks made this session (MOTIR-7897), and the page-locale\n // member this provider last wrote onto <html> (`undefined` = never wrote), so\n // a reconcile re-applies only a value the server actually changed.\n const [fontPicks, setFontPicksState] = useState<Partial<Record<FontSetLocale, string | null>>>(\n {},\n );\n const appliedPageFontPickRef = useRef<string | null | undefined>(undefined);\n\n // Reconcile ONLY the axes this flush actually sent, from the resolved 200 body\n // — never the untouched axes (the per-axis DTO resolves an unpinned `typeId` to\n // the GLOBAL default, which would wrongly pin a follow-the-style user if applied\n // off a different axis's write). The raw state setters skip the localStorage /\n // re-PATCH side effects; for the valid ids the setters send, this is a no-op\n // confirmation, but it adopts any value the server normalised.\n const reconcile = useCallback((sent: AppearanceSyncPatch, resolved: AppearancePreferenceDto) => {\n if ('pattern' in sent) setPatternState(resolved.pattern);\n if ('styleId' in sent && isStyleId(resolved.styleId)) setStyleIdState(resolved.styleId);\n if ('paletteId' in sent && isPaletteId(resolved.paletteId)) setPaletteState(resolved.paletteId);\n if ('typeId' in sent && isTypeId(resolved.typeId)) setTypeChoiceState(resolved.typeId);\n if (sent.fontPicks) {\n const pageLocale = pageFontSetLocale();\n const adopted: Partial<Record<FontSetLocale, string | null>> = {};\n for (const locale of Object.keys(sent.fontPicks) as FontSetLocale[]) {\n const value = resolved.fontPicks?.[locale] ?? null;\n adopted[locale] = value;\n if (locale === pageLocale && value !== appliedPageFontPickRef.current) {\n applyPageFontPick(locale, value);\n appliedPageFontPickRef.current = value;\n }\n }\n setFontPicksState((prev) => ({ ...prev, ...adopted }));\n }\n }, []);\n\n const flushSync = useCallback(() => {\n debounceRef.current = null;\n const patch = pendingPatchRef.current;\n pendingPatchRef.current = {};\n if (Object.keys(patch).length === 0) return;\n\n const seq = ++syncSeqRef.current;\n fetch('/api/appearance-preference', {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(patch),\n })\n .then(async (res) => {\n if (!res.ok) throw new Error(`appearance sync failed: ${res.status}`);\n const body = (await res.json()) as { preference: AppearancePreferenceDto };\n // Drop a response superseded by a newer flush — it must not clobber the\n // newer choice nor flip the affordance.\n if (seq !== syncSeqRef.current) return;\n reconcile(patch, body.preference);\n setSyncState('idle');\n })\n .catch(() => {\n if (seq !== syncSeqRef.current) return;\n // Keep the optimistic local value (localStorage holds it); surface the\n // quiet \"couldn't sync\" affordance without blocking the live switch.\n setSyncState('error');\n });\n }, [reconcile]);\n\n // Merge an axis change into the pending patch and (re)arm the debounce. A\n // no-op for anonymous visitors — they never write to the account.\n const queueSync = useCallback(\n (patch: AppearanceSyncPatch) => {\n if (!signedIn) return;\n const prev = pendingPatchRef.current;\n const next: AppearanceSyncPatch = { ...prev, ...patch };\n // Font picks merge PER LOCALE: a ja pick and a ko pick inside one window\n // must both reach the one PATCH, where a shallow spread keeps only the last.\n if (prev.fontPicks || patch.fontPicks) {\n next.fontPicks = { ...prev.fontPicks, ...patch.fontPicks };\n }\n pendingPatchRef.current = next;\n if (debounceRef.current) clearTimeout(debounceRef.current);\n debounceRef.current = setTimeout(flushSync, SYNC_DEBOUNCE_MS);\n },\n [signedIn, flushSync],\n );\n\n // Flush any pending write on unmount so a just-made choice isn't dropped.\n useEffect(() => {\n return () => {\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n flushSync();\n }\n };\n }, [flushSync]);\n\n const setPattern = useCallback(\n (next: ThemePattern) => {\n setPatternState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.pattern, next);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ pattern: next });\n },\n [queueSync],\n );\n\n const setStyleId = useCallback(\n (next: StyleId) => {\n setStyleIdState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.style, next);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ styleId: next });\n },\n [queueSync],\n );\n\n const setPalette = useCallback(\n (next: PaletteId) => {\n setPaletteState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.palette, next);\n // A value written now is already in today's id space — stamp it, or a\n // load that never ran the init script would re-migrate a fresh `motir`.\n window.localStorage.setItem(THEME_STORAGE_KEYS.paletteIds, PALETTE_IDS_VERSION);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ paletteId: next });\n },\n [queueSync],\n );\n\n const setType = useCallback(\n (next: TypeId) => {\n setTypeChoiceState(next);\n try {\n window.localStorage.setItem(THEME_STORAGE_KEYS.type, next);\n } catch {\n // localStorage unavailable — accept that the choice won't persist.\n }\n queueSync({ typeId: next });\n },\n [queueSync],\n );\n\n const setFontPick = useCallback(\n (locale: FontSetLocale, memberId: string | null) => {\n // Signed-out pages keep the defaults: no device-local copy, no write.\n if (!signedIn) return;\n setFontPicksState((prev) => ({ ...prev, [locale]: memberId }));\n if (locale === pageFontSetLocale()) {\n applyPageFontPick(locale, memberId);\n appliedPageFontPickRef.current = memberId;\n }\n queueSync({ fontPicks: { [locale]: memberId } });\n },\n [signedIn, queueSync],\n );\n\n const value = useMemo<ThemeContextValue>(\n () => ({\n pattern,\n resolvedPattern,\n styleId,\n palette,\n type,\n setPattern,\n setStyleId,\n setPalette,\n setType,\n fontPicks,\n setFontPick,\n syncState,\n }),\n [\n pattern,\n resolvedPattern,\n styleId,\n palette,\n type,\n setPattern,\n setStyleId,\n setPalette,\n setType,\n fontPicks,\n setFontPick,\n syncState,\n ],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) {\n throw new Error('useTheme must be used inside <ThemeProvider>');\n }\n return ctx;\n}\n\n/**\n * Non-throwing variant of {@link useTheme}. Returns `null` when no\n * `<ThemeProvider>` is mounted instead of throwing.\n *\n * Use this in low-level primitives that must also render outside the authed\n * shell — e.g. the `MarkdownEditor` (Subtask 2.3.5) reads `resolvedPattern`\n * to drive the underlying editor's `data-color-mode`, but it's also exercised\n * on the public `/tokens` specimen and in component tests where wrapping every\n * render in a provider would be noise. Callers fall back to a sensible default\n * (`'light'`).\n */\nexport function useOptionalTheme(): ThemeContextValue | null {\n return useContext(ThemeContext);\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export { FormField, FormFieldProps, describedById } from './components/ui/FormFi
|
|
|
23
23
|
export { Modal, ModalProps } from './components/ui/Modal.js';
|
|
24
24
|
export { Pill, PillProps } from './components/ui/Pill.js';
|
|
25
25
|
export { Popover, PopoverContentProps, PopoverProps } from './components/ui/Popover.js';
|
|
26
|
+
export { DatePicker, DatePickerProps } from './components/ui/DatePicker.js';
|
|
26
27
|
export { Combobox, ComboboxOption, ComboboxProps } from './components/ui/Combobox.js';
|
|
27
28
|
export { SectionLabel, SectionLabelProps } from './components/ui/SectionLabel.js';
|
|
28
29
|
export { Segmented, SegmentedOption } from './components/ui/Segmented.js';
|
|
@@ -34,6 +35,8 @@ export { EmptyState, EmptyStateProps } from './components/ui/EmptyState.js';
|
|
|
34
35
|
export { ErrorState, ErrorStateProps } from './components/ui/ErrorState.js';
|
|
35
36
|
export { ChipSkeleton, MultiSelectOption, MultiSelectPicker, MultiSelectPickerProps, MultiSelectTint, ValueChip } from './components/ui/MultiSelectPicker.js';
|
|
36
37
|
export { ColorSwatchOption, ColorSwatchPicker, ColorSwatchPickerProps, STATUS_COLOR_SWATCHES } from './components/ui/ColorSwatchPicker.js';
|
|
38
|
+
export { DEFAULT_DATE_PICKER_LOCALE, DatePickerFormatInputs, DatePickerFormatter, DatePickerLocale, ResolvedDatePickerFormat, defaultDatePickerFormatter, resolveDatePickerFormat } from './components/ui/datePickerFormat.js';
|
|
39
|
+
export { DatePickerFormatProvider, DatePickerFormatProviderProps, useDatePickerFormat } from './components/ui/DatePickerFormatContext.js';
|
|
37
40
|
export { TokensSpecimen, TokensSpecimenProps } from './specimen/TokensSpecimen.js';
|
|
38
41
|
import 'clsx';
|
|
39
42
|
import 'react';
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,7 @@ export * from './components/ui/FormField.js';
|
|
|
57
57
|
export * from './components/ui/Modal.js';
|
|
58
58
|
export * from './components/ui/Pill.js';
|
|
59
59
|
export * from './components/ui/Popover.js';
|
|
60
|
+
export * from './components/ui/DatePicker.js';
|
|
60
61
|
export * from './components/ui/Combobox.js';
|
|
61
62
|
export * from './components/ui/SectionLabel.js';
|
|
62
63
|
export * from './components/ui/Segmented.js';
|
|
@@ -68,6 +69,10 @@ export * from './components/ui/EmptyState.js';
|
|
|
68
69
|
export * from './components/ui/ErrorState.js';
|
|
69
70
|
export * from './components/ui/MultiSelectPicker.js';
|
|
70
71
|
export * from './components/ui/ColorSwatchPicker.js';
|
|
72
|
+
// The DatePicker's date-format injection: the pure types/default/resolver
|
|
73
|
+
// (server-safe) and the client provider + hook, each its own chunk.
|
|
74
|
+
export * from './components/ui/datePickerFormat.js';
|
|
75
|
+
export * from './components/ui/DatePickerFormatContext.js';
|
|
71
76
|
|
|
72
77
|
// ── The isolation specimen (a live /tokens-style render) ─────────────────────
|
|
73
78
|
export * from './specimen/TokensSpecimen.js';
|