@motir/design-system 0.8.2 → 0.10.1

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.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The element currently in NATIVE full screen (`document.fullscreenElement`), or
3
+ * `null` — re-read on every `fullscreenchange`.
4
+ *
5
+ * While an element is in native full screen the browser paints ONLY that
6
+ * element's subtree, in the top layer. Anything portalled to `document.body` —
7
+ * a dialog, a popover, a picker's menu — is not shown at all, whatever its
8
+ * z-index (MOTIR-7658: the roadmap canvas's quick view opened invisibly and
9
+ * held focus and scroll lock until Esc). So every portalling primitive in this
10
+ * package mounts into this element while there is one, and into
11
+ * `document.body` otherwise — exactly as before.
12
+ *
13
+ * `null` on the server and in a document with no Fullscreen API.
14
+ */
15
+ declare function useFullscreenElement(): Element | null;
16
+
17
+ export { useFullscreenElement };
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { useSyncExternalStore } from 'react';
3
+
4
+ function useFullscreenElement() {
5
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
6
+ }
7
+ function subscribe(onChange) {
8
+ document.addEventListener("fullscreenchange", onChange);
9
+ return () => document.removeEventListener("fullscreenchange", onChange);
10
+ }
11
+ function getSnapshot() {
12
+ return document.fullscreenElement ?? null;
13
+ }
14
+ function getServerSnapshot() {
15
+ return null;
16
+ }
17
+
18
+ export { useFullscreenElement };
19
+ //# sourceMappingURL=fullscreen.js.map
20
+ //# sourceMappingURL=fullscreen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/fullscreen.ts"],"names":[],"mappings":";;AAkBO,SAAS,oBAAA,GAAuC;AACrD,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,iBAAiB,CAAA;AACvE;AAEA,SAAS,UAAU,QAAA,EAAkC;AACnD,EAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,QAAQ,CAAA;AACtD,EAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,kBAAA,EAAoB,QAAQ,CAAA;AACxE;AAEA,SAAS,WAAA,GAA8B;AACrC,EAAA,OAAO,SAAS,iBAAA,IAAqB,IAAA;AACvC;AAEA,SAAS,iBAAA,GAA0B;AACjC,EAAA,OAAO,IAAA;AACT","file":"fullscreen.js","sourcesContent":["'use client';\n\nimport { useSyncExternalStore } from 'react';\n\n/**\n * The element currently in NATIVE full screen (`document.fullscreenElement`), or\n * `null` — re-read on every `fullscreenchange`.\n *\n * While an element is in native full screen the browser paints ONLY that\n * element's subtree, in the top layer. Anything portalled to `document.body` —\n * a dialog, a popover, a picker's menu — is not shown at all, whatever its\n * z-index (MOTIR-7658: the roadmap canvas's quick view opened invisibly and\n * held focus and scroll lock until Esc). So every portalling primitive in this\n * package mounts into this element while there is one, and into\n * `document.body` otherwise — exactly as before.\n *\n * `null` on the server and in a document with no Fullscreen API.\n */\nexport function useFullscreenElement(): Element | null {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\nfunction subscribe(onChange: () => void): () => void {\n document.addEventListener('fullscreenchange', onChange);\n return () => document.removeEventListener('fullscreenchange', onChange);\n}\n\nfunction getSnapshot(): Element | null {\n return document.fullscreenElement ?? null;\n}\n\nfunction getServerSnapshot(): null {\n return null;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motir/design-system",
3
- "version": "0.8.2",
3
+ "version": "0.10.1",
4
4
  "description": "Motir's 3-axis design system (colour · style · type): distributable token CSS, the axis registries, the theme-apply API, and framework-agnostic React primitives. Shared by motir-core and planner-scaffolded products.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
@@ -59,9 +59,11 @@
59
59
  }
60
60
  },
61
61
  "devDependencies": {
62
+ "@testing-library/react": "^16.3.2",
62
63
  "@types/node": "^22.19.19",
63
64
  "@types/react": "^19",
64
65
  "@types/react-dom": "^19",
66
+ "happy-dom": "^20.9.0",
65
67
  "tsup": "^8.5.0",
66
68
  "typescript": "^5",
67
69
  "vitest": "^4.1.7"
package/theme.css CHANGED
@@ -1984,7 +1984,24 @@
1984
1984
  dividers inside a wavy card): the full-box `.border`, and the single-side
1985
1985
  dividers `.border-b` / `.border-t` (table rows, section rules) — the latter
1986
1986
  scoped INSIDE `[data-surface]` panels to bound the count to real surfaces (and
1987
- their rows are virtualised, so only ~viewport-many overlays ever render). */
1987
+ their rows are virtualised, so only ~viewport-many overlays ever render).
1988
+
1989
+ ⚠️ THE INK IS AN `outline`, NOT A `border`, AND THE OVERLAY BOX NEVER LEAVES ITS
1990
+ HOST'S PADDING BOX (MOTIR-7693). The line has to straddle the host's padding
1991
+ edge — 1px out, 1px in — to sit on the real border it roughens. Drawn as a
1992
+ 2px border on an overlay at `inset: -1px`, that put the overlay's BOX one
1993
+ pixel past its host on every side, and when the host is also a scroll
1994
+ container (`.border` + `overflow-*-auto`: framed tables, pickers, code blocks)
1995
+ its own `::after` is part of its scrollable overflow — `scrollWidth` /
1996
+ `scrollHeight` one past the client box, so the browser drew a vertical AND a
1997
+ horizontal scroll bar on content that fits. An outline is ink, not layout: it
1998
+ never counts toward scrollable overflow. So the overlay box sits at `inset: 0`
1999
+ and `outline-offset: -1px` puts the 2px line back on exactly the same pixels.
2000
+ The dividers are the same move — a 0-height box ON the edge, a 1px outline
2001
+ either side of it (the line it replaces was 2px straddling the edge), and
2002
+ `left`/`right: 1px` to cancel the outline's run past the box's ends; their
2003
+ `bottom: -1px` overflowed a scroller whose last row drops its real border.
2004
+ tests/e2e/hand-drawn-scroll-frame.spec.ts measures it in Chromium. */
1988
2005
  @scope ([data-style='hand-drawn-indie']) to ([data-style]) {
1989
2006
  .border {
1990
2007
  border-width: 2px;
@@ -1999,9 +2016,10 @@
1999
2016
  .border::after {
2000
2017
  content: '';
2001
2018
  position: absolute;
2002
- inset: -1px;
2019
+ inset: 0;
2003
2020
  pointer-events: none;
2004
- border: 2px solid var(--el-border-strong);
2021
+ outline: 2px solid var(--el-border-strong);
2022
+ outline-offset: -1px;
2005
2023
  border-radius: inherit;
2006
2024
  filter: url(#hd-rough);
2007
2025
  }
@@ -2020,12 +2038,12 @@
2020
2038
  [data-surface] .border-b:not(.border)::after {
2021
2039
  content: '';
2022
2040
  position: absolute;
2023
- left: 0;
2024
- right: 0;
2025
- bottom: -1px;
2041
+ left: 1px;
2042
+ right: 1px;
2043
+ bottom: 0;
2026
2044
  height: 0;
2027
2045
  pointer-events: none;
2028
- border-bottom: 2px solid var(--el-border-strong);
2046
+ outline: 1px solid var(--el-border-strong);
2029
2047
  filter: url(#hd-rough);
2030
2048
  }
2031
2049
  }
@@ -2033,12 +2051,12 @@
2033
2051
  [data-surface] .border-t:not(.border)::after {
2034
2052
  content: '';
2035
2053
  position: absolute;
2036
- left: 0;
2037
- right: 0;
2038
- top: -1px;
2054
+ left: 1px;
2055
+ right: 1px;
2056
+ top: 0;
2039
2057
  height: 0;
2040
2058
  pointer-events: none;
2041
- border-top: 2px solid var(--el-border-strong);
2059
+ outline: 1px solid var(--el-border-strong);
2042
2060
  filter: url(#hd-rough);
2043
2061
  }
2044
2062
  }
@@ -2522,6 +2540,49 @@
2522
2540
  }
2523
2541
  }
2524
2542
 
2543
+ /* Showcase fields under the chrome (motir.co's illustrations). The card rule
2544
+ above repaints every card-radius panel with --el-surface, which turns a
2545
+ showcase field — the deep ground, the accent field — into a grey panel with
2546
+ its light text left on it. A showcase panel names its field with
2547
+ `data-showcase="<field>"`; it keeps that fill under the chrome, and the deep
2548
+ fields take a quieter streak and bevel so the light text keeps its contrast.
2549
+ Palette-derived like every material rule here: the fills are the showcase
2550
+ tokens, the sheen is the style's own achromatic white. */
2551
+ @scope ([data-style='retrofuturism']) to ([data-style]) {
2552
+ [data-showcase='field'] {
2553
+ background-color: var(--el-showcase-field);
2554
+ }
2555
+ [data-showcase='ground'] {
2556
+ background-color: var(--el-showcase-ground);
2557
+ }
2558
+ [data-showcase='ground-raised'] {
2559
+ background-color: var(--el-showcase-ground-raised);
2560
+ }
2561
+ [data-showcase='decision'] {
2562
+ background-color: var(--el-showcase-decision);
2563
+ }
2564
+ [data-showcase='record'] {
2565
+ background-color: var(--el-showcase-record);
2566
+ }
2567
+ [data-showcase='wash'] {
2568
+ background-color: var(--el-showcase-wash);
2569
+ }
2570
+ [data-showcase='wash-warm'] {
2571
+ background-color: var(--el-showcase-wash-warm);
2572
+ }
2573
+ [data-showcase='paper'] {
2574
+ background-color: var(--el-showcase-paper);
2575
+ }
2576
+ [data-showcase='field'],
2577
+ [data-showcase='ground'],
2578
+ [data-showcase='ground-raised'],
2579
+ [data-showcase='decision'],
2580
+ [data-showcase='record'] {
2581
+ --retro-spec: 12%;
2582
+ --retro-bevel-light: 10%;
2583
+ }
2584
+ }
2585
+
2525
2586
  /* Modal panel — the same chrome, with a deeper palette glow so the dialog
2526
2587
  separates cleanly from the grid behind it (and body text stays on the solid
2527
2588
  surface). Overrides the card-radius rule above for the modal radius. */
@@ -3860,6 +3921,91 @@
3860
3921
  --el-sidebar-item-bg-active: var(
3861
3922
  --color-background
3862
3923
  ); /* active row = canvas inset into the rail */
3924
+
3925
+ /* ── Product marks (motir.co's Products menu) ───────────────────────── */
3926
+ /* The small square beside each product's name. One per product, each a
3927
+ palette hue — or a mix of two, where the palette has fewer distinct hues
3928
+ than there are products — so a palette re-skins all of them. */
3929
+ --el-product-ai-planner: var(--color-primary);
3930
+ --el-product-project-management: var(--color-accent-teal);
3931
+ --el-product-project-manager: var(--color-accent);
3932
+ --el-product-ai-debugging: var(--color-destructive);
3933
+ --el-product-mcp: color-mix(in srgb, var(--color-primary) 55%, var(--color-destructive));
3934
+ --el-product-cli: var(--color-charcoal);
3935
+ --el-product-claude-code-connector: var(--color-accent-green);
3936
+ --el-product-claude-code-plugin: color-mix(
3937
+ in srgb,
3938
+ var(--color-accent) 55%,
3939
+ var(--color-accent-green)
3940
+ );
3941
+ --el-product-agent-fleet: color-mix(in srgb, var(--color-primary) 50%, var(--color-accent-teal));
3942
+ --el-product-agent-hosting: var(--color-slate);
3943
+ --el-product-sandbox: color-mix(in srgb, var(--color-accent) 45%, var(--color-charcoal));
3944
+
3945
+ /* ── Logo tile (the wave mark on its rounded square) ─────────────── */
3946
+ /* The mark is the palette's identity hue — its primary FILL, the colour its
3947
+ swatch shows — on a tile chosen so the mark reads: a pale wash of that hue
3948
+ here; a palette whose identity hue is itself bright or pale sets a dark
3949
+ tile in its own block. */
3950
+ --el-logo-mark: var(--color-primary-fill);
3951
+ --el-logo-tile: color-mix(in srgb, var(--color-primary-fill) 14%, var(--color-background));
3952
+
3953
+ /* ── Showcase (motir.co's illustrations) ─────────────────────── */
3954
+ /* The marketing site draws its pictures as flat fields holding small
3955
+ example screens ("Vibe the project" and "How Motir works"). These are
3956
+ the pictures' own element tokens, named for what they paint — the
3957
+ palette decides the colour. Each FIELD has a TEXT token that reads on it
3958
+ in every palette and theme.
3959
+ field — the main picture field (the plan, the progress lanes);
3960
+ field-ink is that colour as text on paper
3961
+ ground — the deep ground (the project-manager panel, the design and
3962
+ run pictures), with its raised step, rule and muted text
3963
+ highlight — the one bright touch, drawn on the ground and on the
3964
+ record field (the person's marks in the history)
3965
+ decision — a choice that waits on the person ("Your call")
3966
+ record — the history / learning field
3967
+ wash — the quiet pictures (docs, the written brief, memory)
3968
+ wash-warm — the quiet warm picture (the plan gate)
3969
+ paper — the small screens inside the fields
3970
+ wave-1..3 — the hero's moving wave lines */
3971
+ --el-showcase-field: var(--color-primary);
3972
+ --el-showcase-field-text: var(--color-background);
3973
+ --el-showcase-field-ink: var(--color-primary);
3974
+ --el-showcase-ground: var(--color-foreground);
3975
+ --el-showcase-ground-text: var(--color-background);
3976
+ --el-showcase-ground-muted: color-mix(
3977
+ in srgb,
3978
+ var(--color-background) 68%,
3979
+ var(--color-foreground)
3980
+ );
3981
+ --el-showcase-ground-raised: color-mix(
3982
+ in srgb,
3983
+ var(--color-foreground) 90%,
3984
+ var(--color-background)
3985
+ );
3986
+ --el-showcase-ground-rule: color-mix(
3987
+ in srgb,
3988
+ var(--color-foreground) 74%,
3989
+ var(--color-background)
3990
+ );
3991
+ --el-showcase-highlight: color-mix(in srgb, var(--color-accent) 55%, var(--color-background));
3992
+ --el-showcase-decision: color-mix(in srgb, var(--color-accent) 70%, var(--color-background));
3993
+ --el-showcase-decision-text: var(--color-foreground);
3994
+ --el-showcase-decision-ink: color-mix(in srgb, var(--color-accent) 65%, var(--color-foreground));
3995
+ --el-showcase-record: color-mix(in srgb, var(--color-accent-teal) 72%, var(--color-foreground));
3996
+ --el-showcase-record-text: var(--color-background);
3997
+ --el-showcase-wash: var(--color-tint-sky);
3998
+ --el-showcase-wash-warm: var(--color-tint-peach);
3999
+ --el-showcase-paper: var(--color-background);
4000
+ --el-showcase-paper-soft: var(--color-surface-soft);
4001
+ --el-showcase-rule: var(--color-hairline);
4002
+ --el-showcase-text: var(--color-foreground);
4003
+ --el-showcase-muted: var(--color-slate);
4004
+ /* The hero's wave lines: the three palette hues the moving colour band
4005
+ cycles through — the accent, the teal, the warm accent. */
4006
+ --el-showcase-wave-1: var(--color-primary);
4007
+ --el-showcase-wave-2: var(--color-accent-teal);
4008
+ --el-showcase-wave-3: var(--color-accent);
3863
4009
  }
3864
4010
 
3865
4011
  /* Dark-mode lift for the sidebar hover token. The other three --el-sidebar-*
@@ -3921,6 +4067,27 @@
3921
4067
  other concrete-hex --el-* alongside the sidebar hover. Light value (40%,
3922
4068
  == the prior bg-black/40) lives in the :root block above. */
3923
4069
  --el-overlay-scrim: #000000a6;
4070
+ /* The logo in dark: the palette's dark primary — the lightened identity hue
4071
+ built to read on a dark page — on a deep wash of it. */
4072
+ --el-logo-mark: var(--color-primary);
4073
+ --el-logo-tile: color-mix(in srgb, var(--color-primary) 18%, var(--color-background));
4074
+ /* Showcase in dark (see the Tier-3 showcase block): the field is the
4075
+ dark accent pulled halfway to the page, so it stays a saturated mid-tone
4076
+ carrying LIGHT text; the ground stays dark instead of inverting — the
4077
+ palette's recessed canvas, the one step below both page and surface; the decision and record fields carry the page's dark ink. */
4078
+ --el-showcase-field: color-mix(in srgb, var(--color-primary) 45%, var(--color-background));
4079
+ --el-showcase-field-text: var(--color-foreground);
4080
+ --el-showcase-field-ink: color-mix(in srgb, var(--color-primary) 75%, var(--color-foreground));
4081
+ --el-showcase-ground: var(--color-canvas);
4082
+ --el-showcase-ground-text: var(--color-foreground);
4083
+ --el-showcase-ground-muted: color-mix(in srgb, var(--color-foreground) 68%, var(--color-canvas));
4084
+ --el-showcase-ground-raised: color-mix(in srgb, var(--color-canvas) 86%, var(--color-foreground));
4085
+ --el-showcase-ground-rule: color-mix(in srgb, var(--color-canvas) 70%, var(--color-foreground));
4086
+ --el-showcase-highlight: color-mix(in srgb, var(--color-accent) 70%, var(--color-foreground));
4087
+ --el-showcase-decision: var(--color-accent);
4088
+ --el-showcase-decision-text: var(--color-background);
4089
+ --el-showcase-decision-ink: color-mix(in srgb, var(--color-accent) 80%, var(--color-foreground));
4090
+ --el-showcase-record: var(--color-accent-teal);
3924
4091
  }
3925
4092
 
3926
4093
  /* ═══════════════════════════════════════════════════════════════════════
@@ -4111,6 +4278,12 @@
4111
4278
  --el-status-in-review: var(--color-primary);
4112
4279
  --el-status-planning: var(--color-accent-teal);
4113
4280
  --el-type-design: var(--color-accent);
4281
+ /* 2026-10 redesign — the logo and showcase roles the motir and bright
4282
+ palettes override, restated so a scoped amethyst tile keeps the base. */
4283
+ --el-logo-mark: var(--color-primary-fill);
4284
+ --el-logo-tile: color-mix(in srgb, var(--color-primary-fill) 14%, var(--color-background));
4285
+ --el-showcase-highlight: color-mix(in srgb, var(--color-accent) 55%, var(--color-background));
4286
+ --el-showcase-decision: color-mix(in srgb, var(--color-accent) 70%, var(--color-background));
4114
4287
  }
4115
4288
 
4116
4289
  /* The base palette's DARK arm — only the tokens the global dark block
@@ -4148,6 +4321,12 @@
4148
4321
  --color-tint-sky: #1a2a3a;
4149
4322
  --color-tint-yellow: #3a341a;
4150
4323
  --el-sidebar-item-bg-hover: #222222;
4324
+ /* 2026-10 redesign — the dark logo and showcase roles, restated (see the
4325
+ light arm above). */
4326
+ --el-logo-mark: var(--color-primary);
4327
+ --el-logo-tile: color-mix(in srgb, var(--color-primary) 18%, var(--color-background));
4328
+ --el-showcase-highlight: color-mix(in srgb, var(--color-accent) 70%, var(--color-foreground));
4329
+ --el-showcase-decision: var(--color-accent);
4151
4330
  }
4152
4331
 
4153
4332
  /* The `motir` BASE type pairing, re-asserted (MOTIR-3933) — same reason as
@@ -4360,6 +4539,10 @@
4360
4539
  verified numerically + by rendered specimen, never eyeballed. See
4361
4540
  docs/palettes/motir.md for the colour-role reference. */
4362
4541
  [data-palette='motir'] {
4542
+ /* The logo, kept as Motir has it: the cool-blue primary (its fill is the
4543
+ ink CTA, not its identity hue) on a 28% wash of that blue. */
4544
+ --el-logo-mark: var(--color-primary);
4545
+ --el-logo-tile: color-mix(in srgb, var(--color-primary) 28%, var(--color-background));
4363
4546
  /* brand & primary — the high-contrast INK CTA + the single cool-blue accent.
4364
4547
  --color-primary is the accent AS text/icon/focus on a surface (the cool
4365
4548
  blue, AA on white & the slate surface); --color-primary-fill is the CTA
@@ -4470,6 +4653,15 @@
4470
4653
  every light surface. Set directly, as --el-sidebar-item-bg-hover is, since
4471
4654
  no Tier-0 source carries it. */
4472
4655
  --el-type-design: #746019;
4656
+ /* The showcase highlight — the one bright touch on motir.co's illustration
4657
+ ground. In Motir it is the palette's Sunglow yellow (the dark Design hue
4658
+ below), and the same in both themes because the showcase ground stays dark
4659
+ in both. Set directly, as --el-type-design is: Motir's yellow is a role
4660
+ value of this palette, not a Tier-0 source other palettes would carry. */
4661
+ --el-showcase-highlight: #ffd02f;
4662
+ /* Motir's decision field is its warm accent at full strength — dark ink on
4663
+ it is 4.9:1; the base mapping lightens the accent for darker palettes. */
4664
+ --el-showcase-decision: var(--color-accent);
4473
4665
  /* progress bars take the warm accent instead of the ink CTA fill */
4474
4666
  --el-progress-fill: var(--color-accent);
4475
4667
  /* the editor's focus border / rings stay the blue focus hue — the accent they
@@ -4483,6 +4675,8 @@
4483
4675
  brighten to clear AA on near-black. */
4484
4676
  [data-theme='dark'] [data-palette='motir']:not([data-theme]),
4485
4677
  [data-palette='motir'][data-theme='dark'] {
4678
+ --el-logo-mark: var(--color-primary); /* parity with the light block */
4679
+ --el-logo-tile: color-mix(in srgb, var(--color-primary) 28%, var(--color-background));
4486
4680
  --color-background: #0c0d0f; /* stark cool near-black canvas */
4487
4681
  --color-foreground: #edeef0;
4488
4682
  --color-surface: #18191c; /* cool slate section */
@@ -4561,6 +4755,8 @@
4561
4755
  palette's dark --color-primary, Miro Sunglow #ffd02f: ΔE2000 27.5 from its
4562
4756
  nearest glyph neighbour, 10.0:1 or better on every dark surface. */
4563
4757
  --el-type-design: #ffd02f;
4758
+ --el-showcase-highlight: #ffd02f; /* see the light block */
4759
+ --el-showcase-decision: var(--color-accent);
4564
4760
  --el-progress-fill: var(--color-accent);
4565
4761
  --el-editor-focus: var(--color-primary);
4566
4762
  }
@@ -5002,6 +5198,9 @@
5002
5198
  clears WCAG AA (≥4.5 text; ≥3.0 icon/UI) in BOTH themes, verified
5003
5199
  numerically. See docs/palettes/amber.md. */
5004
5200
  [data-palette='amber'] {
5201
+ /* The logo: this palette's identity hue is bright, so its mark sits on a
5202
+ dark tile (the palette's ink) rather than a pale wash of itself. */
5203
+ --el-logo-tile: var(--color-foreground);
5005
5204
  /* brand & primary — Binance Gold. The FILL carries DARK ink labels (yellow
5006
5205
  trap); --color-primary is a darkened gold used AS text/icon on a surface. */
5007
5206
  --color-primary: #7d6000; /* darkened gold as text/icon (5.92:1 on white — MOTIR-3774) */
@@ -5075,6 +5274,11 @@
5075
5274
  #0ECB81 / sell-red #F6465D. */
5076
5275
  [data-theme='dark'] [data-palette='amber']:not([data-theme]),
5077
5276
  [data-palette='amber'][data-theme='dark'] {
5277
+ --el-logo-tile: color-mix(
5278
+ in srgb,
5279
+ var(--color-primary) 18%,
5280
+ var(--color-background)
5281
+ ); /* parity with the light block */
5078
5282
  --color-background: #0b0e11; /* Binance deepest trading-floor black */
5079
5283
  --color-foreground: #eaecef;
5080
5284
  --color-surface: #1e2329; /* Binance "Shark" card surface */
@@ -5149,6 +5353,9 @@
5149
5353
  clears WCAG AA in BOTH themes, verified numerically. See
5150
5354
  docs/palettes/sienna.md. */
5151
5355
  [data-palette='sienna'] {
5356
+ /* The logo: this palette's identity hue is bright, so its mark sits on a
5357
+ dark tile (the palette's ink) rather than a pale wash of itself. */
5358
+ --el-logo-tile: var(--color-foreground);
5152
5359
  /* brand & primary — Mistral's flame. The FILL is Mistral Orange #fa520f with
5153
5360
  DARK ink labels; --color-primary is a darkened flame used AS text/icon. */
5154
5361
  --color-primary: #b53b09; /* darkened Mistral flame as text/icon (5.60:1 on the cream — MOTIR-3774) */
@@ -5231,6 +5438,11 @@
5231
5438
  to read as text, the fill a bright flame with dark-ink labels. */
5232
5439
  [data-theme='dark'] [data-palette='sienna']:not([data-theme]),
5233
5440
  [data-palette='sienna'][data-theme='dark'] {
5441
+ --el-logo-tile: color-mix(
5442
+ in srgb,
5443
+ var(--color-primary) 18%,
5444
+ var(--color-background)
5445
+ ); /* parity with the light block */
5234
5446
  --color-background: #15120d; /* warm near-black canvas (from Mistral Black) */
5235
5447
  --color-foreground: #f5efe2;
5236
5448
  --color-surface: #1f1d17;
@@ -5451,6 +5663,9 @@
5451
5663
  clears WCAG AA in BOTH themes, verified numerically. See
5452
5664
  docs/palettes/citrine.md. */
5453
5665
  [data-palette='citrine'] {
5666
+ /* The logo: this palette's identity hue is bright, so its mark sits on a
5667
+ dark tile (the palette's ink) rather than a pale wash of itself. */
5668
+ --el-logo-tile: var(--color-foreground);
5454
5669
  /* brand & primary — Miro Sunglow yellow (yellow-500). FILL carries DARK
5455
5670
  Stratos-ink labels (yellow trap); --color-primary is a documented dark
5456
5671
  yellow step (yellow-700) used AS text/icon on a surface. */
@@ -5549,6 +5764,11 @@
5549
5764
  clear AA on the dark canvas. All values are documented Mirotone steps. */
5550
5765
  [data-theme='dark'] [data-palette='citrine']:not([data-theme]),
5551
5766
  [data-palette='citrine'][data-theme='dark'] {
5767
+ --el-logo-tile: color-mix(
5768
+ in srgb,
5769
+ var(--color-primary) 18%,
5770
+ var(--color-background)
5771
+ ); /* parity with the light block */
5552
5772
  --color-background: #090909; /* Miro black */
5553
5773
  --color-foreground: #f6f7f9; /* gray-50 */
5554
5774
  --color-surface: #1a1b1e; /* gray-900 */
@@ -5639,6 +5859,9 @@
5639
5859
  clears WCAG AA in BOTH themes, verified numerically. See
5640
5860
  docs/palettes/candy.md. */
5641
5861
  [data-palette='candy'] {
5862
+ /* The logo: this palette's identity hue is bright, so its mark sits on a
5863
+ dark tile (the palette's ink) rather than a pale wash of itself. */
5864
+ --el-logo-tile: var(--color-foreground);
5642
5865
  /* brand & primary — a LIGHT bubblegum pink (Radix Pink). FILL is a light pink
5643
5866
  with DARK berry labels; --color-primary is the Pink-11 text step. */
5644
5867
  --color-primary: #b72682; /* Radix Pink-11 88% toward Pink-12 (5.81:1 on white — MOTIR-3774) */
@@ -5713,6 +5936,11 @@
5713
5936
  All values are documented Radix dark steps. */
5714
5937
  [data-theme='dark'] [data-palette='candy']:not([data-theme]),
5715
5938
  [data-palette='candy'][data-theme='dark'] {
5939
+ --el-logo-tile: color-mix(
5940
+ in srgb,
5941
+ var(--color-primary) 18%,
5942
+ var(--color-background)
5943
+ ); /* parity with the light block */
5716
5944
  --color-background: #191117; /* Radix Pink dark-1 — pink-tinted black */
5717
5945
  --color-foreground: #eeeef0; /* Mauve dark-12 */
5718
5946
  --color-surface: #37172f; /* Pink dark-3 — candy-plum section */